").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cc=a.document.documentElement;function dc(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dc(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cc;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cc})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dc(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=La(k.pixelPosition,function(a,c){return c?(c=Ja(a,b),Ha.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ec=a.jQuery,fc=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fc),b&&a.jQuery===m&&(a.jQuery=ec),m},typeof b===K&&(a.jQuery=a.$=m),m});
6 |
--------------------------------------------------------------------------------
/jQuerySelectAreas-Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/360Learning/jquery-select-areas/6b51d31c33aa0a97d3fd1cf25b767773da66352a/jQuerySelectAreas-Preview.png
--------------------------------------------------------------------------------
/jquery.selectareas.js:
--------------------------------------------------------------------------------
1 | /* global window, Image, jQuery */
2 | /**
3 | * @author 360Learning
4 | * @author Catalin Dogaru (https://github.com/cdog - http://code.tutsplus.com/tutorials/how-to-create-a-jquery-image-cropping-plugin-from-scratch-part-i--net-20994)
5 | * @author Adrien David-Sivelle (https://github.com/AdrienDS - Refactoring, Multiselections & Mobile compatibility)
6 | */
7 | (function($) {
8 | $.imageArea = function(parent, id) {
9 | var options = parent.options,
10 | $image = parent.$image,
11 | $trigger = parent.$trigger,
12 | $outline,
13 | $selection,
14 | $resizeHandlers = {},
15 | $btDelete,
16 | resizeHorizontally = true,
17 | resizeVertically = true,
18 | selectionOffset = [0, 0],
19 | selectionOrigin = [0, 0],
20 | area = {
21 | id: id,
22 | x: 0,
23 | y: 0,
24 | z: 0,
25 | height: 0,
26 | width: 0
27 | },
28 | blur = function () {
29 | area.z = 0;
30 | refresh("blur");
31 | },
32 | focus = function () {
33 | parent.blurAll();
34 | area.z = 100;
35 | refresh();
36 | },
37 | getData = function () {
38 | return area;
39 | },
40 | fireEvent = function (event) {
41 | $image.trigger(event, [area.id, parent.areas()]);
42 | },
43 | cancelEvent = function (e) {
44 | var event = e || window.event || {};
45 | event.cancelBubble = true;
46 | event.returnValue = false;
47 | event.stopPropagation && event.stopPropagation(); // jshint ignore: line
48 | event.preventDefault && event.preventDefault(); // jshint ignore: line
49 | },
50 | off = function() {
51 | $.each(arguments, function (key, val) {
52 | on(val);
53 | });
54 | },
55 | on = function (type, handler) {
56 | var browserEvent, mobileEvent;
57 | switch (type) {
58 | case "start":
59 | browserEvent = "mousedown";
60 | mobileEvent = "touchstart";
61 | break;
62 | case "move":
63 | browserEvent = "mousemove";
64 | mobileEvent = "touchmove";
65 | break;
66 | case "stop":
67 | browserEvent = "mouseup";
68 | mobileEvent = "touchend";
69 | break;
70 | default:
71 | return;
72 | }
73 | if (handler && jQuery.isFunction(handler)) {
74 | $(window.document).on(browserEvent, handler).on(mobileEvent, handler);
75 | } else {
76 | $(window.document).off(browserEvent).off(mobileEvent);
77 | }
78 | },
79 | updateSelection = function () {
80 | // Update the outline layer
81 | $outline.css({
82 | cursor: "default",
83 | width: area.width,
84 | height: area.height,
85 | left: area.x,
86 | top: area.y,
87 | "z-index": area.z
88 | });
89 |
90 | // Update the selection layer
91 | $selection.css({
92 | backgroundPosition : ( - area.x - 1) + "px " + ( - area.y - 1) + "px",
93 | cursor : options.allowMove ? "move" : "default",
94 | width: (area.width - 2 > 0) ? (area.width - 2) : 0,
95 | height: (area.height - 2 > 0) ? (area.height - 2) : 0,
96 | left : area.x + 1,
97 | top : area.y + 1,
98 | "z-index": area.z + 2
99 | });
100 | },
101 | updateResizeHandlers = function (show) {
102 | if (! options.allowResize) {
103 | return;
104 | }
105 | if (show) {
106 | $.each($resizeHandlers, function(name, $handler) {
107 | var top,
108 | left,
109 | semiwidth = Math.round($handler.width() / 2),
110 | semiheight = Math.round($handler.height() / 2),
111 | vertical = name[0],
112 | horizontal = name[name.length - 1];
113 |
114 | if (vertical === "n") { // ====== North* ======
115 | top = - semiheight;
116 |
117 | } else if (vertical === "s") { // ====== South* ======
118 | top = area.height - semiheight - 1;
119 |
120 | } else { // === East & West ===
121 | top = Math.round(area.height / 2) - semiheight - 1;
122 | }
123 |
124 | if (horizontal === "e") { // ====== *East ======
125 | left = area.width - semiwidth - 1;
126 |
127 | } else if (horizontal === "w") { // ====== *West ======
128 | left = - semiwidth;
129 |
130 | } else { // == North & South ==
131 | left = Math.round(area.width / 2) - semiwidth - 1;
132 | }
133 |
134 | $handler.css({
135 | display: "block",
136 | left: area.x + left,
137 | top: area.y + top,
138 | "z-index": area.z + 1
139 | });
140 | });
141 | } else {
142 | $(".select-areas-resize-handler").each(function() {
143 | $(this).css({ display: "none" });
144 | });
145 | }
146 | },
147 | updateBtDelete = function (visible) {
148 | if ($btDelete) {
149 | $btDelete.css({
150 | display: visible ? "block" : "none",
151 | left: area.x + area.width + 1,
152 | top: area.y - $btDelete.outerHeight() - 1,
153 | "z-index": area.z + 1
154 | });
155 | }
156 | },
157 | updateCursor = function (cursorType) {
158 | $outline.css({
159 | cursor: cursorType
160 | });
161 |
162 | $selection.css({
163 | cursor: cursorType
164 | });
165 | },
166 | refresh = function(sender) {
167 | switch (sender) {
168 | case "startSelection":
169 | parent._refresh();
170 | updateSelection();
171 | updateResizeHandlers();
172 | updateBtDelete(true);
173 | break;
174 |
175 | case "pickSelection":
176 | case "pickResizeHandler":
177 | updateResizeHandlers();
178 | break;
179 |
180 | case "resizeSelection":
181 | updateSelection();
182 | updateResizeHandlers();
183 | updateCursor("crosshair");
184 | updateBtDelete(true);
185 | break;
186 |
187 | case "moveSelection":
188 | updateSelection();
189 | updateResizeHandlers();
190 | updateCursor("move");
191 | updateBtDelete(true);
192 | break;
193 |
194 | case "blur":
195 | updateSelection();
196 | updateResizeHandlers();
197 | updateBtDelete();
198 | break;
199 |
200 | //case "releaseSelection":
201 | default:
202 | updateSelection();
203 | updateResizeHandlers(true);
204 | updateBtDelete(true);
205 | }
206 | },
207 | startSelection = function (event) {
208 | cancelEvent(event);
209 |
210 | // Reset the selection size
211 | area.width = options.minSize[0];
212 | area.height = options.minSize[1];
213 | focus();
214 | on("move", resizeSelection);
215 | on("stop", releaseSelection);
216 |
217 | // Get the selection origin
218 | selectionOrigin = getMousePosition(event);
219 | if (selectionOrigin[0] + area.width > $image.width()) {
220 | selectionOrigin[0] = $image.width() - area.width;
221 | }
222 | if (selectionOrigin[1] + area.height > $image.height()) {
223 | selectionOrigin[1] = $image.height() - area.height;
224 | }
225 | // And set its position
226 | area.x = selectionOrigin[0];
227 | area.y = selectionOrigin[1];
228 |
229 | refresh("startSelection");
230 | },
231 | pickSelection = function (event) {
232 | cancelEvent(event);
233 | focus();
234 | on("move", moveSelection);
235 | on("stop", releaseSelection);
236 |
237 | var mousePosition = getMousePosition(event);
238 |
239 | // Get the selection offset relative to the mouse position
240 | selectionOffset[0] = mousePosition[0] - area.x;
241 | selectionOffset[1] = mousePosition[1] - area.y;
242 |
243 | refresh("pickSelection");
244 | },
245 | pickResizeHandler = function (event) {
246 | cancelEvent(event);
247 | focus();
248 |
249 | var card = event.target.className.split(" ")[1];
250 | if (card[card.length - 1] === "w") {
251 | selectionOrigin[0] += area.width;
252 | area.x = selectionOrigin[0] - area.width;
253 | }
254 | if (card[0] === "n") {
255 | selectionOrigin[1] += area.height;
256 | area.y = selectionOrigin[1] - area.height;
257 | }
258 | if (card === "n" || card === "s") {
259 | resizeHorizontally = false;
260 | } else if (card === "e" || card === "w") {
261 | resizeVertically = false;
262 | }
263 |
264 | on("move", resizeSelection);
265 | on("stop", releaseSelection);
266 |
267 | refresh("pickResizeHandler");
268 | },
269 | resizeSelection = function (event) {
270 | cancelEvent(event);
271 | focus();
272 |
273 | var mousePosition = getMousePosition(event);
274 |
275 | // Get the selection size
276 | var height = mousePosition[1] - selectionOrigin[1],
277 | width = mousePosition[0] - selectionOrigin[0];
278 |
279 | // If the selection size is smaller than the minimum size set it to minimum size
280 | if (Math.abs(width) < options.minSize[0]) {
281 | width = (width >= 0) ? options.minSize[0] : - options.minSize[0];
282 | }
283 | if (Math.abs(height) < options.minSize[1]) {
284 | height = (height >= 0) ? options.minSize[1] : - options.minSize[1];
285 | }
286 | // Test if the selection size exceeds the image bounds
287 | if (selectionOrigin[0] + width < 0 || selectionOrigin[0] + width > $image.width()) {
288 | width = - width;
289 | }
290 | if (selectionOrigin[1] + height < 0 || selectionOrigin[1] + height > $image.height()) {
291 | height = - height;
292 | }
293 | // Test if the selection size is bigger than the maximum size (ignored if minSize > maxSize)
294 | if (options.maxSize[0] > options.minSize[0] && options.maxSize[1] > options.minSize[1]) {
295 | if (Math.abs(width) > options.maxSize[0]) {
296 | width = (width >= 0) ? options.maxSize[0] : - options.maxSize[0];
297 | }
298 |
299 | if (Math.abs(height) > options.maxSize[1]) {
300 | height = (height >= 0) ? options.maxSize[1] : - options.maxSize[1];
301 | }
302 | }
303 |
304 | // Set the selection size
305 | if (resizeHorizontally) {
306 | area.width = width;
307 | }
308 | if (resizeVertically) {
309 | area.height = height;
310 | }
311 | // If any aspect ratio is specified
312 | if (options.aspectRatio) {
313 | // Calculate the new width and height
314 | if ((width > 0 && height > 0) || (width < 0 && height < 0)) {
315 | if (resizeHorizontally) {
316 | height = Math.round(width / options.aspectRatio);
317 | } else {
318 | width = Math.round(height * options.aspectRatio);
319 | }
320 | } else {
321 | if (resizeHorizontally) {
322 | height = - Math.round(width / options.aspectRatio);
323 | } else {
324 | width = - Math.round(height * options.aspectRatio);
325 | }
326 | }
327 | // Test if the new size exceeds the image bounds
328 | if (selectionOrigin[0] + width > $image.width()) {
329 | width = $image.width() - selectionOrigin[0];
330 | height = (height > 0) ? Math.round(width / options.aspectRatio) : - Math.round(width / options.aspectRatio);
331 | }
332 |
333 | if (selectionOrigin[1] + height < 0) {
334 | height = - selectionOrigin[1];
335 | width = (width > 0) ? - Math.round(height * options.aspectRatio) : Math.round(height * options.aspectRatio);
336 | }
337 |
338 | if (selectionOrigin[1] + height > $image.height()) {
339 | height = $image.height() - selectionOrigin[1];
340 | width = (width > 0) ? Math.round(height * options.aspectRatio) : - Math.round(height * options.aspectRatio);
341 | }
342 |
343 | // Set the selection size
344 | area.width = width;
345 | area.height = height;
346 | }
347 |
348 | if (area.width < 0) {
349 | area.width = Math.abs(area.width);
350 | area.x = selectionOrigin[0] - area.width;
351 | } else {
352 | area.x = selectionOrigin[0];
353 | }
354 | if (area.height < 0) {
355 | area.height = Math.abs(area.height);
356 | area.y = selectionOrigin[1] - area.height;
357 | } else {
358 | area.y = selectionOrigin[1];
359 | }
360 |
361 | fireEvent("changing");
362 | refresh("resizeSelection");
363 | },
364 | moveSelection = function (event) {
365 | cancelEvent(event);
366 | if (! options.allowMove) {
367 | return;
368 | }
369 | focus();
370 |
371 | var mousePosition = getMousePosition(event);
372 | moveTo({
373 | x: mousePosition[0] - selectionOffset[0],
374 | y: mousePosition[1] - selectionOffset[1]
375 | });
376 |
377 | fireEvent("changing");
378 | },
379 | moveTo = function (point) {
380 | // Set the selection position on the x-axis relative to the bounds
381 | // of the image
382 | if (point.x > 0) {
383 | if (point.x + area.width < $image.width()) {
384 | area.x = point.x;
385 | } else {
386 | area.x = $image.width() - area.width;
387 | }
388 | } else {
389 | area.x = 0;
390 | }
391 | // Set the selection position on the y-axis relative to the bounds
392 | // of the image
393 | if (point.y > 0) {
394 | if (point.y + area.height < $image.height()) {
395 | area.y = point.y;
396 | } else {
397 | area.y = $image.height() - area.height;
398 | }
399 | } else {
400 | area.y = 0;
401 | }
402 | refresh("moveSelection");
403 | },
404 | releaseSelection = function (event) {
405 | cancelEvent(event);
406 | off("move", "stop");
407 |
408 | // Update the selection origin
409 | selectionOrigin[0] = area.x;
410 | selectionOrigin[1] = area.y;
411 |
412 | // Reset the resize constraints
413 | resizeHorizontally = true;
414 | resizeVertically = true;
415 |
416 | fireEvent("changed");
417 |
418 | refresh("releaseSelection");
419 | },
420 | deleteSelection = function (event) {
421 | cancelEvent(event);
422 | $selection.remove();
423 | $outline.remove();
424 | $.each($resizeHandlers, function(card, $handler) {
425 | $handler.remove();
426 | });
427 | if ($btDelete) {
428 | $btDelete.remove();
429 | }
430 | parent._remove(id);
431 | fireEvent("changed");
432 | },
433 | getElementOffset = function (object) {
434 | var offset = $(object).offset();
435 |
436 | return [offset.left, offset.top];
437 | },
438 | getMousePosition = function (event) {
439 | var imageOffset = getElementOffset($image);
440 |
441 | if (! event.pageX) {
442 | if (event.originalEvent) {
443 | event = event.originalEvent;
444 | }
445 |
446 | if(event.changedTouches) {
447 | event = event.changedTouches[0];
448 | }
449 |
450 | if(event.touches) {
451 | event = event.touches[0];
452 | }
453 | }
454 | var x = event.pageX - imageOffset[0],
455 | y = event.pageY - imageOffset[1];
456 |
457 | x = (x < 0) ? 0 : (x > $image.width()) ? $image.width() : x;
458 | y = (y < 0) ? 0 : (y > $image.height()) ? $image.height() : y;
459 |
460 | return [x, y];
461 | };
462 |
463 |
464 | // Initialize an outline layer and place it above the trigger layer
465 | $outline = $("
")
466 | .css({
467 | opacity : options.outlineOpacity,
468 | position : "absolute"
469 | })
470 | .insertAfter($trigger);
471 |
472 | // Initialize a selection layer and place it above the outline layer
473 | $selection = $("
")
474 | .addClass("select-areas-background-area")
475 | .css({
476 | background : "#fff url(" + $image.attr("src") + ") no-repeat",
477 | backgroundSize : $image.width() + "px " + $image.height() + "px",
478 | position : "absolute"
479 | })
480 | .insertAfter($outline);
481 |
482 | // Initialize all handlers
483 | if (options.allowResize) {
484 | $.each(["nw", "n", "ne", "e", "se", "s", "sw", "w"], function (key, card) {
485 | $resizeHandlers[card] = $("
")
486 | .css({
487 | opacity : 0.5,
488 | position : "absolute",
489 | cursor : card + "-resize"
490 | })
491 | .insertAfter($selection)
492 | .mousedown(pickResizeHandler)
493 | .bind("touchstart", pickResizeHandler);
494 | });
495 | }
496 | // initialize delete button
497 | if (options.allowDelete) {
498 | var bindToDelete = function ($obj) {
499 | $obj.click(deleteSelection)
500 | .bind("touchstart", deleteSelection)
501 | .bind("tap", deleteSelection);
502 | return $obj;
503 | };
504 | $btDelete = bindToDelete($("
"))
505 | .append(bindToDelete($("
")))
506 | .insertAfter($selection);
507 | }
508 |
509 | if (options.allowMove) {
510 | $selection.mousedown(pickSelection).bind("touchstart", pickSelection);
511 | }
512 |
513 | focus();
514 |
515 | return {
516 | getData: getData,
517 | startSelection: startSelection,
518 | deleteSelection: deleteSelection,
519 | options: options,
520 | blur: blur,
521 | focus: focus,
522 | nudge: function (point) {
523 | point.x = area.x;
524 | point.y = area.y;
525 | if (point.d) {
526 | point.y = area.y + point.d;
527 | }
528 | if (point.u) {
529 | point.y = area.y - point.u;
530 | }
531 | if (point.l) {
532 | point.x = area.x - point.l;
533 | }
534 | if (point.r) {
535 | point.x = area.x + point.r;
536 | }
537 | moveTo(point);
538 | fireEvent("changed");
539 | },
540 | set: function (dimensions, silent) {
541 | area = $.extend(area, dimensions);
542 | selectionOrigin[0] = area.x;
543 | selectionOrigin[1] = area.y;
544 | if (! silent) {
545 | fireEvent("changed");
546 | }
547 | },
548 | contains: function (point) {
549 | return (point.x >= area.x) && (point.x <= area.x + area.width) &&
550 | (point.y >= area.y) && (point.y <= area.y + area.height);
551 | }
552 | };
553 | };
554 |
555 |
556 | $.imageSelectAreas = function() { };
557 |
558 | $.imageSelectAreas.prototype.init = function (object, customOptions) {
559 | var that = this,
560 | defaultOptions = {
561 | allowEdit: true,
562 | allowMove: true,
563 | allowResize: true,
564 | allowSelect: true,
565 | allowDelete: true,
566 | allowNudge: true,
567 | aspectRatio: 0,
568 | minSize: [0, 0],
569 | maxSize: [0, 0],
570 | width: 0,
571 | maxAreas: 0,
572 | outlineOpacity: 0.5,
573 | overlayOpacity: 0.5,
574 | areas: [],
575 | onChanging: null,
576 | onChanged: null
577 | };
578 |
579 | this.options = $.extend(defaultOptions, customOptions);
580 |
581 | if (! this.options.allowEdit) {
582 | this.options.allowSelect = this.options.allowMove = this.options.allowResize = this.options.allowDelete = false;
583 | }
584 |
585 | this._areas = {};
586 |
587 | // Initialize the image layer
588 | this.$image = $(object);
589 |
590 | this.ratio = 1;
591 | if (this.options.width && this.$image.width() && this.options.width !== this.$image.width()) {
592 | this.ratio = this.options.width / this.$image.width();
593 | this.$image.width(this.options.width);
594 | }
595 |
596 | if (this.options.onChanging) {
597 | this.$image.on("changing", this.options.onChanging);
598 | }
599 | if (this.options.onChanged) {
600 | this.$image.on("changed", this.options.onChanged);
601 | }
602 | if (this.options.onLoaded) {
603 | this.$image.on("loaded", this.options.onLoaded);
604 | }
605 |
606 | // Initialize an image holder
607 | this.$holder = $("
")
608 | .css({
609 | position : "relative",
610 | width: this.$image.width(),
611 | height: this.$image.height()
612 | });
613 |
614 | // Wrap the holder around the image
615 | this.$image.wrap(this.$holder)
616 | .css({
617 | position : "absolute"
618 | });
619 |
620 | // Initialize an overlay layer and place it above the image
621 | this.$overlay = $("
")
622 | .css({
623 | opacity : this.options.overlayOpacity,
624 | position : "absolute",
625 | width: this.$image.width(),
626 | height: this.$image.height()
627 | })
628 | .insertAfter(this.$image);
629 |
630 | // Initialize a trigger layer and place it above the overlay layer
631 | this.$trigger = $("
")
632 | .css({
633 | backgroundColor : "#000000",
634 | opacity : 0,
635 | position : "absolute",
636 | width: this.$image.width(),
637 | height: this.$image.height()
638 | })
639 | .insertAfter(this.$overlay);
640 |
641 | $.each(this.options.areas, function (key, area) {
642 | that._add(area, true);
643 | });
644 |
645 |
646 | this.blurAll();
647 | this._refresh();
648 |
649 | if (this.options.allowSelect) {
650 | // Bind an event handler to the "mousedown" event of the trigger layer
651 | this.$trigger.mousedown($.proxy(this.newArea, this)).on("touchstart", $.proxy(this.newArea, this));
652 | }
653 | if (this.options.allowNudge) {
654 | $('html').keydown(function (e) { // move selection with arrow keys
655 | var codes = {
656 | 37: "l",
657 | 38: "u",
658 | 39: "r",
659 | 40: "d"
660 | },
661 | direction = codes[e.which],
662 | selectedArea;
663 |
664 | if (direction) {
665 | that._eachArea(function (area) {
666 | if (area.getData().z === 100) {
667 | selectedArea = area;
668 | return false;
669 | }
670 | });
671 | if (selectedArea) {
672 | var move = {};
673 | move[direction] = 1;
674 | selectedArea.nudge(move);
675 | }
676 | }
677 | });
678 | }
679 | };
680 |
681 | $.imageSelectAreas.prototype._refresh = function () {
682 | var nbAreas = this.areas().length;
683 | this.$overlay.css({
684 | display : nbAreas? "block" : "none"
685 | });
686 | if (nbAreas) {
687 | this.$image.addClass("blurred");
688 | } else {
689 | this.$image.removeClass("blurred");
690 | }
691 | this.$trigger.css({
692 | cursor : this.options.allowSelect ? "crosshair" : "default"
693 | });
694 | };
695 |
696 | $.imageSelectAreas.prototype._eachArea = function (cb) {
697 | $.each(this._areas, function (id, area) {
698 | if (area) {
699 | return cb(area, id);
700 | }
701 | });
702 | };
703 |
704 | $.imageSelectAreas.prototype._remove = function (id) {
705 | delete this._areas[id];
706 | this._refresh();
707 | };
708 |
709 | $.imageSelectAreas.prototype.remove = function (id) {
710 | if (this._areas[id]) {
711 | this._areas[id].deleteSelection();
712 | }
713 | };
714 |
715 | $.imageSelectAreas.prototype.newArea = function (event) {
716 | var id = -1;
717 | this.blurAll();
718 | if (this.options.maxAreas && this.options.maxAreas <= this.areas().length) {
719 | return id;
720 | }
721 | this._eachArea(function (area, index) {
722 | id = Math.max(id, parseInt(index, 10));
723 | });
724 | id += 1;
725 |
726 | this._areas[id] = $.imageArea(this, id);
727 | if (event) {
728 | this._areas[id].startSelection(event);
729 | }
730 | return id;
731 | };
732 |
733 | $.imageSelectAreas.prototype.set = function (id, options, silent) {
734 | if (this._areas[id]) {
735 | options.id = id;
736 | this._areas[id].set(options, silent);
737 | this._areas[id].focus();
738 | }
739 | };
740 |
741 | $.imageSelectAreas.prototype._add = function (options, silent) {
742 | var id = this.newArea();
743 | this.set(id, options, silent);
744 | };
745 |
746 | $.imageSelectAreas.prototype.add = function (options) {
747 | var that = this;
748 | this.blurAll();
749 | if ($.isArray(options)) {
750 | $.each(options, function (key, val) {
751 | that._add(val);
752 | });
753 | } else {
754 | this._add(options);
755 | }
756 | this._refresh();
757 | if (! this.options.allowSelect && ! this.options.allowMove && ! this.options.allowResize && ! this.options.allowDelete) {
758 | this.blurAll();
759 | }
760 | };
761 |
762 | $.imageSelectAreas.prototype.reset = function () {
763 | var that = this;
764 | this._eachArea(function (area, id) {
765 | that.remove(id);
766 | });
767 | this._refresh();
768 | };
769 |
770 | $.imageSelectAreas.prototype.destroy = function () {
771 | this.reset();
772 | this.$holder.remove();
773 | this.$overlay.remove();
774 | this.$trigger.remove();
775 | this.$image.css("width", "").css("position", "").unwrap();
776 | this.$image.removeData("mainImageSelectAreas");
777 | this.$image.off('changing changed loaded');
778 | };
779 |
780 | $.imageSelectAreas.prototype.areas = function () {
781 | var ret = [];
782 | this._eachArea(function (area) {
783 | ret.push(area.getData());
784 | });
785 | return ret;
786 | };
787 |
788 | $.imageSelectAreas.prototype.relativeAreas = function () {
789 | var areas = this.areas(),
790 | ret = [],
791 | ratio = this.ratio,
792 | scale = function (val) {
793 | return Math.floor(val / ratio);
794 | };
795 |
796 | for (var i = 0; i < areas.length; i++) {
797 | ret[i] = $.extend({}, areas[i]);
798 | ret[i].x = scale(ret[i].x);
799 | ret[i].y = scale(ret[i].y);
800 | ret[i].width = scale(ret[i].width);
801 | ret[i].height = scale(ret[i].height);
802 | }
803 | return ret;
804 | };
805 |
806 | $.imageSelectAreas.prototype.blurAll = function () {
807 | this._eachArea(function (area) {
808 | area.blur();
809 | });
810 | };
811 |
812 | $.imageSelectAreas.prototype.contains = function (point) {
813 | var res = false;
814 | this._eachArea(function (area) {
815 | if (area.contains(point)) {
816 | res = true;
817 | return false;
818 | }
819 | });
820 | return res;
821 | };
822 |
823 | $.selectAreas = function(object, options) {
824 | var $object = $(object);
825 | if (! $object.data("mainImageSelectAreas")) {
826 | var mainImageSelectAreas = new $.imageSelectAreas();
827 | mainImageSelectAreas.init(object, options);
828 | $object.data("mainImageSelectAreas", mainImageSelectAreas);
829 | $object.trigger("loaded");
830 | }
831 | return $object.data("mainImageSelectAreas");
832 | };
833 |
834 |
835 | $.fn.selectAreas = function(customOptions) {
836 | if ( $.imageSelectAreas.prototype[customOptions] ) { // Method call
837 | var ret = $.imageSelectAreas.prototype[ customOptions ].apply( $.selectAreas(this), Array.prototype.slice.call( arguments, 1 ));
838 | return typeof ret === "undefined" ? this : ret;
839 |
840 | } else if ( typeof customOptions === "object" || ! customOptions ) { // Initialization
841 | //Iterate over each object
842 | this.each(function() {
843 | var currentObject = this,
844 | image = new Image();
845 |
846 | // And attach selectAreas when the object is loaded
847 | image.onload = function() {
848 | $.selectAreas(currentObject, customOptions);
849 | };
850 |
851 | // Reset the src because cached images don"t fire load sometimes
852 | image.src = currentObject.src;
853 |
854 | });
855 | return this;
856 |
857 | } else {
858 | $.error( "Method " + customOptions + " does not exist on jQuery.selectAreas" );
859 | }
860 | };
861 | }) (jQuery);
862 |
--------------------------------------------------------------------------------
/jquery.selectareas.min.js:
--------------------------------------------------------------------------------
1 | !function(a){a.imageArea=function(b,c){var g,h,j,d=b.options,e=b.$image,f=b.$trigger,i={},k=!0,l=!0,m=[0,0],n=[0,0],o={id:c,x:0,y:0,z:0,height:0,width:0},p=function(){o.z=0,A("blur")},q=function(){b.blurAll(),o.z=100,A()},r=function(){return o},s=function(a){e.trigger(a,[o.id,b.areas()])},t=function(a){var b=a||window.event||{};b.cancelBubble=!0,b.returnValue=!1,b.stopPropagation&&b.stopPropagation(),b.preventDefault&&b.preventDefault()},u=function(){a.each(arguments,function(a,b){v(b)})},v=function(b,c){var d,e;switch(b){case"start":d="mousedown",e="touchstart";break;case"move":d="mousemove",e="touchmove";break;case"stop":d="mouseup",e="touchend";break;default:return}c&&jQuery.isFunction(c)?a(window.document).on(d,c).on(e,c):a(window.document).off(d).off(e)},w=function(){g.css({cursor:"default",width:o.width,height:o.height,left:o.x,top:o.y,"z-index":o.z}),h.css({backgroundPosition:-o.x-1+"px "+(-o.y-1)+"px",cursor:d.allowMove?"move":"default",width:o.width-2>0?o.width-2:0,height:o.height-2>0?o.height-2:0,left:o.x+1,top:o.y+1,"z-index":o.z+2})},x=function(b){d.allowResize&&(b?a.each(i,function(a,b){var c,d,e=Math.round(b.width()/2),f=Math.round(b.height()/2),g=a[0],h=a[a.length-1];c="n"===g?-f:"s"===g?o.height-f-1:Math.round(o.height/2)-f-1,d="e"===h?o.width-e-1:"w"===h?-e:Math.round(o.width/2)-e-1,b.css({display:"block",left:o.x+d,top:o.y+c,"z-index":o.z+1})}):a(".select-areas-resize-handler").each(function(){a(this).css({display:"none"})}))},y=function(a){j&&j.css({display:a?"block":"none",left:o.x+o.width+1,top:o.y-j.outerHeight()-1,"z-index":o.z+1})},z=function(a){g.css({cursor:a}),h.css({cursor:a})},A=function(a){switch(a){case"startSelection":b._refresh(),w(),x(),y(!0);break;case"pickSelection":case"pickResizeHandler":x();break;case"resizeSelection":w(),x(),z("crosshair"),y(!0);break;case"moveSelection":w(),x(),z("move"),y(!0);break;case"blur":w(),x(),y();break;default:w(),x(!0),y(!0)}},B=function(a){t(a),o.width=d.minSize[0],o.height=d.minSize[1],q(),v("move",E),v("stop",H),n=K(a),n[0]+o.width>e.width()&&(n[0]=e.width()-o.width),n[1]+o.height>e.height()&&(n[1]=e.height()-o.height),o.x=n[0],o.y=n[1],A("startSelection")},C=function(a){t(a),q(),v("move",F),v("stop",H);var b=K(a);m[0]=b[0]-o.x,m[1]=b[1]-o.y,A("pickSelection")},D=function(a){t(a),q();var b=a.target.className.split(" ")[1];"w"===b[b.length-1]&&(n[0]+=o.width,o.x=n[0]-o.width),"n"===b[0]&&(n[1]+=o.height,o.y=n[1]-o.height),"n"===b||"s"===b?k=!1:"e"!==b&&"w"!==b||(l=!1),v("move",E),v("stop",H),A("pickResizeHandler")},E=function(a){t(a),q();var b=K(a),c=b[1]-n[1],f=b[0]-n[0];Math.abs(f)
=0?d.minSize[0]:-d.minSize[0]),Math.abs(c)=0?d.minSize[1]:-d.minSize[1]),(n[0]+f<0||n[0]+f>e.width())&&(f=-f),(n[1]+c<0||n[1]+c>e.height())&&(c=-c),d.maxSize[0]>d.minSize[0]&&d.maxSize[1]>d.minSize[1]&&(Math.abs(f)>d.maxSize[0]&&(f=f>=0?d.maxSize[0]:-d.maxSize[0]),Math.abs(c)>d.maxSize[1]&&(c=c>=0?d.maxSize[1]:-d.maxSize[1])),k&&(o.width=f),l&&(o.height=c),d.aspectRatio&&(f>0&&c>0||f<0&&c<0?k?c=Math.round(f/d.aspectRatio):f=Math.round(c*d.aspectRatio):k?c=-Math.round(f/d.aspectRatio):f=-Math.round(c*d.aspectRatio),n[0]+f>e.width()&&(f=e.width()-n[0],c=c>0?Math.round(f/d.aspectRatio):-Math.round(f/d.aspectRatio)),n[1]+c<0&&(c=-n[1],f=f>0?-Math.round(c*d.aspectRatio):Math.round(c*d.aspectRatio)),n[1]+c>e.height()&&(c=e.height()-n[1],f=f>0?Math.round(c*d.aspectRatio):-Math.round(c*d.aspectRatio)),o.width=f,o.height=c),o.width<0?(o.width=Math.abs(o.width),o.x=n[0]-o.width):o.x=n[0],o.height<0?(o.height=Math.abs(o.height),o.y=n[1]-o.height):o.y=n[1],s("changing"),A("resizeSelection")},F=function(a){if(t(a),d.allowMove){q();var b=K(a);G({x:b[0]-m[0],y:b[1]-m[1]}),s("changing")}},G=function(a){a.x>0?a.x+o.width0?a.y+o.heighte.width()?e.width():c,d=d<0?0:d>e.height()?e.height():d,[c,d]};if(g=a('').css({opacity:d.outlineOpacity,position:"absolute"}).insertAfter(f),h=a("").addClass("select-areas-background-area").css({background:"#fff url("+e.attr("src")+") no-repeat",backgroundSize:e.width()+"px "+e.height()+"px",position:"absolute"}).insertAfter(g),d.allowResize&&a.each(["nw","n","ne","e","se","s","sw","w"],function(b,c){i[c]=a('').css({opacity:.5,position:"absolute",cursor:c+"-resize"}).insertAfter(h).mousedown(D).bind("touchstart",D)}),d.allowDelete){var L=function(a){return a.click(I).bind("touchstart",I).bind("tap",I),a};j=L(a('')).append(L(a(''))).insertAfter(h)}return d.allowMove&&h.mousedown(C).bind("touchstart",C),q(),{getData:r,startSelection:B,deleteSelection:I,options:d,blur:p,focus:q,nudge:function(a){a.x=o.x,a.y=o.y,a.d&&(a.y=o.y+a.d),a.u&&(a.y=o.y-a.u),a.l&&(a.x=o.x-a.l),a.r&&(a.x=o.x+a.r),G(a),s("changed")},set:function(b,c){o=a.extend(o,b),n[0]=o.x,n[1]=o.y,c||s("changed")},contains:function(a){return a.x>=o.x&&a.x<=o.x+o.width&&a.y>=o.y&&a.y<=o.y+o.height}}},a.imageSelectAreas=function(){},a.imageSelectAreas.prototype.init=function(b,c){var d=this,e={allowEdit:!0,allowMove:!0,allowResize:!0,allowSelect:!0,allowDelete:!0,allowNudge:!0,aspectRatio:0,minSize:[0,0],maxSize:[0,0],width:0,maxAreas:0,outlineOpacity:.5,overlayOpacity:.5,areas:[],onChanging:null,onChanged:null};this.options=a.extend(e,c),this.options.allowEdit||(this.options.allowSelect=this.options.allowMove=this.options.allowResize=this.options.allowDelete=!1),this._areas={},this.$image=a(b),this.ratio=1,this.options.width&&this.$image.width()&&this.options.width!==this.$image.width()&&(this.ratio=this.options.width/this.$image.width(),this.$image.width(this.options.width)),this.options.onChanging&&this.$image.on("changing",this.options.onChanging),this.options.onChanged&&this.$image.on("changed",this.options.onChanged),this.options.onLoaded&&this.$image.on("loaded",this.options.onLoaded),this.$holder=a("").css({position:"relative",width:this.$image.width(),height:this.$image.height()}),this.$image.wrap(this.$holder).css({position:"absolute"}),this.$overlay=a('').css({opacity:this.options.overlayOpacity,position:"absolute",width:this.$image.width(),height:this.$image.height()}).insertAfter(this.$image),this.$trigger=a("").css({backgroundColor:"#000000",opacity:0,position:"absolute",width:this.$image.width(),height:this.$image.height()}).insertAfter(this.$overlay),a.each(this.options.areas,function(a,b){d._add(b,!0)}),this.blurAll(),this._refresh(),this.options.allowSelect&&this.$trigger.mousedown(a.proxy(this.newArea,this)).on("touchstart",a.proxy(this.newArea,this)),this.options.allowNudge&&a("html").keydown(function(a){var e,b={37:"l",38:"u",39:"r",40:"d"},c=b[a.which];if(c&&(d._eachArea(function(a){if(100===a.getData().z)return e=a,!1}),e)){var f={};f[c]=1,e.nudge(f)}})},a.imageSelectAreas.prototype._refresh=function(){var a=this.areas().length;this.$overlay.css({display:a?"block":"none"}),a?this.$image.addClass("blurred"):this.$image.removeClass("blurred"),this.$trigger.css({cursor:this.options.allowSelect?"crosshair":"default"})},a.imageSelectAreas.prototype._eachArea=function(b){a.each(this._areas,function(a,c){if(c)return b(c,a)})},a.imageSelectAreas.prototype._remove=function(a){delete this._areas[a],this._refresh()},a.imageSelectAreas.prototype.remove=function(a){this._areas[a]&&this._areas[a].deleteSelection()},a.imageSelectAreas.prototype.newArea=function(b){var c=-1;return this.blurAll(),this.options.maxAreas&&this.options.maxAreas<=this.areas().length?c:(this._eachArea(function(a,b){c=Math.max(c,parseInt(b,10))}),c+=1,this._areas[c]=a.imageArea(this,c),b&&this._areas[c].startSelection(b),c)},a.imageSelectAreas.prototype.set=function(a,b,c){this._areas[a]&&(b.id=a,this._areas[a].set(b,c),this._areas[a].focus())},a.imageSelectAreas.prototype._add=function(a,b){var c=this.newArea();this.set(c,a,b)},a.imageSelectAreas.prototype.add=function(b){var c=this;this.blurAll(),a.isArray(b)?a.each(b,function(a,b){c._add(b)}):this._add(b),this._refresh(),this.options.allowSelect||this.options.allowMove||this.options.allowResize||this.options.allowDelete||this.blurAll()},a.imageSelectAreas.prototype.reset=function(){var a=this;this._eachArea(function(b,c){a.remove(c)}),this._refresh()},a.imageSelectAreas.prototype.destroy=function(){this.reset(),this.$holder.remove(),this.$overlay.remove(),this.$trigger.remove(),this.$image.css("width","").css("position","").unwrap(),this.$image.removeData("mainImageSelectAreas"),this.$image.off("changing changed loaded")},a.imageSelectAreas.prototype.areas=function(){var a=[];return this._eachArea(function(b){a.push(b.getData())}),a},a.imageSelectAreas.prototype.relativeAreas=function(){for(var b=this.areas(),c=[],d=this.ratio,e=function(a){return Math.floor(a/d)},f=0;f
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/resources/jquery.selectareas.css:
--------------------------------------------------------------------------------
1 | .select-areas-overlay {
2 | background-color: #000;
3 | overflow: hidden;
4 | position: absolute;
5 | }
6 | .blurred {
7 | filter: url("/filters.svg#blur3px");
8 | -webkit-filter: blur(3px);
9 | -moz-filter: blur(3px);
10 | -o-filter: blur(3px);
11 | filter: blur(3px);
12 | }
13 |
14 | .select-areas-outline {
15 | background: #fff url('outline.gif');
16 | overflow: hidden;
17 | }
18 |
19 | .select-areas-resize-handler {
20 | background-color: #000;
21 | border: 1px #fff solid;
22 | height: 8px;
23 | width: 8px;
24 | overflow: hidden;
25 | }
26 |
27 | .select-areas-delete-area {
28 | background: url('bt-delete.png');
29 | cursor: pointer;
30 | height: 16px;
31 | width: 16px;
32 | }
33 | .delete-area {
34 | position: absolute;
35 | cursor: pointer;
36 | padding: 5px;
37 | }
38 |
--------------------------------------------------------------------------------
/resources/jquery.selectareas.ie8.css:
--------------------------------------------------------------------------------
1 | .select-areas-overlay {
2 | background: none;
3 | }
4 | .select-areas-outline {
5 | background: none;
6 | visibility: hidden;
7 | }
8 | .select-areas-outline {
9 | visibility: hidden;
10 | }
11 | .select-areas-background-area {
12 | background: none !important;
13 | margin: -1px 0 0 -1px;
14 | border: 2px solid green;
15 | }
16 | .blurred {
17 | filter: none;
18 | -webkit-filter: none;
19 | -moz-filter: none;
20 | -o-filter: none;
21 | }
22 |
--------------------------------------------------------------------------------
/resources/outline.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/360Learning/jquery-select-areas/6b51d31c33aa0a97d3fd1cf25b767773da66352a/resources/outline.gif
--------------------------------------------------------------------------------