source: trunk/themes/default/js/plugins/jquery.Jcrop.js @ 20820

Last change on this file since 20820 was 20820, checked in by rvelices, 11 years ago

upgraded jquery jcrop from 0.9.10 to 0.9.12

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