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 {{{
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
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
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.
34 $.Jcrop = function (obj, opt) {
35 var options = $.extend({}, $.Jcrop.defaults),
37 _ua = navigator.userAgent.toLowerCase(),
38 is_msie = /msie/.test(_ua),
39 ie6mode = /msie [1-6]\./.test(_ua);
41 // Internal Methods {{{
43 return Math.round(n) + 'px';
45 function cssClass(cl) {
46 return options.baseClass + '-' + cl;
48 function supportsColorFade() {
49 return $.fx.step.hasOwnProperty('backgroundColor');
51 function getPos(obj) //{{{
53 var pos = $(obj).offset();
54 return [pos.left, pos.top];
57 function mouseAbs(e) //{{{
59 return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
62 function setOptions(opt) //{{{
64 if (typeof(opt) !== 'object') opt = {};
65 options = $.extend(options, opt);
67 $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) {
68 if (typeof(options[e]) !== 'function') options[e] = function () {};
72 function startDragMode(mode, pos, touch) //{{{
74 docOffset = getPos($img);
75 Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');
77 if (mode === 'move') {
78 return Tracker.activateHandlers(createMover(pos), doneSelect, touch);
81 var fc = Coords.getFixed();
82 var opp = oppLockCorner(mode);
83 var opc = Coords.getCorner(oppLockCorner(opp));
85 Coords.setPressed(Coords.getCorner(opp));
86 Coords.setCurrent(opc);
88 Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect, touch);
91 function dragmodeHandler(mode, f) //{{{
93 return function (pos) {
94 if (!options.aspectRatio) {
125 Coords.setCurrent(pos);
130 function createMover(pos) //{{{
133 KeyManager.watchKeys();
135 return function (pos) {
136 Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
143 function oppLockCorner(ord) //{{{
165 function createDragger(ord) //{{{
167 return function (e) {
168 if (options.disabled) {
171 if ((ord === 'move') && !options.allowMove) {
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);
180 startDragMode(ord, mouseAbs(e));
187 function presize($obj, w, h) //{{{
189 var nw = $obj.width(),
191 if ((nw > w) && w > 0) {
193 nh = (w / $obj.width()) * $obj.height();
195 if ((nh > h) && h > 0) {
197 nw = (h / $obj.height()) * $obj.width();
199 xscale = $obj.width() / nw;
200 yscale = $obj.height() / nh;
201 $obj.width(nw).height(nh);
204 function unscale(c) //{{{
216 function doneSelect(pos) //{{{
218 var c = Coords.getFixed();
219 if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
220 Selection.enableHandles();
225 Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
228 function newSelection(e) //{{{
230 if (options.disabled) {
233 if (!options.allowSelect) {
237 docOffset = getPos($img);
238 Selection.disableHandles();
239 Tracker.setCursor('crosshair');
240 var pos = mouseAbs(e);
241 Coords.setPressed(pos);
243 Tracker.activateHandlers(selectDrag, doneSelect, e.type.substring(0,5)==='touch');
244 KeyManager.watchKeys();
251 function selectDrag(pos) //{{{
253 Coords.setCurrent(pos);
257 function newTracker() //{{{
259 var trk = $('<div></div>').addClass(cssClass('tracker'));
263 backgroundColor: 'white'
271 // Initialization {{{
272 // Sanitize some options {{{
273 if (typeof(obj) !== 'object') {
276 if (typeof(opt) !== 'object') {
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.
288 visibility: 'visible',
291 position: 'absolute',
296 var $origimg = $(obj),
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);
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);
314 var $img = $origimg.clone().removeAttr('id').css(img_css).show();
316 $img.width($origimg.width());
317 $img.height($origimg.height());
318 $origimg.after($img).hide();
321 $img = $origimg.css(img_css).show();
323 if (options.shade === null) { options.shade = true; }
326 presize($img, options.boxWidth, options.boxHeight);
328 var boundx = $img.width(),
329 boundy = $img.height(),
332 $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
333 position: 'relative',
334 backgroundColor: options.bgColor
335 }).insertAfter($origimg).append($img);
337 if (options.addClass) {
338 $div.addClass(options.addClass);
341 var $img2 = $('<div />'),
343 $img_holder = $('<div />')
344 .width('100%').height('100%').css({
346 position: 'absolute',
350 $hdl_holder = $('<div />')
351 .width('100%').height('100%').css('zIndex', 320),
355 position: 'absolute',
357 }).dblclick(function(){
358 var c = Coords.getFixed();
359 options.onDblClick.call(api,c);
360 }).insertBefore($img).append($img_holder, $hdl_holder);
365 .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),
367 $img_holder.append($img2);
377 var bound = options.boundary;
378 var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
379 position: 'absolute',
383 }).mousedown(newSelection);
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;
392 docOffset = getPos($img);
395 // Internal Modules {{{
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;
405 for(i=0; i<events.length; i++) {
406 var eventName = events[i];
407 eventName = 'on' + eventName;
408 var isSupported = (eventName in el);
410 el.setAttribute(eventName, 'return;');
411 isSupported = typeof el[eventName] == 'function';
413 support[events[i]] = isSupported;
415 return support.touchstart && support.touchend && support.touchmove;
422 function detectSupport() {
423 if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport;
424 else return hasTouchSupport();
427 createDragger: function (ord) {
428 return function (e) {
429 if (options.disabled) {
432 if ((ord === 'move') && !options.allowMove) {
435 docOffset = getPos($img);
437 startDragMode(ord, mouseAbs(Touch.cfilter(e)), true);
443 newSelection: function (e) {
444 return newSelection(Touch.cfilter(e));
446 cfilter: function (e){
447 e.pageX = e.originalEvent.changedTouches[0].pageX;
448 e.pageY = e.originalEvent.changedTouches[0].pageY;
451 isSupported: hasTouchSupport,
452 support: detectSupport()
457 var Coords = (function () {
464 function setPressed(pos) //{{{
471 function setCurrent(pos) //{{{
480 function getOffset() //{{{
485 function moveOffset(offset) //{{{
497 if (boundy < y2 + oy) {
498 oy += boundy - (y2 + oy);
500 if (boundx < x2 + ox) {
501 ox += boundx - (x2 + ox);
510 function getCorner(ord) //{{{
525 function getFixed() //{{{
527 if (!options.aspectRatio) {
530 // This function could use some optimization I think...
531 var aspect = options.aspectRatio,
532 min_x = options.minSize[0] / xscale,
535 //min_y = options.minSize[1]/yscale,
536 max_x = options.maxSize[0] / xscale,
537 max_y = options.maxSize[1] / yscale,
542 real_ratio = rwa / rha,
551 if (real_ratio < aspect) {
554 xx = rw < 0 ? x1 - w : w + x1;
558 h = Math.abs((xx - x1) / aspect);
559 yy = rh < 0 ? y1 - h : h + y1;
560 } else if (xx > boundx) {
562 h = Math.abs((xx - x1) / aspect);
563 yy = rh < 0 ? y1 - h : h + y1;
568 yy = rh < 0 ? y1 - h : y1 + h;
571 w = Math.abs((yy - y1) * aspect);
572 xx = rw < 0 ? x1 - w : w + x1;
573 } else if (yy > boundy) {
575 w = Math.abs(yy - y1) * aspect;
576 xx = rw < 0 ? x1 - w : w + x1;
581 if (xx > x1) { // right side
582 if (xx - x1 < min_x) {
584 } else if (xx - x1 > max_x) {
588 yy = y1 + (xx - x1) / aspect;
590 yy = y1 - (xx - x1) / aspect;
592 } else if (xx < x1) { // left side
593 if (x1 - xx < min_x) {
595 } else if (x1 - xx > max_x) {
599 yy = y1 + (x1 - xx) / aspect;
601 yy = y1 - (x1 - xx) / aspect;
608 } else if (xx > boundx) {
616 } else if (yy > boundy) {
621 return makeObj(flipCoords(x1, y1, xx, yy));
624 function rebound(p) //{{{
626 if (p[0] < 0) p[0] = 0;
627 if (p[1] < 0) p[1] = 0;
629 if (p[0] > boundx) p[0] = boundx;
630 if (p[1] > boundy) p[1] = boundy;
632 return [Math.round(p[0]), Math.round(p[1])];
635 function flipCoords(x1, y1, x2, y2) //{{{
649 return [xa, ya, xb, yb];
652 function getRect() //{{{
658 if (xlimit && (Math.abs(xsize) > xlimit)) {
659 x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
661 if (ylimit && (Math.abs(ysize) > ylimit)) {
662 y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
665 if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
666 y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
668 if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
669 x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
709 return makeObj(flipCoords(x1, y1, x2, y2));
712 function makeObj(a) //{{{
726 flipCoords: flipCoords,
727 setPressed: setPressed,
728 setCurrent: setCurrent,
729 getOffset: getOffset,
730 moveOffset: moveOffset,
731 getCorner: getCorner,
738 var Shade = (function() {
740 holder = $('<div />').css({
741 position: 'absolute',
747 left: createShade().height(boundy),
748 right: createShade().height(boundy),
749 bottom: createShade()
752 function resizeShades(w,h) {
753 shades.left.css({ height: px(h) });
754 shades.right.css({ height: px(h) });
756 function updateAuto()
758 return updateShade(Coords.getFixed());
760 function updateShade(c)
771 height: px(boundy-c.y2)
775 width: px(boundx-c.x2)
781 function createShade() {
782 return $('<div />').css({
783 position: 'absolute',
784 backgroundColor: options.shadeColor||options.bgColor
787 function enableShade() {
790 holder.insertBefore($img);
792 Selection.setBgOpacity(1,0,1);
795 setBgColor(options.shadeColor||options.bgColor,1);
796 if (Selection.isAwake())
798 setOpacity(options.bgOpacity,1);
800 else setOpacity(1,1);
803 function setBgColor(color,now) {
804 colorChangeMacro(getShades(),color,now);
806 function disableShade() {
811 if (Selection.isAwake()) {
812 Selection.setBgOpacity(options.bgOpacity,1,1);
814 Selection.setBgOpacity(1,1,1);
815 Selection.disableHandles();
817 colorChangeMacro($div,0,1);
820 function setOpacity(opacity,now) {
822 if (options.bgFade && !now) {
827 duration: options.fadeTime
830 else holder.css({opacity:1-opacity});
833 function refreshAll() {
834 options.shade ? enableShade() : disableShade();
835 if (Selection.isAwake()) setOpacity(options.bgOpacity);
837 function getShades() {
838 return holder.children();
843 updateRaw: updateShade,
844 getShades: getShades,
845 setBgColor: setBgColor,
847 disable: disableShade,
848 resize: resizeShades,
854 // Selection Module {{{
855 var Selection = (function () {
864 function insertBorder(type) //{{{
866 var jq = $('<div />').css({
867 position: 'absolute',
868 opacity: options.borderOpacity
869 }).addClass(cssClass(type));
870 $img_holder.append(jq);
874 function dragDiv(ord, zi) //{{{
876 var jq = $('<div />').mousedown(createDragger(ord)).css({
877 cursor: ord + '-resize',
878 position: 'absolute',
880 }).addClass('ord-'+ord);
883 jq.bind('touchstart.jcrop', Touch.createDragger(ord));
886 $hdl_holder.append(jq);
890 function insertHandle(ord) //{{{
892 var hs = options.handleSize,
894 div = dragDiv(ord, hdep++).css({
895 opacity: options.handleOpacity
896 }).addClass(cssClass('handle'));
898 if (hs) { div.width(hs).height(hs); }
903 function insertDragbar(ord) //{{{
905 return dragDiv(ord, hdep++).addClass('jcrop-dragbar');
908 function createDragbars(li) //{{{
911 for (i = 0; i < li.length; i++) {
912 dragbar[li[i]] = insertDragbar(li[i]);
916 function createBorders(li) //{{{
919 for (i = 0; i < li.length; 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;
926 borders[li[i]] = insertBorder(cl);
930 function createHandles(li) //{{{
933 for (i = 0; i < li.length; i++) {
934 handle[li[i]] = insertHandle(li[i]);
938 function moveto(x, y) //{{{
940 if (!options.shade) {
952 function resize(w, h) //{{{
954 $sel.width(Math.round(w)).height(Math.round(h));
957 function refresh() //{{{
959 var c = Coords.getFixed();
961 Coords.setPressed([c.x, c.y]);
962 Coords.setCurrent([c.x2, c.y2]);
969 function updateVisible(select) //{{{
972 return update(select);
976 function update(select) //{{{
978 var c = Coords.getFixed();
982 if (options.shade) Shade.updateRaw(c);
987 options.onSelect.call(api, unscale(c));
989 options.onChange.call(api, unscale(c));
993 function setBgOpacity(opacity,force,now) //{{{
995 if (!awake && !force) return;
996 if (options.bgFade && !now) {
1001 duration: options.fadeTime
1004 $img.css('opacity', opacity);
1008 function show() //{{{
1012 if (options.shade) Shade.opacity(bgopacity);
1013 else setBgOpacity(bgopacity,true);
1018 function release() //{{{
1023 if (options.shade) Shade.opacity(1);
1024 else setBgOpacity(1);
1027 options.onRelease.call(api);
1030 function showHandles() //{{{
1037 function enableHandles() //{{{
1040 if (options.allowResize) {
1046 function disableHandles() //{{{
1052 function animMode(v) //{{{
1063 function done() //{{{
1069 // Insert draggable elements {{{
1070 // Insert border divs for outline
1072 if (options.dragEdges && $.isArray(options.createDragbars))
1073 createDragbars(options.createDragbars);
1075 if ($.isArray(options.createHandles))
1076 createHandles(options.createHandles);
1078 if (options.drawBorders && $.isArray(options.createBorders))
1079 createBorders(options.createBorders);
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();
1088 var $track = newTracker().mousedown(createDragger('move')).css({
1090 position: 'absolute',
1094 if (Touch.support) {
1095 $track.bind('touchstart.jcrop', Touch.createDragger('move'));
1098 $img_holder.append($track);
1102 updateVisible: updateVisible,
1106 isAwake: function () {
1109 setCursor: function (cursor) {
1110 $track.css('cursor', cursor);
1112 enableHandles: enableHandles,
1113 enableOnly: function () {
1116 showHandles: showHandles,
1117 disableHandles: disableHandles,
1119 setBgOpacity: setBgOpacity,
1125 // Tracker Module {{{
1126 var Tracker = (function () {
1127 var onMove = function () {},
1128 onDone = function () {},
1129 trackDoc = options.trackDocument;
1131 function toFront(touch) //{{{
1139 .bind('touchmove.jcrop', trackTouchMove)
1140 .bind('touchend.jcrop', trackTouchEnd);
1144 .bind('mousemove.jcrop',trackMove)
1145 .bind('mouseup.jcrop',trackUp);
1148 function toBack() //{{{
1153 $(document).unbind('.jcrop');
1156 function trackMove(e) //{{{
1158 onMove(mouseAbs(e));
1162 function trackUp(e) //{{{
1165 e.stopPropagation();
1170 onDone(mouseAbs(e));
1172 if (Selection.isAwake()) {
1173 options.onSelect.call(api, unscale(Coords.getFixed()));
1177 onMove = function () {};
1178 onDone = function () {};
1184 function activateHandlers(move, done, touch) //{{{
1193 function trackTouchMove(e) //{{{
1195 onMove(mouseAbs(Touch.cfilter(e)));
1199 function trackTouchEnd(e) //{{{
1201 return trackUp(Touch.cfilter(e));
1204 function setCursor(t) //{{{
1206 $trk.css('cursor', t);
1211 $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
1216 activateHandlers: activateHandlers,
1217 setCursor: setCursor
1221 // KeyManager Module {{{
1222 var KeyManager = (function () {
1223 var $keymgr = $('<input type="radio" />').css({
1227 }).addClass('jcrop-keymgr'),
1229 $keywrap = $('<div />').css({
1230 position: 'absolute',
1234 function watchKeys() //{{{
1236 if (options.keySupport) {
1242 function onBlur(e) //{{{
1247 function doNudge(e, x, y) //{{{
1249 if (options.allowMove) {
1250 Coords.moveOffset([x, y]);
1251 Selection.updateVisible(true);
1254 e.stopPropagation();
1257 function parseKey(e) //{{{
1259 if (e.ctrlKey || e.metaKey) {
1262 shift_down = e.shiftKey ? true : false;
1263 var nudge = shift_down ? 10 : 1;
1265 switch (e.keyCode) {
1267 doNudge(e, -nudge, 0);
1270 doNudge(e, nudge, 0);
1273 doNudge(e, 0, -nudge);
1276 doNudge(e, 0, nudge);
1279 if (options.allowSelect) Selection.release();
1289 if (options.keySupport) {
1290 $keymgr.keydown(parseKey).blur(onBlur);
1291 if (ie6mode || !options.fixedSupport) {
1293 position: 'absolute',
1296 $keywrap.append($keymgr).insertBefore($img);
1298 $keymgr.insertBefore($img);
1304 watchKeys: watchKeys
1310 function setClass(cname) //{{{
1312 $div.removeClass().addClass(cssClass('holder')).addClass(cname);
1315 function animateTo(a, callback) //{{{
1317 var x1 = a[0] / xscale,
1326 var animto = Coords.flipCoords(x1, y1, x2, y2),
1327 c = Coords.getFixed(),
1328 initcr = [c.x, c.y, c.x2, c.y2],
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],
1336 velocity = options.swingSpeed;
1343 Selection.animMode(true);
1346 function queueAnimator() {
1347 window.setTimeout(animator, interv);
1349 var animator = (function () {
1350 return function () {
1351 pcent += (100 - pcent) / velocity;
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));
1358 if (pcent >= 99.8) {
1362 setSelectRaw(animat);
1366 Selection.animMode(false);
1367 if (typeof(callback) === 'function') {
1376 function setSelect(rect) //{{{
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();
1383 function setSelectRaw(l) //{{{
1385 Coords.setPressed([l[0], l[1]]);
1386 Coords.setCurrent([l[2], l[3]]);
1390 function tellSelect() //{{{
1392 return unscale(Coords.getFixed());
1395 function tellScaled() //{{{
1397 return Coords.getFixed();
1400 function setOptionsNew(opt) //{{{
1406 function disableCrop() //{{{
1408 options.disabled = true;
1409 Selection.disableHandles();
1410 Selection.setCursor('default');
1411 Tracker.setCursor('default');
1414 function enableCrop() //{{{
1416 options.disabled = false;
1420 function cancelCrop() //{{{
1423 Tracker.activateHandlers(null, null);
1426 function destroy() //{{{
1430 $origimg.css('visibility','visible');
1431 $(obj).removeData('Jcrop');
1434 function setImage(src, callback) //{{{
1436 Selection.release();
1438 var img = new Image();
1439 img.onload = function () {
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);
1456 if (typeof(callback) === 'function') {
1463 function colorChangeMacro($obj,color,now) {
1464 var mycolor = color || options.bgColor;
1465 if (options.bgFade && supportsColorFade() && options.fadeTime && !now) {
1467 backgroundColor: mycolor
1470 duration: options.fadeTime
1473 $obj.css('backgroundColor', mycolor);
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.
1480 if (options.allowResize) {
1482 Selection.enableOnly();
1484 Selection.enableHandles();
1487 Selection.disableHandles();
1490 Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
1491 Selection.setCursor(options.allowMove ? 'move' : 'default');
1493 if (options.hasOwnProperty('trueSize')) {
1494 xscale = options.trueSize[0] / boundx;
1495 yscale = options.trueSize[1] / boundy;
1498 if (options.hasOwnProperty('setSelect')) {
1499 setSelect(options.setSelect);
1501 delete(options.setSelect);
1506 if (options.bgColor != bgcolor) {
1508 options.shade? Shade.getShades(): $div,
1510 (options.shadeColor || options.bgColor):
1513 bgcolor = options.bgColor;
1516 if (bgopacity != options.bgOpacity) {
1517 bgopacity = options.bgOpacity;
1518 if (options.shade) Shade.refresh();
1519 else Selection.setBgOpacity(bgopacity);
1522 xlimit = options.maxSize[0] || 0;
1523 ylimit = options.maxSize[1] || 0;
1524 xmin = options.minSize[0] || 0;
1525 ymin = options.minSize[1] || 0;
1527 if (options.hasOwnProperty('outerImage')) {
1528 $img.attr('src', options.outerImage);
1529 delete(options.outerImage);
1532 Selection.refresh();
1537 if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection);
1540 interfaceUpdate(true);
1544 animateTo: animateTo,
1545 setSelect: setSelect,
1546 setOptions: setOptionsNew,
1547 tellSelect: tellSelect,
1548 tellScaled: tellScaled,
1551 disable: disableCrop,
1554 release: Selection.release,
1557 focus: KeyManager.watchKeys,
1559 getBounds: function () {
1560 return [boundx * xscale, boundy * yscale];
1562 getWidgetSize: function () {
1563 return [boundx, boundy];
1565 getScaleFactor: function () {
1566 return [xscale, yscale];
1568 getOptions: function() {
1569 // careful: internal values are returned
1579 if (is_msie) $div.bind('selectstart', function () { return false; });
1581 $origimg.data('Jcrop', api);
1584 $.fn.Jcrop = function (options, callback) //{{{
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);
1596 // If we haven't been attached, preload and attach
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);
1605 $(this).css({display:'block',visibility:'hidden'});
1606 api = $.Jcrop(this, options);
1607 if ($.isFunction(callback)) callback.call(api);
1612 // Return "this" so the object is chainable (jQuery-style)
1616 // $.Jcrop.Loader - basic image loader {{{
1618 $.Jcrop.Loader = function(imgobj,success,error){
1619 var $img = $(imgobj), img = $img[0];
1621 function completeCheck(){
1623 $img.unbind('.jcloader');
1624 if ($.isFunction(success)) success.call(img);
1626 else window.setTimeout(completeCheck,50);
1630 .bind('load.jcloader',completeCheck)
1631 .bind('error.jcloader',function(e){
1632 $img.unbind('.jcloader');
1633 if ($.isFunction(error)) error.call(img);
1636 if (img.complete && $.isFunction(success)){
1637 $img.unbind('.jcloader');
1643 // Global Defaults {{{
1644 $.Jcrop.defaults = {
1651 trackDocument: true,
1665 createHandles: ['n','s','e','w','nw','ne','se','sw'],
1666 createDragbars: ['n','s','e','w'],
1667 createBorders: ['n','s','e','w'],
1686 // Callbacks / Event Handlers
1687 onChange: function () {},
1688 onSelect: function () {},
1689 onDblClick: function () {},
1690 onRelease: function () {}