PageRenderTime 76ms CodeModel.GetById 11ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/BlogEngine.NET/admin/FileManager/JCrop/js/jquery.Jcrop.js

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