source: branches/2.4/themes/default/js/plugins/jquery.Jcrop.js @ 16077

Last change on this file since 16077 was 13038, checked in by rvelices, 12 years ago

multisize - added the coi (still to affine the admin ui + language)
multisize - derivatives can be revuild from a larger derviative instead of the original

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