github.com/apremalal/vamps-core@v1.0.1-0.20161221121535-d430b56ec174/server/webapps/app/base/plugins/jcrop/js/jquery.Jcrop.js (about) 1 /** 2 * jquery.Jcrop.js v0.9.12 3 * jQuery Image Cropping Plugin - released under MIT License 4 * Author: Kelly Hallman <khallman@gmail.com> 5 * http://github.com/tapmodo/Jcrop 6 * Copyright (c) 2008-2013 Tapmodo Interactive LLC {{{ 7 * 8 * Permission is hereby granted, free of charge, to any person 9 * obtaining a copy of this software and associated documentation 10 * files (the "Software"), to deal in the Software without 11 * restriction, including without limitation the rights to use, 12 * copy, modify, merge, publish, distribute, sublicense, and/or sell 13 * copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following 15 * conditions: 16 * 17 * The above copyright notice and this permission notice shall be 18 * included in all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 * OTHER DEALINGS IN THE SOFTWARE. 28 * 29 * }}} 30 */ 31 32 (function ($) { 33 34 $.Jcrop = function (obj, opt) { 35 var options = $.extend({}, $.Jcrop.defaults), 36 docOffset, 37 _ua = navigator.userAgent.toLowerCase(), 38 is_msie = /msie/.test(_ua), 39 ie6mode = /msie [1-6]\./.test(_ua); 40 41 // Internal Methods {{{ 42 function px(n) { 43 return Math.round(n) + 'px'; 44 } 45 function cssClass(cl) { 46 return options.baseClass + '-' + cl; 47 } 48 function supportsColorFade() { 49 return $.fx.step.hasOwnProperty('backgroundColor'); 50 } 51 function getPos(obj) //{{{ 52 { 53 var pos = $(obj).offset(); 54 return [pos.left, pos.top]; 55 } 56 //}}} 57 function mouseAbs(e) //{{{ 58 { 59 return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])]; 60 } 61 //}}} 62 function setOptions(opt) //{{{ 63 { 64 if (typeof(opt) !== 'object') opt = {}; 65 options = $.extend(options, opt); 66 67 $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) { 68 if (typeof(options[e]) !== 'function') options[e] = function () {}; 69 }); 70 } 71 //}}} 72 function startDragMode(mode, pos, touch) //{{{ 73 { 74 docOffset = getPos($img); 75 Tracker.setCursor(mode === 'move' ? mode : mode + '-resize'); 76 77 if (mode === 'move') { 78 return Tracker.activateHandlers(createMover(pos), doneSelect, touch); 79 } 80 81 var fc = Coords.getFixed(); 82 var opp = oppLockCorner(mode); 83 var opc = Coords.getCorner(oppLockCorner(opp)); 84 85 Coords.setPressed(Coords.getCorner(opp)); 86 Coords.setCurrent(opc); 87 88 Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect, touch); 89 } 90 //}}} 91 function dragmodeHandler(mode, f) //{{{ 92 { 93 return function (pos) { 94 if (!options.aspectRatio) { 95 switch (mode) { 96 case 'e': 97 pos[1] = f.y2; 98 break; 99 case 'w': 100 pos[1] = f.y2; 101 break; 102 case 'n': 103 pos[0] = f.x2; 104 break; 105 case 's': 106 pos[0] = f.x2; 107 break; 108 } 109 } else { 110 switch (mode) { 111 case 'e': 112 pos[1] = f.y + 1; 113 break; 114 case 'w': 115 pos[1] = f.y + 1; 116 break; 117 case 'n': 118 pos[0] = f.x + 1; 119 break; 120 case 's': 121 pos[0] = f.x + 1; 122 break; 123 } 124 } 125 Coords.setCurrent(pos); 126 Selection.update(); 127 }; 128 } 129 //}}} 130 function createMover(pos) //{{{ 131 { 132 var lloc = pos; 133 KeyManager.watchKeys(); 134 135 return function (pos) { 136 Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]); 137 lloc = pos; 138 139 Selection.update(); 140 }; 141 } 142 //}}} 143 function oppLockCorner(ord) //{{{ 144 { 145 switch (ord) { 146 case 'n': 147 return 'sw'; 148 case 's': 149 return 'nw'; 150 case 'e': 151 return 'nw'; 152 case 'w': 153 return 'ne'; 154 case 'ne': 155 return 'sw'; 156 case 'nw': 157 return 'se'; 158 case 'se': 159 return 'nw'; 160 case 'sw': 161 return 'ne'; 162 } 163 } 164 //}}} 165 function createDragger(ord) //{{{ 166 { 167 return function (e) { 168 if (options.disabled) { 169 return false; 170 } 171 if ((ord === 'move') && !options.allowMove) { 172 return false; 173 } 174 175 // Fix position of crop area when dragged the very first time. 176 // Necessary when crop image is in a hidden element when page is loaded. 177 docOffset = getPos($img); 178 179 btndown = true; 180 startDragMode(ord, mouseAbs(e)); 181 e.stopPropagation(); 182 e.preventDefault(); 183 return false; 184 }; 185 } 186 //}}} 187 function presize($obj, w, h) //{{{ 188 { 189 var nw = $obj.width(), 190 nh = $obj.height(); 191 if ((nw > w) && w > 0) { 192 nw = w; 193 nh = (w / $obj.width()) * $obj.height(); 194 } 195 if ((nh > h) && h > 0) { 196 nh = h; 197 nw = (h / $obj.height()) * $obj.width(); 198 } 199 xscale = $obj.width() / nw; 200 yscale = $obj.height() / nh; 201 $obj.width(nw).height(nh); 202 } 203 //}}} 204 function unscale(c) //{{{ 205 { 206 return { 207 x: c.x * xscale, 208 y: c.y * yscale, 209 x2: c.x2 * xscale, 210 y2: c.y2 * yscale, 211 w: c.w * xscale, 212 h: c.h * yscale 213 }; 214 } 215 //}}} 216 function doneSelect(pos) //{{{ 217 { 218 var c = Coords.getFixed(); 219 if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) { 220 Selection.enableHandles(); 221 Selection.done(); 222 } else { 223 Selection.release(); 224 } 225 Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); 226 } 227 //}}} 228 function newSelection(e) //{{{ 229 { 230 if (options.disabled) { 231 return false; 232 } 233 if (!options.allowSelect) { 234 return false; 235 } 236 btndown = true; 237 docOffset = getPos($img); 238 Selection.disableHandles(); 239 Tracker.setCursor('crosshair'); 240 var pos = mouseAbs(e); 241 Coords.setPressed(pos); 242 Selection.update(); 243 Tracker.activateHandlers(selectDrag, doneSelect, e.type.substring(0,5)==='touch'); 244 KeyManager.watchKeys(); 245 246 e.stopPropagation(); 247 e.preventDefault(); 248 return false; 249 } 250 //}}} 251 function selectDrag(pos) //{{{ 252 { 253 Coords.setCurrent(pos); 254 Selection.update(); 255 } 256 //}}} 257 function newTracker() //{{{ 258 { 259 var trk = $('<div></div>').addClass(cssClass('tracker')); 260 if (is_msie) { 261 trk.css({ 262 opacity: 0, 263 backgroundColor: 'white' 264 }); 265 } 266 return trk; 267 } 268 //}}} 269 270 // }}} 271 // Initialization {{{ 272 // Sanitize some options {{{ 273 if (typeof(obj) !== 'object') { 274 obj = $(obj)[0]; 275 } 276 if (typeof(opt) !== 'object') { 277 opt = {}; 278 } 279 // }}} 280 setOptions(opt); 281 // Initialize some jQuery objects {{{ 282 // The values are SET on the image(s) for the interface 283 // If the original image has any of these set, they will be reset 284 // However, if you destroy() the Jcrop instance the original image's 285 // character in the DOM will be as you left it. 286 var img_css = { 287 border: 'none', 288 visibility: 'visible', 289 margin: 0, 290 padding: 0, 291 position: 'absolute', 292 top: 0, 293 left: 0 294 }; 295 296 var $origimg = $(obj), 297 img_mode = true; 298 299 if (obj.tagName == 'IMG') { 300 // Fix size of crop image. 301 // Necessary when crop image is within a hidden element when page is loaded. 302 if ($origimg[0].width != 0 && $origimg[0].height != 0) { 303 // Obtain dimensions from contained img element. 304 $origimg.width($origimg[0].width); 305 $origimg.height($origimg[0].height); 306 } else { 307 // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). 308 var tempImage = new Image(); 309 tempImage.src = $origimg[0].src; 310 $origimg.width(tempImage.width); 311 $origimg.height(tempImage.height); 312 } 313 314 var $img = $origimg.clone().removeAttr('id').css(img_css).show(); 315 316 $img.width($origimg.width()); 317 $img.height($origimg.height()); 318 $origimg.after($img).hide(); 319 320 } else { 321 $img = $origimg.css(img_css).show(); 322 img_mode = false; 323 if (options.shade === null) { options.shade = true; } 324 } 325 326 presize($img, options.boxWidth, options.boxHeight); 327 328 var boundx = $img.width(), 329 boundy = $img.height(), 330 331 332 $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({ 333 position: 'relative', 334 backgroundColor: options.bgColor 335 }).insertAfter($origimg).append($img); 336 337 if (options.addClass) { 338 $div.addClass(options.addClass); 339 } 340 341 var $img2 = $('<div />'), 342 343 $img_holder = $('<div />') 344 .width('100%').height('100%').css({ 345 zIndex: 310, 346 position: 'absolute', 347 overflow: 'hidden' 348 }), 349 350 $hdl_holder = $('<div />') 351 .width('100%').height('100%').css('zIndex', 320), 352 353 $sel = $('<div />') 354 .css({ 355 position: 'absolute', 356 zIndex: 600 357 }).dblclick(function(){ 358 var c = Coords.getFixed(); 359 options.onDblClick.call(api,c); 360 }).insertBefore($img).append($img_holder, $hdl_holder); 361 362 if (img_mode) { 363 364 $img2 = $('<img />') 365 .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy), 366 367 $img_holder.append($img2); 368 369 } 370 371 if (ie6mode) { 372 $sel.css({ 373 overflowY: 'hidden' 374 }); 375 } 376 377 var bound = options.boundary; 378 var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({ 379 position: 'absolute', 380 top: px(-bound), 381 left: px(-bound), 382 zIndex: 290 383 }).mousedown(newSelection); 384 385 /* }}} */ 386 // Set more variables {{{ 387 var bgcolor = options.bgColor, 388 bgopacity = options.bgOpacity, 389 xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true, 390 btndown, animating, shift_down; 391 392 docOffset = getPos($img); 393 // }}} 394 // }}} 395 // Internal Modules {{{ 396 // Touch Module {{{ 397 var Touch = (function () { 398 // Touch support detection function adapted (under MIT License) 399 // from code by Jeffrey Sambells - http://github.com/iamamused/ 400 function hasTouchSupport() { 401 var support = {}, events = ['touchstart', 'touchmove', 'touchend'], 402 el = document.createElement('div'), i; 403 404 try { 405 for(i=0; i<events.length; i++) { 406 var eventName = events[i]; 407 eventName = 'on' + eventName; 408 var isSupported = (eventName in el); 409 if (!isSupported) { 410 el.setAttribute(eventName, 'return;'); 411 isSupported = typeof el[eventName] == 'function'; 412 } 413 support[events[i]] = isSupported; 414 } 415 return support.touchstart && support.touchend && support.touchmove; 416 } 417 catch(err) { 418 return false; 419 } 420 } 421 422 function detectSupport() { 423 if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport; 424 else return hasTouchSupport(); 425 } 426 return { 427 createDragger: function (ord) { 428 return function (e) { 429 if (options.disabled) { 430 return false; 431 } 432 if ((ord === 'move') && !options.allowMove) { 433 return false; 434 } 435 docOffset = getPos($img); 436 btndown = true; 437 startDragMode(ord, mouseAbs(Touch.cfilter(e)), true); 438 e.stopPropagation(); 439 e.preventDefault(); 440 return false; 441 }; 442 }, 443 newSelection: function (e) { 444 return newSelection(Touch.cfilter(e)); 445 }, 446 cfilter: function (e){ 447 e.pageX = e.originalEvent.changedTouches[0].pageX; 448 e.pageY = e.originalEvent.changedTouches[0].pageY; 449 return e; 450 }, 451 isSupported: hasTouchSupport, 452 support: detectSupport() 453 }; 454 }()); 455 // }}} 456 // Coords Module {{{ 457 var Coords = (function () { 458 var x1 = 0, 459 y1 = 0, 460 x2 = 0, 461 y2 = 0, 462 ox, oy; 463 464 function setPressed(pos) //{{{ 465 { 466 pos = rebound(pos); 467 x2 = x1 = pos[0]; 468 y2 = y1 = pos[1]; 469 } 470 //}}} 471 function setCurrent(pos) //{{{ 472 { 473 pos = rebound(pos); 474 ox = pos[0] - x2; 475 oy = pos[1] - y2; 476 x2 = pos[0]; 477 y2 = pos[1]; 478 } 479 //}}} 480 function getOffset() //{{{ 481 { 482 return [ox, oy]; 483 } 484 //}}} 485 function moveOffset(offset) //{{{ 486 { 487 var ox = offset[0], 488 oy = offset[1]; 489 490 if (0 > x1 + ox) { 491 ox -= ox + x1; 492 } 493 if (0 > y1 + oy) { 494 oy -= oy + y1; 495 } 496 497 if (boundy < y2 + oy) { 498 oy += boundy - (y2 + oy); 499 } 500 if (boundx < x2 + ox) { 501 ox += boundx - (x2 + ox); 502 } 503 504 x1 += ox; 505 x2 += ox; 506 y1 += oy; 507 y2 += oy; 508 } 509 //}}} 510 function getCorner(ord) //{{{ 511 { 512 var c = getFixed(); 513 switch (ord) { 514 case 'ne': 515 return [c.x2, c.y]; 516 case 'nw': 517 return [c.x, c.y]; 518 case 'se': 519 return [c.x2, c.y2]; 520 case 'sw': 521 return [c.x, c.y2]; 522 } 523 } 524 //}}} 525 function getFixed() //{{{ 526 { 527 if (!options.aspectRatio) { 528 return getRect(); 529 } 530 // This function could use some optimization I think... 531 var aspect = options.aspectRatio, 532 min_x = options.minSize[0] / xscale, 533 534 535 //min_y = options.minSize[1]/yscale, 536 max_x = options.maxSize[0] / xscale, 537 max_y = options.maxSize[1] / yscale, 538 rw = x2 - x1, 539 rh = y2 - y1, 540 rwa = Math.abs(rw), 541 rha = Math.abs(rh), 542 real_ratio = rwa / rha, 543 xx, yy, w, h; 544 545 if (max_x === 0) { 546 max_x = boundx * 10; 547 } 548 if (max_y === 0) { 549 max_y = boundy * 10; 550 } 551 if (real_ratio < aspect) { 552 yy = y2; 553 w = rha * aspect; 554 xx = rw < 0 ? x1 - w : w + x1; 555 556 if (xx < 0) { 557 xx = 0; 558 h = Math.abs((xx - x1) / aspect); 559 yy = rh < 0 ? y1 - h : h + y1; 560 } else if (xx > boundx) { 561 xx = boundx; 562 h = Math.abs((xx - x1) / aspect); 563 yy = rh < 0 ? y1 - h : h + y1; 564 } 565 } else { 566 xx = x2; 567 h = rwa / aspect; 568 yy = rh < 0 ? y1 - h : y1 + h; 569 if (yy < 0) { 570 yy = 0; 571 w = Math.abs((yy - y1) * aspect); 572 xx = rw < 0 ? x1 - w : w + x1; 573 } else if (yy > boundy) { 574 yy = boundy; 575 w = Math.abs(yy - y1) * aspect; 576 xx = rw < 0 ? x1 - w : w + x1; 577 } 578 } 579 580 // Magic %-) 581 if (xx > x1) { // right side 582 if (xx - x1 < min_x) { 583 xx = x1 + min_x; 584 } else if (xx - x1 > max_x) { 585 xx = x1 + max_x; 586 } 587 if (yy > y1) { 588 yy = y1 + (xx - x1) / aspect; 589 } else { 590 yy = y1 - (xx - x1) / aspect; 591 } 592 } else if (xx < x1) { // left side 593 if (x1 - xx < min_x) { 594 xx = x1 - min_x; 595 } else if (x1 - xx > max_x) { 596 xx = x1 - max_x; 597 } 598 if (yy > y1) { 599 yy = y1 + (x1 - xx) / aspect; 600 } else { 601 yy = y1 - (x1 - xx) / aspect; 602 } 603 } 604 605 if (xx < 0) { 606 x1 -= xx; 607 xx = 0; 608 } else if (xx > boundx) { 609 x1 -= xx - boundx; 610 xx = boundx; 611 } 612 613 if (yy < 0) { 614 y1 -= yy; 615 yy = 0; 616 } else if (yy > boundy) { 617 y1 -= yy - boundy; 618 yy = boundy; 619 } 620 621 return makeObj(flipCoords(x1, y1, xx, yy)); 622 } 623 //}}} 624 function rebound(p) //{{{ 625 { 626 if (p[0] < 0) p[0] = 0; 627 if (p[1] < 0) p[1] = 0; 628 629 if (p[0] > boundx) p[0] = boundx; 630 if (p[1] > boundy) p[1] = boundy; 631 632 return [Math.round(p[0]), Math.round(p[1])]; 633 } 634 //}}} 635 function flipCoords(x1, y1, x2, y2) //{{{ 636 { 637 var xa = x1, 638 xb = x2, 639 ya = y1, 640 yb = y2; 641 if (x2 < x1) { 642 xa = x2; 643 xb = x1; 644 } 645 if (y2 < y1) { 646 ya = y2; 647 yb = y1; 648 } 649 return [xa, ya, xb, yb]; 650 } 651 //}}} 652 function getRect() //{{{ 653 { 654 var xsize = x2 - x1, 655 ysize = y2 - y1, 656 delta; 657 658 if (xlimit && (Math.abs(xsize) > xlimit)) { 659 x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit); 660 } 661 if (ylimit && (Math.abs(ysize) > ylimit)) { 662 y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit); 663 } 664 665 if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) { 666 y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale); 667 } 668 if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) { 669 x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale); 670 } 671 672 if (x1 < 0) { 673 x2 -= x1; 674 x1 -= x1; 675 } 676 if (y1 < 0) { 677 y2 -= y1; 678 y1 -= y1; 679 } 680 if (x2 < 0) { 681 x1 -= x2; 682 x2 -= x2; 683 } 684 if (y2 < 0) { 685 y1 -= y2; 686 y2 -= y2; 687 } 688 if (x2 > boundx) { 689 delta = x2 - boundx; 690 x1 -= delta; 691 x2 -= delta; 692 } 693 if (y2 > boundy) { 694 delta = y2 - boundy; 695 y1 -= delta; 696 y2 -= delta; 697 } 698 if (x1 > boundx) { 699 delta = x1 - boundy; 700 y2 -= delta; 701 y1 -= delta; 702 } 703 if (y1 > boundy) { 704 delta = y1 - boundy; 705 y2 -= delta; 706 y1 -= delta; 707 } 708 709 return makeObj(flipCoords(x1, y1, x2, y2)); 710 } 711 //}}} 712 function makeObj(a) //{{{ 713 { 714 return { 715 x: a[0], 716 y: a[1], 717 x2: a[2], 718 y2: a[3], 719 w: a[2] - a[0], 720 h: a[3] - a[1] 721 }; 722 } 723 //}}} 724 725 return { 726 flipCoords: flipCoords, 727 setPressed: setPressed, 728 setCurrent: setCurrent, 729 getOffset: getOffset, 730 moveOffset: moveOffset, 731 getCorner: getCorner, 732 getFixed: getFixed 733 }; 734 }()); 735 736 //}}} 737 // Shade Module {{{ 738 var Shade = (function() { 739 var enabled = false, 740 holder = $('<div />').css({ 741 position: 'absolute', 742 zIndex: 240, 743 opacity: 0 744 }), 745 shades = { 746 top: createShade(), 747 left: createShade().height(boundy), 748 right: createShade().height(boundy), 749 bottom: createShade() 750 }; 751 752 function resizeShades(w,h) { 753 shades.left.css({ height: px(h) }); 754 shades.right.css({ height: px(h) }); 755 } 756 function updateAuto() 757 { 758 return updateShade(Coords.getFixed()); 759 } 760 function updateShade(c) 761 { 762 shades.top.css({ 763 left: px(c.x), 764 width: px(c.w), 765 height: px(c.y) 766 }); 767 shades.bottom.css({ 768 top: px(c.y2), 769 left: px(c.x), 770 width: px(c.w), 771 height: px(boundy-c.y2) 772 }); 773 shades.right.css({ 774 left: px(c.x2), 775 width: px(boundx-c.x2) 776 }); 777 shades.left.css({ 778 width: px(c.x) 779 }); 780 } 781 function createShade() { 782 return $('<div />').css({ 783 position: 'absolute', 784 backgroundColor: options.shadeColor||options.bgColor 785 }).appendTo(holder); 786 } 787 function enableShade() { 788 if (!enabled) { 789 enabled = true; 790 holder.insertBefore($img); 791 updateAuto(); 792 Selection.setBgOpacity(1,0,1); 793 $img2.hide(); 794 795 setBgColor(options.shadeColor||options.bgColor,1); 796 if (Selection.isAwake()) 797 { 798 setOpacity(options.bgOpacity,1); 799 } 800 else setOpacity(1,1); 801 } 802 } 803 function setBgColor(color,now) { 804 colorChangeMacro(getShades(),color,now); 805 } 806 function disableShade() { 807 if (enabled) { 808 holder.remove(); 809 $img2.show(); 810 enabled = false; 811 if (Selection.isAwake()) { 812 Selection.setBgOpacity(options.bgOpacity,1,1); 813 } else { 814 Selection.setBgOpacity(1,1,1); 815 Selection.disableHandles(); 816 } 817 colorChangeMacro($div,0,1); 818 } 819 } 820 function setOpacity(opacity,now) { 821 if (enabled) { 822 if (options.bgFade && !now) { 823 holder.animate({ 824 opacity: 1-opacity 825 },{ 826 queue: false, 827 duration: options.fadeTime 828 }); 829 } 830 else holder.css({opacity:1-opacity}); 831 } 832 } 833 function refreshAll() { 834 options.shade ? enableShade() : disableShade(); 835 if (Selection.isAwake()) setOpacity(options.bgOpacity); 836 } 837 function getShades() { 838 return holder.children(); 839 } 840 841 return { 842 update: updateAuto, 843 updateRaw: updateShade, 844 getShades: getShades, 845 setBgColor: setBgColor, 846 enable: enableShade, 847 disable: disableShade, 848 resize: resizeShades, 849 refresh: refreshAll, 850 opacity: setOpacity 851 }; 852 }()); 853 // }}} 854 // Selection Module {{{ 855 var Selection = (function () { 856 var awake, 857 hdep = 370, 858 borders = {}, 859 handle = {}, 860 dragbar = {}, 861 seehandles = false; 862 863 // Private Methods 864 function insertBorder(type) //{{{ 865 { 866 var jq = $('<div />').css({ 867 position: 'absolute', 868 opacity: options.borderOpacity 869 }).addClass(cssClass(type)); 870 $img_holder.append(jq); 871 return jq; 872 } 873 //}}} 874 function dragDiv(ord, zi) //{{{ 875 { 876 var jq = $('<div />').mousedown(createDragger(ord)).css({ 877 cursor: ord + '-resize', 878 position: 'absolute', 879 zIndex: zi 880 }).addClass('ord-'+ord); 881 882 if (Touch.support) { 883 jq.bind('touchstart.jcrop', Touch.createDragger(ord)); 884 } 885 886 $hdl_holder.append(jq); 887 return jq; 888 } 889 //}}} 890 function insertHandle(ord) //{{{ 891 { 892 var hs = options.handleSize, 893 894 div = dragDiv(ord, hdep++).css({ 895 opacity: options.handleOpacity 896 }).addClass(cssClass('handle')); 897 898 if (hs) { div.width(hs).height(hs); } 899 900 return div; 901 } 902 //}}} 903 function insertDragbar(ord) //{{{ 904 { 905 return dragDiv(ord, hdep++).addClass('jcrop-dragbar'); 906 } 907 //}}} 908 function createDragbars(li) //{{{ 909 { 910 var i; 911 for (i = 0; i < li.length; i++) { 912 dragbar[li[i]] = insertDragbar(li[i]); 913 } 914 } 915 //}}} 916 function createBorders(li) //{{{ 917 { 918 var cl,i; 919 for (i = 0; i < li.length; i++) { 920 switch(li[i]){ 921 case'n': cl='hline'; break; 922 case's': cl='hline bottom'; break; 923 case'e': cl='vline right'; break; 924 case'w': cl='vline'; break; 925 } 926 borders[li[i]] = insertBorder(cl); 927 } 928 } 929 //}}} 930 function createHandles(li) //{{{ 931 { 932 var i; 933 for (i = 0; i < li.length; i++) { 934 handle[li[i]] = insertHandle(li[i]); 935 } 936 } 937 //}}} 938 function moveto(x, y) //{{{ 939 { 940 if (!options.shade) { 941 $img2.css({ 942 top: px(-y), 943 left: px(-x) 944 }); 945 } 946 $sel.css({ 947 top: px(y), 948 left: px(x) 949 }); 950 } 951 //}}} 952 function resize(w, h) //{{{ 953 { 954 $sel.width(Math.round(w)).height(Math.round(h)); 955 } 956 //}}} 957 function refresh() //{{{ 958 { 959 var c = Coords.getFixed(); 960 961 Coords.setPressed([c.x, c.y]); 962 Coords.setCurrent([c.x2, c.y2]); 963 964 updateVisible(); 965 } 966 //}}} 967 968 // Internal Methods 969 function updateVisible(select) //{{{ 970 { 971 if (awake) { 972 return update(select); 973 } 974 } 975 //}}} 976 function update(select) //{{{ 977 { 978 var c = Coords.getFixed(); 979 980 resize(c.w, c.h); 981 moveto(c.x, c.y); 982 if (options.shade) Shade.updateRaw(c); 983 984 awake || show(); 985 986 if (select) { 987 options.onSelect.call(api, unscale(c)); 988 } else { 989 options.onChange.call(api, unscale(c)); 990 } 991 } 992 //}}} 993 function setBgOpacity(opacity,force,now) //{{{ 994 { 995 if (!awake && !force) return; 996 if (options.bgFade && !now) { 997 $img.animate({ 998 opacity: opacity 999 },{ 1000 queue: false, 1001 duration: options.fadeTime 1002 }); 1003 } else { 1004 $img.css('opacity', opacity); 1005 } 1006 } 1007 //}}} 1008 function show() //{{{ 1009 { 1010 $sel.show(); 1011 1012 if (options.shade) Shade.opacity(bgopacity); 1013 else setBgOpacity(bgopacity,true); 1014 1015 awake = true; 1016 } 1017 //}}} 1018 function release() //{{{ 1019 { 1020 disableHandles(); 1021 $sel.hide(); 1022 1023 if (options.shade) Shade.opacity(1); 1024 else setBgOpacity(1); 1025 1026 awake = false; 1027 options.onRelease.call(api); 1028 } 1029 //}}} 1030 function showHandles() //{{{ 1031 { 1032 if (seehandles) { 1033 $hdl_holder.show(); 1034 } 1035 } 1036 //}}} 1037 function enableHandles() //{{{ 1038 { 1039 seehandles = true; 1040 if (options.allowResize) { 1041 $hdl_holder.show(); 1042 return true; 1043 } 1044 } 1045 //}}} 1046 function disableHandles() //{{{ 1047 { 1048 seehandles = false; 1049 $hdl_holder.hide(); 1050 } 1051 //}}} 1052 function animMode(v) //{{{ 1053 { 1054 if (v) { 1055 animating = true; 1056 disableHandles(); 1057 } else { 1058 animating = false; 1059 enableHandles(); 1060 } 1061 } 1062 //}}} 1063 function done() //{{{ 1064 { 1065 animMode(false); 1066 refresh(); 1067 } 1068 //}}} 1069 // Insert draggable elements {{{ 1070 // Insert border divs for outline 1071 1072 if (options.dragEdges && $.isArray(options.createDragbars)) 1073 createDragbars(options.createDragbars); 1074 1075 if ($.isArray(options.createHandles)) 1076 createHandles(options.createHandles); 1077 1078 if (options.drawBorders && $.isArray(options.createBorders)) 1079 createBorders(options.createBorders); 1080 1081 //}}} 1082 1083 // This is a hack for iOS5 to support drag/move touch functionality 1084 $(document).bind('touchstart.jcrop-ios',function(e) { 1085 if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation(); 1086 }); 1087 1088 var $track = newTracker().mousedown(createDragger('move')).css({ 1089 cursor: 'move', 1090 position: 'absolute', 1091 zIndex: 360 1092 }); 1093 1094 if (Touch.support) { 1095 $track.bind('touchstart.jcrop', Touch.createDragger('move')); 1096 } 1097 1098 $img_holder.append($track); 1099 disableHandles(); 1100 1101 return { 1102 updateVisible: updateVisible, 1103 update: update, 1104 release: release, 1105 refresh: refresh, 1106 isAwake: function () { 1107 return awake; 1108 }, 1109 setCursor: function (cursor) { 1110 $track.css('cursor', cursor); 1111 }, 1112 enableHandles: enableHandles, 1113 enableOnly: function () { 1114 seehandles = true; 1115 }, 1116 showHandles: showHandles, 1117 disableHandles: disableHandles, 1118 animMode: animMode, 1119 setBgOpacity: setBgOpacity, 1120 done: done 1121 }; 1122 }()); 1123 1124 //}}} 1125 // Tracker Module {{{ 1126 var Tracker = (function () { 1127 var onMove = function () {}, 1128 onDone = function () {}, 1129 trackDoc = options.trackDocument; 1130 1131 function toFront(touch) //{{{ 1132 { 1133 $trk.css({ 1134 zIndex: 450 1135 }); 1136 1137 if (touch) 1138 $(document) 1139 .bind('touchmove.jcrop', trackTouchMove) 1140 .bind('touchend.jcrop', trackTouchEnd); 1141 1142 else if (trackDoc) 1143 $(document) 1144 .bind('mousemove.jcrop',trackMove) 1145 .bind('mouseup.jcrop',trackUp); 1146 } 1147 //}}} 1148 function toBack() //{{{ 1149 { 1150 $trk.css({ 1151 zIndex: 290 1152 }); 1153 $(document).unbind('.jcrop'); 1154 } 1155 //}}} 1156 function trackMove(e) //{{{ 1157 { 1158 onMove(mouseAbs(e)); 1159 return false; 1160 } 1161 //}}} 1162 function trackUp(e) //{{{ 1163 { 1164 e.preventDefault(); 1165 e.stopPropagation(); 1166 1167 if (btndown) { 1168 btndown = false; 1169 1170 onDone(mouseAbs(e)); 1171 1172 if (Selection.isAwake()) { 1173 options.onSelect.call(api, unscale(Coords.getFixed())); 1174 } 1175 1176 toBack(); 1177 onMove = function () {}; 1178 onDone = function () {}; 1179 } 1180 1181 return false; 1182 } 1183 //}}} 1184 function activateHandlers(move, done, touch) //{{{ 1185 { 1186 btndown = true; 1187 onMove = move; 1188 onDone = done; 1189 toFront(touch); 1190 return false; 1191 } 1192 //}}} 1193 function trackTouchMove(e) //{{{ 1194 { 1195 onMove(mouseAbs(Touch.cfilter(e))); 1196 return false; 1197 } 1198 //}}} 1199 function trackTouchEnd(e) //{{{ 1200 { 1201 return trackUp(Touch.cfilter(e)); 1202 } 1203 //}}} 1204 function setCursor(t) //{{{ 1205 { 1206 $trk.css('cursor', t); 1207 } 1208 //}}} 1209 1210 if (!trackDoc) { 1211 $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp); 1212 } 1213 1214 $img.before($trk); 1215 return { 1216 activateHandlers: activateHandlers, 1217 setCursor: setCursor 1218 }; 1219 }()); 1220 //}}} 1221 // KeyManager Module {{{ 1222 var KeyManager = (function () { 1223 var $keymgr = $('<input type="radio" />').css({ 1224 position: 'fixed', 1225 left: '-120px', 1226 width: '12px' 1227 }).addClass('jcrop-keymgr'), 1228 1229 $keywrap = $('<div />').css({ 1230 position: 'absolute', 1231 overflow: 'hidden' 1232 }).append($keymgr); 1233 1234 function watchKeys() //{{{ 1235 { 1236 if (options.keySupport) { 1237 $keymgr.show(); 1238 $keymgr.focus(); 1239 } 1240 } 1241 //}}} 1242 function onBlur(e) //{{{ 1243 { 1244 $keymgr.hide(); 1245 } 1246 //}}} 1247 function doNudge(e, x, y) //{{{ 1248 { 1249 if (options.allowMove) { 1250 Coords.moveOffset([x, y]); 1251 Selection.updateVisible(true); 1252 } 1253 e.preventDefault(); 1254 e.stopPropagation(); 1255 } 1256 //}}} 1257 function parseKey(e) //{{{ 1258 { 1259 if (e.ctrlKey || e.metaKey) { 1260 return true; 1261 } 1262 shift_down = e.shiftKey ? true : false; 1263 var nudge = shift_down ? 10 : 1; 1264 1265 switch (e.keyCode) { 1266 case 37: 1267 doNudge(e, -nudge, 0); 1268 break; 1269 case 39: 1270 doNudge(e, nudge, 0); 1271 break; 1272 case 38: 1273 doNudge(e, 0, -nudge); 1274 break; 1275 case 40: 1276 doNudge(e, 0, nudge); 1277 break; 1278 case 27: 1279 if (options.allowSelect) Selection.release(); 1280 break; 1281 case 9: 1282 return true; 1283 } 1284 1285 return false; 1286 } 1287 //}}} 1288 1289 if (options.keySupport) { 1290 $keymgr.keydown(parseKey).blur(onBlur); 1291 if (ie6mode || !options.fixedSupport) { 1292 $keymgr.css({ 1293 position: 'absolute', 1294 left: '-20px' 1295 }); 1296 $keywrap.append($keymgr).insertBefore($img); 1297 } else { 1298 $keymgr.insertBefore($img); 1299 } 1300 } 1301 1302 1303 return { 1304 watchKeys: watchKeys 1305 }; 1306 }()); 1307 //}}} 1308 // }}} 1309 // API methods {{{ 1310 function setClass(cname) //{{{ 1311 { 1312 $div.removeClass().addClass(cssClass('holder')).addClass(cname); 1313 } 1314 //}}} 1315 function animateTo(a, callback) //{{{ 1316 { 1317 var x1 = a[0] / xscale, 1318 y1 = a[1] / yscale, 1319 x2 = a[2] / xscale, 1320 y2 = a[3] / yscale; 1321 1322 if (animating) { 1323 return; 1324 } 1325 1326 var animto = Coords.flipCoords(x1, y1, x2, y2), 1327 c = Coords.getFixed(), 1328 initcr = [c.x, c.y, c.x2, c.y2], 1329 animat = initcr, 1330 interv = options.animationDelay, 1331 ix1 = animto[0] - initcr[0], 1332 iy1 = animto[1] - initcr[1], 1333 ix2 = animto[2] - initcr[2], 1334 iy2 = animto[3] - initcr[3], 1335 pcent = 0, 1336 velocity = options.swingSpeed; 1337 1338 x1 = animat[0]; 1339 y1 = animat[1]; 1340 x2 = animat[2]; 1341 y2 = animat[3]; 1342 1343 Selection.animMode(true); 1344 var anim_timer; 1345 1346 function queueAnimator() { 1347 window.setTimeout(animator, interv); 1348 } 1349 var animator = (function () { 1350 return function () { 1351 pcent += (100 - pcent) / velocity; 1352 1353 animat[0] = Math.round(x1 + ((pcent / 100) * ix1)); 1354 animat[1] = Math.round(y1 + ((pcent / 100) * iy1)); 1355 animat[2] = Math.round(x2 + ((pcent / 100) * ix2)); 1356 animat[3] = Math.round(y2 + ((pcent / 100) * iy2)); 1357 1358 if (pcent >= 99.8) { 1359 pcent = 100; 1360 } 1361 if (pcent < 100) { 1362 setSelectRaw(animat); 1363 queueAnimator(); 1364 } else { 1365 Selection.done(); 1366 Selection.animMode(false); 1367 if (typeof(callback) === 'function') { 1368 callback.call(api); 1369 } 1370 } 1371 }; 1372 }()); 1373 queueAnimator(); 1374 } 1375 //}}} 1376 function setSelect(rect) //{{{ 1377 { 1378 setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]); 1379 options.onSelect.call(api, unscale(Coords.getFixed())); 1380 Selection.enableHandles(); 1381 } 1382 //}}} 1383 function setSelectRaw(l) //{{{ 1384 { 1385 Coords.setPressed([l[0], l[1]]); 1386 Coords.setCurrent([l[2], l[3]]); 1387 Selection.update(); 1388 } 1389 //}}} 1390 function tellSelect() //{{{ 1391 { 1392 return unscale(Coords.getFixed()); 1393 } 1394 //}}} 1395 function tellScaled() //{{{ 1396 { 1397 return Coords.getFixed(); 1398 } 1399 //}}} 1400 function setOptionsNew(opt) //{{{ 1401 { 1402 setOptions(opt); 1403 interfaceUpdate(); 1404 } 1405 //}}} 1406 function disableCrop() //{{{ 1407 { 1408 options.disabled = true; 1409 Selection.disableHandles(); 1410 Selection.setCursor('default'); 1411 Tracker.setCursor('default'); 1412 } 1413 //}}} 1414 function enableCrop() //{{{ 1415 { 1416 options.disabled = false; 1417 interfaceUpdate(); 1418 } 1419 //}}} 1420 function cancelCrop() //{{{ 1421 { 1422 Selection.done(); 1423 Tracker.activateHandlers(null, null); 1424 } 1425 //}}} 1426 function destroy() //{{{ 1427 { 1428 $div.remove(); 1429 $origimg.show(); 1430 $origimg.css('visibility','visible'); 1431 $(obj).removeData('Jcrop'); 1432 } 1433 //}}} 1434 function setImage(src, callback) //{{{ 1435 { 1436 Selection.release(); 1437 disableCrop(); 1438 var img = new Image(); 1439 img.onload = function () { 1440 var iw = img.width; 1441 var ih = img.height; 1442 var bw = options.boxWidth; 1443 var bh = options.boxHeight; 1444 $img.width(iw).height(ih); 1445 $img.attr('src', src); 1446 $img2.attr('src', src); 1447 presize($img, bw, bh); 1448 boundx = $img.width(); 1449 boundy = $img.height(); 1450 $img2.width(boundx).height(boundy); 1451 $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2)); 1452 $div.width(boundx).height(boundy); 1453 Shade.resize(boundx,boundy); 1454 enableCrop(); 1455 1456 if (typeof(callback) === 'function') { 1457 callback.call(api); 1458 } 1459 }; 1460 img.src = src; 1461 } 1462 //}}} 1463 function colorChangeMacro($obj,color,now) { 1464 var mycolor = color || options.bgColor; 1465 if (options.bgFade && supportsColorFade() && options.fadeTime && !now) { 1466 $obj.animate({ 1467 backgroundColor: mycolor 1468 }, { 1469 queue: false, 1470 duration: options.fadeTime 1471 }); 1472 } else { 1473 $obj.css('backgroundColor', mycolor); 1474 } 1475 } 1476 function interfaceUpdate(alt) //{{{ 1477 // This method tweaks the interface based on options object. 1478 // Called when options are changed and at end of initialization. 1479 { 1480 if (options.allowResize) { 1481 if (alt) { 1482 Selection.enableOnly(); 1483 } else { 1484 Selection.enableHandles(); 1485 } 1486 } else { 1487 Selection.disableHandles(); 1488 } 1489 1490 Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); 1491 Selection.setCursor(options.allowMove ? 'move' : 'default'); 1492 1493 if (options.hasOwnProperty('trueSize')) { 1494 xscale = options.trueSize[0] / boundx; 1495 yscale = options.trueSize[1] / boundy; 1496 } 1497 1498 if (options.hasOwnProperty('setSelect')) { 1499 setSelect(options.setSelect); 1500 Selection.done(); 1501 delete(options.setSelect); 1502 } 1503 1504 Shade.refresh(); 1505 1506 if (options.bgColor != bgcolor) { 1507 colorChangeMacro( 1508 options.shade? Shade.getShades(): $div, 1509 options.shade? 1510 (options.shadeColor || options.bgColor): 1511 options.bgColor 1512 ); 1513 bgcolor = options.bgColor; 1514 } 1515 1516 if (bgopacity != options.bgOpacity) { 1517 bgopacity = options.bgOpacity; 1518 if (options.shade) Shade.refresh(); 1519 else Selection.setBgOpacity(bgopacity); 1520 } 1521 1522 xlimit = options.maxSize[0] || 0; 1523 ylimit = options.maxSize[1] || 0; 1524 xmin = options.minSize[0] || 0; 1525 ymin = options.minSize[1] || 0; 1526 1527 if (options.hasOwnProperty('outerImage')) { 1528 $img.attr('src', options.outerImage); 1529 delete(options.outerImage); 1530 } 1531 1532 Selection.refresh(); 1533 } 1534 //}}} 1535 //}}} 1536 1537 if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection); 1538 1539 $hdl_holder.hide(); 1540 interfaceUpdate(true); 1541 1542 var api = { 1543 setImage: setImage, 1544 animateTo: animateTo, 1545 setSelect: setSelect, 1546 setOptions: setOptionsNew, 1547 tellSelect: tellSelect, 1548 tellScaled: tellScaled, 1549 setClass: setClass, 1550 1551 disable: disableCrop, 1552 enable: enableCrop, 1553 cancel: cancelCrop, 1554 release: Selection.release, 1555 destroy: destroy, 1556 1557 focus: KeyManager.watchKeys, 1558 1559 getBounds: function () { 1560 return [boundx * xscale, boundy * yscale]; 1561 }, 1562 getWidgetSize: function () { 1563 return [boundx, boundy]; 1564 }, 1565 getScaleFactor: function () { 1566 return [xscale, yscale]; 1567 }, 1568 getOptions: function() { 1569 // careful: internal values are returned 1570 return options; 1571 }, 1572 1573 ui: { 1574 holder: $div, 1575 selection: $sel 1576 } 1577 }; 1578 1579 if (is_msie) $div.bind('selectstart', function () { return false; }); 1580 1581 $origimg.data('Jcrop', api); 1582 return api; 1583 }; 1584 $.fn.Jcrop = function (options, callback) //{{{ 1585 { 1586 var api; 1587 // Iterate over each object, attach Jcrop 1588 this.each(function () { 1589 // If we've already attached to this object 1590 if ($(this).data('Jcrop')) { 1591 // The API can be requested this way (undocumented) 1592 if (options === 'api') return $(this).data('Jcrop'); 1593 // Otherwise, we just reset the options... 1594 else $(this).data('Jcrop').setOptions(options); 1595 } 1596 // If we haven't been attached, preload and attach 1597 else { 1598 if (this.tagName == 'IMG') 1599 $.Jcrop.Loader(this,function(){ 1600 $(this).css({display:'block',visibility:'hidden'}); 1601 api = $.Jcrop(this, options); 1602 if ($.isFunction(callback)) callback.call(api); 1603 }); 1604 else { 1605 $(this).css({display:'block',visibility:'hidden'}); 1606 api = $.Jcrop(this, options); 1607 if ($.isFunction(callback)) callback.call(api); 1608 } 1609 } 1610 }); 1611 1612 // Return "this" so the object is chainable (jQuery-style) 1613 return this; 1614 }; 1615 //}}} 1616 // $.Jcrop.Loader - basic image loader {{{ 1617 1618 $.Jcrop.Loader = function(imgobj,success,error){ 1619 var $img = $(imgobj), img = $img[0]; 1620 1621 function completeCheck(){ 1622 if (img.complete) { 1623 $img.unbind('.jcloader'); 1624 if ($.isFunction(success)) success.call(img); 1625 } 1626 else window.setTimeout(completeCheck,50); 1627 } 1628 1629 $img 1630 .bind('load.jcloader',completeCheck) 1631 .bind('error.jcloader',function(e){ 1632 $img.unbind('.jcloader'); 1633 if ($.isFunction(error)) error.call(img); 1634 }); 1635 1636 if (img.complete && $.isFunction(success)){ 1637 $img.unbind('.jcloader'); 1638 success.call(img); 1639 } 1640 }; 1641 1642 //}}} 1643 // Global Defaults {{{ 1644 $.Jcrop.defaults = { 1645 1646 // Basic Settings 1647 allowSelect: true, 1648 allowMove: true, 1649 allowResize: true, 1650 1651 trackDocument: true, 1652 1653 // Styling Options 1654 baseClass: 'jcrop', 1655 addClass: null, 1656 bgColor: 'black', 1657 bgOpacity: 0.6, 1658 bgFade: false, 1659 borderOpacity: 0.4, 1660 handleOpacity: 0.5, 1661 handleSize: null, 1662 1663 aspectRatio: 0, 1664 keySupport: true, 1665 createHandles: ['n','s','e','w','nw','ne','se','sw'], 1666 createDragbars: ['n','s','e','w'], 1667 createBorders: ['n','s','e','w'], 1668 drawBorders: true, 1669 dragEdges: true, 1670 fixedSupport: true, 1671 touchSupport: null, 1672 1673 shade: null, 1674 1675 boxWidth: 0, 1676 boxHeight: 0, 1677 boundary: 2, 1678 fadeTime: 400, 1679 animationDelay: 20, 1680 swingSpeed: 3, 1681 1682 minSelect: [0, 0], 1683 maxSize: [0, 0], 1684 minSize: [0, 0], 1685 1686 // Callbacks / Event Handlers 1687 onChange: function () {}, 1688 onSelect: function () {}, 1689 onDblClick: function () {}, 1690 onRelease: function () {} 1691 }; 1692 1693 // }}} 1694 }(jQuery));