| /** |
| * jquery.Jcrop.js v0.9.12 |
| * jQuery Image Cropping Plugin - released under MIT License |
| * Author: Kelly Hallman <khallman@gmail.com> |
| * http://github.com/tapmodo/Jcrop |
| * Copyright (c) 2008-2013 Tapmodo Interactive LLC {{{ |
| * |
| * Permission is hereby granted, free of charge, to any person |
| * obtaining a copy of this software and associated documentation |
| * files (the "Software"), to deal in the Software without |
| * restriction, including without limitation the rights to use, |
| * copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following |
| * conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
| * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * }}} |
| */ |
| |
| (function ($) { |
| |
| $.Jcrop = function (obj, opt) { |
| var options = $.extend({}, $.Jcrop.defaults), |
| docOffset, |
| _ua = navigator.userAgent.toLowerCase(), |
| is_msie = /msie/.test(_ua), |
| ie6mode = /msie [1-6]\./.test(_ua); |
| |
| // Internal Methods {{{ |
| function px(n) { |
| return Math.round(n) + 'px'; |
| } |
| function cssClass(cl) { |
| return options.baseClass + '-' + cl; |
| } |
| function supportsColorFade() { |
| return $.fx.step.hasOwnProperty('backgroundColor'); |
| } |
| function getPos(obj) //{{{ |
| { |
| var pos = $(obj).offset(); |
| return [pos.left, pos.top]; |
| } |
| //}}} |
| function mouseAbs(e) //{{{ |
| { |
| return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])]; |
| } |
| //}}} |
| function setOptions(opt) //{{{ |
| { |
| if (typeof(opt) !== 'object') opt = {}; |
| options = $.extend(options, opt); |
| |
| $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) { |
| if (typeof(options[e]) !== 'function') options[e] = function () {}; |
| }); |
| } |
| //}}} |
| function startDragMode(mode, pos, touch) //{{{ |
| { |
| docOffset = getPos($img); |
| Tracker.setCursor(mode === 'move' ? mode : mode + '-resize'); |
| |
| if (mode === 'move') { |
| return Tracker.activateHandlers(createMover(pos), doneSelect, touch); |
| } |
| |
| var fc = Coords.getFixed(); |
| var opp = oppLockCorner(mode); |
| var opc = Coords.getCorner(oppLockCorner(opp)); |
| |
| Coords.setPressed(Coords.getCorner(opp)); |
| Coords.setCurrent(opc); |
| |
| Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect, touch); |
| } |
| //}}} |
| function dragmodeHandler(mode, f) //{{{ |
| { |
| return function (pos) { |
| if (!options.aspectRatio) { |
| switch (mode) { |
| case 'e': |
| pos[1] = f.y2; |
| break; |
| case 'w': |
| pos[1] = f.y2; |
| break; |
| case 'n': |
| pos[0] = f.x2; |
| break; |
| case 's': |
| pos[0] = f.x2; |
| break; |
| } |
| } else { |
| switch (mode) { |
| case 'e': |
| pos[1] = f.y + 1; |
| break; |
| case 'w': |
| pos[1] = f.y + 1; |
| break; |
| case 'n': |
| pos[0] = f.x + 1; |
| break; |
| case 's': |
| pos[0] = f.x + 1; |
| break; |
| } |
| } |
| Coords.setCurrent(pos); |
| Selection.update(); |
| }; |
| } |
| //}}} |
| function createMover(pos) //{{{ |
| { |
| var lloc = pos; |
| KeyManager.watchKeys(); |
| |
| return function (pos) { |
| Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]); |
| lloc = pos; |
| |
| Selection.update(); |
| }; |
| } |
| //}}} |
| function oppLockCorner(ord) //{{{ |
| { |
| switch (ord) { |
| case 'n': |
| return 'sw'; |
| case 's': |
| return 'nw'; |
| case 'e': |
| return 'nw'; |
| case 'w': |
| return 'ne'; |
| case 'ne': |
| return 'sw'; |
| case 'nw': |
| return 'se'; |
| case 'se': |
| return 'nw'; |
| case 'sw': |
| return 'ne'; |
| } |
| } |
| //}}} |
| function createDragger(ord) //{{{ |
| { |
| return function (e) { |
| if (options.disabled) { |
| return false; |
| } |
| if ((ord === 'move') && !options.allowMove) { |
| return false; |
| } |
| |
| // Fix position of crop area when dragged the very first time. |
| // Necessary when crop image is in a hidden element when page is loaded. |
| docOffset = getPos($img); |
| |
| btndown = true; |
| startDragMode(ord, mouseAbs(e)); |
| e.stopPropagation(); |
| e.preventDefault(); |
| return false; |
| }; |
| } |
| //}}} |
| function presize($obj, w, h) //{{{ |
| { |
| var nw = $obj.width(), |
| nh = $obj.height(); |
| if ((nw > w) && w > 0) { |
| nw = w; |
| nh = (w / $obj.width()) * $obj.height(); |
| } |
| if ((nh > h) && h > 0) { |
| nh = h; |
| nw = (h / $obj.height()) * $obj.width(); |
| } |
| xscale = $obj.width() / nw; |
| yscale = $obj.height() / nh; |
| $obj.width(nw).height(nh); |
| } |
| //}}} |
| function unscale(c) //{{{ |
| { |
| return { |
| x: c.x * xscale, |
| y: c.y * yscale, |
| x2: c.x2 * xscale, |
| y2: c.y2 * yscale, |
| w: c.w * xscale, |
| h: c.h * yscale |
| }; |
| } |
| //}}} |
| function doneSelect(pos) //{{{ |
| { |
| var c = Coords.getFixed(); |
| if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) { |
| Selection.enableHandles(); |
| Selection.done(); |
| } else { |
| Selection.release(); |
| } |
| Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); |
| } |
| //}}} |
| function newSelection(e) //{{{ |
| { |
| if (options.disabled) { |
| return false; |
| } |
| if (!options.allowSelect) { |
| return false; |
| } |
| btndown = true; |
| docOffset = getPos($img); |
| Selection.disableHandles(); |
| Tracker.setCursor('crosshair'); |
| var pos = mouseAbs(e); |
| Coords.setPressed(pos); |
| Selection.update(); |
| Tracker.activateHandlers(selectDrag, doneSelect, e.type.substring(0,5)==='touch'); |
| KeyManager.watchKeys(); |
| |
| e.stopPropagation(); |
| e.preventDefault(); |
| return false; |
| } |
| //}}} |
| function selectDrag(pos) //{{{ |
| { |
| Coords.setCurrent(pos); |
| Selection.update(); |
| } |
| //}}} |
| function newTracker() //{{{ |
| { |
| var trk = $('<div></div>').addClass(cssClass('tracker')); |
| if (is_msie) { |
| trk.css({ |
| opacity: 0, |
| backgroundColor: 'white' |
| }); |
| } |
| return trk; |
| } |
| //}}} |
| |
| // }}} |
| // Initialization {{{ |
| // Sanitize some options {{{ |
| if (typeof(obj) !== 'object') { |
| obj = $(obj)[0]; |
| } |
| if (typeof(opt) !== 'object') { |
| opt = {}; |
| } |
| // }}} |
| setOptions(opt); |
| // Initialize some jQuery objects {{{ |
| // The values are SET on the image(s) for the interface |
| // If the original image has any of these set, they will be reset |
| // However, if you destroy() the Jcrop instance the original image's |
| // character in the DOM will be as you left it. |
| var img_css = { |
| border: 'none', |
| visibility: 'visible', |
| margin: 0, |
| padding: 0, |
| position: 'absolute', |
| top: 0, |
| left: 0 |
| }; |
| |
| var $origimg = $(obj), |
| img_mode = true; |
| |
| if (obj.tagName == 'IMG') { |
| // Fix size of crop image. |
| // Necessary when crop image is within a hidden element when page is loaded. |
| if ($origimg[0].width != 0 && $origimg[0].height != 0) { |
| // Obtain dimensions from contained img element. |
| $origimg.width($origimg[0].width); |
| $origimg.height($origimg[0].height); |
| } else { |
| // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). |
| var tempImage = new Image(); |
| tempImage.src = $origimg[0].src; |
| $origimg.width(tempImage.width); |
| $origimg.height(tempImage.height); |
| } |
| |
| var $img = $origimg.clone().removeAttr('id').css(img_css).show(); |
| |
| $img.width($origimg.width()); |
| $img.height($origimg.height()); |
| $origimg.after($img).hide(); |
| |
| } else { |
| $img = $origimg.css(img_css).show(); |
| img_mode = false; |
| if (options.shade === null) { options.shade = true; } |
| } |
| |
| presize($img, options.boxWidth, options.boxHeight); |
| |
| var boundx = $img.width(), |
| boundy = $img.height(), |
| |
| |
| $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({ |
| position: 'relative', |
| backgroundColor: options.bgColor |
| }).insertAfter($origimg).append($img); |
| |
| if (options.addClass) { |
| $div.addClass(options.addClass); |
| } |
| |
| var $img2 = $('<div />'), |
| |
| $img_holder = $('<div />') |
| .width('100%').height('100%').css({ |
| zIndex: 310, |
| position: 'absolute', |
| overflow: 'hidden' |
| }), |
| |
| $hdl_holder = $('<div />') |
| .width('100%').height('100%').css('zIndex', 320), |
| |
| $sel = $('<div />') |
| .css({ |
| position: 'absolute', |
| zIndex: 600 |
| }).dblclick(function(){ |
| var c = Coords.getFixed(); |
| options.onDblClick.call(api,c); |
| }).insertBefore($img).append($img_holder, $hdl_holder); |
| |
| if (img_mode) { |
| |
| $img2 = $('<img />') |
| .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy), |
| |
| $img_holder.append($img2); |
| |
| } |
| |
| if (ie6mode) { |
| $sel.css({ |
| overflowY: 'hidden' |
| }); |
| } |
| |
| var bound = options.boundary; |
| var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({ |
| position: 'absolute', |
| top: px(-bound), |
| left: px(-bound), |
| zIndex: 290 |
| }).mousedown(newSelection); |
| |
| /* }}} */ |
| // Set more variables {{{ |
| var bgcolor = options.bgColor, |
| bgopacity = options.bgOpacity, |
| xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true, |
| btndown, animating, shift_down; |
| |
| docOffset = getPos($img); |
| // }}} |
| // }}} |
| // Internal Modules {{{ |
| // Touch Module {{{ |
| var Touch = (function () { |
| // Touch support detection function adapted (under MIT License) |
| // from code by Jeffrey Sambells - http://github.com/iamamused/ |
| function hasTouchSupport() { |
| var support = {}, events = ['touchstart', 'touchmove', 'touchend'], |
| el = document.createElement('div'), i; |
| |
| try { |
| for(i=0; i<events.length; i++) { |
| var eventName = events[i]; |
| eventName = 'on' + eventName; |
| var isSupported = (eventName in el); |
| if (!isSupported) { |
| el.setAttribute(eventName, 'return;'); |
| isSupported = typeof el[eventName] == 'function'; |
| } |
| support[events[i]] = isSupported; |
| } |
| return support.touchstart && support.touchend && support.touchmove; |
| } |
| catch(err) { |
| return false; |
| } |
| } |
| |
| function detectSupport() { |
| if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport; |
| else return hasTouchSupport(); |
| } |
| return { |
| createDragger: function (ord) { |
| return function (e) { |
| if (options.disabled) { |
| return false; |
| } |
| if ((ord === 'move') && !options.allowMove) { |
| return false; |
| } |
| docOffset = getPos($img); |
| btndown = true; |
| startDragMode(ord, mouseAbs(Touch.cfilter(e)), true); |
| e.stopPropagation(); |
| e.preventDefault(); |
| return false; |
| }; |
| }, |
| newSelection: function (e) { |
| return newSelection(Touch.cfilter(e)); |
| }, |
| cfilter: function (e){ |
| e.pageX = e.originalEvent.changedTouches[0].pageX; |
| e.pageY = e.originalEvent.changedTouches[0].pageY; |
| return e; |
| }, |
| isSupported: hasTouchSupport, |
| support: detectSupport() |
| }; |
| }()); |
| // }}} |
| // Coords Module {{{ |
| var Coords = (function () { |
| var x1 = 0, |
| y1 = 0, |
| x2 = 0, |
| y2 = 0, |
| ox, oy; |
| |
| function setPressed(pos) //{{{ |
| { |
| pos = rebound(pos); |
| x2 = x1 = pos[0]; |
| y2 = y1 = pos[1]; |
| } |
| //}}} |
| function setCurrent(pos) //{{{ |
| { |
| pos = rebound(pos); |
| ox = pos[0] - x2; |
| oy = pos[1] - y2; |
| x2 = pos[0]; |
| y2 = pos[1]; |
| } |
| //}}} |
| function getOffset() //{{{ |
| { |
| return [ox, oy]; |
| } |
| //}}} |
| function moveOffset(offset) //{{{ |
| { |
| var ox = offset[0], |
| oy = offset[1]; |
| |
| if (0 > x1 + ox) { |
| ox -= ox + x1; |
| } |
| if (0 > y1 + oy) { |
| oy -= oy + y1; |
| } |
| |
| if (boundy < y2 + oy) { |
| oy += boundy - (y2 + oy); |
| } |
| if (boundx < x2 + ox) { |
| ox += boundx - (x2 + ox); |
| } |
| |
| x1 += ox; |
| x2 += ox; |
| y1 += oy; |
| y2 += oy; |
| } |
| //}}} |
| function getCorner(ord) //{{{ |
| { |
| var c = getFixed(); |
| switch (ord) { |
| case 'ne': |
| return [c.x2, c.y]; |
| case 'nw': |
| return [c.x, c.y]; |
| case 'se': |
| return [c.x2, c.y2]; |
| case 'sw': |
| return [c.x, c.y2]; |
| } |
| } |
| //}}} |
| function getFixed() //{{{ |
| { |
| if (!options.aspectRatio) { |
| return getRect(); |
| } |
| // This function could use some optimization I think... |
| var aspect = options.aspectRatio, |
| min_x = options.minSize[0] / xscale, |
| |
| |
| //min_y = options.minSize[1]/yscale, |
| max_x = options.maxSize[0] / xscale, |
| max_y = options.maxSize[1] / yscale, |
| rw = x2 - x1, |
| rh = y2 - y1, |
| rwa = Math.abs(rw), |
| rha = Math.abs(rh), |
| real_ratio = rwa / rha, |
| xx, yy, w, h; |
| |
| if (max_x === 0) { |
| max_x = boundx * 10; |
| } |
| if (max_y === 0) { |
| max_y = boundy * 10; |
| } |
| if (real_ratio < aspect) { |
| yy = y2; |
| w = rha * aspect; |
| xx = rw < 0 ? x1 - w : w + x1; |
| |
| if (xx < 0) { |
| xx = 0; |
| h = Math.abs((xx - x1) / aspect); |
| yy = rh < 0 ? y1 - h : h + y1; |
| } else if (xx > boundx) { |
| xx = boundx; |
| h = Math.abs((xx - x1) / aspect); |
| yy = rh < 0 ? y1 - h : h + y1; |
| } |
| } else { |
| xx = x2; |
| h = rwa / aspect; |
| yy = rh < 0 ? y1 - h : y1 + h; |
| if (yy < 0) { |
| yy = 0; |
| w = Math.abs((yy - y1) * aspect); |
| xx = rw < 0 ? x1 - w : w + x1; |
| } else if (yy > boundy) { |
| yy = boundy; |
| w = Math.abs(yy - y1) * aspect; |
| xx = rw < 0 ? x1 - w : w + x1; |
| } |
| } |
| |
| // Magic %-) |
| if (xx > x1) { // right side |
| if (xx - x1 < min_x) { |
| xx = x1 + min_x; |
| } else if (xx - x1 > max_x) { |
| xx = x1 + max_x; |
| } |
| if (yy > y1) { |
| yy = y1 + (xx - x1) / aspect; |
| } else { |
| yy = y1 - (xx - x1) / aspect; |
| } |
| } else if (xx < x1) { // left side |
| if (x1 - xx < min_x) { |
| xx = x1 - min_x; |
| } else if (x1 - xx > max_x) { |
| xx = x1 - max_x; |
| } |
| if (yy > y1) { |
| yy = y1 + (x1 - xx) / aspect; |
| } else { |
| yy = y1 - (x1 - xx) / aspect; |
| } |
| } |
| |
| if (xx < 0) { |
| x1 -= xx; |
| xx = 0; |
| } else if (xx > boundx) { |
| x1 -= xx - boundx; |
| xx = boundx; |
| } |
| |
| if (yy < 0) { |
| y1 -= yy; |
| yy = 0; |
| } else if (yy > boundy) { |
| y1 -= yy - boundy; |
| yy = boundy; |
| } |
| |
| return makeObj(flipCoords(x1, y1, xx, yy)); |
| } |
| //}}} |
| function rebound(p) //{{{ |
| { |
| if (p[0] < 0) p[0] = 0; |
| if (p[1] < 0) p[1] = 0; |
| |
| if (p[0] > boundx) p[0] = boundx; |
| if (p[1] > boundy) p[1] = boundy; |
| |
| return [Math.round(p[0]), Math.round(p[1])]; |
| } |
| //}}} |
| function flipCoords(x1, y1, x2, y2) //{{{ |
| { |
| var xa = x1, |
| xb = x2, |
| ya = y1, |
| yb = y2; |
| if (x2 < x1) { |
| xa = x2; |
| xb = x1; |
| } |
| if (y2 < y1) { |
| ya = y2; |
| yb = y1; |
| } |
| return [xa, ya, xb, yb]; |
| } |
| //}}} |
| function getRect() //{{{ |
| { |
| var xsize = x2 - x1, |
| ysize = y2 - y1, |
| delta; |
| |
| if (xlimit && (Math.abs(xsize) > xlimit)) { |
| x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit); |
| } |
| if (ylimit && (Math.abs(ysize) > ylimit)) { |
| y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit); |
| } |
| |
| if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) { |
| y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale); |
| } |
| if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) { |
| x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale); |
| } |
| |
| if (x1 < 0) { |
| x2 -= x1; |
| x1 -= x1; |
| } |
| if (y1 < 0) { |
| y2 -= y1; |
| y1 -= y1; |
| } |
| if (x2 < 0) { |
| x1 -= x2; |
| x2 -= x2; |
| } |
| if (y2 < 0) { |
| y1 -= y2; |
| y2 -= y2; |
| } |
| if (x2 > boundx) { |
| delta = x2 - boundx; |
| x1 -= delta; |
| x2 -= delta; |
| } |
| if (y2 > boundy) { |
| delta = y2 - boundy; |
| y1 -= delta; |
| y2 -= delta; |
| } |
| if (x1 > boundx) { |
| delta = x1 - boundy; |
| y2 -= delta; |
| y1 -= delta; |
| } |
| if (y1 > boundy) { |
| delta = y1 - boundy; |
| y2 -= delta; |
| y1 -= delta; |
| } |
| |
| return makeObj(flipCoords(x1, y1, x2, y2)); |
| } |
| //}}} |
| function makeObj(a) //{{{ |
| { |
| return { |
| x: a[0], |
| y: a[1], |
| x2: a[2], |
| y2: a[3], |
| w: a[2] - a[0], |
| h: a[3] - a[1] |
| }; |
| } |
| //}}} |
| |
| return { |
| flipCoords: flipCoords, |
| setPressed: setPressed, |
| setCurrent: setCurrent, |
| getOffset: getOffset, |
| moveOffset: moveOffset, |
| getCorner: getCorner, |
| getFixed: getFixed |
| }; |
| }()); |
| |
| //}}} |
| // Shade Module {{{ |
| var Shade = (function() { |
| var enabled = false, |
| holder = $('<div />').css({ |
| position: 'absolute', |
| zIndex: 240, |
| opacity: 0 |
| }), |
| shades = { |
| top: createShade(), |
| left: createShade().height(boundy), |
| right: createShade().height(boundy), |
| bottom: createShade() |
| }; |
| |
| function resizeShades(w,h) { |
| shades.left.css({ height: px(h) }); |
| shades.right.css({ height: px(h) }); |
| } |
| function updateAuto() |
| { |
| return updateShade(Coords.getFixed()); |
| } |
| function updateShade(c) |
| { |
| shades.top.css({ |
| left: px(c.x), |
| width: px(c.w), |
| height: px(c.y) |
| }); |
| shades.bottom.css({ |
| top: px(c.y2), |
| left: px(c.x), |
| width: px(c.w), |
| height: px(boundy-c.y2) |
| }); |
| shades.right.css({ |
| left: px(c.x2), |
| width: px(boundx-c.x2) |
| }); |
| shades.left.css({ |
| width: px(c.x) |
| }); |
| } |
| function createShade() { |
| return $('<div />').css({ |
| position: 'absolute', |
| backgroundColor: options.shadeColor||options.bgColor |
| }).appendTo(holder); |
| } |
| function enableShade() { |
| if (!enabled) { |
| enabled = true; |
| holder.insertBefore($img); |
| updateAuto(); |
| Selection.setBgOpacity(1,0,1); |
| $img2.hide(); |
| |
| setBgColor(options.shadeColor||options.bgColor,1); |
| if (Selection.isAwake()) |
| { |
| setOpacity(options.bgOpacity,1); |
| } |
| else setOpacity(1,1); |
| } |
| } |
| function setBgColor(color,now) { |
| colorChangeMacro(getShades(),color,now); |
| } |
| function disableShade() { |
| if (enabled) { |
| holder.remove(); |
| $img2.show(); |
| enabled = false; |
| if (Selection.isAwake()) { |
| Selection.setBgOpacity(options.bgOpacity,1,1); |
| } else { |
| Selection.setBgOpacity(1,1,1); |
| Selection.disableHandles(); |
| } |
| colorChangeMacro($div,0,1); |
| } |
| } |
| function setOpacity(opacity,now) { |
| if (enabled) { |
| if (options.bgFade && !now) { |
| holder.animate({ |
| opacity: 1-opacity |
| },{ |
| queue: false, |
| duration: options.fadeTime |
| }); |
| } |
| else holder.css({opacity:1-opacity}); |
| } |
| } |
| function refreshAll() { |
| options.shade ? enableShade() : disableShade(); |
| if (Selection.isAwake()) setOpacity(options.bgOpacity); |
| } |
| function getShades() { |
| return holder.children(); |
| } |
| |
| return { |
| update: updateAuto, |
| updateRaw: updateShade, |
| getShades: getShades, |
| setBgColor: setBgColor, |
| enable: enableShade, |
| disable: disableShade, |
| resize: resizeShades, |
| refresh: refreshAll, |
| opacity: setOpacity |
| }; |
| }()); |
| // }}} |
| // Selection Module {{{ |
| var Selection = (function () { |
| var awake, |
| hdep = 370, |
| borders = {}, |
| handle = {}, |
| dragbar = {}, |
| seehandles = false; |
| |
| // Private Methods |
| function insertBorder(type) //{{{ |
| { |
| var jq = $('<div />').css({ |
| position: 'absolute', |
| opacity: options.borderOpacity |
| }).addClass(cssClass(type)); |
| $img_holder.append(jq); |
| return jq; |
| } |
| //}}} |
| function dragDiv(ord, zi) //{{{ |
| { |
| var jq = $('<div />').mousedown(createDragger(ord)).css({ |
| cursor: ord + '-resize', |
| position: 'absolute', |
| zIndex: zi |
| }).addClass('ord-'+ord); |
| |
| if (Touch.support) { |
| jq.bind('touchstart.jcrop', Touch.createDragger(ord)); |
| } |
| |
| $hdl_holder.append(jq); |
| return jq; |
| } |
| //}}} |
| function insertHandle(ord) //{{{ |
| { |
| var hs = options.handleSize, |
| |
| div = dragDiv(ord, hdep++).css({ |
| opacity: options.handleOpacity |
| }).addClass(cssClass('handle')); |
| |
| if (hs) { div.width(hs).height(hs); } |
| |
| return div; |
| } |
| //}}} |
| function insertDragbar(ord) //{{{ |
| { |
| return dragDiv(ord, hdep++).addClass('jcrop-dragbar'); |
| } |
| //}}} |
| function createDragbars(li) //{{{ |
| { |
| var i; |
| for (i = 0; i < li.length; i++) { |
| dragbar[li[i]] = insertDragbar(li[i]); |
| } |
| } |
| //}}} |
| function createBorders(li) //{{{ |
| { |
| var cl,i; |
| for (i = 0; i < li.length; i++) { |
| switch(li[i]){ |
| case'n': cl='hline'; break; |
| case's': cl='hline bottom'; break; |
| case'e': cl='vline right'; break; |
| case'w': cl='vline'; break; |
| } |
| borders[li[i]] = insertBorder(cl); |
| } |
| } |
| //}}} |
| function createHandles(li) //{{{ |
| { |
| var i; |
| for (i = 0; i < li.length; i++) { |
| handle[li[i]] = insertHandle(li[i]); |
| } |
| } |
| //}}} |
| function moveto(x, y) //{{{ |
| { |
| if (!options.shade) { |
| $img2.css({ |
| top: px(-y), |
| left: px(-x) |
| }); |
| } |
| $sel.css({ |
| top: px(y), |
| left: px(x) |
| }); |
| } |
| //}}} |
| function resize(w, h) //{{{ |
| { |
| $sel.width(Math.round(w)).height(Math.round(h)); |
| } |
| //}}} |
| function refresh() //{{{ |
| { |
| var c = Coords.getFixed(); |
| |
| Coords.setPressed([c.x, c.y]); |
| Coords.setCurrent([c.x2, c.y2]); |
| |
| updateVisible(); |
| } |
| //}}} |
| |
| // Internal Methods |
| function updateVisible(select) //{{{ |
| { |
| if (awake) { |
| return update(select); |
| } |
| } |
| //}}} |
| function update(select) //{{{ |
| { |
| var c = Coords.getFixed(); |
| |
| resize(c.w, c.h); |
| moveto(c.x, c.y); |
| if (options.shade) Shade.updateRaw(c); |
| |
| awake || show(); |
| |
| if (select) { |
| options.onSelect.call(api, unscale(c)); |
| } else { |
| options.onChange.call(api, unscale(c)); |
| } |
| } |
| //}}} |
| function setBgOpacity(opacity,force,now) //{{{ |
| { |
| if (!awake && !force) return; |
| if (options.bgFade && !now) { |
| $img.animate({ |
| opacity: opacity |
| },{ |
| queue: false, |
| duration: options.fadeTime |
| }); |
| } else { |
| $img.css('opacity', opacity); |
| } |
| } |
| //}}} |
| function show() //{{{ |
| { |
| $sel.show(); |
| |
| if (options.shade) Shade.opacity(bgopacity); |
| else setBgOpacity(bgopacity,true); |
| |
| awake = true; |
| } |
| //}}} |
| function release() //{{{ |
| { |
| disableHandles(); |
| $sel.hide(); |
| |
| if (options.shade) Shade.opacity(1); |
| else setBgOpacity(1); |
| |
| awake = false; |
| options.onRelease.call(api); |
| } |
| //}}} |
| function showHandles() //{{{ |
| { |
| if (seehandles) { |
| $hdl_holder.show(); |
| } |
| } |
| //}}} |
| function enableHandles() //{{{ |
| { |
| seehandles = true; |
| if (options.allowResize) { |
| $hdl_holder.show(); |
| return true; |
| } |
| } |
| //}}} |
| function disableHandles() //{{{ |
| { |
| seehandles = false; |
| $hdl_holder.hide(); |
| } |
| //}}} |
| function animMode(v) //{{{ |
| { |
| if (v) { |
| animating = true; |
| disableHandles(); |
| } else { |
| animating = false; |
| enableHandles(); |
| } |
| } |
| //}}} |
| function done() //{{{ |
| { |
| animMode(false); |
| refresh(); |
| } |
| //}}} |
| // Insert draggable elements {{{ |
| // Insert border divs for outline |
| |
| if (options.dragEdges && $.isArray(options.createDragbars)) |
| createDragbars(options.createDragbars); |
| |
| if ($.isArray(options.createHandles)) |
| createHandles(options.createHandles); |
| |
| if (options.drawBorders && $.isArray(options.createBorders)) |
| createBorders(options.createBorders); |
| |
| //}}} |
| |
| // This is a hack for iOS5 to support drag/move touch functionality |
| $(document).bind('touchstart.jcrop-ios',function(e) { |
| if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation(); |
| }); |
| |
| var $track = newTracker().mousedown(createDragger('move')).css({ |
| cursor: 'move', |
| position: 'absolute', |
| zIndex: 360 |
| }); |
| |
| if (Touch.support) { |
| $track.bind('touchstart.jcrop', Touch.createDragger('move')); |
| } |
| |
| $img_holder.append($track); |
| disableHandles(); |
| |
| return { |
| updateVisible: updateVisible, |
| update: update, |
| release: release, |
| refresh: refresh, |
| isAwake: function () { |
| return awake; |
| }, |
| setCursor: function (cursor) { |
| $track.css('cursor', cursor); |
| }, |
| enableHandles: enableHandles, |
| enableOnly: function () { |
| seehandles = true; |
| }, |
| showHandles: showHandles, |
| disableHandles: disableHandles, |
| animMode: animMode, |
| setBgOpacity: setBgOpacity, |
| done: done |
| }; |
| }()); |
| |
| //}}} |
| // Tracker Module {{{ |
| var Tracker = (function () { |
| var onMove = function () {}, |
| onDone = function () {}, |
| trackDoc = options.trackDocument; |
| |
| function toFront(touch) //{{{ |
| { |
| $trk.css({ |
| zIndex: 450 |
| }); |
| |
| if (touch) |
| $(document) |
| .bind('touchmove.jcrop', trackTouchMove) |
| .bind('touchend.jcrop', trackTouchEnd); |
| |
| else if (trackDoc) |
| $(document) |
| .bind('mousemove.jcrop',trackMove) |
| .bind('mouseup.jcrop',trackUp); |
| } |
| //}}} |
| function toBack() //{{{ |
| { |
| $trk.css({ |
| zIndex: 290 |
| }); |
| $(document).unbind('.jcrop'); |
| } |
| //}}} |
| function trackMove(e) //{{{ |
| { |
| onMove(mouseAbs(e)); |
| return false; |
| } |
| //}}} |
| function trackUp(e) //{{{ |
| { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| if (btndown) { |
| btndown = false; |
| |
| onDone(mouseAbs(e)); |
| |
| if (Selection.isAwake()) { |
| options.onSelect.call(api, unscale(Coords.getFixed())); |
| } |
| |
| toBack(); |
| onMove = function () {}; |
| onDone = function () {}; |
| } |
| |
| return false; |
| } |
| //}}} |
| function activateHandlers(move, done, touch) //{{{ |
| { |
| btndown = true; |
| onMove = move; |
| onDone = done; |
| toFront(touch); |
| return false; |
| } |
| //}}} |
| function trackTouchMove(e) //{{{ |
| { |
| onMove(mouseAbs(Touch.cfilter(e))); |
| return false; |
| } |
| //}}} |
| function trackTouchEnd(e) //{{{ |
| { |
| return trackUp(Touch.cfilter(e)); |
| } |
| //}}} |
| function setCursor(t) //{{{ |
| { |
| $trk.css('cursor', t); |
| } |
| //}}} |
| |
| if (!trackDoc) { |
| $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp); |
| } |
| |
| $img.before($trk); |
| return { |
| activateHandlers: activateHandlers, |
| setCursor: setCursor |
| }; |
| }()); |
| //}}} |
| // KeyManager Module {{{ |
| var KeyManager = (function () { |
| var $keymgr = $('<input type="radio" />').css({ |
| position: 'fixed', |
| left: '-120px', |
| width: '12px' |
| }).addClass('jcrop-keymgr'), |
| |
| $keywrap = $('<div />').css({ |
| position: 'absolute', |
| overflow: 'hidden' |
| }).append($keymgr); |
| |
| function watchKeys() //{{{ |
| { |
| if (options.keySupport) { |
| $keymgr.show(); |
| $keymgr.focus(); |
| } |
| } |
| //}}} |
| function onBlur(e) //{{{ |
| { |
| $keymgr.hide(); |
| } |
| //}}} |
| function doNudge(e, x, y) //{{{ |
| { |
| if (options.allowMove) { |
| Coords.moveOffset([x, y]); |
| Selection.updateVisible(true); |
| } |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| //}}} |
| function parseKey(e) //{{{ |
| { |
| if (e.ctrlKey || e.metaKey) { |
| return true; |
| } |
| shift_down = e.shiftKey ? true : false; |
| var nudge = shift_down ? 10 : 1; |
| |
| switch (e.keyCode) { |
| case 37: |
| doNudge(e, -nudge, 0); |
| break; |
| case 39: |
| doNudge(e, nudge, 0); |
| break; |
| case 38: |
| doNudge(e, 0, -nudge); |
| break; |
| case 40: |
| doNudge(e, 0, nudge); |
| break; |
| case 27: |
| if (options.allowSelect) Selection.release(); |
| break; |
| case 9: |
| return true; |
| } |
| |
| return false; |
| } |
| //}}} |
| |
| if (options.keySupport) { |
| $keymgr.keydown(parseKey).blur(onBlur); |
| if (ie6mode || !options.fixedSupport) { |
| $keymgr.css({ |
| position: 'absolute', |
| left: '-20px' |
| }); |
| $keywrap.append($keymgr).insertBefore($img); |
| } else { |
| $keymgr.insertBefore($img); |
| } |
| } |
| |
| |
| return { |
| watchKeys: watchKeys |
| }; |
| }()); |
| //}}} |
| // }}} |
| // API methods {{{ |
| function setClass(cname) //{{{ |
| { |
| $div.removeClass().addClass(cssClass('holder')).addClass(cname); |
| } |
| //}}} |
| function animateTo(a, callback) //{{{ |
| { |
| var x1 = a[0] / xscale, |
| y1 = a[1] / yscale, |
| x2 = a[2] / xscale, |
| y2 = a[3] / yscale; |
| |
| if (animating) { |
| return; |
| } |
| |
| var animto = Coords.flipCoords(x1, y1, x2, y2), |
| c = Coords.getFixed(), |
| initcr = [c.x, c.y, c.x2, c.y2], |
| animat = initcr, |
| interv = options.animationDelay, |
| ix1 = animto[0] - initcr[0], |
| iy1 = animto[1] - initcr[1], |
| ix2 = animto[2] - initcr[2], |
| iy2 = animto[3] - initcr[3], |
| pcent = 0, |
| velocity = options.swingSpeed; |
| |
| x1 = animat[0]; |
| y1 = animat[1]; |
| x2 = animat[2]; |
| y2 = animat[3]; |
| |
| Selection.animMode(true); |
| var anim_timer; |
| |
| function queueAnimator() { |
| window.setTimeout(animator, interv); |
| } |
| var animator = (function () { |
| return function () { |
| pcent += (100 - pcent) / velocity; |
| |
| animat[0] = Math.round(x1 + ((pcent / 100) * ix1)); |
| animat[1] = Math.round(y1 + ((pcent / 100) * iy1)); |
| animat[2] = Math.round(x2 + ((pcent / 100) * ix2)); |
| animat[3] = Math.round(y2 + ((pcent / 100) * iy2)); |
| |
| if (pcent >= 99.8) { |
| pcent = 100; |
| } |
| if (pcent < 100) { |
| setSelectRaw(animat); |
| queueAnimator(); |
| } else { |
| Selection.done(); |
| Selection.animMode(false); |
| if (typeof(callback) === 'function') { |
| callback.call(api); |
| } |
| } |
| }; |
| }()); |
| queueAnimator(); |
| } |
| //}}} |
| function setSelect(rect) //{{{ |
| { |
| setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]); |
| options.onSelect.call(api, unscale(Coords.getFixed())); |
| Selection.enableHandles(); |
| } |
| //}}} |
| function setSelectRaw(l) //{{{ |
| { |
| Coords.setPressed([l[0], l[1]]); |
| Coords.setCurrent([l[2], l[3]]); |
| Selection.update(); |
| } |
| //}}} |
| function tellSelect() //{{{ |
| { |
| return unscale(Coords.getFixed()); |
| } |
| //}}} |
| function tellScaled() //{{{ |
| { |
| return Coords.getFixed(); |
| } |
| //}}} |
| function setOptionsNew(opt) //{{{ |
| { |
| setOptions(opt); |
| interfaceUpdate(); |
| } |
| //}}} |
| function disableCrop() //{{{ |
| { |
| options.disabled = true; |
| Selection.disableHandles(); |
| Selection.setCursor('default'); |
| Tracker.setCursor('default'); |
| } |
| //}}} |
| function enableCrop() //{{{ |
| { |
| options.disabled = false; |
| interfaceUpdate(); |
| } |
| //}}} |
| function cancelCrop() //{{{ |
| { |
| Selection.done(); |
| Tracker.activateHandlers(null, null); |
| } |
| //}}} |
| function destroy() //{{{ |
| { |
| $div.remove(); |
| $origimg.show(); |
| $origimg.css('visibility','visible'); |
| $(obj).removeData('Jcrop'); |
| } |
| //}}} |
| function setImage(src, callback) //{{{ |
| { |
| Selection.release(); |
| disableCrop(); |
| var img = new Image(); |
| img.onload = function () { |
| var iw = img.width; |
| var ih = img.height; |
| var bw = options.boxWidth; |
| var bh = options.boxHeight; |
| $img.width(iw).height(ih); |
| $img.attr('src', src); |
| $img2.attr('src', src); |
| presize($img, bw, bh); |
| boundx = $img.width(); |
| boundy = $img.height(); |
| $img2.width(boundx).height(boundy); |
| $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2)); |
| $div.width(boundx).height(boundy); |
| Shade.resize(boundx,boundy); |
| enableCrop(); |
| |
| if (typeof(callback) === 'function') { |
| callback.call(api); |
| } |
| }; |
| img.src = src; |
| } |
| //}}} |
| function colorChangeMacro($obj,color,now) { |
| var mycolor = color || options.bgColor; |
| if (options.bgFade && supportsColorFade() && options.fadeTime && !now) { |
| $obj.animate({ |
| backgroundColor: mycolor |
| }, { |
| queue: false, |
| duration: options.fadeTime |
| }); |
| } else { |
| $obj.css('backgroundColor', mycolor); |
| } |
| } |
| function interfaceUpdate(alt) //{{{ |
| // This method tweaks the interface based on options object. |
| // Called when options are changed and at end of initialization. |
| { |
| if (options.allowResize) { |
| if (alt) { |
| Selection.enableOnly(); |
| } else { |
| Selection.enableHandles(); |
| } |
| } else { |
| Selection.disableHandles(); |
| } |
| |
| Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); |
| Selection.setCursor(options.allowMove ? 'move' : 'default'); |
| |
| if (options.hasOwnProperty('trueSize')) { |
| xscale = options.trueSize[0] / boundx; |
| yscale = options.trueSize[1] / boundy; |
| } |
| |
| if (options.hasOwnProperty('setSelect')) { |
| setSelect(options.setSelect); |
| Selection.done(); |
| delete(options.setSelect); |
| } |
| |
| Shade.refresh(); |
| |
| if (options.bgColor != bgcolor) { |
| colorChangeMacro( |
| options.shade? Shade.getShades(): $div, |
| options.shade? |
| (options.shadeColor || options.bgColor): |
| options.bgColor |
| ); |
| bgcolor = options.bgColor; |
| } |
| |
| if (bgopacity != options.bgOpacity) { |
| bgopacity = options.bgOpacity; |
| if (options.shade) Shade.refresh(); |
| else Selection.setBgOpacity(bgopacity); |
| } |
| |
| xlimit = options.maxSize[0] || 0; |
| ylimit = options.maxSize[1] || 0; |
| xmin = options.minSize[0] || 0; |
| ymin = options.minSize[1] || 0; |
| |
| if (options.hasOwnProperty('outerImage')) { |
| $img.attr('src', options.outerImage); |
| delete(options.outerImage); |
| } |
| |
| Selection.refresh(); |
| } |
| //}}} |
| //}}} |
| |
| if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection); |
| |
| $hdl_holder.hide(); |
| interfaceUpdate(true); |
| |
| var api = { |
| setImage: setImage, |
| animateTo: animateTo, |
| setSelect: setSelect, |
| setOptions: setOptionsNew, |
| tellSelect: tellSelect, |
| tellScaled: tellScaled, |
| setClass: setClass, |
| |
| disable: disableCrop, |
| enable: enableCrop, |
| cancel: cancelCrop, |
| release: Selection.release, |
| destroy: destroy, |
| |
| focus: KeyManager.watchKeys, |
| |
| getBounds: function () { |
| return [boundx * xscale, boundy * yscale]; |
| }, |
| getWidgetSize: function () { |
| return [boundx, boundy]; |
| }, |
| getScaleFactor: function () { |
| return [xscale, yscale]; |
| }, |
| getOptions: function() { |
| // careful: internal values are returned |
| return options; |
| }, |
| |
| ui: { |
| holder: $div, |
| selection: $sel |
| } |
| }; |
| |
| if (is_msie) $div.bind('selectstart', function () { return false; }); |
| |
| $origimg.data('Jcrop', api); |
| return api; |
| }; |
| $.fn.Jcrop = function (options, callback) //{{{ |
| { |
| var api; |
| // Iterate over each object, attach Jcrop |
| this.each(function () { |
| // If we've already attached to this object |
| if ($(this).data('Jcrop')) { |
| // The API can be requested this way (undocumented) |
| if (options === 'api') return $(this).data('Jcrop'); |
| // Otherwise, we just reset the options... |
| else $(this).data('Jcrop').setOptions(options); |
| } |
| // If we haven't been attached, preload and attach |
| else { |
| if (this.tagName == 'IMG') |
| $.Jcrop.Loader(this,function(){ |
| $(this).css({display:'block',visibility:'hidden'}); |
| api = $.Jcrop(this, options); |
| if ($.isFunction(callback)) callback.call(api); |
| }); |
| else { |
| $(this).css({display:'block',visibility:'hidden'}); |
| api = $.Jcrop(this, options); |
| if ($.isFunction(callback)) callback.call(api); |
| } |
| } |
| }); |
| |
| // Return "this" so the object is chainable (jQuery-style) |
| return this; |
| }; |
| //}}} |
| // $.Jcrop.Loader - basic image loader {{{ |
| |
| $.Jcrop.Loader = function(imgobj,success,error){ |
| var $img = $(imgobj), img = $img[0]; |
| |
| function completeCheck(){ |
| if (img.complete) { |
| $img.unbind('.jcloader'); |
| if ($.isFunction(success)) success.call(img); |
| } |
| else window.setTimeout(completeCheck,50); |
| } |
| |
| $img |
| .bind('load.jcloader',completeCheck) |
| .bind('error.jcloader',function(e){ |
| $img.unbind('.jcloader'); |
| if ($.isFunction(error)) error.call(img); |
| }); |
| |
| if (img.complete && $.isFunction(success)){ |
| $img.unbind('.jcloader'); |
| success.call(img); |
| } |
| }; |
| |
| //}}} |
| // Global Defaults {{{ |
| $.Jcrop.defaults = { |
| |
| // Basic Settings |
| allowSelect: true, |
| allowMove: true, |
| allowResize: true, |
| |
| trackDocument: true, |
| |
| // Styling Options |
| baseClass: 'jcrop', |
| addClass: null, |
| bgColor: 'black', |
| bgOpacity: 0.6, |
| bgFade: false, |
| borderOpacity: 0.4, |
| handleOpacity: 0.5, |
| handleSize: null, |
| |
| aspectRatio: 0, |
| keySupport: true, |
| createHandles: ['n','s','e','w','nw','ne','se','sw'], |
| createDragbars: ['n','s','e','w'], |
| createBorders: ['n','s','e','w'], |
| drawBorders: true, |
| dragEdges: true, |
| fixedSupport: true, |
| touchSupport: null, |
| |
| shade: null, |
| |
| boxWidth: 0, |
| boxHeight: 0, |
| boundary: 2, |
| fadeTime: 400, |
| animationDelay: 20, |
| swingSpeed: 3, |
| |
| minSelect: [0, 0], |
| maxSize: [0, 0], |
| minSize: [0, 0], |
| |
| // Callbacks / Event Handlers |
| onChange: function () {}, |
| onSelect: function () {}, |
| onDblClick: function () {}, |
| onRelease: function () {} |
| }; |
| |
| // }}} |
| }(jQuery)); |