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