]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/jcrop/jquery.Jcrop.dev.js
WordPress 3.4.2
[autoinstalls/wordpress.git] / wp-includes / js / jcrop / jquery.Jcrop.dev.js
1 /**
2  * jquery.Jcrop.js v0.9.8
3  * jQuery Image Cropping Plugin
4  * @author Kelly Hallman <khallman@gmail.com>
5  * Copyright (c) 2008-2009 Kelly Hallman - released under MIT License {{{
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 {
35         // Initialization {{{
36
37         // Sanitize some options {{{
38         var obj = obj, opt = opt;
39
40         if (typeof(obj) !== 'object') obj = $(obj)[0];
41         if (typeof(opt) !== 'object') opt = { };
42
43         // Some on-the-fly fixes for MSIE...sigh
44         if (!('trackDocument' in opt))
45         {
46                 opt.trackDocument = $.browser.msie ? false : true;
47                 if ($.browser.msie && $.browser.version.split('.')[0] == '8')
48                         opt.trackDocument = true;
49         }
50
51         if (!('keySupport' in opt))
52                         opt.keySupport = $.browser.msie ? false : true;
53                 
54         // }}}
55         // Extend the default options {{{
56         var defaults = {
57
58                 // Basic Settings
59                 trackDocument:          false,
60                 baseClass:                      'jcrop',
61                 addClass:                       null,
62
63                 // Styling Options
64                 bgColor:                        'black',
65                 bgOpacity:                      .6,
66                 borderOpacity:          .4,
67                 handleOpacity:          .5,
68
69                 handlePad:                      5,
70                 handleSize:                     9,
71                 handleOffset:           5,
72                 edgeMargin:                     14,
73
74                 aspectRatio:            0,
75                 keySupport:                     true,
76                 cornerHandles:          true,
77                 sideHandles:            true,
78                 drawBorders:            true,
79                 dragEdges:                      true,
80
81                 boxWidth:                       0,
82                 boxHeight:                      0,
83
84                 boundary:                       8,
85                 animationDelay:         20,
86                 swingSpeed:                     3,
87
88                 allowSelect:            true,
89                 allowMove:                      true,
90                 allowResize:            true,
91
92                 minSelect:                      [ 0, 0 ],
93                 maxSize:                        [ 0, 0 ],
94                 minSize:                        [ 0, 0 ],
95
96                 // Callbacks / Event Handlers
97                 onChange: function() { },
98                 onSelect: function() { }
99
100         };
101         var options = defaults;
102         setOptions(opt);
103
104         // }}}
105         // Initialize some jQuery objects {{{
106
107         var $origimg = $(obj);
108         var $img = $origimg.clone().removeAttr('id').css({ position: 'absolute' });
109
110         $img.width($origimg.width());
111         $img.height($origimg.height());
112         $origimg.after($img).hide();
113
114         presize($img,options.boxWidth,options.boxHeight);
115
116         var boundx = $img.width(),
117                 boundy = $img.height(),
118
119                 $div = $('<div />')
120                         .width(boundx).height(boundy)
121                         .addClass(cssClass('holder'))
122                         .css({
123                                 position: 'relative',
124                                 backgroundColor: options.bgColor
125                         }).insertAfter($origimg).append($img);
126         ;
127         
128         if (options.addClass) $div.addClass(options.addClass);
129         //$img.wrap($div);
130
131         var $img2 = $('<img />')/*{{{*/
132                         .attr('src',$img.attr('src'))
133                         .css('position','absolute')
134                         .width(boundx).height(boundy)
135         ;/*}}}*/
136         var $img_holder = $('<div />')/*{{{*/
137                 .width(pct(100)).height(pct(100))
138                 .css({
139                         zIndex: 310,
140                         position: 'absolute',
141                         overflow: 'hidden'
142                 })
143                 .append($img2)
144         ;/*}}}*/
145         var $hdl_holder = $('<div />')/*{{{*/
146                 .width(pct(100)).height(pct(100))
147                 .css('zIndex',320);
148         /*}}}*/
149         var $sel = $('<div />')/*{{{*/
150                 .css({
151                         position: 'absolute',
152                         zIndex: 300
153                 })
154                 .insertBefore($img)
155                 .append($img_holder,$hdl_holder)
156         ;/*}}}*/
157
158         var bound = options.boundary;
159         var $trk = newTracker().width(boundx+(bound*2)).height(boundy+(bound*2))
160                 .css({ position: 'absolute', top: px(-bound), left: px(-bound), zIndex: 290 })
161                 .mousedown(newSelection);       
162         
163         /* }}} */
164         // Set more variables {{{
165
166         var xlimit, ylimit, xmin, ymin;
167         var xscale, yscale, enabled = true;
168         var docOffset = getPos($img),
169                 // Internal states
170                 btndown, lastcurs, dimmed, animating,
171                 shift_down;
172
173         // }}}
174                 
175
176                 // }}}
177         // Internal Modules {{{
178
179         var Coords = function()/*{{{*/
180         {
181                 var x1 = 0, y1 = 0, x2 = 0, y2 = 0, ox, oy;
182
183                 function setPressed(pos)/*{{{*/
184                 {
185                         var pos = rebound(pos);
186                         x2 = x1 = pos[0];
187                         y2 = y1 = pos[1];
188                 };
189                 /*}}}*/
190                 function setCurrent(pos)/*{{{*/
191                 {
192                         var pos = rebound(pos);
193                         ox = pos[0] - x2;
194                         oy = pos[1] - y2;
195                         x2 = pos[0];
196                         y2 = pos[1];
197                 };
198                 /*}}}*/
199                 function getOffset()/*{{{*/
200                 {
201                         return [ ox, oy ];
202                 };
203                 /*}}}*/
204                 function moveOffset(offset)/*{{{*/
205                 {
206                         var ox = offset[0], oy = offset[1];
207
208                         if (0 > x1 + ox) ox -= ox + x1;
209                         if (0 > y1 + oy) oy -= oy + y1;
210
211                         if (boundy < y2 + oy) oy += boundy - (y2 + oy);
212                         if (boundx < x2 + ox) ox += boundx - (x2 + ox);
213
214                         x1 += ox;
215                         x2 += ox;
216                         y1 += oy;
217                         y2 += oy;
218                 };
219                 /*}}}*/
220                 function getCorner(ord)/*{{{*/
221                 {
222                         var c = getFixed();
223                         switch(ord)
224                         {
225                                 case 'ne': return [ c.x2, c.y ];
226                                 case 'nw': return [ c.x, c.y ];
227                                 case 'se': return [ c.x2, c.y2 ];
228                                 case 'sw': return [ c.x, c.y2 ];
229                         }
230                 };
231                 /*}}}*/
232                 function getFixed()/*{{{*/
233                 {
234                         if (!options.aspectRatio) return getRect();
235                         // This function could use some optimization I think...
236                         var aspect = options.aspectRatio,
237                                 min_x = options.minSize[0]/xscale, 
238                                 min_y = options.minSize[1]/yscale,
239                                 max_x = options.maxSize[0]/xscale, 
240                                 max_y = options.maxSize[1]/yscale,
241                                 rw = x2 - x1,
242                                 rh = y2 - y1,
243                                 rwa = Math.abs(rw),
244                                 rha = Math.abs(rh),
245                                 real_ratio = rwa / rha,
246                                 xx, yy
247                         ;
248                         if (max_x == 0) { max_x = boundx * 10 }
249                         if (max_y == 0) { max_y = boundy * 10 }
250                         if (real_ratio < aspect)
251                         {
252                                 yy = y2;
253                                 w = rha * aspect;
254                                 xx = rw < 0 ? x1 - w : w + x1;
255
256                                 if (xx < 0)
257                                 {
258                                         xx = 0;
259                                         h = Math.abs((xx - x1) / aspect);
260                                         yy = rh < 0 ? y1 - h: h + y1;
261                                 }
262                                 else if (xx > boundx)
263                                 {
264                                         xx = boundx;
265                                         h = Math.abs((xx - x1) / aspect);
266                                         yy = rh < 0 ? y1 - h : h + y1;
267                                 }
268                         }
269                         else
270                         {
271                                 xx = x2;
272                                 h = rwa / aspect;
273                                 yy = rh < 0 ? y1 - h : y1 + h;
274                                 if (yy < 0)
275                                 {
276                                         yy = 0;
277                                         w = Math.abs((yy - y1) * aspect);
278                                         xx = rw < 0 ? x1 - w : w + x1;
279                                 }
280                                 else if (yy > boundy)
281                                 {
282                                         yy = boundy;
283                                         w = Math.abs(yy - y1) * aspect;
284                                         xx = rw < 0 ? x1 - w : w + x1;
285                                 }
286                         }
287
288                         // Magic %-)
289                         if(xx > x1) { // right side
290                           if(xx - x1 < min_x) {
291                                 xx = x1 + min_x;
292                           } else if (xx - x1 > max_x) {
293                                 xx = x1 + max_x;
294                           }
295                           if(yy > y1) {
296                                 yy = y1 + (xx - x1)/aspect;
297                           } else {
298                                 yy = y1 - (xx - x1)/aspect;
299                           }
300                         } else if (xx < x1) { // left side
301                           if(x1 - xx < min_x) {
302                                 xx = x1 - min_x
303                           } else if (x1 - xx > max_x) {
304                                 xx = x1 - max_x;
305                           }
306                           if(yy > y1) {
307                                 yy = y1 + (x1 - xx)/aspect;
308                           } else {
309                                 yy = y1 - (x1 - xx)/aspect;
310                           }
311                         }
312
313                         if(xx < 0) {
314                                 x1 -= xx;
315                                 xx = 0;
316                         } else  if (xx > boundx) {
317                                 x1 -= xx - boundx;
318                                 xx = boundx;
319                         }
320
321                         if(yy < 0) {
322                                 y1 -= yy;
323                                 yy = 0;
324                         } else  if (yy > boundy) {
325                                 y1 -= yy - boundy;
326                                 yy = boundy;
327                         }
328
329                         return last = makeObj(flipCoords(x1,y1,xx,yy));
330                 };
331                 /*}}}*/
332                 function rebound(p)/*{{{*/
333                 {
334                         if (p[0] < 0) p[0] = 0;
335                         if (p[1] < 0) p[1] = 0;
336
337                         if (p[0] > boundx) p[0] = boundx;
338                         if (p[1] > boundy) p[1] = boundy;
339
340                         return [ p[0], p[1] ];
341                 };
342                 /*}}}*/
343                 function flipCoords(x1,y1,x2,y2)/*{{{*/
344                 {
345                         var xa = x1, xb = x2, ya = y1, yb = y2;
346                         if (x2 < x1)
347                         {
348                                 xa = x2;
349                                 xb = x1;
350                         }
351                         if (y2 < y1)
352                         {
353                                 ya = y2;
354                                 yb = y1;
355                         }
356                         return [ Math.round(xa), Math.round(ya), Math.round(xb), Math.round(yb) ];
357                 };
358                 /*}}}*/
359                 function getRect()/*{{{*/
360                 {
361                         var xsize = x2 - x1;
362                         var ysize = y2 - y1;
363
364                         if (xlimit && (Math.abs(xsize) > xlimit))
365                                 x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
366                         if (ylimit && (Math.abs(ysize) > ylimit))
367                                 y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
368
369                         if (ymin && (Math.abs(ysize) < ymin))
370                                 y2 = (ysize > 0) ? (y1 + ymin) : (y1 - ymin);
371                         if (xmin && (Math.abs(xsize) < xmin))
372                                 x2 = (xsize > 0) ? (x1 + xmin) : (x1 - xmin);
373
374                         if (x1 < 0) { x2 -= x1; x1 -= x1; }
375                         if (y1 < 0) { y2 -= y1; y1 -= y1; }
376                         if (x2 < 0) { x1 -= x2; x2 -= x2; }
377                         if (y2 < 0) { y1 -= y2; y2 -= y2; }
378                         if (x2 > boundx) { var delta = x2 - boundx; x1 -= delta; x2 -= delta; }
379                         if (y2 > boundy) { var delta = y2 - boundy; y1 -= delta; y2 -= delta; }
380                         if (x1 > boundx) { var delta = x1 - boundy; y2 -= delta; y1 -= delta; }
381                         if (y1 > boundy) { var delta = y1 - boundy; y2 -= delta; y1 -= delta; }
382
383                         return makeObj(flipCoords(x1,y1,x2,y2));
384                 };
385                 /*}}}*/
386                 function makeObj(a)/*{{{*/
387                 {
388                         return { x: a[0], y: a[1], x2: a[2], y2: a[3],
389                                 w: a[2] - a[0], h: a[3] - a[1] };
390                 };
391                 /*}}}*/
392
393                 return {
394                         flipCoords: flipCoords,
395                         setPressed: setPressed,
396                         setCurrent: setCurrent,
397                         getOffset: getOffset,
398                         moveOffset: moveOffset,
399                         getCorner: getCorner,
400                         getFixed: getFixed
401                 };
402         }();
403
404         /*}}}*/
405         var Selection = function()/*{{{*/
406         {
407                 var start, end, dragmode, awake, hdep = 370;
408                 var borders = { };
409                 var handle = { };
410                 var seehandles = false;
411                 var hhs = options.handleOffset;
412
413                 /* Insert draggable elements {{{*/
414
415                 // Insert border divs for outline
416                 if (options.drawBorders) {
417                         borders = {
418                                         top: insertBorder('hline')
419                                                 .css('top',$.browser.msie?px(-1):px(0)),
420                                         bottom: insertBorder('hline'),
421                                         left: insertBorder('vline'),
422                                         right: insertBorder('vline')
423                         };
424                 }
425
426                 // Insert handles on edges
427                 if (options.dragEdges) {
428                         handle.t = insertDragbar('n');
429                         handle.b = insertDragbar('s');
430                         handle.r = insertDragbar('e');
431                         handle.l = insertDragbar('w');
432                 }
433
434                 // Insert side handles
435                 options.sideHandles &&
436                         createHandles(['n','s','e','w']);
437
438                 // Insert corner handles
439                 options.cornerHandles &&
440                         createHandles(['sw','nw','ne','se']);
441
442                 /*}}}*/
443                 // Private Methods
444                 function insertBorder(type)/*{{{*/
445                 {
446                         var jq = $('<div />')
447                                 .css({position: 'absolute', opacity: options.borderOpacity })
448                                 .addClass(cssClass(type));
449                         $img_holder.append(jq);
450                         return jq;
451                 };
452                 /*}}}*/
453                 function dragDiv(ord,zi)/*{{{*/
454                 {
455                         var jq = $('<div />')
456                                 .mousedown(createDragger(ord))
457                                 .css({
458                                         cursor: ord+'-resize',
459                                         position: 'absolute',
460                                         zIndex: zi 
461                                 })
462                         ;
463                         $hdl_holder.append(jq);
464                         return jq;
465                 };
466                 /*}}}*/
467                 function insertHandle(ord)/*{{{*/
468                 {
469                         return dragDiv(ord,hdep++)
470                                 .css({ top: px(-hhs+1), left: px(-hhs+1), opacity: options.handleOpacity })
471                                 .addClass(cssClass('handle'));
472                 };
473                 /*}}}*/
474                 function insertDragbar(ord)/*{{{*/
475                 {
476                         var s = options.handleSize,
477                                 o = hhs,
478                                 h = s, w = s,
479                                 t = o, l = o;
480
481                         switch(ord)
482                         {
483                                 case 'n': case 's': w = pct(100); break;
484                                 case 'e': case 'w': h = pct(100); break;
485                         }
486
487                         return dragDiv(ord,hdep++).width(w).height(h)
488                                 .css({ top: px(-t+1), left: px(-l+1)});
489                 };
490                 /*}}}*/
491                 function createHandles(li)/*{{{*/
492                 {
493                         for(i in li) handle[li[i]] = insertHandle(li[i]);
494                 };
495                 /*}}}*/
496                 function moveHandles(c)/*{{{*/
497                 {
498                         var midvert  = Math.round((c.h / 2) - hhs),
499                                 midhoriz = Math.round((c.w / 2) - hhs),
500                                 north = west = -hhs+1,
501                                 east = c.w - hhs,
502                                 south = c.h - hhs,
503                                 x, y;
504
505                         'e' in handle &&
506                                 handle.e.css({ top: px(midvert), left: px(east) }) &&
507                                 handle.w.css({ top: px(midvert) }) &&
508                                 handle.s.css({ top: px(south), left: px(midhoriz) }) &&
509                                 handle.n.css({ left: px(midhoriz) });
510
511                         'ne' in handle &&
512                                 handle.ne.css({ left: px(east) }) &&
513                                 handle.se.css({ top: px(south), left: px(east) }) &&
514                                 handle.sw.css({ top: px(south) });
515
516                         'b' in handle &&
517                                 handle.b.css({ top: px(south) }) &&
518                                 handle.r.css({ left: px(east) });
519                 };
520                 /*}}}*/
521                 function moveto(x,y)/*{{{*/
522                 {
523                         $img2.css({ top: px(-y), left: px(-x) });
524                         $sel.css({ top: px(y), left: px(x) });
525                 };
526                 /*}}}*/
527                 function resize(w,h)/*{{{*/
528                 {
529                         $sel.width(w).height(h);
530                 };
531                 /*}}}*/
532                 function refresh()/*{{{*/
533                 {
534                         var c = Coords.getFixed();
535
536                         Coords.setPressed([c.x,c.y]);
537                         Coords.setCurrent([c.x2,c.y2]);
538
539                         updateVisible();
540                 };
541                 /*}}}*/
542
543                 // Internal Methods
544                 function updateVisible()/*{{{*/
545                         { if (awake) return update(); };
546                 /*}}}*/
547                 function update()/*{{{*/
548                 {
549                         var c = Coords.getFixed();
550
551                         resize(c.w,c.h);
552                         moveto(c.x,c.y);
553
554                         options.drawBorders &&
555                                 borders['right'].css({ left: px(c.w-1) }) &&
556                                         borders['bottom'].css({ top: px(c.h-1) });
557
558                         seehandles && moveHandles(c);
559                         awake || show();
560
561                         options.onChange(unscale(c));
562                 };
563                 /*}}}*/
564                 function show()/*{{{*/
565                 {
566                         $sel.show();
567                         $img.css('opacity',options.bgOpacity);
568                         awake = true;
569                 };
570                 /*}}}*/
571                 function release()/*{{{*/
572                 {
573                         disableHandles();
574                         $sel.hide();
575                         $img.css('opacity',1);
576                         awake = false;
577                 };
578                 /*}}}*/
579                 function showHandles()//{{{
580                 {
581                         if (seehandles)
582                         {
583                                 moveHandles(Coords.getFixed());
584                                 $hdl_holder.show();
585                         }
586                 };
587                 //}}}
588                 function enableHandles()/*{{{*/
589                 { 
590                         seehandles = true;
591                         if (options.allowResize)
592                         {
593                                 moveHandles(Coords.getFixed());
594                                 $hdl_holder.show();
595                                 return true;
596                         }
597                 };
598                 /*}}}*/
599                 function disableHandles()/*{{{*/
600                 {
601                         seehandles = false;
602                         $hdl_holder.hide();
603                 };
604                 /*}}}*/
605                 function animMode(v)/*{{{*/
606                 {
607                         (animating = v) ? disableHandles(): enableHandles();
608                 };
609                 /*}}}*/
610                 function done()/*{{{*/
611                 {
612                         animMode(false);
613                         refresh();
614                 };
615                 /*}}}*/
616
617                 var $track = newTracker().mousedown(createDragger('move'))
618                                 .css({ cursor: 'move', position: 'absolute', zIndex: 360 })
619
620                 $img_holder.append($track);
621                 disableHandles();
622
623                 return {
624                         updateVisible: updateVisible,
625                         update: update,
626                         release: release,
627                         refresh: refresh,
628                         setCursor: function (cursor) { $track.css('cursor',cursor); },
629                         enableHandles: enableHandles,
630                         enableOnly: function() { seehandles = true; },
631                         showHandles: showHandles,
632                         disableHandles: disableHandles,
633                         animMode: animMode,
634                         done: done
635                 };
636         }();
637         /*}}}*/
638         var Tracker = function()/*{{{*/
639         {
640                 var onMove              = function() { },
641                         onDone          = function() { },
642                         trackDoc        = options.trackDocument;
643
644                 if (!trackDoc)
645                 {
646                         $trk
647                                 .mousemove(trackMove)
648                                 .mouseup(trackUp)
649                                 .mouseout(trackUp)
650                         ;
651                 }
652
653                 function toFront()/*{{{*/
654                 {
655                         $trk.css({zIndex:450});
656                         if (trackDoc)
657                         {
658                                 $(document)
659                                         .mousemove(trackMove)
660                                         .mouseup(trackUp)
661                                 ;
662                         }
663                 }
664                 /*}}}*/
665                 function toBack()/*{{{*/
666                 {
667                         $trk.css({zIndex:290});
668                         if (trackDoc)
669                         {
670                                 $(document)
671                                         .unbind('mousemove',trackMove)
672                                         .unbind('mouseup',trackUp)
673                                 ;
674                         }
675                 }
676                 /*}}}*/
677                 function trackMove(e)/*{{{*/
678                 {
679                         onMove(mouseAbs(e));
680                 };
681                 /*}}}*/
682                 function trackUp(e)/*{{{*/
683                 {
684                         e.preventDefault();
685                         e.stopPropagation();
686
687                         if (btndown)
688                         {
689                                 btndown = false;
690
691                                 onDone(mouseAbs(e));
692                                 options.onSelect(unscale(Coords.getFixed()));
693                                 toBack();
694                                 onMove = function() { };
695                                 onDone = function() { };
696                         }
697
698                         return false;
699                 };
700                 /*}}}*/
701
702                 function activateHandlers(move,done)/* {{{ */
703                 {
704                         btndown = true;
705                         onMove = move;
706                         onDone = done;
707                         toFront();
708                         return false;
709                 };
710                 /* }}} */
711
712                 function setCursor(t) { $trk.css('cursor',t); };
713
714                 $img.before($trk);
715                 return {
716                         activateHandlers: activateHandlers,
717                         setCursor: setCursor
718                 };
719         }();
720         /*}}}*/
721         var KeyManager = function()/*{{{*/
722         {
723                 var $keymgr = $('<input type="radio" />')
724                                 .css({ position: 'absolute', left: '-30px' })
725                                 .keypress(parseKey)
726                                 .blur(onBlur),
727
728                         $keywrap = $('<div />')
729                                 .css({
730                                         position: 'absolute',
731                                         overflow: 'hidden'
732                                 })
733                                 .append($keymgr)
734                 ;
735
736                 function watchKeys()/*{{{*/
737                 {
738                         if (options.keySupport)
739                         {
740                                 $keymgr.show();
741                                 $keymgr.focus();
742                         }
743                 };
744                 /*}}}*/
745                 function onBlur(e)/*{{{*/
746                 {
747                         $keymgr.hide();
748                 };
749                 /*}}}*/
750                 function doNudge(e,x,y)/*{{{*/
751                 {
752                         if (options.allowMove) {
753                                 Coords.moveOffset([x,y]);
754                                 Selection.updateVisible();
755                         };
756                         e.preventDefault();
757                         e.stopPropagation();
758                 };
759                 /*}}}*/
760                 function parseKey(e)/*{{{*/
761                 {
762                         if (e.ctrlKey) return true;
763                         shift_down = e.shiftKey ? true : false;
764                         var nudge = shift_down ? 10 : 1;
765                         switch(e.keyCode)
766                         {
767                                 case 37: doNudge(e,-nudge,0); break;
768                                 case 39: doNudge(e,nudge,0); break;
769                                 case 38: doNudge(e,0,-nudge); break;
770                                 case 40: doNudge(e,0,nudge); break;
771
772                                 case 27: Selection.release(); break;
773
774                                 case 9: return true;
775                         }
776
777                         return nothing(e);
778                 };
779                 /*}}}*/
780                 
781                 if (options.keySupport) $keywrap.insertBefore($img);
782                 return {
783                         watchKeys: watchKeys
784                 };
785         }();
786         /*}}}*/
787
788         // }}}
789         // Internal Methods {{{
790
791         function px(n) { return '' + parseInt(n) + 'px'; };
792         function pct(n) { return '' + parseInt(n) + '%'; };
793         function cssClass(cl) { return options.baseClass + '-' + cl; };
794         function getPos(obj)/*{{{*/
795         {
796                 // Updated in v0.9.4 to use built-in dimensions plugin
797                 var pos = $(obj).offset();
798                 return [ pos.left, pos.top ];
799         };
800         /*}}}*/
801         function mouseAbs(e)/*{{{*/
802         {
803                 return [ (e.pageX - docOffset[0]), (e.pageY - docOffset[1]) ];
804         };
805         /*}}}*/
806         function myCursor(type)/*{{{*/
807         {
808                 if (type != lastcurs)
809                 {
810                         Tracker.setCursor(type);
811                         //Handles.xsetCursor(type);
812                         lastcurs = type;
813                 }
814         };
815         /*}}}*/
816         function startDragMode(mode,pos)/*{{{*/
817         {
818                 docOffset = getPos($img);
819                 Tracker.setCursor(mode=='move'?mode:mode+'-resize');
820
821                 if (mode == 'move')
822                         return Tracker.activateHandlers(createMover(pos), doneSelect);
823
824                 var fc = Coords.getFixed();
825                 var opp = oppLockCorner(mode);
826                 var opc = Coords.getCorner(oppLockCorner(opp));
827
828                 Coords.setPressed(Coords.getCorner(opp));
829                 Coords.setCurrent(opc);
830
831                 Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect);
832         };
833         /*}}}*/
834         function dragmodeHandler(mode,f)/*{{{*/
835         {
836                 return function(pos) {
837                         if (!options.aspectRatio) switch(mode)
838                         {
839                                 case 'e': pos[1] = f.y2; break;
840                                 case 'w': pos[1] = f.y2; break;
841                                 case 'n': pos[0] = f.x2; break;
842                                 case 's': pos[0] = f.x2; break;
843                         }
844                         else switch(mode)
845                         {
846                                 case 'e': pos[1] = f.y+1; break;
847                                 case 'w': pos[1] = f.y+1; break;
848                                 case 'n': pos[0] = f.x+1; break;
849                                 case 's': pos[0] = f.x+1; break;
850                         }
851                         Coords.setCurrent(pos);
852                         Selection.update();
853                 };
854         };
855         /*}}}*/
856         function createMover(pos)/*{{{*/
857         {
858                 var lloc = pos;
859                 KeyManager.watchKeys();
860
861                 return function(pos)
862                 {
863                         Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
864                         lloc = pos;
865                         
866                         Selection.update();
867                 };
868         };
869         /*}}}*/
870         function oppLockCorner(ord)/*{{{*/
871         {
872                 switch(ord)
873                 {
874                         case 'n': return 'sw';
875                         case 's': return 'nw';
876                         case 'e': return 'nw';
877                         case 'w': return 'ne';
878                         case 'ne': return 'sw';
879                         case 'nw': return 'se';
880                         case 'se': return 'nw';
881                         case 'sw': return 'ne';
882                 };
883         };
884         /*}}}*/
885         function createDragger(ord)/*{{{*/
886         {
887                 return function(e) {
888                         if (options.disabled) return false;
889                         if ((ord == 'move') && !options.allowMove) return false;
890                         btndown = true;
891                         startDragMode(ord,mouseAbs(e));
892                         e.stopPropagation();
893                         e.preventDefault();
894                         return false;
895                 };
896         };
897         /*}}}*/
898         function presize($obj,w,h)/*{{{*/
899         {
900                 var nw = $obj.width(), nh = $obj.height();
901                 if ((nw > w) && w > 0)
902                 {
903                         nw = w;
904                         nh = (w/$obj.width()) * $obj.height();
905                 }
906                 if ((nh > h) && h > 0)
907                 {
908                         nh = h;
909                         nw = (h/$obj.height()) * $obj.width();
910                 }
911                 xscale = $obj.width() / nw;
912                 yscale = $obj.height() / nh;
913                 $obj.width(nw).height(nh);
914         };
915         /*}}}*/
916         function unscale(c)/*{{{*/
917         {
918                 return {
919                         x: parseInt(c.x * xscale), y: parseInt(c.y * yscale), 
920                         x2: parseInt(c.x2 * xscale), y2: parseInt(c.y2 * yscale), 
921                         w: parseInt(c.w * xscale), h: parseInt(c.h * yscale)
922                 };
923         };
924         /*}}}*/
925         function doneSelect(pos)/*{{{*/
926         {
927                 var c = Coords.getFixed();
928                 if (c.w > options.minSelect[0] && c.h > options.minSelect[1])
929                 {
930                         Selection.enableHandles();
931                         Selection.done();
932                 }
933                 else
934                 {
935                         Selection.release();
936                 }
937                 Tracker.setCursor( options.allowSelect?'crosshair':'default' );
938         };
939         /*}}}*/
940         function newSelection(e)/*{{{*/
941         {
942                 if (options.disabled) return false;
943                 if (!options.allowSelect) return false;
944                 btndown = true;
945                 docOffset = getPos($img);
946                 Selection.disableHandles();
947                 myCursor('crosshair');
948                 var pos = mouseAbs(e);
949                 Coords.setPressed(pos);
950                 Tracker.activateHandlers(selectDrag,doneSelect);
951                 KeyManager.watchKeys();
952                 Selection.update();
953
954                 e.stopPropagation();
955                 e.preventDefault();
956                 return false;
957         };
958         /*}}}*/
959         function selectDrag(pos)/*{{{*/
960         {
961                 Coords.setCurrent(pos);
962                 Selection.update();
963         };
964         /*}}}*/
965         function newTracker()
966         {
967                 var trk = $('<div></div>').addClass(cssClass('tracker'));
968                 $.browser.msie && trk.css({ opacity: 0, backgroundColor: 'white' });
969                 return trk;
970         };
971
972         // }}}
973         // API methods {{{
974                 
975         function animateTo(a)/*{{{*/
976         {
977                 var x1 = a[0] / xscale,
978                         y1 = a[1] / yscale,
979                         x2 = a[2] / xscale,
980                         y2 = a[3] / yscale;
981
982                 if (animating) return;
983
984                 var animto = Coords.flipCoords(x1,y1,x2,y2);
985                 var c = Coords.getFixed();
986                 var animat = initcr = [ c.x, c.y, c.x2, c.y2 ];
987                 var interv = options.animationDelay;
988
989                 var x = animat[0];
990                 var y = animat[1];
991                 var x2 = animat[2];
992                 var y2 = animat[3];
993                 var ix1 = animto[0] - initcr[0];
994                 var iy1 = animto[1] - initcr[1];
995                 var ix2 = animto[2] - initcr[2];
996                 var iy2 = animto[3] - initcr[3];
997                 var pcent = 0;
998                 var velocity = options.swingSpeed;
999
1000                 Selection.animMode(true);
1001
1002                 var animator = function()
1003                 {
1004                         return function()
1005                         {
1006                                 pcent += (100 - pcent) / velocity;
1007
1008                                 animat[0] = x + ((pcent / 100) * ix1);
1009                                 animat[1] = y + ((pcent / 100) * iy1);
1010                                 animat[2] = x2 + ((pcent / 100) * ix2);
1011                                 animat[3] = y2 + ((pcent / 100) * iy2);
1012
1013                                 if (pcent < 100) animateStart();
1014                                         else Selection.done();
1015
1016                                 if (pcent >= 99.8) pcent = 100;
1017
1018                                 setSelectRaw(animat);
1019                         };
1020                 }();
1021
1022                 function animateStart()
1023                         { window.setTimeout(animator,interv); };
1024
1025                 animateStart();
1026         };
1027         /*}}}*/
1028         function setSelect(rect)//{{{
1029         {
1030                 setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]);
1031         };
1032         //}}}
1033         function setSelectRaw(l) /*{{{*/
1034         {
1035                 Coords.setPressed([l[0],l[1]]);
1036                 Coords.setCurrent([l[2],l[3]]);
1037                 Selection.update();
1038         };
1039         /*}}}*/
1040         function setOptions(opt)/*{{{*/
1041         {
1042                 if (typeof(opt) != 'object') opt = { };
1043                 options = $.extend(options,opt);
1044
1045                 if (typeof(options.onChange)!=='function')
1046                         options.onChange = function() { };
1047
1048                 if (typeof(options.onSelect)!=='function')
1049                         options.onSelect = function() { };
1050
1051         };
1052         /*}}}*/
1053         function tellSelect()/*{{{*/
1054         {
1055                 return unscale(Coords.getFixed());
1056         };
1057         /*}}}*/
1058         function tellScaled()/*{{{*/
1059         {
1060                 return Coords.getFixed();
1061         };
1062         /*}}}*/
1063         function setOptionsNew(opt)/*{{{*/
1064         {
1065                 setOptions(opt);
1066                 interfaceUpdate();
1067         };
1068         /*}}}*/
1069         function disableCrop()//{{{
1070         {
1071                 options.disabled = true;
1072                 Selection.disableHandles();
1073                 Selection.setCursor('default');
1074                 Tracker.setCursor('default');
1075         };
1076         //}}}
1077         function enableCrop()//{{{
1078         {
1079                 options.disabled = false;
1080                 interfaceUpdate();
1081         };
1082         //}}}
1083         function cancelCrop()//{{{
1084         {
1085                 Selection.done();
1086                 Tracker.activateHandlers(null,null);
1087         };
1088         //}}}
1089         function destroy()//{{{
1090         {
1091                 $div.remove();
1092                 $origimg.show();
1093         };
1094         //}}}
1095
1096         function interfaceUpdate(alt)//{{{
1097         // This method tweaks the interface based on options object.
1098         // Called when options are changed and at end of initialization.
1099         {
1100                 options.allowResize ?
1101                         alt?Selection.enableOnly():Selection.enableHandles():
1102                         Selection.disableHandles();
1103
1104                 Tracker.setCursor( options.allowSelect? 'crosshair': 'default' );
1105                 Selection.setCursor( options.allowMove? 'move': 'default' );
1106
1107                 $div.css('backgroundColor',options.bgColor);
1108
1109                 if ('setSelect' in options) {
1110                         setSelect(opt.setSelect);
1111                         Selection.done();
1112                         delete(options.setSelect);
1113                 }
1114
1115                 if ('trueSize' in options) {
1116                         xscale = options.trueSize[0] / boundx;
1117                         yscale = options.trueSize[1] / boundy;
1118                 }
1119
1120                 xlimit = options.maxSize[0] || 0;
1121                 ylimit = options.maxSize[1] || 0;
1122                 xmin = options.minSize[0] || 0;
1123                 ymin = options.minSize[1] || 0;
1124
1125                 if ('outerImage' in options)
1126                 {
1127                         $img.attr('src',options.outerImage);
1128                         delete(options.outerImage);
1129                 }
1130
1131                 Selection.refresh();
1132         };
1133         //}}}
1134
1135         // }}}
1136
1137         $hdl_holder.hide();
1138         interfaceUpdate(true);
1139         
1140         var api = {
1141                 animateTo: animateTo,
1142                 setSelect: setSelect,
1143                 setOptions: setOptionsNew,
1144                 tellSelect: tellSelect,
1145                 tellScaled: tellScaled,
1146
1147                 disable: disableCrop,
1148                 enable: enableCrop,
1149                 cancel: cancelCrop,
1150
1151                 focus: KeyManager.watchKeys,
1152
1153                 getBounds: function() { return [ boundx * xscale, boundy * yscale ]; },
1154                 getWidgetSize: function() { return [ boundx, boundy ]; },
1155
1156                 release: Selection.release,
1157                 destroy: destroy
1158
1159         };
1160
1161         $origimg.data('Jcrop',api);
1162         return api;
1163 };
1164
1165 $.fn.Jcrop = function(options)/*{{{*/
1166 {
1167         function attachWhenDone(from)/*{{{*/
1168         {
1169                 var loadsrc = options.useImg || from.src;
1170                 var img = new Image();
1171                 img.onload = function() { $.Jcrop(from,options); };
1172                 img.src = loadsrc;
1173         };
1174         /*}}}*/
1175         if (typeof(options) !== 'object') options = { };
1176
1177         // Iterate over each object, attach Jcrop
1178         this.each(function()
1179         {
1180                 // If we've already attached to this object
1181                 if ($(this).data('Jcrop'))
1182                 {
1183                         // The API can be requested this way (undocumented)
1184                         if (options == 'api') return $(this).data('Jcrop');
1185                         // Otherwise, we just reset the options...
1186                         else $(this).data('Jcrop').setOptions(options);
1187                 }
1188                 // If we haven't been attached, preload and attach
1189                 else attachWhenDone(this);
1190         });
1191
1192         // Return "this" so we're chainable a la jQuery plugin-style!
1193         return this;
1194 };
1195 /*}}}*/
1196
1197 })(jQuery);