[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /* 2 * imgAreaSelect jQuery plugin 3 * version 0.9.10-wp-6.2 4 * 5 * Copyright (c) 2008-2013 Michal Wojciechowski (odyniec.net) 6 * 7 * Dual licensed under the MIT (MIT-LICENSE.txt) 8 * and GPL (GPL-LICENSE.txt) licenses. 9 * 10 * https://github.com/odyniec/imgareaselect 11 * 12 */ 13 14 (function($) { 15 16 /* 17 * Math functions will be used extensively, so it's convenient to make a few 18 * shortcuts 19 */ 20 var abs = Math.abs, 21 max = Math.max, 22 min = Math.min, 23 floor = Math.floor; 24 25 /** 26 * Create a new HTML div element 27 * 28 * @return A jQuery object representing the new element 29 */ 30 function div() { 31 return $('<div/>'); 32 } 33 34 /** 35 * imgAreaSelect initialization 36 * 37 * @param img 38 * A HTML image element to attach the plugin to 39 * @param options 40 * An options object 41 */ 42 $.imgAreaSelect = function (img, options) { 43 var 44 /* jQuery object representing the image */ 45 $img = $(img), 46 47 /* Has the image finished loading? */ 48 imgLoaded, 49 50 /* Plugin elements */ 51 52 /* Container box */ 53 $box = div(), 54 /* Selection area */ 55 $area = div(), 56 /* Border (four divs) */ 57 $border = div().add(div()).add(div()).add(div()), 58 /* Outer area (four divs) */ 59 $outer = div().add(div()).add(div()).add(div()), 60 /* Handles (empty by default, initialized in setOptions()) */ 61 $handles = $([]), 62 63 /* 64 * Additional element to work around a cursor problem in Opera 65 * (explained later) 66 */ 67 $areaOpera, 68 69 /* Image position (relative to viewport) */ 70 left, top, 71 72 /* Image offset (as returned by .offset()) */ 73 imgOfs = { left: 0, top: 0 }, 74 75 /* Image dimensions (as returned by .width() and .height()) */ 76 imgWidth, imgHeight, 77 78 /* 79 * jQuery object representing the parent element that the plugin 80 * elements are appended to 81 */ 82 $parent, 83 84 /* Parent element offset (as returned by .offset()) */ 85 parOfs = { left: 0, top: 0 }, 86 87 /* Base z-index for plugin elements */ 88 zIndex = 0, 89 90 /* Plugin elements position */ 91 position = 'absolute', 92 93 /* X/Y coordinates of the starting point for move/resize operations */ 94 startX, startY, 95 96 /* Horizontal and vertical scaling factors */ 97 scaleX, scaleY, 98 99 /* Current resize mode ("nw", "se", etc.) */ 100 resize, 101 102 /* Selection area constraints */ 103 minWidth, minHeight, maxWidth, maxHeight, 104 105 /* Aspect ratio to maintain (floating point number) */ 106 aspectRatio, 107 108 /* Are the plugin elements currently displayed? */ 109 shown, 110 111 /* Current selection (relative to parent element) */ 112 x1, y1, x2, y2, 113 114 /* Current selection (relative to scaled image) */ 115 selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 }, 116 117 /* Document element */ 118 docElem = document.documentElement, 119 120 /* User agent */ 121 ua = navigator.userAgent, 122 123 /* Various helper variables used throughout the code */ 124 $p, d, i, o, w, h, adjusted; 125 126 /* 127 * Translate selection coordinates (relative to scaled image) to viewport 128 * coordinates (relative to parent element) 129 */ 130 131 /** 132 * Translate selection X to viewport X 133 * 134 * @param x 135 * Selection X 136 * @return Viewport X 137 */ 138 function viewX(x) { 139 return x + imgOfs.left - parOfs.left; 140 } 141 142 /** 143 * Translate selection Y to viewport Y 144 * 145 * @param y 146 * Selection Y 147 * @return Viewport Y 148 */ 149 function viewY(y) { 150 return y + imgOfs.top - parOfs.top; 151 } 152 153 /* 154 * Translate viewport coordinates to selection coordinates 155 */ 156 157 /** 158 * Translate viewport X to selection X 159 * 160 * @param x 161 * Viewport X 162 * @return Selection X 163 */ 164 function selX(x) { 165 return x - imgOfs.left + parOfs.left; 166 } 167 168 /** 169 * Translate viewport Y to selection Y 170 * 171 * @param y 172 * Viewport Y 173 * @return Selection Y 174 */ 175 function selY(y) { 176 return y - imgOfs.top + parOfs.top; 177 } 178 179 /* 180 * Translate event coordinates (relative to document) to viewport 181 * coordinates 182 */ 183 184 /** 185 * Get event X and translate it to viewport X 186 * 187 * @param event 188 * The event object 189 * @return Viewport X 190 */ 191 function evX(event) { 192 return max(event.pageX || 0, touchCoords(event).x) - parOfs.left; 193 } 194 195 /** 196 * Get event Y and translate it to viewport Y 197 * 198 * @param event 199 * The event object 200 * @return Viewport Y 201 */ 202 function evY(event) { 203 return max(event.pageY || 0, touchCoords(event).y) - parOfs.top; 204 } 205 206 /** 207 * Get X and Y coordinates of a touch event 208 * 209 * @param event 210 * The event object 211 * @return Coordinates object 212 */ 213 function touchCoords(event) { 214 var oev = event.originalEvent || {}; 215 216 if (oev.touches && oev.touches.length) 217 return { x: oev.touches[0].pageX, y: oev.touches[0].pageY }; 218 else 219 return { x: 0, y: 0 }; 220 } 221 222 /** 223 * Get the current selection 224 * 225 * @param noScale 226 * If set to <code>true</code>, scaling is not applied to the 227 * returned selection 228 * @return Selection object 229 */ 230 function getSelection(noScale) { 231 var sx = noScale || scaleX, sy = noScale || scaleY; 232 233 return { x1: floor(selection.x1 * sx), 234 y1: floor(selection.y1 * sy), 235 x2: floor(selection.x2 * sx), 236 y2: floor(selection.y2 * sy), 237 width: floor(selection.x2 * sx) - floor(selection.x1 * sx), 238 height: floor(selection.y2 * sy) - floor(selection.y1 * sy) }; 239 } 240 241 /** 242 * Set the current selection 243 * 244 * @param x1 245 * X coordinate of the upper left corner of the selection area 246 * @param y1 247 * Y coordinate of the upper left corner of the selection area 248 * @param x2 249 * X coordinate of the lower right corner of the selection area 250 * @param y2 251 * Y coordinate of the lower right corner of the selection area 252 * @param noScale 253 * If set to <code>true</code>, scaling is not applied to the 254 * new selection 255 */ 256 function setSelection(x1, y1, x2, y2, noScale) { 257 var sx = noScale || scaleX, sy = noScale || scaleY; 258 259 selection = { 260 x1: floor(x1 / sx || 0), 261 y1: floor(y1 / sy || 0), 262 x2: floor(x2 / sx || 0), 263 y2: floor(y2 / sy || 0) 264 }; 265 266 selection.width = selection.x2 - selection.x1; 267 selection.height = selection.y2 - selection.y1; 268 } 269 270 /** 271 * Recalculate image and parent offsets 272 */ 273 function adjust() { 274 /* 275 * Do not adjust if image has not yet loaded or if width is not a 276 * positive number. The latter might happen when imgAreaSelect is put 277 * on a parent element which is then hidden. 278 */ 279 if (!imgLoaded || !$img.width()) 280 return; 281 282 /* 283 * Get image offset. The .offset() method returns float values, so they 284 * need to be rounded. 285 */ 286 imgOfs = { left: floor($img.offset().left), top: floor($img.offset().top) }; 287 288 /* Get image dimensions */ 289 imgWidth = $img.innerWidth(); 290 imgHeight = $img.innerHeight(); 291 292 imgOfs.top += ($img.outerHeight() - imgHeight) >> 1; 293 imgOfs.left += ($img.outerWidth() - imgWidth) >> 1; 294 295 /* Set minimum and maximum selection area dimensions */ 296 minWidth = floor(options.minWidth / scaleX) || 0; 297 minHeight = floor(options.minHeight / scaleY) || 0; 298 maxWidth = floor(min(options.maxWidth / scaleX || 1<<24, imgWidth)); 299 maxHeight = floor(min(options.maxHeight / scaleY || 1<<24, imgHeight)); 300 301 /* 302 * Workaround for jQuery 1.3.2 incorrect offset calculation, originally 303 * observed in Safari 3. Firefox 2 is also affected. 304 */ 305 if ($().jquery == '1.3.2' && position == 'fixed' && 306 !docElem['getBoundingClientRect']) 307 { 308 imgOfs.top += max(document.body.scrollTop, docElem.scrollTop); 309 imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft); 310 } 311 312 /* Determine parent element offset */ 313 parOfs = /absolute|relative/.test($parent.css('position')) ? 314 { left: floor($parent.offset().left) - $parent.scrollLeft(), 315 top: floor($parent.offset().top) - $parent.scrollTop() } : 316 position == 'fixed' ? 317 { left: $(document).scrollLeft(), top: $(document).scrollTop() } : 318 { left: 0, top: 0 }; 319 320 left = viewX(0); 321 top = viewY(0); 322 323 /* 324 * Check if selection area is within image boundaries, adjust if 325 * necessary 326 */ 327 if (selection.x2 > imgWidth || selection.y2 > imgHeight) 328 doResize(); 329 } 330 331 /** 332 * Update plugin elements 333 * 334 * @param resetKeyPress 335 * If set to <code>false</code>, this instance's keypress 336 * event handler is not activated 337 */ 338 function update(resetKeyPress) { 339 /* If plugin elements are hidden, do nothing */ 340 if (!shown) return; 341 342 /* 343 * Set the position and size of the container box and the selection area 344 * inside it 345 */ 346 $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) }) 347 .add($area).width(w = selection.width).height(h = selection.height); 348 349 /* 350 * Reset the position of selection area, borders, and handles (IE6/IE7 351 * position them incorrectly if we don't do this) 352 */ 353 $area.add($border).add($handles).css({ left: 0, top: 0 }); 354 355 /* Set border dimensions */ 356 $border 357 .width(max(w - $border.outerWidth() + $border.innerWidth(), 0)) 358 .height(max(h - $border.outerHeight() + $border.innerHeight(), 0)); 359 360 /* Arrange the outer area elements */ 361 $($outer[0]).css({ left: left, top: top, 362 width: selection.x1, height: imgHeight }); 363 $($outer[1]).css({ left: left + selection.x1, top: top, 364 width: w, height: selection.y1 }); 365 $($outer[2]).css({ left: left + selection.x2, top: top, 366 width: imgWidth - selection.x2, height: imgHeight }); 367 $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2, 368 width: w, height: imgHeight - selection.y2 }); 369 370 w -= $handles.outerWidth(); 371 h -= $handles.outerHeight(); 372 373 /* Arrange handles */ 374 switch ($handles.length) { 375 case 8: 376 $($handles[4]).css({ left: w >> 1 }); 377 $($handles[5]).css({ left: w, top: h >> 1 }); 378 $($handles[6]).css({ left: w >> 1, top: h }); 379 $($handles[7]).css({ top: h >> 1 }); 380 case 4: 381 $handles.slice(1,3).css({ left: w }); 382 $handles.slice(2,4).css({ top: h }); 383 } 384 385 if (resetKeyPress !== false) { 386 /* 387 * Need to reset the document keypress event handler -- unbind the 388 * current handler 389 */ 390 if ($.imgAreaSelect.onKeyPress != docKeyPress) 391 $(document).off($.imgAreaSelect.keyPress, 392 $.imgAreaSelect.onKeyPress); 393 394 if (options.keys) 395 /* 396 * Set the document keypress event handler to this instance's 397 * docKeyPress() function 398 */ 399 $(document).on( $.imgAreaSelect.keyPress, function() { 400 $.imgAreaSelect.onKeyPress = docKeyPress; 401 }); 402 } 403 404 /* 405 * Internet Explorer displays 1px-wide dashed borders incorrectly by 406 * filling the spaces between dashes with white. Toggling the margin 407 * property between 0 and "auto" fixes this in IE6 and IE7 (IE8 is still 408 * broken). This workaround is not perfect, as it requires setTimeout() 409 * and thus causes the border to flicker a bit, but I haven't found a 410 * better solution. 411 * 412 * Note: This only happens with CSS borders, set with the borderWidth, 413 * borderOpacity, borderColor1, and borderColor2 options (which are now 414 * deprecated). Borders created with GIF background images are fine. 415 */ 416 if (msie && $border.outerWidth() - $border.innerWidth() == 2) { 417 $border.css('margin', 0); 418 setTimeout(function () { $border.css('margin', 'auto'); }, 0); 419 } 420 } 421 422 /** 423 * Do the complete update sequence: recalculate offsets, update the 424 * elements, and set the correct values of x1, y1, x2, and y2. 425 * 426 * @param resetKeyPress 427 * If set to <code>false</code>, this instance's keypress 428 * event handler is not activated 429 */ 430 function doUpdate(resetKeyPress) { 431 adjust(); 432 update(resetKeyPress); 433 updateSelectionRelativeToParentElement(); 434 } 435 436 /** 437 * Set the correct values of x1, y1, x2, and y2. 438 */ 439 function updateSelectionRelativeToParentElement() { 440 x1 = viewX(selection.x1); y1 = viewY(selection.y1); 441 x2 = viewX(selection.x2); y2 = viewY(selection.y2); 442 } 443 444 /** 445 * Hide or fade out an element (or multiple elements) 446 * 447 * @param $elem 448 * A jQuery object containing the element(s) to hide/fade out 449 * @param fn 450 * Callback function to be called when fadeOut() completes 451 */ 452 function hide($elem, fn) { 453 options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide(); 454 } 455 456 /** 457 * Selection area mousemove event handler 458 * 459 * @param event 460 * The event object 461 */ 462 function areaMouseMove(event) { 463 var x = selX(evX(event)) - selection.x1, 464 y = selY(evY(event)) - selection.y1; 465 466 if (!adjusted) { 467 adjust(); 468 adjusted = true; 469 470 $box.one('mouseout', function () { adjusted = false; }); 471 } 472 473 /* Clear the resize mode */ 474 resize = ''; 475 476 if (options.resizable) { 477 /* 478 * Check if the mouse pointer is over the resize margin area and set 479 * the resize mode accordingly 480 */ 481 if (y <= options.resizeMargin) 482 resize = 'n'; 483 else if (y >= selection.height - options.resizeMargin) 484 resize = 's'; 485 if (x <= options.resizeMargin) 486 resize += 'w'; 487 else if (x >= selection.width - options.resizeMargin) 488 resize += 'e'; 489 } 490 491 $box.css('cursor', resize ? resize + '-resize' : 492 options.movable ? 'move' : ''); 493 if ($areaOpera) 494 $areaOpera.toggle(); 495 } 496 497 /** 498 * Document mouseup event handler 499 * 500 * @param event 501 * The event object 502 */ 503 function docMouseUp(event) { 504 /* Set back the default cursor */ 505 $('body').css('cursor', ''); 506 /* 507 * If autoHide is enabled, or if the selection has zero width/height, 508 * hide the selection and the outer area 509 */ 510 if (options.autoHide || selection.width * selection.height == 0) 511 hide($box.add($outer), function () { $(this).hide(); }); 512 513 $(document).off('mousemove touchmove', selectingMouseMove); 514 $box.on('mousemove touchmove', areaMouseMove); 515 516 options.onSelectEnd(img, getSelection()); 517 } 518 519 /** 520 * Selection area mousedown event handler 521 * 522 * @param event 523 * The event object 524 * @return false 525 */ 526 function areaMouseDown(event) { 527 if (event.type == 'mousedown' && event.which != 1) return false; 528 529 /* 530 * With mobile browsers, there is no "moving the pointer over" action, 531 * so we need to simulate one mousemove event happening prior to 532 * mousedown/touchstart. 533 */ 534 areaMouseMove(event); 535 536 adjust(); 537 538 if (resize) { 539 /* Resize mode is in effect */ 540 $('body').css('cursor', resize + '-resize'); 541 542 x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']); 543 y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']); 544 545 $(document).on('mousemove touchmove', selectingMouseMove) 546 .one('mouseup touchend', docMouseUp); 547 $box.off('mousemove touchmove', areaMouseMove); 548 } 549 else if (options.movable) { 550 startX = left + selection.x1 - evX(event); 551 startY = top + selection.y1 - evY(event); 552 553 $box.off('mousemove touchmove', areaMouseMove); 554 555 $(document).on('mousemove touchmove', movingMouseMove) 556 .one('mouseup touchend', function () { 557 options.onSelectEnd(img, getSelection()); 558 559 $(document).off('mousemove touchmove', movingMouseMove); 560 $box.on('mousemove touchmove', areaMouseMove); 561 }); 562 } 563 else 564 $img.mousedown(event); 565 566 return false; 567 } 568 569 /** 570 * Adjust the x2/y2 coordinates to maintain aspect ratio (if defined) 571 * 572 * @param xFirst 573 * If set to <code>true</code>, calculate x2 first. Otherwise, 574 * calculate y2 first. 575 */ 576 function fixAspectRatio(xFirst) { 577 if (aspectRatio) 578 if (xFirst) { 579 x2 = max(left, min(left + imgWidth, 580 x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))); 581 y2 = floor(max(top, min(top + imgHeight, 582 y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)))); 583 x2 = floor(x2); 584 } 585 else { 586 y2 = max(top, min(top + imgHeight, 587 y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))); 588 x2 = floor(max(left, min(left + imgWidth, 589 x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)))); 590 y2 = floor(y2); 591 } 592 } 593 594 /** 595 * Resize the selection area respecting the minimum/maximum dimensions and 596 * aspect ratio 597 */ 598 function doResize() { 599 /* 600 * Make sure x1, x2, y1, y2 are initialized to avoid the following calculation 601 * getting incorrect results. 602 */ 603 if ( x1 == null || x2 == null || y1 == null || y2 == null ) { 604 updateSelectionRelativeToParentElement(); 605 } 606 607 /* 608 * Make sure the top left corner of the selection area stays within 609 * image boundaries (it might not if the image source was dynamically 610 * changed). 611 */ 612 x1 = min(x1, left + imgWidth); 613 y1 = min(y1, top + imgHeight); 614 615 if (abs(x2 - x1) < minWidth) { 616 /* Selection width is smaller than minWidth */ 617 x2 = x1 - minWidth * (x2 < x1 || -1); 618 619 if (x2 < left) 620 x1 = left + minWidth; 621 else if (x2 > left + imgWidth) 622 x1 = left + imgWidth - minWidth; 623 } 624 625 if (abs(y2 - y1) < minHeight) { 626 /* Selection height is smaller than minHeight */ 627 y2 = y1 - minHeight * (y2 < y1 || -1); 628 629 if (y2 < top) 630 y1 = top + minHeight; 631 else if (y2 > top + imgHeight) 632 y1 = top + imgHeight - minHeight; 633 } 634 635 x2 = max(left, min(x2, left + imgWidth)); 636 y2 = max(top, min(y2, top + imgHeight)); 637 638 fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio); 639 640 if (abs(x2 - x1) > maxWidth) { 641 /* Selection width is greater than maxWidth */ 642 x2 = x1 - maxWidth * (x2 < x1 || -1); 643 fixAspectRatio(); 644 } 645 646 if (abs(y2 - y1) > maxHeight) { 647 /* Selection height is greater than maxHeight */ 648 y2 = y1 - maxHeight * (y2 < y1 || -1); 649 fixAspectRatio(true); 650 } 651 652 selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)), 653 y1: selY(min(y1, y2)), y2: selY(max(y1, y2)), 654 width: abs(x2 - x1), height: abs(y2 - y1) }; 655 656 update(); 657 658 options.onSelectChange(img, getSelection()); 659 } 660 661 /** 662 * Mousemove event handler triggered when the user is selecting an area 663 * 664 * @param event 665 * The event object 666 * @return false 667 */ 668 function selectingMouseMove(event) { 669 x2 = /w|e|^$/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2); 670 y2 = /n|s|^$/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2); 671 672 doResize(); 673 674 return false; 675 } 676 677 /** 678 * Move the selection area 679 * 680 * @param newX1 681 * New viewport X1 682 * @param newY1 683 * New viewport Y1 684 */ 685 function doMove(newX1, newY1) { 686 x2 = (x1 = newX1) + selection.width; 687 y2 = (y1 = newY1) + selection.height; 688 689 $.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2), 690 y2: selY(y2) }); 691 692 update(); 693 694 options.onSelectChange(img, getSelection()); 695 } 696 697 /** 698 * Mousemove event handler triggered when the selection area is being moved 699 * 700 * @param event 701 * The event object 702 * @return false 703 */ 704 function movingMouseMove(event) { 705 x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width)); 706 y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height)); 707 708 doMove(x1, y1); 709 710 event.preventDefault(); 711 return false; 712 } 713 714 /** 715 * Start selection 716 */ 717 function startSelection() { 718 $(document).off('mousemove touchmove', startSelection); 719 adjust(); 720 721 x2 = x1; 722 y2 = y1; 723 doResize(); 724 725 resize = ''; 726 727 if (!$outer.is(':visible')) 728 /* Show the plugin elements */ 729 $box.add($outer).hide().fadeIn(options.fadeSpeed||0); 730 731 shown = true; 732 733 $(document).off('mouseup touchend', cancelSelection) 734 .on('mousemove touchmove', selectingMouseMove) 735 .one('mouseup touchend', docMouseUp); 736 $box.off('mousemove touchmove', areaMouseMove); 737 738 options.onSelectStart(img, getSelection()); 739 } 740 741 /** 742 * Cancel selection 743 */ 744 function cancelSelection() { 745 $(document).off('mousemove touchmove', startSelection) 746 .off('mouseup touchend', cancelSelection); 747 hide($box.add($outer)); 748 749 setSelection(selX(x1), selY(y1), selX(x1), selY(y1)); 750 751 /* If this is an API call, callback functions should not be triggered */ 752 if (!(this instanceof $.imgAreaSelect)) { 753 options.onSelectChange(img, getSelection()); 754 options.onSelectEnd(img, getSelection()); 755 } 756 } 757 758 /** 759 * Image mousedown event handler 760 * 761 * @param event 762 * The event object 763 * @return false 764 */ 765 function imgMouseDown(event) { 766 /* Ignore the event if animation is in progress */ 767 if (event.which > 1 || $outer.is(':animated')) return false; 768 769 adjust(); 770 startX = x1 = evX(event); 771 startY = y1 = evY(event); 772 773 /* Selection will start when the mouse is moved */ 774 $(document).on({ 'mousemove touchmove': startSelection, 775 'mouseup touchend': cancelSelection }); 776 777 return false; 778 } 779 780 /** 781 * Window resize event handler 782 */ 783 function windowResize() { 784 doUpdate(false); 785 } 786 787 /** 788 * Image load event handler. This is the final part of the initialization 789 * process. 790 */ 791 function imgLoad() { 792 imgLoaded = true; 793 794 /* Set options */ 795 setOptions(options = $.extend({ 796 classPrefix: 'imgareaselect', 797 movable: true, 798 parent: 'body', 799 resizable: true, 800 resizeMargin: 10, 801 onInit: function () {}, 802 onSelectStart: function () {}, 803 onSelectChange: function () {}, 804 onSelectEnd: function () {} 805 }, options)); 806 807 $box.add($outer).css({ visibility: '' }); 808 809 if (options.show) { 810 shown = true; 811 adjust(); 812 update(); 813 $box.add($outer).hide().fadeIn(options.fadeSpeed||0); 814 } 815 816 /* 817 * Call the onInit callback. The setTimeout() call is used to ensure 818 * that the plugin has been fully initialized and the object instance is 819 * available (so that it can be obtained in the callback). 820 */ 821 setTimeout(function () { options.onInit(img, getSelection()); }, 0); 822 } 823 824 /** 825 * Document keypress event handler 826 * 827 * @param event 828 * The event object 829 * @return false 830 */ 831 var docKeyPress = function(event) { 832 var k = options.keys, d, t, key = event.keyCode; 833 834 d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt : 835 !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl : 836 !isNaN(k.shift) && event.shiftKey ? k.shift : 837 !isNaN(k.arrows) ? k.arrows : 10; 838 839 if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) || 840 (k.ctrl == 'resize' && event.ctrlKey) || 841 (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey))) 842 { 843 /* Resize selection */ 844 845 switch (key) { 846 case 37: 847 /* Left */ 848 d = -d; 849 case 39: 850 /* Right */ 851 t = max(x1, x2); 852 x1 = min(x1, x2); 853 x2 = max(t + d, x1); 854 fixAspectRatio(); 855 break; 856 case 38: 857 /* Up */ 858 d = -d; 859 case 40: 860 /* Down */ 861 t = max(y1, y2); 862 y1 = min(y1, y2); 863 y2 = max(t + d, y1); 864 fixAspectRatio(true); 865 break; 866 default: 867 return; 868 } 869 870 doResize(); 871 } 872 else { 873 /* Move selection */ 874 875 x1 = min(x1, x2); 876 y1 = min(y1, y2); 877 878 switch (key) { 879 case 37: 880 /* Left */ 881 doMove(max(x1 - d, left), y1); 882 break; 883 case 38: 884 /* Up */ 885 doMove(x1, max(y1 - d, top)); 886 break; 887 case 39: 888 /* Right */ 889 doMove(x1 + min(d, imgWidth - selX(x2)), y1); 890 break; 891 case 40: 892 /* Down */ 893 doMove(x1, y1 + min(d, imgHeight - selY(y2))); 894 break; 895 default: 896 return; 897 } 898 } 899 900 return false; 901 }; 902 903 /** 904 * Apply style options to plugin element (or multiple elements) 905 * 906 * @param $elem 907 * A jQuery object representing the element(s) to style 908 * @param props 909 * An object that maps option names to corresponding CSS 910 * properties 911 */ 912 function styleOptions($elem, props) { 913 for (var option in props) 914 if (options[option] !== undefined) 915 $elem.css(props[option], options[option]); 916 } 917 918 /** 919 * Set plugin options 920 * 921 * @param newOptions 922 * The new options object 923 */ 924 function setOptions(newOptions) { 925 if (newOptions.parent) 926 ($parent = $(newOptions.parent)).append($box.add($outer)); 927 928 /* Merge the new options with the existing ones */ 929 $.extend(options, newOptions); 930 931 adjust(); 932 933 if (newOptions.handles != null) { 934 /* Recreate selection area handles */ 935 $handles.remove(); 936 $handles = $([]); 937 938 i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0; 939 940 while (i--) 941 $handles = $handles.add(div()); 942 943 /* Add a class to handles and set the CSS properties */ 944 $handles.addClass(options.classPrefix + '-handle').css({ 945 position: 'absolute', 946 /* 947 * The font-size property needs to be set to zero, otherwise 948 * Internet Explorer makes the handles too large 949 */ 950 fontSize: '0', 951 zIndex: zIndex + 1 || 1 952 }); 953 954 /* 955 * If handle width/height has not been set with CSS rules, set the 956 * default 5px 957 */ 958 if (!parseInt($handles.css('width')) >= 0) 959 $handles.width(10).height(10); 960 961 /* 962 * If the borderWidth option is in use, add a solid border to 963 * handles 964 */ 965 if (o = options.borderWidth) 966 $handles.css({ borderWidth: o, borderStyle: 'solid' }); 967 968 /* Apply other style options */ 969 styleOptions($handles, { borderColor1: 'border-color', 970 borderColor2: 'background-color', 971 borderOpacity: 'opacity' }); 972 } 973 974 /* Calculate scale factors */ 975 scaleX = options.imageWidth / imgWidth || 1; 976 scaleY = options.imageHeight / imgHeight || 1; 977 978 /* Set selection */ 979 if (newOptions.x1 != null) { 980 setSelection(newOptions.x1, newOptions.y1, newOptions.x2, 981 newOptions.y2); 982 newOptions.show = !newOptions.hide; 983 } 984 985 if (newOptions.keys) 986 /* Enable keyboard support */ 987 options.keys = $.extend({ shift: 1, ctrl: 'resize' }, 988 newOptions.keys); 989 990 /* Add classes to plugin elements */ 991 $outer.addClass(options.classPrefix + '-outer'); 992 $area.addClass(options.classPrefix + '-selection'); 993 for (i = 0; i++ < 4;) 994 $($border[i-1]).addClass(options.classPrefix + '-border' + i); 995 996 /* Apply style options */ 997 styleOptions($area, { selectionColor: 'background-color', 998 selectionOpacity: 'opacity' }); 999 styleOptions($border, { borderOpacity: 'opacity', 1000 borderWidth: 'border-width' }); 1001 styleOptions($outer, { outerColor: 'background-color', 1002 outerOpacity: 'opacity' }); 1003 if (o = options.borderColor1) 1004 $($border[0]).css({ borderStyle: 'solid', borderColor: o }); 1005 if (o = options.borderColor2) 1006 $($border[1]).css({ borderStyle: 'dashed', borderColor: o }); 1007 1008 /* Append all the selection area elements to the container box */ 1009 $box.append($area.add($border).add($areaOpera)).append($handles); 1010 1011 if (msie) { 1012 if (o = ($outer.css('filter')||'').match(/opacity=(\d+)/)) 1013 $outer.css('opacity', o[1]/100); 1014 if (o = ($border.css('filter')||'').match(/opacity=(\d+)/)) 1015 $border.css('opacity', o[1]/100); 1016 } 1017 1018 if (newOptions.hide) 1019 hide($box.add($outer)); 1020 else if (newOptions.show && imgLoaded) { 1021 shown = true; 1022 $box.add($outer).fadeIn(options.fadeSpeed||0); 1023 doUpdate(); 1024 } 1025 1026 /* Calculate the aspect ratio factor */ 1027 aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1]; 1028 1029 $img.add($outer).off('mousedown', imgMouseDown); 1030 1031 if (options.disable || options.enable === false) { 1032 /* Disable the plugin */ 1033 $box.off({ 'mousemove touchmove': areaMouseMove, 1034 'mousedown touchstart': areaMouseDown }); 1035 $(window).off('resize', windowResize); 1036 } 1037 else { 1038 if (options.enable || options.disable === false) { 1039 /* Enable the plugin */ 1040 if (options.resizable || options.movable) 1041 $box.on({ 'mousemove touchmove': areaMouseMove, 1042 'mousedown touchstart': areaMouseDown }); 1043 1044 $(window).on( 'resize', windowResize); 1045 } 1046 1047 if (!options.persistent) 1048 $img.add($outer).on('mousedown touchstart', imgMouseDown); 1049 } 1050 1051 options.enable = options.disable = undefined; 1052 } 1053 1054 /** 1055 * Remove plugin completely 1056 */ 1057 this.remove = function () { 1058 /* 1059 * Call setOptions with { disable: true } to unbind the event handlers 1060 */ 1061 setOptions({ disable: true }); 1062 $box.add($outer).remove(); 1063 }; 1064 1065 /* 1066 * Public API 1067 */ 1068 1069 /** 1070 * Get current options 1071 * 1072 * @return An object containing the set of options currently in use 1073 */ 1074 this.getOptions = function () { return options; }; 1075 1076 /** 1077 * Set plugin options 1078 * 1079 * @param newOptions 1080 * The new options object 1081 */ 1082 this.setOptions = setOptions; 1083 1084 /** 1085 * Get the current selection 1086 * 1087 * @param noScale 1088 * If set to <code>true</code>, scaling is not applied to the 1089 * returned selection 1090 * @return Selection object 1091 */ 1092 this.getSelection = getSelection; 1093 1094 /** 1095 * Set the current selection 1096 * 1097 * @param x1 1098 * X coordinate of the upper left corner of the selection area 1099 * @param y1 1100 * Y coordinate of the upper left corner of the selection area 1101 * @param x2 1102 * X coordinate of the lower right corner of the selection area 1103 * @param y2 1104 * Y coordinate of the lower right corner of the selection area 1105 * @param noScale 1106 * If set to <code>true</code>, scaling is not applied to the 1107 * new selection 1108 */ 1109 this.setSelection = setSelection; 1110 1111 /** 1112 * Cancel selection 1113 */ 1114 this.cancelSelection = cancelSelection; 1115 1116 /** 1117 * Update plugin elements 1118 * 1119 * @param resetKeyPress 1120 * If set to <code>false</code>, this instance's keypress 1121 * event handler is not activated 1122 */ 1123 this.update = doUpdate; 1124 1125 /* Do the dreaded browser detection */ 1126 var msie = (/msie ([\w.]+)/i.exec(ua)||[])[1], 1127 opera = /opera/i.test(ua), 1128 safari = /webkit/i.test(ua) && !/chrome/i.test(ua); 1129 1130 /* 1131 * Traverse the image's parent elements (up to <body>) and find the 1132 * highest z-index 1133 */ 1134 $p = $img; 1135 1136 while ($p.length) { 1137 zIndex = max(zIndex, 1138 !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex); 1139 /* Also check if any of the ancestor elements has fixed position */ 1140 if ($p.css('position') == 'fixed') 1141 position = 'fixed'; 1142 1143 $p = $p.parent(':not(body)'); 1144 } 1145 1146 /* 1147 * If z-index is given as an option, it overrides the one found by the 1148 * above loop 1149 */ 1150 zIndex = options.zIndex || zIndex; 1151 1152 if (msie) 1153 $img.attr('unselectable', 'on'); 1154 1155 /* 1156 * In MSIE and WebKit, we need to use the keydown event instead of keypress 1157 */ 1158 $.imgAreaSelect.keyPress = msie || safari ? 'keydown' : 'keypress'; 1159 1160 /* 1161 * There is a bug affecting the CSS cursor property in Opera (observed in 1162 * versions up to 10.00) that prevents the cursor from being updated unless 1163 * the mouse leaves and enters the element again. To trigger the mouseover 1164 * event, we're adding an additional div to $box and we're going to toggle 1165 * it when mouse moves inside the selection area. 1166 */ 1167 if (opera) 1168 $areaOpera = div().css({ width: '100%', height: '100%', 1169 position: 'absolute', zIndex: zIndex + 2 || 2 }); 1170 1171 /* 1172 * We initially set visibility to "hidden" as a workaround for a weird 1173 * behaviour observed in Google Chrome 1.0.154.53 (on Windows XP). Normally 1174 * we would just set display to "none", but, for some reason, if we do so 1175 * then Chrome refuses to later display the element with .show() or 1176 * .fadeIn(). 1177 */ 1178 $box.add($outer).css({ visibility: 'hidden', position: position, 1179 overflow: 'hidden', zIndex: zIndex || '0' }); 1180 $box.css({ zIndex: zIndex + 2 || 2 }); 1181 $area.add($border).css({ position: 'absolute', fontSize: '0' }); 1182 1183 /* 1184 * If the image has been fully loaded, or if it is not really an image (eg. 1185 * a div), call imgLoad() immediately; otherwise, bind it to be called once 1186 * on image load event. 1187 */ 1188 img.complete || img.readyState == 'complete' || !$img.is('img') ? 1189 imgLoad() : $img.one('load', imgLoad); 1190 1191 /* 1192 * MSIE 9.0 doesn't always fire the image load event -- resetting the src 1193 * attribute seems to trigger it. The check is for version 7 and above to 1194 * accommodate for MSIE 9 running in compatibility mode. 1195 */ 1196 if (!imgLoaded && msie && msie >= 7) 1197 img.src = img.src; 1198 }; 1199 1200 /** 1201 * Invoke imgAreaSelect on a jQuery object containing the image(s) 1202 * 1203 * @param options 1204 * Options object 1205 * @return The jQuery object or a reference to imgAreaSelect instance (if the 1206 * <code>instance</code> option was specified) 1207 */ 1208 $.fn.imgAreaSelect = function (options) { 1209 options = options || {}; 1210 1211 this.each(function () { 1212 /* Is there already an imgAreaSelect instance bound to this element? */ 1213 if ($(this).data('imgAreaSelect')) { 1214 /* Yes there is -- is it supposed to be removed? */ 1215 if (options.remove) { 1216 /* Remove the plugin */ 1217 $(this).data('imgAreaSelect').remove(); 1218 $(this).removeData('imgAreaSelect'); 1219 } 1220 else 1221 /* Reset options */ 1222 $(this).data('imgAreaSelect').setOptions(options); 1223 } 1224 else if (!options.remove) { 1225 /* No exising instance -- create a new one */ 1226 1227 /* 1228 * If neither the "enable" nor the "disable" option is present, add 1229 * "enable" as the default 1230 */ 1231 if (options.enable === undefined && options.disable === undefined) 1232 options.enable = true; 1233 1234 $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options)); 1235 } 1236 }); 1237 1238 if (options.instance) 1239 /* 1240 * Return the imgAreaSelect instance bound to the first element in the 1241 * set 1242 */ 1243 return $(this).data('imgAreaSelect'); 1244 1245 return this; 1246 }; 1247 1248 })(jQuery);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |