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));