WordPress.org

Make WordPress Core

Ticket #20728: jcrop.diff

File jcrop.diff, 71.8 KB (added by niallkennedy, 6 years ago)

Jcrop 0.9.10 JS and CSS diff

  • wp-includes/js/jcrop/jquery.Jcrop.css

     
    1 /* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */
    2 .jcrop-holder { text-align: left; }
     1/* jquery.Jcrop.css v0.9.10 - MIT License */
    32
    4 .jcrop-vline, .jcrop-hline
     3/*
     4  The outer-most container in a typical Jcrop instance
     5  If you are having difficulty with formatting related to styles
     6  on a parent element, place any fixes here or in a like selector
     7
     8  You can also style this element if you want to add a border, etc
     9  A better method for styling can be seen below with .jcrop-light
     10  (Add a class to the holder and style elements for that extended class)
     11*/
     12.jcrop-holder {
     13  direction: ltr;
     14  text-align: left;
     15}
     16
     17/* These styles define the border lines */
     18.jcrop-vline,.jcrop-hline{background:#FFF url(Jcrop.gif) top left repeat;font-size:0;position:absolute;}
     19.jcrop-vline{height:100%;width:1px!important;}
     20.jcrop-hline{height:1px!important;width:100%;}
     21.jcrop-vline.right{right:0;}
     22.jcrop-hline.bottom{bottom:0;}
     23
     24/* Handle style - size is set by Jcrop handleSize option (currently) */
     25.jcrop-handle{background-color:#333;border:1px #eee solid;font-size:1px;}
     26
     27/* This style is used for invisible click targets */
     28.jcrop-tracker
    529{
    6         font-size: 0;
    7         position: absolute;
    8         background: white url('Jcrop.gif') top left repeat;
     30  height: 100%;
     31  width: 100%;
     32  -webkit-tap-highlight-color: transparent; /* "turn off" link highlight */
     33  -webkit-touch-callout: none;              /* disable callout, image save panel */
     34  -webkit-user-select: none;                /* disable cut copy paste */
    935}
    10 .jcrop-vline { height: 100%; width: 1px !important; }
    11 .jcrop-hline { width: 100%; height: 1px !important; }
    12 .jcrop-handle {
    13         font-size: 1px;
    14         width: 7px !important;
    15         height: 7px !important;
    16         border: 1px #eee solid;
    17         background-color: #333;
    18         *width: 9px;
    19         *height: 9px;
    20 }
    2136
    22 .jcrop-tracker { width: 100%; height: 100%; }
     37/* Positioning of handles and drag bars */
     38.jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0;}
     39.jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px;}
     40.jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%;}
     41.jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%;}
     42.jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0;}
     43.jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0;}
     44.jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0;}
     45.jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px;}
     46.jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%;}
     47.jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px;}
     48.jcrop-dragbar.ord-n{margin-top:-4px;}
     49.jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px;}
     50.jcrop-dragbar.ord-e{margin-right:-4px;right:0;}
     51.jcrop-dragbar.ord-w{margin-left:-4px;}
    2352
    24 .custom .jcrop-vline,
    25 .custom .jcrop-hline
     53/* The "jcrop-light" class/extension */
     54.jcrop-light .jcrop-vline,.jcrop-light .jcrop-hline
    2655{
    27         background: yellow;
     56        background:#FFF;
     57        filter:Alpha(opacity=70)!important;
     58        opacity:.70!important;
    2859}
    29 .custom .jcrop-handle
     60.jcrop-light .jcrop-handle
    3061{
    31         border-color: black;
    32         background-color: #C7BB00;
    33         -moz-border-radius: 3px;
    34         -webkit-border-radius: 3px;
     62        -moz-border-radius:3px;
     63        -webkit-border-radius:3px;
     64        background-color:#000;
     65        border-color:#FFF;
     66        border-radius:3px;
    3567}
     68
     69/* The "jcrop-dark" class/extension */
     70.jcrop-dark .jcrop-vline,.jcrop-dark .jcrop-hline
     71{
     72        background:#000;
     73        filter:Alpha(opacity=70)!important;
     74        opacity:.7!important;
     75}
     76.jcrop-dark .jcrop-handle
     77{
     78        -moz-border-radius:3px;
     79        -webkit-border-radius:3px;
     80        background-color:#FFF;
     81        border-color:#000;
     82        border-radius:3px;
     83}
     84
     85/* Fix for twitter bootstrap et al. */
     86.jcrop-holder img,img.jcrop-preview{ max-width: none; }
  • wp-includes/js/jcrop/jquery.Jcrop.dev.js

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