From e8f1f9b3dbf366d351b6f3ff1fb8c049730a5801 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Tue, 15 Sep 2015 22:33:14 +0200 Subject: [PATCH] Fixed avatar file upload and updated cropper. --- .../app/controllers/_profile/avatar.js.coffee | 16 +- .../javascripts/app/lib/base/cropper.js | 2463 +++++++++++++++++ .../javascripts/app/lib/base/cropper.min.js | 10 - app/assets/stylesheets/application.css | 2 +- app/assets/stylesheets/cropper.css | 290 ++ app/assets/stylesheets/cropper.min.css | 9 - 6 files changed, 2763 insertions(+), 27 deletions(-) create mode 100644 app/assets/javascripts/app/lib/base/cropper.js delete mode 100755 app/assets/javascripts/app/lib/base/cropper.min.js create mode 100644 app/assets/stylesheets/cropper.css delete mode 100755 app/assets/stylesheets/cropper.min.css diff --git a/app/assets/javascripts/app/controllers/_profile/avatar.js.coffee b/app/assets/javascripts/app/controllers/_profile/avatar.js.coffee index 3e98cc3b9..25e86e88e 100644 --- a/app/assets/javascripts/app/controllers/_profile/avatar.js.coffee +++ b/app/assets/javascripts/app/controllers/_profile/avatar.js.coffee @@ -208,16 +208,18 @@ class ImageCropper extends App.ControllerModal initializeCropper: => @image.cropper - aspectRatio: 1, - guides: false, - autoCrop: true, - autoCropArea: 1, - preview: ".imageCropper-preview" + aspectRatio: 1 + guides: false + autoCrop: true + autoCropArea: 1 + minContainerWidth: 500 + minContainerHeight: 300 + preview: '.imageCropper-preview' onSubmit: (e) => e.preventDefault() - @options.callback( @image.cropper("getDataURL") ) - @image.cropper("destroy") + @options.callback( @image.cropper('getCroppedCanvas').toDataURL() ) + @image.cropper('destroy') @hide() diff --git a/app/assets/javascripts/app/lib/base/cropper.js b/app/assets/javascripts/app/lib/base/cropper.js new file mode 100644 index 000000000..30b8e2f61 --- /dev/null +++ b/app/assets/javascripts/app/lib/base/cropper.js @@ -0,0 +1,2463 @@ +/*! + * Cropper v1.0.0-rc.1 + * https://github.com/fengyuanchen/cropper + * + * Copyright (c) 2014-2015 Fengyuan Chen and contributors + * Released under the MIT license + * + * Date: 2015-09-05T04:29:32.906Z + */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node / CommonJS + factory(require('jquery')); + } else { + // Browser globals. + factory(jQuery); + } +})(function ($) { + + 'use strict'; + + // Globals + var $window = $(window); + var $document = $(document); + var location = window.location; + + // Constants + var NAMESPACE = 'cropper'; + var PREVIEW = 'preview.' + NAMESPACE; + + // Classes + var CLASS_MODAL = 'cropper-modal'; + var CLASS_HIDE = 'cropper-hide'; + var CLASS_HIDDEN = 'cropper-hidden'; + var CLASS_INVISIBLE = 'cropper-invisible'; + var CLASS_MOVE = 'cropper-move'; + var CLASS_CROP = 'cropper-crop'; + var CLASS_DISABLED = 'cropper-disabled'; + var CLASS_BG = 'cropper-bg'; + + // Events + var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown'; + var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove'; + var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel'; + var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll'; + var EVENT_DBLCLICK = 'dblclick'; + var EVENT_LOAD = 'load.' + NAMESPACE; + var EVENT_ERROR = 'error.' + NAMESPACE; + var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace + var EVENT_BUILD = 'build.' + NAMESPACE; + var EVENT_BUILT = 'built.' + NAMESPACE; + var EVENT_CROP_START = 'cropstart.' + NAMESPACE; + var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE; + var EVENT_CROP_END = 'cropend.' + NAMESPACE; + var EVENT_CROP = 'crop.' + NAMESPACE; + var EVENT_ZOOM = 'zoom.' + NAMESPACE; + + // RegExps + var REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/; + + // Actions + var ACTION_EAST = 'e'; + var ACTION_WEST = 'w'; + var ACTION_SOUTH = 's'; + var ACTION_NORTH = 'n'; + var ACTION_SOUTH_EAST = 'se'; + var ACTION_SOUTH_WEST = 'sw'; + var ACTION_NORTH_EAST = 'ne'; + var ACTION_NORTH_WEST = 'nw'; + var ACTION_ALL = 'all'; + var ACTION_CROP = 'crop'; + var ACTION_MOVE = 'move'; + var ACTION_ZOOM = 'zoom'; + var ACTION_NONE = 'none'; + + // Supports + var SUPPORT_CANVAS = $.isFunction($('')[0].getContext); + + // Maths + var sqrt = Math.sqrt; + var min = Math.min; + var max = Math.max; + var abs = Math.abs; + var sin = Math.sin; + var cos = Math.cos; + var num = parseFloat; + + // Prototype + var prototype = {}; + + function isNumber(n) { + return typeof n === 'number' && !isNaN(n); + } + + function isUndefined(n) { + return typeof n === 'undefined'; + } + + function toArray(obj, offset) { + var args = []; + + // This is necessary for IE8 + if (isNumber(offset)) { + args.push(offset); + } + + return args.slice.apply(obj, args); + } + + // Custom proxy to avoid jQuery's guid + function proxy(fn, context) { + var args = toArray(arguments, 2); + + return function () { + return fn.apply(context, args.concat(toArray(arguments))); + }; + } + + function isCrossOriginURL(url) { + var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i); + + return parts && ( + parts[1] !== location.protocol || + parts[2] !== location.hostname || + parts[3] !== location.port + ); + } + + function addTimestamp(url) { + var timestamp = 'timestamp=' + (new Date()).getTime(); + + return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp); + } + + function getNaturalSize(image, callback) { + var newImage; + + // Modern browsers + if (image.naturalWidth) { + return callback(image.naturalWidth, image.naturalHeight); + } + + // IE8: Don't use `new Image()` here (#319) + newImage = document.createElement('img'); + + newImage.onload = function () { + callback(this.width, this.height); + }; + + newImage.src = image.src; + } + + function getTransform(options) { + var transforms = []; + var rotate = options.rotate; + var scaleX = options.scaleX; + var scaleY = options.scaleY; + + if (isNumber(rotate)) { + transforms.push('rotate(' + rotate + 'deg)'); + } + + if (isNumber(scaleX) && isNumber(scaleY)) { + transforms.push('scale(' + scaleX + ',' + scaleY + ')'); + } + + return transforms.length ? transforms.join(' ') : 'none'; + } + + function getRotatedSizes(data, reverse) { + var deg = abs(data.degree) % 180; + var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180; + var sinArc = sin(arc); + var cosArc = cos(arc); + var width = data.width; + var height = data.height; + var aspectRatio = data.aspectRatio; + var newWidth; + var newHeight; + + if (!reverse) { + newWidth = width * cosArc + height * sinArc; + newHeight = width * sinArc + height * cosArc; + } else { + newWidth = width / (cosArc + sinArc / aspectRatio); + newHeight = newWidth / aspectRatio; + } + + return { + width: newWidth, + height: newHeight + }; + } + + function getSourceCanvas(image, data) { + var canvas = $('')[0]; + var context = canvas.getContext('2d'); + var x = 0; + var y = 0; + var width = data.naturalWidth; + var height = data.naturalHeight; + var rotate = data.rotate; + var scaleX = data.scaleX; + var scaleY = data.scaleY; + var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1); + var rotatable = isNumber(rotate) && rotate !== 0; + var advanced = rotatable || scalable; + var canvasWidth = width; + var canvasHeight = height; + var translateX; + var translateY; + var rotated; + + if (scalable) { + translateX = width / 2; + translateY = height / 2; + } + + if (rotatable) { + rotated = getRotatedSizes({ + width: width, + height: height, + degree: rotate + }); + + canvasWidth = rotated.width; + canvasHeight = rotated.height; + translateX = rotated.width / 2; + translateY = rotated.height / 2; + } + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + if (advanced) { + x = -width / 2; + y = -height / 2; + + context.save(); + context.translate(translateX, translateY); + } + + if (rotatable) { + context.rotate(rotate * Math.PI / 180); + } + + // Should call `scale` after rotated + if (scalable) { + context.scale(scaleX, scaleY); + } + + context.drawImage(image, x, y, width, height); + + if (advanced) { + context.restore(); + } + + return canvas; + } + + function Cropper(element, options) { + this.$element = $(element); + this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options); + this.ready = false; + this.built = false; + this.complete = false; + this.rotated = false; + this.cropped = false; + this.disabled = false; + this.replaced = false; + this.isImg = false; + this.originalUrl = ''; + this.canvas = null; + this.cropBox = null; + this.init(); + } + + $.extend(prototype, { + init: function () { + var $this = this.$element; + var url; + + if ($this.is('img')) { + this.isImg = true; + + // Should use `$.fn.attr` here. e.g.: "img/picture.jpg" + this.originalUrl = url = $this.attr('src'); + + // Stop when it's a blank image + if (!url) { + return; + } + + // Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg" + url = $this.prop('src'); + } else if ($this.is('canvas') && SUPPORT_CANVAS) { + url = $this[0].toDataURL(); + } + + this.load(url); + }, + + // A shortcut for triggering custom events + trigger: function (type, data) { + var e = $.Event(type, data); + + this.$element.trigger(e); + + return e.isDefaultPrevented(); + }, + + load: function (url) { + var options = this.options; + var $this = this.$element; + var crossOrigin = ''; + var bustCacheUrl; + var $clone; + + if (!url) { + return; + } + + this.url = url; + + // Trigger build event first + $this.one(EVENT_BUILD, options.build); + + if (this.trigger(EVENT_BUILD)) { + return; + } + + if (options.checkImageOrigin && isCrossOriginURL(url)) { + crossOrigin = ' crossOrigin="anonymous"'; + + // Bust cache (#148), only when there was not a "crossOrigin" property + if (!$this.prop('crossOrigin')) { + bustCacheUrl = addTimestamp(url); + } + } + + this.$clone = $clone = $(''); + + if (this.isImg) { + if ($this[0].complete) { + this.start(); + } else { + $this.one(EVENT_LOAD, $.proxy(this.start, this)); + } + } else { + $clone. + one(EVENT_LOAD, $.proxy(this.start, this)). + one(EVENT_ERROR, $.proxy(this.stop, this)). + addClass(CLASS_HIDE). + insertAfter($this); + } + }, + + start: function () { + var image = this.isImg ? this.$element[0] : this.$clone[0]; + + getNaturalSize(image, $.proxy(function (naturalWidth, naturalHeight) { + this.image = { + naturalWidth: naturalWidth, + naturalHeight: naturalHeight, + aspectRatio: naturalWidth / naturalHeight + }; + + this.ready = true; + this.build(); + }, this)); + }, + + stop: function () { + this.$clone.remove(); + this.$clone = null; + } + }); + + $.extend(prototype, { + build: function () { + var options = this.options; + var $this = this.$element; + var $clone = this.$clone; + var $cropper; + var $cropBox; + var $face; + + if (!this.ready) { + return; + } + + // Unbuild first when replace + if (this.built) { + this.unbuild(); + } + + // Create cropper elements + this.$container = $this.parent(); + this.$cropper = $cropper = $(Cropper.TEMPLATE); + this.$canvas = $cropper.find('.cropper-canvas').append($clone); + this.$dragBox = $cropper.find('.cropper-drag-box'); + this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box'); + this.$viewBox = $cropper.find('.cropper-view-box'); + this.$face = $face = $cropBox.find('.cropper-face'); + + // Hide the original image + $this.addClass(CLASS_HIDDEN).after($cropper); + + // Show the clone image if is hidden + if (!this.isImg) { + $clone.removeClass(CLASS_HIDE); + } + + this.initPreview(); + this.bind(); + + // Format aspect ratio (0 -> NaN) + options.aspectRatio = num(options.aspectRatio) || NaN; + + if (options.autoCrop) { + this.cropped = true; + + if (options.modal) { + this.$dragBox.addClass(CLASS_MODAL); + } + } else { + $cropBox.addClass(CLASS_HIDDEN); + } + + if (!options.guides) { + $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN); + } + + if (!options.center) { + $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN); + } + + if (options.cropBoxMovable) { + $face.addClass(CLASS_MOVE).data('action', ACTION_ALL); + } + + if (!options.highlight) { + $face.addClass(CLASS_INVISIBLE); + } + + if (options.background) { + $cropper.addClass(CLASS_BG); + } + + if (!options.cropBoxResizable) { + $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN); + } + + this.setDragMode(options.dragCrop ? ACTION_CROP : (options.movable ? ACTION_MOVE : ACTION_NONE)); + + this.render(); + this.built = true; + this.setData(options.data); + $this.one(EVENT_BUILT, options.built); + + // Trigger the built event asynchronously to keep `data('cropper')` is defined + setTimeout($.proxy(function () { + this.trigger(EVENT_BUILT); + this.complete = true; + }, this), 0); + }, + + unbuild: function () { + if (!this.built) { + return; + } + + this.built = false; + this.initialImage = null; + + // Clear `initialCanvas` is necessary when replace + this.initialCanvas = null; + this.initialCropBox = null; + this.container = null; + this.canvas = null; + + // Clear `cropBox` is necessary when replace + this.cropBox = null; + this.unbind(); + + this.resetPreview(); + this.$preview = null; + + this.$viewBox = null; + this.$cropBox = null; + this.$dragBox = null; + this.$canvas = null; + this.$container = null; + + this.$cropper.remove(); + this.$cropper = null; + } + }); + + $.extend(prototype, { + render: function () { + this.initContainer(); + this.initCanvas(); + this.initCropBox(); + + this.renderCanvas(); + + if (this.cropped) { + this.renderCropBox(); + } + }, + + initContainer: function () { + var options = this.options; + var $this = this.$element; + var $container = this.$container; + var $cropper = this.$cropper; + + $cropper.addClass(CLASS_HIDDEN); + $this.removeClass(CLASS_HIDDEN); + + $cropper.css((this.container = { + width: max($container.width(), num(options.minContainerWidth) || 200), + height: max($container.height(), num(options.minContainerHeight) || 100) + })); + + $this.addClass(CLASS_HIDDEN); + $cropper.removeClass(CLASS_HIDDEN); + }, + + // Canvas (image wrapper) + initCanvas: function () { + var container = this.container; + var containerWidth = container.width; + var containerHeight = container.height; + var image = this.image; + var aspectRatio = image.aspectRatio; + var canvas = { + aspectRatio: aspectRatio, + width: containerWidth, + height: containerHeight + }; + + if (containerHeight * aspectRatio > containerWidth) { + canvas.height = containerWidth / aspectRatio; + } else { + canvas.width = containerHeight * aspectRatio; + } + + canvas.oldLeft = canvas.left = (containerWidth - canvas.width) / 2; + canvas.oldTop = canvas.top = (containerHeight - canvas.height) / 2; + + this.canvas = canvas; + this.limitCanvas(true, true); + this.initialImage = $.extend({}, image); + this.initialCanvas = $.extend({}, canvas); + }, + + limitCanvas: function (size, position) { + var options = this.options; + var strict = options.strict; + var container = this.container; + var containerWidth = container.width; + var containerHeight = container.height; + var canvas = this.canvas; + var aspectRatio = canvas.aspectRatio; + var cropBox = this.cropBox; + var cropped = this.cropped && cropBox; + var initialCanvas = this.initialCanvas || canvas; + var initialCanvasWidth = initialCanvas.width; + var initialCanvasHeight = initialCanvas.height; + var minCanvasWidth; + var minCanvasHeight; + + if (size) { + minCanvasWidth = num(options.minCanvasWidth) || 0; + minCanvasHeight = num(options.minCanvasHeight) || 0; + + if (minCanvasWidth) { + if (strict) { + minCanvasWidth = max(cropped ? cropBox.width : initialCanvasWidth, minCanvasWidth); + } + + minCanvasHeight = minCanvasWidth / aspectRatio; + } else if (minCanvasHeight) { + if (strict) { + minCanvasHeight = max(cropped ? cropBox.height : initialCanvasHeight, minCanvasHeight); + } + + minCanvasWidth = minCanvasHeight * aspectRatio; + } else if (strict) { + if (cropped) { + minCanvasWidth = cropBox.width; + minCanvasHeight = cropBox.height; + + if (minCanvasHeight * aspectRatio > minCanvasWidth) { + minCanvasWidth = minCanvasHeight * aspectRatio; + } else { + minCanvasHeight = minCanvasWidth / aspectRatio; + } + } else { + minCanvasWidth = initialCanvasWidth; + minCanvasHeight = initialCanvasHeight; + } + } + + $.extend(canvas, { + minWidth: minCanvasWidth, + minHeight: minCanvasHeight, + maxWidth: Infinity, + maxHeight: Infinity + }); + } + + if (position) { + if (strict) { + if (cropped) { + canvas.minLeft = min(cropBox.left, (cropBox.left + cropBox.width) - canvas.width); + canvas.minTop = min(cropBox.top, (cropBox.top + cropBox.height) - canvas.height); + canvas.maxLeft = cropBox.left; + canvas.maxTop = cropBox.top; + } else { + canvas.minLeft = min(0, containerWidth - canvas.width); + canvas.minTop = min(0, containerHeight - canvas.height); + canvas.maxLeft = max(0, containerWidth - canvas.width); + canvas.maxTop = max(0, containerHeight - canvas.height); + } + } else { + canvas.minLeft = -canvas.width; + canvas.minTop = -canvas.height; + canvas.maxLeft = containerWidth; + canvas.maxTop = containerHeight; + } + } + }, + + renderCanvas: function (changed) { + var options = this.options; + var canvas = this.canvas; + var image = this.image; + var aspectRatio; + var rotated; + + if (this.rotated) { + this.rotated = false; + + // Computes rotated sizes with image sizes + rotated = getRotatedSizes({ + width: image.width, + height: image.height, + degree: image.rotate + }); + + aspectRatio = rotated.width / rotated.height; + + if (aspectRatio !== canvas.aspectRatio) { + canvas.left -= (rotated.width - canvas.width) / 2; + canvas.top -= (rotated.height - canvas.height) / 2; + canvas.width = rotated.width; + canvas.height = rotated.height; + canvas.aspectRatio = aspectRatio; + this.limitCanvas(true, false); + } + } + + if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) { + canvas.left = canvas.oldLeft; + } + + if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) { + canvas.top = canvas.oldTop; + } + + canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth); + canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight); + + this.limitCanvas(false, true); + + canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft); + canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop); + + this.$canvas.css({ + width: canvas.width, + height: canvas.height, + left: canvas.left, + top: canvas.top + }); + + this.renderImage(); + + if (this.cropped && options.strict) { + this.limitCropBox(true, true); + } + + if (changed) { + this.output(); + } + }, + + renderImage: function (changed) { + var canvas = this.canvas; + var image = this.image; + var reversed; + + if (image.rotate) { + reversed = getRotatedSizes({ + width: canvas.width, + height: canvas.height, + degree: image.rotate, + aspectRatio: image.aspectRatio + }, true); + } + + $.extend(image, reversed ? { + width: reversed.width, + height: reversed.height, + left: (canvas.width - reversed.width) / 2, + top: (canvas.height - reversed.height) / 2 + } : { + width: canvas.width, + height: canvas.height, + left: 0, + top: 0 + }); + + this.$clone.css({ + width: image.width, + height: image.height, + marginLeft: image.left, + marginTop: image.top, + transform: getTransform(image) + }); + + if (changed) { + this.output(); + } + }, + + initCropBox: function () { + var options = this.options; + var canvas = this.canvas; + var aspectRatio = options.aspectRatio; + var autoCropArea = num(options.autoCropArea) || 0.8; + var cropBox = { + width: canvas.width, + height: canvas.height + }; + + if (aspectRatio) { + if (canvas.height * aspectRatio > canvas.width) { + cropBox.height = cropBox.width / aspectRatio; + } else { + cropBox.width = cropBox.height * aspectRatio; + } + } + + this.cropBox = cropBox; + this.limitCropBox(true, true); + + // Initialize auto crop area + cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth); + cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight); + + // The width of auto crop area must large than "minWidth", and the height too. (#164) + cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea); + cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea); + cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2; + cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2; + + this.initialCropBox = $.extend({}, cropBox); + }, + + limitCropBox: function (size, position) { + var options = this.options; + var strict = options.strict; + var container = this.container; + var containerWidth = container.width; + var containerHeight = container.height; + var canvas = this.canvas; + var cropBox = this.cropBox; + var aspectRatio = options.aspectRatio; + var minCropBoxWidth; + var minCropBoxHeight; + + if (size) { + minCropBoxWidth = num(options.minCropBoxWidth) || 0; + minCropBoxHeight = num(options.minCropBoxHeight) || 0; + + // The min/maxCropBoxWidth/Height must less than container width/height + cropBox.minWidth = min(containerWidth, minCropBoxWidth); + cropBox.minHeight = min(containerHeight, minCropBoxHeight); + cropBox.maxWidth = min(containerWidth, strict ? canvas.width : containerWidth); + cropBox.maxHeight = min(containerHeight, strict ? canvas.height : containerHeight); + + if (aspectRatio) { + + // Compare crop box size with container first + if (cropBox.maxHeight * aspectRatio > cropBox.maxWidth) { + cropBox.minHeight = cropBox.minWidth / aspectRatio; + cropBox.maxHeight = cropBox.maxWidth / aspectRatio; + } else { + cropBox.minWidth = cropBox.minHeight * aspectRatio; + cropBox.maxWidth = cropBox.maxHeight * aspectRatio; + } + } + + // The "minWidth" must be less than "maxWidth", and the "minHeight" too. + cropBox.minWidth = min(cropBox.maxWidth, cropBox.minWidth); + cropBox.minHeight = min(cropBox.maxHeight, cropBox.minHeight); + } + + if (position) { + if (strict) { + cropBox.minLeft = max(0, canvas.left); + cropBox.minTop = max(0, canvas.top); + cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width; + cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height; + } else { + cropBox.minLeft = 0; + cropBox.minTop = 0; + cropBox.maxLeft = containerWidth - cropBox.width; + cropBox.maxTop = containerHeight - cropBox.height; + } + } + }, + + renderCropBox: function () { + var options = this.options; + var container = this.container; + var containerWidth = container.width; + var containerHeight = container.height; + var cropBox = this.cropBox; + + if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) { + cropBox.left = cropBox.oldLeft; + } + + if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) { + cropBox.top = cropBox.oldTop; + } + + cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth); + cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight); + + this.limitCropBox(false, true); + + cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft); + cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop); + + if (options.movable && options.cropBoxMovable) { + + // Turn to move the canvas when the crop box is equal to the container + this.$face.data('action', (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL); + } + + this.$cropBox.css({ + width: cropBox.width, + height: cropBox.height, + left: cropBox.left, + top: cropBox.top + }); + + if (this.cropped && options.strict) { + this.limitCanvas(true, true); + } + + if (!this.disabled) { + this.output(); + } + }, + + output: function () { + this.preview(); + + if (this.complete) { + this.trigger(EVENT_CROP, this.getData()); + } else if (!this.built) { + + // Only trigger one crop event before complete + this.$element.one(EVENT_BUILT, $.proxy(function () { + this.trigger(EVENT_CROP, this.getData()); + }, this)); + } + } + }); + + $.extend(prototype, { + initPreview: function () { + var url = this.url; + + this.$preview = $(this.options.preview); + this.$viewBox.html(''); + this.$preview.each(function () { + var $this = $(this); + + // Save the original size for recover + $this.data(PREVIEW, { + width: $this.width(), + height: $this.height(), + original: $this.html() + }); + + /** + * Override img element styles + * Add `display:block` to avoid margin top issue + * (Occur only when margin-top <= -height) + */ + $this.html( + '' + ); + }); + }, + + resetPreview: function () { + this.$preview.each(function () { + var $this = $(this); + + $this.html($this.data(PREVIEW).original).removeData(PREVIEW); + }); + }, + + preview: function () { + var image = this.image; + var canvas = this.canvas; + var cropBox = this.cropBox; + var width = image.width; + var height = image.height; + var left = cropBox.left - canvas.left - image.left; + var top = cropBox.top - canvas.top - image.top; + + if (!this.cropped || this.disabled) { + return; + } + + this.$viewBox.find('img').css({ + width: width, + height: height, + marginLeft: -left, + marginTop: -top, + transform: getTransform(image) + }); + + this.$preview.each(function () { + var $this = $(this); + var data = $this.data(PREVIEW); + var ratio = data.width / cropBox.width; + var newWidth = data.width; + var newHeight = cropBox.height * ratio; + + if (newHeight > data.height) { + ratio = data.height / cropBox.height; + newWidth = cropBox.width * ratio; + newHeight = data.height; + } + + $this.width(newWidth).height(newHeight).find('img').css({ + width: width * ratio, + height: height * ratio, + marginLeft: -left * ratio, + marginTop: -top * ratio, + transform: getTransform(image) + }); + }); + } + }); + + $.extend(prototype, { + bind: function () { + var options = this.options; + var $this = this.$element; + var $cropper = this.$cropper; + + if ($.isFunction(options.cropstart)) { + $this.on(EVENT_CROP_START, options.cropstart); + } + + if ($.isFunction(options.cropmove)) { + $this.on(EVENT_CROP_MOVE, options.cropmove); + } + + if ($.isFunction(options.cropend)) { + $this.on(EVENT_CROP_END, options.cropend); + } + + if ($.isFunction(options.crop)) { + $this.on(EVENT_CROP, options.crop); + } + + if ($.isFunction(options.zoom)) { + $this.on(EVENT_ZOOM, options.zoom); + } + + $cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this)); + + if (options.zoomable && options.mouseWheelZoom) { + $cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this)); + } + + if (options.doubleClickToggle) { + $cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this)); + } + + $document. + on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))). + on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this))); + + if (options.responsive) { + $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this))); + } + }, + + unbind: function () { + var options = this.options; + var $this = this.$element; + var $cropper = this.$cropper; + + if ($.isFunction(options.cropstart)) { + $this.off(EVENT_CROP_START, options.cropstart); + } + + if ($.isFunction(options.cropmove)) { + $this.off(EVENT_CROP_MOVE, options.cropmove); + } + + if ($.isFunction(options.cropend)) { + $this.off(EVENT_CROP_END, options.cropend); + } + + if ($.isFunction(options.crop)) { + $this.off(EVENT_CROP, options.crop); + } + + if ($.isFunction(options.zoom)) { + $this.off(EVENT_ZOOM, options.zoom); + } + + $cropper.off(EVENT_MOUSE_DOWN, this.cropStart); + + if (options.zoomable && options.mouseWheelZoom) { + $cropper.off(EVENT_WHEEL, this.wheel); + } + + if (options.doubleClickToggle) { + $cropper.off(EVENT_DBLCLICK, this.dblclick); + } + + $document. + off(EVENT_MOUSE_MOVE, this._cropMove). + off(EVENT_MOUSE_UP, this._cropEnd); + + if (options.responsive) { + $window.off(EVENT_RESIZE, this._resize); + } + } + }); + + $.extend(prototype, { + resize: function () { + var $container = this.$container; + var container = this.container; + var canvasData; + var cropBoxData; + var ratio; + + // Check `container` is necessary for IE8 + if (this.disabled || !container) { + return; + } + + ratio = $container.width() / container.width; + + // Resize when width changed or height changed + if (ratio !== 1 || $container.height() !== container.height) { + canvasData = this.getCanvasData(); + cropBoxData = this.getCropBoxData(); + + this.render(); + this.setCanvasData($.each(canvasData, function (i, n) { + canvasData[i] = n * ratio; + })); + this.setCropBoxData($.each(cropBoxData, function (i, n) { + cropBoxData[i] = n * ratio; + })); + } + }, + + dblclick: function () { + if (this.disabled) { + return; + } + + if (this.$dragBox.hasClass(CLASS_CROP)) { + this.setDragMode(ACTION_MOVE); + } else { + this.setDragMode(ACTION_CROP); + } + }, + + wheel: function (event) { + var originalEvent = event.originalEvent; + var e = originalEvent; + var ratio = num(this.options.wheelZoomRatio) || 0.1; + var delta = 1; + + if (this.disabled) { + return; + } + + event.preventDefault(); + + if (e.deltaY) { + delta = e.deltaY > 0 ? 1 : -1; + } else if (e.wheelDelta) { + delta = -e.wheelDelta / 120; + } else if (e.detail) { + delta = e.detail > 0 ? 1 : -1; + } + + this.zoom(-delta * ratio, originalEvent); + }, + + cropStart: function (event) { + var options = this.options; + var originalEvent = event.originalEvent; + var touches = originalEvent && originalEvent.touches; + var e = event; + var touchesLength; + var action; + + if (this.disabled) { + return; + } + + if (touches) { + touchesLength = touches.length; + + if (touchesLength > 1) { + if (options.zoomable && options.touchDragZoom && touchesLength === 2) { + e = touches[1]; + this.startX2 = e.pageX; + this.startY2 = e.pageY; + action = ACTION_ZOOM; + } else { + return; + } + } + + e = touches[0]; + } + + action = action || $(e.target).data('action'); + + if (REGEXP_ACTIONS.test(action)) { + if (this.trigger(EVENT_CROP_START, { + originalEvent: originalEvent, + action: action + })) { + return; + } + + event.preventDefault(); + + this.action = action; + this.cropping = false; + + // IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y` + // IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y` + this.startX = e.pageX || originalEvent && originalEvent.pageX; + this.startY = e.pageY || originalEvent && originalEvent.pageY; + + if (action === ACTION_CROP) { + this.cropping = true; + this.$dragBox.addClass(CLASS_MODAL); + } + } + }, + + cropMove: function (event) { + var options = this.options; + var originalEvent = event.originalEvent; + var touches = originalEvent && originalEvent.touches; + var e = event; + var action = this.action; + var touchesLength; + + if (this.disabled) { + return; + } + + if (touches) { + touchesLength = touches.length; + + if (touchesLength > 1) { + if (options.zoomable && options.touchDragZoom && touchesLength === 2) { + e = touches[1]; + this.endX2 = e.pageX; + this.endY2 = e.pageY; + } else { + return; + } + } + + e = touches[0]; + } + + if (action) { + if (this.trigger(EVENT_CROP_MOVE, { + originalEvent: originalEvent, + action: action + })) { + return; + } + + event.preventDefault(); + + this.endX = e.pageX || originalEvent && originalEvent.pageX; + this.endY = e.pageY || originalEvent && originalEvent.pageY; + + this.change(e.shiftKey, action === ACTION_ZOOM ? originalEvent : null); + } + }, + + cropEnd: function (event) { + var originalEvent = event.originalEvent; + var action = this.action; + + if (this.disabled) { + return; + } + + if (action) { + event.preventDefault(); + + if (this.cropping) { + this.cropping = false; + this.$dragBox.toggleClass(CLASS_MODAL, this.cropped && this.options.modal); + } + + this.action = ''; + + this.trigger(EVENT_CROP_END, { + originalEvent: originalEvent, + action: action + }); + } + } + }); + + $.extend(prototype, { + change: function (shiftKey, originalEvent) { + var options = this.options; + var aspectRatio = options.aspectRatio; + var action = this.action; + var container = this.container; + var canvas = this.canvas; + var cropBox = this.cropBox; + var width = cropBox.width; + var height = cropBox.height; + var left = cropBox.left; + var top = cropBox.top; + var right = left + width; + var bottom = top + height; + var minLeft = 0; + var minTop = 0; + var maxWidth = container.width; + var maxHeight = container.height; + var renderable = true; + var offset; + var range; + + // Locking aspect ratio in "free mode" by holding shift key (#259) + if (!aspectRatio && shiftKey) { + aspectRatio = width && height ? width / height : 1; + } + + if (options.strict) { + minLeft = cropBox.minLeft; + minTop = cropBox.minTop; + maxWidth = minLeft + min(container.width, canvas.width); + maxHeight = minTop + min(container.height, canvas.height); + } + + range = { + x: this.endX - this.startX, + y: this.endY - this.startY + }; + + if (aspectRatio) { + range.X = range.y * aspectRatio; + range.Y = range.x / aspectRatio; + } + + switch (action) { + // Move crop box + case ACTION_ALL: + left += range.x; + top += range.y; + break; + + // Resize crop box + case ACTION_EAST: + if (range.x >= 0 && (right >= maxWidth || aspectRatio && + (top <= minTop || bottom >= maxHeight))) { + + renderable = false; + break; + } + + width += range.x; + + if (aspectRatio) { + height = width / aspectRatio; + top -= range.Y / 2; + } + + if (width < 0) { + action = ACTION_WEST; + width = 0; + } + + break; + + case ACTION_NORTH: + if (range.y <= 0 && (top <= minTop || aspectRatio && + (left <= minLeft || right >= maxWidth))) { + + renderable = false; + break; + } + + height -= range.y; + top += range.y; + + if (aspectRatio) { + width = height * aspectRatio; + left += range.X / 2; + } + + if (height < 0) { + action = ACTION_SOUTH; + height = 0; + } + + break; + + case ACTION_WEST: + if (range.x <= 0 && (left <= minLeft || aspectRatio && + (top <= minTop || bottom >= maxHeight))) { + + renderable = false; + break; + } + + width -= range.x; + left += range.x; + + if (aspectRatio) { + height = width / aspectRatio; + top += range.Y / 2; + } + + if (width < 0) { + action = ACTION_EAST; + width = 0; + } + + break; + + case ACTION_SOUTH: + if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && + (left <= minLeft || right >= maxWidth))) { + + renderable = false; + break; + } + + height += range.y; + + if (aspectRatio) { + width = height * aspectRatio; + left -= range.X / 2; + } + + if (height < 0) { + action = ACTION_NORTH; + height = 0; + } + + break; + + case ACTION_NORTH_EAST: + if (aspectRatio) { + if (range.y <= 0 && (top <= minTop || right >= maxWidth)) { + renderable = false; + break; + } + + height -= range.y; + top += range.y; + width = height * aspectRatio; + } else { + if (range.x >= 0) { + if (right < maxWidth) { + width += range.x; + } else if (range.y <= 0 && top <= minTop) { + renderable = false; + } + } else { + width += range.x; + } + + if (range.y <= 0) { + if (top > minTop) { + height -= range.y; + top += range.y; + } + } else { + height -= range.y; + top += range.y; + } + } + + if (width < 0 && height < 0) { + action = ACTION_SOUTH_WEST; + height = 0; + width = 0; + } else if (width < 0) { + action = ACTION_NORTH_WEST; + width = 0; + } else if (height < 0) { + action = ACTION_SOUTH_EAST; + height = 0; + } + + break; + + case ACTION_NORTH_WEST: + if (aspectRatio) { + if (range.y <= 0 && (top <= minTop || left <= minLeft)) { + renderable = false; + break; + } + + height -= range.y; + top += range.y; + width = height * aspectRatio; + left += range.X; + } else { + if (range.x <= 0) { + if (left > minLeft) { + width -= range.x; + left += range.x; + } else if (range.y <= 0 && top <= minTop) { + renderable = false; + } + } else { + width -= range.x; + left += range.x; + } + + if (range.y <= 0) { + if (top > minTop) { + height -= range.y; + top += range.y; + } + } else { + height -= range.y; + top += range.y; + } + } + + if (width < 0 && height < 0) { + action = ACTION_SOUTH_EAST; + height = 0; + width = 0; + } else if (width < 0) { + action = ACTION_NORTH_EAST; + width = 0; + } else if (height < 0) { + action = ACTION_SOUTH_WEST; + height = 0; + } + + break; + + case ACTION_SOUTH_WEST: + if (aspectRatio) { + if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) { + renderable = false; + break; + } + + width -= range.x; + left += range.x; + height = width / aspectRatio; + } else { + if (range.x <= 0) { + if (left > minLeft) { + width -= range.x; + left += range.x; + } else if (range.y >= 0 && bottom >= maxHeight) { + renderable = false; + } + } else { + width -= range.x; + left += range.x; + } + + if (range.y >= 0) { + if (bottom < maxHeight) { + height += range.y; + } + } else { + height += range.y; + } + } + + if (width < 0 && height < 0) { + action = ACTION_NORTH_EAST; + height = 0; + width = 0; + } else if (width < 0) { + action = ACTION_SOUTH_EAST; + width = 0; + } else if (height < 0) { + action = ACTION_NORTH_WEST; + height = 0; + } + + break; + + case ACTION_SOUTH_EAST: + if (aspectRatio) { + if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) { + renderable = false; + break; + } + + width += range.x; + height = width / aspectRatio; + } else { + if (range.x >= 0) { + if (right < maxWidth) { + width += range.x; + } else if (range.y >= 0 && bottom >= maxHeight) { + renderable = false; + } + } else { + width += range.x; + } + + if (range.y >= 0) { + if (bottom < maxHeight) { + height += range.y; + } + } else { + height += range.y; + } + } + + if (width < 0 && height < 0) { + action = ACTION_NORTH_WEST; + height = 0; + width = 0; + } else if (width < 0) { + action = ACTION_SOUTH_WEST; + width = 0; + } else if (height < 0) { + action = ACTION_NORTH_EAST; + height = 0; + } + + break; + + // Move canvas + case ACTION_MOVE: + this.move(range.x, range.y); + renderable = false; + break; + + // Zoom canvas + case ACTION_ZOOM: + this.zoom((function (x1, y1, x2, y2) { + var z1 = sqrt(x1 * x1 + y1 * y1); + var z2 = sqrt(x2 * x2 + y2 * y2); + + return (z2 - z1) / z1; + })( + abs(this.startX - this.startX2), + abs(this.startY - this.startY2), + abs(this.endX - this.endX2), + abs(this.endY - this.endY2) + ), originalEvent); + this.startX2 = this.endX2; + this.startY2 = this.endY2; + renderable = false; + break; + + // Create crop box + case ACTION_CROP: + if (range.x && range.y) { + offset = this.$cropper.offset(); + left = this.startX - offset.left; + top = this.startY - offset.top; + width = cropBox.minWidth; + height = cropBox.minHeight; + + if (range.x > 0) { + if (range.y > 0) { + action = ACTION_SOUTH_EAST; + } else { + action = ACTION_NORTH_EAST; + top -= height; + } + } else { + if (range.y > 0) { + action = ACTION_SOUTH_WEST; + left -= width; + } else { + action = ACTION_NORTH_WEST; + left -= width; + top -= height; + } + } + + // Show the crop box if is hidden + if (!this.cropped) { + this.cropped = true; + this.$cropBox.removeClass(CLASS_HIDDEN); + } + } + + break; + + // No default + } + + if (renderable) { + cropBox.width = width; + cropBox.height = height; + cropBox.left = left; + cropBox.top = top; + this.action = action; + + this.renderCropBox(); + } + + // Override + this.startX = this.endX; + this.startY = this.endY; + } + }); + + $.extend(prototype, { + + // Show the crop box manually + crop: function () { + if (!this.built || this.disabled) { + return; + } + + if (!this.cropped) { + this.cropped = true; + this.limitCropBox(true, true); + + if (this.options.modal) { + this.$dragBox.addClass(CLASS_MODAL); + } + + this.$cropBox.removeClass(CLASS_HIDDEN); + } + + this.setCropBoxData(this.initialCropBox); + }, + + // Reset the image and crop box to their initial states + reset: function () { + if (!this.built || this.disabled) { + return; + } + + this.image = $.extend({}, this.initialImage); + this.canvas = $.extend({}, this.initialCanvas); + + // Required for strict mode + this.cropBox = $.extend({}, this.initialCropBox); + + this.renderCanvas(); + + if (this.cropped) { + this.renderCropBox(); + } + }, + + // Clear the crop box + clear: function () { + if (!this.cropped || this.disabled) { + return; + } + + $.extend(this.cropBox, { + left: 0, + top: 0, + width: 0, + height: 0 + }); + + this.cropped = false; + this.renderCropBox(); + + this.limitCanvas(); + + // Render canvas after crop box rendered + this.renderCanvas(); + + this.$dragBox.removeClass(CLASS_MODAL); + this.$cropBox.addClass(CLASS_HIDDEN); + }, + + /** + * Replace the image's src and rebuild the cropper + * + * @param {String} url + */ + replace: function (url) { + if (!this.disabled && url) { + if (this.isImg) { + this.replaced = true; + this.$element.attr('src', url); + } + + // Clear previous data + this.options.data = null; + this.load(url); + } + }, + + // Enable (unfreeze) the cropper + enable: function () { + if (this.built) { + this.disabled = false; + this.$cropper.removeClass(CLASS_DISABLED); + } + }, + + // Disable (freeze) the cropper + disable: function () { + if (this.built) { + this.disabled = true; + this.$cropper.addClass(CLASS_DISABLED); + } + }, + + // Destroy the cropper and remove the instance from the image + destroy: function () { + var $this = this.$element; + + if (this.ready) { + if (this.isImg && this.replaced) { + $this.attr('src', this.originalUrl); + } + + this.unbuild(); + $this.removeClass(CLASS_HIDDEN); + } else { + if (this.isImg) { + $this.off(EVENT_LOAD, this.start); + } else if (this.$clone) { + this.$clone.remove(); + } + } + + $this.removeData(NAMESPACE); + }, + + /** + * Move the canvas + * + * @param {Number} offsetX + * @param {Number} offsetY (optional) + */ + move: function (offsetX, offsetY) { + var canvas = this.canvas; + + // If "offsetY" is not present, its default value is "offsetX" + if (isUndefined(offsetY)) { + offsetY = offsetX; + } + + offsetX = num(offsetX); + offsetY = num(offsetY); + + if (this.built && !this.disabled && this.options.movable) { + canvas.left += isNumber(offsetX) ? offsetX : 0; + canvas.top += isNumber(offsetY) ? offsetY : 0; + this.renderCanvas(true); + } + }, + + /** + * Zoom the canvas + * + * @param {Number} ratio + * @param {Event} _originalEvent (private) + */ + zoom: function (ratio, _originalEvent) { + var canvas = this.canvas; + var width; + var height; + + ratio = num(ratio); + + if (ratio && this.built && !this.disabled && this.options.zoomable) { + if (this.trigger(EVENT_ZOOM, { + originalEvent: _originalEvent, + ratio: ratio + })) { + return; + } + + if (ratio < 0) { + ratio = 1 / (1 - ratio); + } else { + ratio = 1 + ratio; + } + + width = canvas.width * ratio; + height = canvas.height * ratio; + canvas.left -= (width - canvas.width) / 2; + canvas.top -= (height - canvas.height) / 2; + canvas.width = width; + canvas.height = height; + this.renderCanvas(true); + this.setDragMode(ACTION_MOVE); + } + }, + + /** + * Rotate the canvas + * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate() + * + * @param {Number} degree + */ + rotate: function (degree) { + var image = this.image; + var rotate = image.rotate || 0; + + degree = num(degree) || 0; + + if (this.built && !this.disabled && this.options.rotatable) { + image.rotate = (rotate + degree) % 360; + this.rotated = true; + this.renderCanvas(true); + } + }, + + /** + * Scale the image + * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale() + * + * @param {Number} scaleX + * @param {Number} scaleY (optional) + */ + scale: function (scaleX, scaleY) { + var image = this.image; + + // If "scaleY" is not present, its default value is "scaleX" + if (isUndefined(scaleY)) { + scaleY = scaleX; + } + + scaleX = num(scaleX); + scaleY = num(scaleY); + + if (this.built && !this.disabled && this.options.scalable) { + image.scaleX = isNumber(scaleX) ? scaleX : 1; + image.scaleY = isNumber(scaleY) ? scaleY : 1; + this.renderImage(true); + } + }, + + /** + * Get the cropped area position and size data (base on the original image) + * + * @param {Boolean} rounded (optional) + * @return {Object} data + */ + getData: function (rounded) { + var options = this.options; + var image = this.image; + var canvas = this.canvas; + var cropBox = this.cropBox; + var ratio; + var data; + + if (this.built && this.cropped) { + data = { + x: cropBox.left - canvas.left, + y: cropBox.top - canvas.top, + width: cropBox.width, + height: cropBox.height + }; + + ratio = image.width / image.naturalWidth; + + $.each(data, function (i, n) { + n = n / ratio; + data[i] = rounded ? Math.round(n) : n; + }); + + } else { + data = { + x: 0, + y: 0, + width: 0, + height: 0 + }; + } + + if (options.rotatable) { + data.rotate = image.rotate || 0; + } + + if (options.scalable) { + data.scaleX = image.scaleX || 1; + data.scaleY = image.scaleY || 1; + } + + return data; + }, + + /** + * Set the cropped area position and size with new data + * + * @param {Object} data + */ + setData: function (data) { + var image = this.image; + var canvas = this.canvas; + var cropBoxData = {}; + var ratio; + + if ($.isFunction(data)) { + data = data.call(this.$element); + } + + if (this.built && !this.disabled && $.isPlainObject(data)) { + if (isNumber(data.rotate) && data.rotate !== image.rotate && + this.options.rotatable) { + + image.rotate = data.rotate; + this.rotated = true; + this.renderCanvas(true); + } + + ratio = image.width / image.naturalWidth; + + if (isNumber(data.x)) { + cropBoxData.left = data.x * ratio + canvas.left; + } + + if (isNumber(data.y)) { + cropBoxData.top = data.y * ratio + canvas.top; + } + + if (isNumber(data.width)) { + cropBoxData.width = data.width * ratio; + } + + if (isNumber(data.height)) { + cropBoxData.height = data.height * ratio; + } + + this.setCropBoxData(cropBoxData); + } + }, + + /** + * Get the container size data + * + * @return {Object} data + */ + getContainerData: function () { + return this.built ? this.container : {}; + }, + + /** + * Get the image position and size data + * + * @return {Object} data + */ + getImageData: function () { + return this.ready ? this.image : {}; + }, + + /** + * Get the canvas position and size data + * + * @return {Object} data + */ + getCanvasData: function () { + var canvas = this.canvas; + var data; + + if (this.built) { + data = { + left: canvas.left, + top: canvas.top, + width: canvas.width, + height: canvas.height + }; + } + + return data || {}; + }, + + /** + * Set the canvas position and size with new data + * + * @param {Object} data + */ + setCanvasData: function (data) { + var canvas = this.canvas; + var aspectRatio = canvas.aspectRatio; + + if ($.isFunction(data)) { + data = data.call(this.$element); + } + + if (this.built && !this.disabled && $.isPlainObject(data)) { + if (isNumber(data.left)) { + canvas.left = data.left; + } + + if (isNumber(data.top)) { + canvas.top = data.top; + } + + if (isNumber(data.width)) { + canvas.width = data.width; + canvas.height = data.width / aspectRatio; + } else if (isNumber(data.height)) { + canvas.height = data.height; + canvas.width = data.height * aspectRatio; + } + + this.renderCanvas(true); + } + }, + + /** + * Get the crop box position and size data + * + * @return {Object} data + */ + getCropBoxData: function () { + var cropBox = this.cropBox; + var data; + + if (this.built && this.cropped) { + data = { + left: cropBox.left, + top: cropBox.top, + width: cropBox.width, + height: cropBox.height + }; + } + + return data || {}; + }, + + /** + * Set the crop box position and size with new data + * + * @param {Object} data + */ + setCropBoxData: function (data) { + var cropBox = this.cropBox; + var aspectRatio = this.options.aspectRatio; + var widthChanged; + var heightChanged; + + if ($.isFunction(data)) { + data = data.call(this.$element); + } + + if (this.built && this.cropped && !this.disabled && $.isPlainObject(data)) { + + if (isNumber(data.left)) { + cropBox.left = data.left; + } + + if (isNumber(data.top)) { + cropBox.top = data.top; + } + + if (isNumber(data.width) && data.width !== cropBox.width) { + widthChanged = true; + cropBox.width = data.width; + } + + if (isNumber(data.height) && data.height !== cropBox.height) { + heightChanged = true; + cropBox.height = data.height; + } + + if (aspectRatio) { + if (widthChanged) { + cropBox.height = cropBox.width / aspectRatio; + } else if (heightChanged) { + cropBox.width = cropBox.height * aspectRatio; + } + } + + this.renderCropBox(); + } + }, + + /** + * Get a canvas drawn the cropped image + * + * @param {Object} options (optional) + * @return {HTMLCanvasElement} canvas + */ + getCroppedCanvas: function (options) { + var originalWidth; + var originalHeight; + var canvasWidth; + var canvasHeight; + var scaledWidth; + var scaledHeight; + var scaledRatio; + var aspectRatio; + var canvas; + var context; + var data; + + if (!this.built || !this.cropped || !SUPPORT_CANVAS) { + return; + } + + if (!$.isPlainObject(options)) { + options = {}; + } + + data = this.getData(); + originalWidth = data.width; + originalHeight = data.height; + aspectRatio = originalWidth / originalHeight; + + if ($.isPlainObject(options)) { + scaledWidth = options.width; + scaledHeight = options.height; + + if (scaledWidth) { + scaledHeight = scaledWidth / aspectRatio; + scaledRatio = scaledWidth / originalWidth; + } else if (scaledHeight) { + scaledWidth = scaledHeight * aspectRatio; + scaledRatio = scaledHeight / originalHeight; + } + } + + canvasWidth = scaledWidth || originalWidth; + canvasHeight = scaledHeight || originalHeight; + + canvas = $('')[0]; + canvas.width = canvasWidth; + canvas.height = canvasHeight; + context = canvas.getContext('2d'); + + if (options.fillColor) { + context.fillStyle = options.fillColor; + context.fillRect(0, 0, canvasWidth, canvasHeight); + } + + // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage + context.drawImage.apply(context, (function () { + var source = getSourceCanvas(this.$clone[0], this.image); + var sourceWidth = source.width; + var sourceHeight = source.height; + var args = [source]; + + // Source canvas + var srcX = data.x; + var srcY = data.y; + var srcWidth; + var srcHeight; + + // Destination canvas + var dstX; + var dstY; + var dstWidth; + var dstHeight; + + if (srcX <= -originalWidth || srcX > sourceWidth) { + srcX = srcWidth = dstX = dstWidth = 0; + } else if (srcX <= 0) { + dstX = -srcX; + srcX = 0; + srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX); + } else if (srcX <= sourceWidth) { + dstX = 0; + srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX); + } + + if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) { + srcY = srcHeight = dstY = dstHeight = 0; + } else if (srcY <= 0) { + dstY = -srcY; + srcY = 0; + srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY); + } else if (srcY <= sourceHeight) { + dstY = 0; + srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY); + } + + args.push(srcX, srcY, srcWidth, srcHeight); + + // Scale destination sizes + if (scaledRatio) { + dstX *= scaledRatio; + dstY *= scaledRatio; + dstWidth *= scaledRatio; + dstHeight *= scaledRatio; + } + + // Avoid "IndexSizeError" in IE and Firefox + if (dstWidth > 0 && dstHeight > 0) { + args.push(dstX, dstY, dstWidth, dstHeight); + } + + return args; + }).call(this)); + + return canvas; + }, + + /** + * Change the aspect ratio of the crop box + * + * @param {Number} aspectRatio + */ + setAspectRatio: function (aspectRatio) { + var options = this.options; + + if (!this.disabled && !isUndefined(aspectRatio)) { + + // 0 -> NaN + options.aspectRatio = num(aspectRatio) || NaN; + + if (this.built) { + this.initCropBox(); + + if (this.cropped) { + this.renderCropBox(); + } + } + } + }, + + /** + * Change the drag mode + * + * @param {String} mode (optional) + */ + setDragMode: function (mode) { + var options = this.options; + var croppable; + var movable; + + if (this.ready && !this.disabled) { + croppable = options.dragCrop && mode === ACTION_CROP; + movable = options.movable && mode === ACTION_MOVE; + mode = (croppable || movable) ? mode : ACTION_NONE; + + this.$dragBox. + data('action', mode). + toggleClass(CLASS_CROP, croppable). + toggleClass(CLASS_MOVE, movable); + + if (!options.cropBoxMovable) { + + // Sync drag mode to crop box when it is not movable(#300) + this.$face. + data('action', mode). + toggleClass(CLASS_CROP, croppable). + toggleClass(CLASS_MOVE, movable); + } + } + } + }); + + $.extend(Cropper.prototype, prototype); + + Cropper.DEFAULTS = { + + // Define the aspect ratio of the crop box + aspectRatio: NaN, + + // An object with the previous cropping result data + data: null, + + // A jQuery selector for adding extra containers to preview + preview: '', + + // Strict mode, the image cannot zoom out less than the container + strict: true, + + // Rebuild when resize the window + responsive: true, + + // Check if the target image is cross origin + checkImageOrigin: true, + + // Show the black modal + modal: true, + + // Show the dashed lines for guiding + guides: true, + + // Show the center indicator for guiding + center: true, + + // Show the white modal to highlight the crop box + highlight: true, + + // Show the grid background + background: true, + + // Enable to crop the image automatically when initialize + autoCrop: true, + + // Define the percentage of automatic cropping area when initializes + autoCropArea: 0.8, + + // Enable to create new crop box by dragging over the image + dragCrop: true, + + // Enable to move the image + movable: true, + + // Enable to rotate the image + rotatable: true, + + // Enable to scale the image + scalable: true, + + // Enable to zoom the image + zoomable: true, + + // Enable to zoom the image by wheeling mouse + mouseWheelZoom: true, + + // Define zoom ratio when zoom the image by wheeling mouse + wheelZoomRatio: 0.1, + + // Enable to zoom the image by dragging touch + touchDragZoom: true, + + // Enable to move the crop box + cropBoxMovable: true, + + // Enable to resize the crop box + cropBoxResizable: true, + + // Toggle drag mode between "crop" and "move" when double click on the cropper + doubleClickToggle: true, + + // Size limitation + minCanvasWidth: 0, + minCanvasHeight: 0, + minCropBoxWidth: 0, + minCropBoxHeight: 0, + minContainerWidth: 200, + minContainerHeight: 100, + + // Shortcuts of events + build: null, + built: null, + cropstart: null, + cropmove: null, + cropend: null, + crop: null, + zoom: null + }; + + Cropper.setDefaults = function (options) { + $.extend(Cropper.DEFAULTS, options); + }; + + Cropper.TEMPLATE = ( + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + '
' + ); + + // Save the other cropper + Cropper.other = $.fn.cropper; + + // Register as jQuery plugin + $.fn.cropper = function (options) { + var args = toArray(arguments, 1); + var result; + + this.each(function () { + var $this = $(this); + var data = $this.data(NAMESPACE); + var fn; + + if (!data) { + if (/destroy/.test(options)) { + return; + } + + $this.data(NAMESPACE, (data = new Cropper(this, options))); + } + + if (typeof options === 'string' && $.isFunction(fn = data[options])) { + result = fn.apply(data, args); + } + }); + + return isUndefined(result) ? this : result; + }; + + $.fn.cropper.Constructor = Cropper; + $.fn.cropper.setDefaults = Cropper.setDefaults; + + // No conflict + $.fn.cropper.noConflict = function () { + $.fn.cropper = Cropper.other; + return this; + }; + +}); diff --git a/app/assets/javascripts/app/lib/base/cropper.min.js b/app/assets/javascripts/app/lib/base/cropper.min.js deleted file mode 100755 index bf6e3c304..000000000 --- a/app/assets/javascripts/app/lib/base/cropper.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/*! - * Cropper v0.9.3 - * https://github.com/fengyuanchen/cropper - * - * Copyright (c) 2014-2015 Fengyuan Chen and contributors - * Released under the MIT license - * - * Date: 2015-05-10T07:25:08.257Z - */ -!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){"use strict";function b(a){return"number"==typeof a&&!isNaN(a)}function c(a){return"undefined"==typeof a}function d(a,c){var d=[];return b(c)&&d.push(c),d.slice.apply(a,d)}function e(a,b){var c=d(arguments,2);return function(){return a.apply(b,c.concat(d(arguments)))}}function f(a){var b=a.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);return b&&(b[1]!==n.protocol||b[2]!==n.hostname||b[3]!==n.port)}function g(a){var b="timestamp="+(new Date).getTime();return a+(-1===a.indexOf("?")?"?":"&")+b}function h(a){return a?"rotate("+a+"deg)":"none"}function i(a,b){var c,d,e=Q(a.degree)%180,f=(e>90?180-e:e)*Math.PI/180,g=R(f),h=S(f),i=a.width,j=a.height,k=a.aspectRatio;return b?(c=i/(h+g/k),d=c/k):(c=i*h+j*g,d=i*g+j*h),{width:c,height:d}}function j(b,c){var d=a("")[0],e=d.getContext("2d"),f=c.naturalWidth,g=c.naturalHeight,h=c.rotate,j=i({width:f,height:g,degree:h});return h?(d.width=j.width,d.height=j.height,e.save(),e.translate(j.width/2,j.height/2),e.rotate(h*Math.PI/180),e.drawImage(b,-f/2,-g/2,f,g),e.restore()):(d.width=f,d.height=g,e.drawImage(b,0,0,f,g)),d}function k(b,c){this.$element=a(b),this.options=a.extend({},k.DEFAULTS,a.isPlainObject(c)&&c),this.ready=!1,this.built=!1,this.rotated=!1,this.cropped=!1,this.disabled=!1,this.canvas=null,this.cropBox=null,this.load()}var l=a(window),m=a(document),n=window.location,o=".cropper",p="preview"+o,q=/^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/,r="cropper-modal",s="cropper-hide",t="cropper-hidden",u="cropper-invisible",v="cropper-move",w="cropper-crop",x="cropper-disabled",y="cropper-bg",z="mousedown touchstart",A="mousemove touchmove",B="mouseup mouseleave touchend touchleave touchcancel",C="wheel mousewheel DOMMouseScroll",D="dblclick",E="resize"+o,F="build"+o,G="built"+o,H="dragstart"+o,I="dragmove"+o,J="dragend"+o,K="zoomin"+o,L="zoomout"+o,M=a.isFunction(a("")[0].getContext),N=Math.sqrt,O=Math.min,P=Math.max,Q=Math.abs,R=Math.sin,S=Math.cos,T=parseFloat,U={};U.load=function(b){var c,d,e,h,i=this.options,j=this.$element;if(!b)if(j.is("img")){if(!j.attr("src"))return;b=j.prop("src")}else j.is("canvas")&&M&&(b=j[0].toDataURL());b&&(e=a.Event(F),j.one(F,i.build).trigger(e),e.isDefaultPrevented()||(i.checkImageOrigin&&f(b)&&(c="anonymous",j.prop("crossOrigin")||(d=g(b))),this.$clone=h=a(""),h.one("load",a.proxy(function(){var a=h.prop("naturalWidth")||h.width(),c=h.prop("naturalHeight")||h.height();this.image={naturalWidth:a,naturalHeight:c,aspectRatio:a/c,rotate:0},this.url=b,this.ready=!0,this.build()},this)).one("error",function(){h.remove()}).attr({crossOrigin:c,src:d||b}),h.addClass(s).insertAfter(j)))},U.build=function(){var b,c,d=this.$element,e=this.$clone,f=this.options;this.ready&&(this.built&&this.unbuild(),this.$cropper=b=a(k.TEMPLATE),d.addClass(t),e.removeClass(s),this.$container=d.parent().append(b),this.$canvas=b.find(".cropper-canvas").append(e),this.$dragBox=b.find(".cropper-drag-box"),this.$cropBox=c=b.find(".cropper-crop-box"),this.$viewBox=b.find(".cropper-view-box"),this.addListeners(),this.initPreview(),f.aspectRatio=T(f.aspectRatio)||NaN,f.autoCrop?(this.cropped=!0,f.modal&&this.$dragBox.addClass(r)):c.addClass(t),f.background&&b.addClass(y),f.highlight||c.find(".cropper-face").addClass(u),f.guides||c.find(".cropper-dashed").addClass(t),f.movable||c.find(".cropper-face").data("drag","move"),f.resizable||c.find(".cropper-line, .cropper-point").addClass(t),this.setDragMode(f.dragCrop?"crop":"move"),this.built=!0,this.render(),this.setData(f.data),d.one(G,f.built).trigger(G))},U.unbuild=function(){this.built&&(this.built=!1,this.initialImage=null,this.initialCanvas=null,this.initialCropBox=null,this.container=null,this.canvas=null,this.cropBox=null,this.removeListeners(),this.resetPreview(),this.$preview=null,this.$viewBox=null,this.$cropBox=null,this.$dragBox=null,this.$canvas=null,this.$container=null,this.$cropper.remove(),this.$cropper=null)},a.extend(U,{render:function(){this.initContainer(),this.initCanvas(),this.initCropBox(),this.renderCanvas(),this.cropped&&this.renderCropBox()},initContainer:function(){var a=this.$element,b=this.$container,c=this.$cropper,d=this.options;c.addClass(t),a.removeClass(t),c.css(this.container={width:P(b.width(),T(d.minContainerWidth)||200),height:P(b.height(),T(d.minContainerHeight)||100)}),a.addClass(t),c.removeClass(t)},initCanvas:function(){var b=this.container,c=b.width,d=b.height,e=this.image,f=e.aspectRatio,g={aspectRatio:f,width:c,height:d};d*f>c?g.height=c/f:g.width=d*f,g.oldLeft=g.left=(c-g.width)/2,g.oldTop=g.top=(d-g.height)/2,this.canvas=g,this.limitCanvas(!0,!0),this.initialImage=a.extend({},e),this.initialCanvas=a.extend({},g)},limitCanvas:function(b,c){var d,e,f=this.options,g=f.strict,h=this.container,i=h.width,j=h.height,k=this.canvas,l=k.aspectRatio,m=this.cropBox,n=this.cropped&&m,o=this.initialCanvas||k,p=o.width,q=o.height;b&&(d=T(f.minCanvasWidth)||0,e=T(f.minCanvasHeight)||0,d?(g&&(d=P(n?m.width:p,d)),e=d/l):e?(g&&(e=P(n?m.height:q,e)),d=e*l):g&&(n?(d=m.width,e=m.height,e*l>d?d=e*l:e=d/l):(d=p,e=q)),a.extend(k,{minWidth:d,minHeight:e,maxWidth:1/0,maxHeight:1/0})),c&&(g?n?(k.minLeft=O(m.left,m.left+m.width-k.width),k.minTop=O(m.top,m.top+m.height-k.height),k.maxLeft=m.left,k.maxTop=m.top):(k.minLeft=O(0,i-k.width),k.minTop=O(0,j-k.height),k.maxLeft=P(0,i-k.width),k.maxTop=P(0,j-k.height)):(k.minLeft=-k.width,k.minTop=-k.height,k.maxLeft=i,k.maxTop=j))},renderCanvas:function(a){var b,c,d=this.options,e=this.canvas,f=this.image;this.rotated&&(this.rotated=!1,c=i({width:f.width,height:f.height,degree:f.rotate}),b=c.width/c.height,b!==e.aspectRatio&&(e.left-=(c.width-e.width)/2,e.top-=(c.height-e.height)/2,e.width=c.width,e.height=c.height,e.aspectRatio=b,this.limitCanvas(!0,!1))),(e.width>e.maxWidth||e.widthe.maxHeight||e.heightc.width?f.height=f.width/d:f.width=f.height*d),this.cropBox=f,this.limitCropBox(!0,!0),f.width=O(P(f.width,f.minWidth),f.maxWidth),f.height=O(P(f.height,f.minHeight),f.maxHeight),f.width=P(f.minWidth,f.width*e),f.height=P(f.minHeight,f.height*e),f.oldLeft=f.left=c.left+(c.width-f.width)/2,f.oldTop=f.top=c.top+(c.height-f.height)/2,this.initialCropBox=a.extend({},f)},limitCropBox:function(a,b){var c,d,e=this.options,f=e.strict,g=this.container,h=g.width,i=g.height,j=this.canvas,k=this.cropBox,l=e.aspectRatio;a&&(c=T(e.minCropBoxWidth)||0,d=T(e.minCropBoxHeight)||0,k.minWidth=O(h,c),k.minHeight=O(i,d),k.maxWidth=O(h,f?j.width:h),k.maxHeight=O(i,f?j.height:i),l&&(k.maxHeight*l>k.maxWidth?(k.minHeight=k.minWidth/l,k.maxHeight=k.maxWidth/l):(k.minWidth=k.minHeight*l,k.maxWidth=k.maxHeight*l)),k.minWidth=O(k.maxWidth,k.minWidth),k.minHeight=O(k.maxHeight,k.minHeight)),b&&(f?(k.minLeft=P(0,j.left),k.minTop=P(0,j.top),k.maxLeft=O(h,j.left+j.width)-k.width,k.maxTop=O(i,j.top+j.height)-k.height):(k.minLeft=0,k.minTop=0,k.maxLeft=h-k.width,k.maxTop=i-k.height))},renderCropBox:function(){var a=this.options,b=this.container,c=b.width,d=b.height,e=this.$cropBox,f=this.cropBox;(f.width>f.maxWidth||f.widthf.maxHeight||f.height'),this.$preview.each(function(){var c=a(this);c.data(p,{width:c.width(),height:c.height(),original:c.html()}).html('')})},U.resetPreview=function(){this.$preview.each(function(){var b=a(this);b.html(b.data(p).original).removeData(p)})},U.preview=function(){var b=this.image,c=this.canvas,d=this.cropBox,e=b.width,f=b.height,g=d.left-c.left-b.left,i=d.top-c.top-b.top,j=b.rotate;this.cropped&&!this.disabled&&(this.$viewBox.find("img").css({width:e,height:f,marginLeft:-g,marginTop:-i,transform:h(j)}),this.$preview.each(function(){var b=a(this),c=b.data(p),k=c.width/d.width,l=c.width,m=d.height*k;m>c.height&&(k=c.height/d.height,l=d.width*k,m=c.height),b.width(l).height(m).find("img").css({width:e*k,height:f*k,marginLeft:-g*k,marginTop:-i*k,transform:h(j)})}))},U.addListeners=function(){var b=this.options;this.$element.on(H,b.dragstart).on(I,b.dragmove).on(J,b.dragend).on(K,b.zoomin).on(L,b.zoomout),this.$cropper.on(z,a.proxy(this.dragstart,this)).on(D,a.proxy(this.dblclick,this)),b.zoomable&&b.mouseWheelZoom&&this.$cropper.on(C,a.proxy(this.wheel,this)),m.on(A,this._dragmove=e(this.dragmove,this)).on(B,this._dragend=e(this.dragend,this)),b.responsive&&l.on(E,this._resize=e(this.resize,this))},U.removeListeners=function(){var a=this.options;this.$element.off(H,a.dragstart).off(I,a.dragmove).off(J,a.dragend).off(K,a.zoomin).off(L,a.zoomout),this.$cropper.off(z,this.dragstart).off(D,this.dblclick),a.zoomable&&a.mouseWheelZoom&&this.$cropper.off(C,this.wheel),m.off(A,this._dragmove).off(B,this._dragend),a.responsive&&l.off(E,this._resize)},a.extend(U,{resize:function(){var b,c,d,e=this.$container,f=this.container;this.disabled||(d=e.width()/f.width,(1!==d||e.height()!==f.height)&&(b=this.getCanvasData(),c=this.getCropBoxData(),this.render(),this.setCanvasData(a.each(b,function(a,c){b[a]=c*d})),this.setCropBoxData(a.each(c,function(a,b){c[a]=b*d}))))},dblclick:function(){this.disabled||this.setDragMode(this.$dragBox.hasClass(w)?"move":"crop")},wheel:function(a){var b=a.originalEvent,c=1;this.disabled||(a.preventDefault(),b.deltaY?c=b.deltaY>0?1:-1:b.wheelDelta?c=-b.wheelDelta/120:b.detail&&(c=b.detail>0?1:-1),this.zoom(.1*-c))},dragstart:function(b){var c,d,e,f=this.options,g=b.originalEvent,h=g&&g.touches,i=b;if(!this.disabled){if(h){if(e=h.length,e>1){if(!f.zoomable||!f.touchDragZoom||2!==e)return;i=h[1],this.startX2=i.pageX,this.startY2=i.pageY,c="zoom"}i=h[0]}if(c=c||a(i.target).data("drag"),q.test(c)){if(b.preventDefault(),d=a.Event(H,{originalEvent:g,dragType:c}),this.$element.trigger(d),d.isDefaultPrevented())return;this.dragType=c,this.cropping=!1,this.startX=i.pageX,this.startY=i.pageY,"crop"===c&&(this.cropping=!0,this.$dragBox.addClass(r))}}},dragmove:function(b){var c,d,e=this.options,f=b.originalEvent,g=f&&f.touches,h=b,i=this.dragType;if(!this.disabled){if(g){if(d=g.length,d>1){if(!e.zoomable||!e.touchDragZoom||2!==d)return;h=g[1],this.endX2=h.pageX,this.endY2=h.pageY}h=g[0]}if(i){if(b.preventDefault(),c=a.Event(I,{originalEvent:f,dragType:i}),this.$element.trigger(c),c.isDefaultPrevented())return;this.endX=h.pageX,this.endY=h.pageY,this.change()}}},dragend:function(b){var c,d=this.dragType;if(!this.disabled&&d){if(b.preventDefault(),c=a.Event(J,{originalEvent:b.originalEvent,dragType:d}),this.$element.trigger(c),c.isDefaultPrevented())return;this.cropping&&(this.cropping=!1,this.$dragBox.toggleClass(r,this.cropped&&this.options.modal)),this.dragType=""}}}),a.extend(U,{crop:function(){this.built&&!this.disabled&&(this.cropped||(this.cropped=!0,this.limitCropBox(!0,!0),this.options.modal&&this.$dragBox.addClass(r),this.$cropBox.removeClass(t)),this.setCropBoxData(this.initialCropBox))},reset:function(){this.built&&!this.disabled&&(this.image=a.extend({},this.initialImage),this.canvas=a.extend({},this.initialCanvas),this.cropBox=a.extend({},this.initialCropBox),this.renderCanvas(),this.cropped&&this.renderCropBox())},clear:function(){this.cropped&&!this.disabled&&(a.extend(this.cropBox,{left:0,top:0,width:0,height:0}),this.cropped=!1,this.renderCropBox(),this.limitCanvas(),this.renderCanvas(),this.$dragBox.removeClass(r),this.$cropBox.addClass(t))},destroy:function(){var a=this.$element;this.ready?(this.unbuild(),a.removeClass(t)):this.$clone&&this.$clone.remove(),a.removeData("cropper")},replace:function(a){!this.disabled&&a&&this.load(a)},enable:function(){this.built&&(this.disabled=!1,this.$cropper.removeClass(x))},disable:function(){this.built&&(this.disabled=!0,this.$cropper.addClass(x))},move:function(a,c){var d=this.canvas;this.built&&!this.disabled&&b(a)&&b(c)&&(d.left+=a,d.top+=c,this.renderCanvas(!0))},zoom:function(b){var c,d,e,f=this.canvas;if(b=T(b),b&&this.built&&!this.disabled&&this.options.zoomable){if(c=a.Event(b>0?K:L),this.$element.trigger(c),c.isDefaultPrevented())return;b=-1>=b?1/(1-b):1>=b?1+b:b,d=f.width*b,e=f.height*b,f.left-=(d-f.width)/2,f.top-=(e-f.height)/2,f.width=d,f.height=e,this.renderCanvas(!0),this.setDragMode("move")}},rotate:function(a){var b=this.image;a=T(a),a&&this.built&&!this.disabled&&this.options.rotatable&&(b.rotate=(b.rotate+a)%360,this.rotated=!0,this.renderCanvas(!0))},getData:function(){var b,c,d=this.cropBox,e=this.canvas,f=this.image;return this.built&&this.cropped?(c={x:d.left-e.left,y:d.top-e.top,width:d.width,height:d.height},b=f.width/f.naturalWidth,a.each(c,function(a,d){d/=b,c[a]=d})):c={x:0,y:0,width:0,height:0},c.rotate=this.ready?f.rotate:0,c},setData:function(c){var d,e=this.image,f=this.canvas,g={};this.built&&!this.disabled&&a.isPlainObject(c)&&(b(c.rotate)&&c.rotate!==e.rotate&&this.options.rotatable&&(e.rotate=c.rotate,this.rotated=!0,this.renderCanvas(!0)),d=e.width/e.naturalWidth,b(c.x)&&(g.left=c.x*d+f.left),b(c.y)&&(g.top=c.y*d+f.top),b(c.width)&&(g.width=c.width*d),b(c.height)&&(g.height=c.height*d),this.setCropBoxData(g))},getContainerData:function(){return this.built?this.container:{}},getImageData:function(){return this.ready?this.image:{}},getCanvasData:function(){var a,b=this.canvas;return this.built&&(a={left:b.left,top:b.top,width:b.width,height:b.height}),a||{}},setCanvasData:function(c){var d=this.canvas,e=d.aspectRatio;this.built&&!this.disabled&&a.isPlainObject(c)&&(b(c.left)&&(d.left=c.left),b(c.top)&&(d.top=c.top),b(c.width)?(d.width=c.width,d.height=c.width/e):b(c.height)&&(d.height=c.height,d.width=c.height*e),this.renderCanvas(!0))},getCropBoxData:function(){var a,b=this.cropBox;return this.built&&this.cropped&&(a={left:b.left,top:b.top,width:b.width,height:b.height}),a||{}},setCropBoxData:function(c){var d=this.cropBox,e=this.options.aspectRatio;this.built&&this.cropped&&!this.disabled&&a.isPlainObject(c)&&(b(c.left)&&(d.left=c.left),b(c.top)&&(d.top=c.top),b(c.width)&&(d.width=c.width),b(c.height)&&(d.height=c.height),e&&(b(c.width)?d.height=d.width/e:b(c.height)&&(d.width=d.height*e)),this.renderCropBox())},getCroppedCanvas:function(b){var c,d,e,f,g,h,i,k,l,m,n;if(this.built&&this.cropped&&M)return a.isPlainObject(b)||(b={}),n=this.getData(),c=n.width,d=n.height,k=c/d,a.isPlainObject(b)&&(g=b.width,h=b.height,g?(h=g/k,i=g/c):h&&(g=h*k,i=h/d)),e=g||c,f=h||d,l=a("")[0],l.width=e,l.height=f,m=l.getContext("2d"),b.fillColor&&(m.fillStyle=b.fillColor,m.fillRect(0,0,e,f)),m.drawImage.apply(m,function(){var a,b,e,f,g,h,k=j(this.$clone[0],this.image),l=k.width,m=k.height,o=[k],p=n.x,q=n.y;return-c>=p||p>l?p=a=e=g=0:0>=p?(e=-p,p=0,a=g=O(l,c+p)):l>=p&&(e=0,a=g=O(c,l-p)),0>=a||-d>=q||q>m?q=b=f=h=0:0>=q?(f=-q,q=0,b=h=O(m,d+q)):m>=q&&(f=0,b=h=O(d,m-q)),o.push(p,q,a,b),i&&(e*=i,f*=i,g*=i,h*=i),g>0&&h>0&&o.push(e,f,g,h),o}.call(this)),l},setAspectRatio:function(a){var b=this.options;this.disabled||c(a)||(b.aspectRatio=T(a)||NaN,this.built&&(this.initCropBox(),this.cropped&&this.renderCropBox()))},setDragMode:function(a){var b=this.$dragBox,c=!1,d=!1;if(this.ready&&!this.disabled){switch(a){case"crop":this.options.dragCrop?(c=!0,b.data("drag",a)):d=!0;break;case"move":d=!0,b.data("drag",a);break;default:b.removeData("drag")}b.toggleClass(w,c).toggleClass(v,d)}}}),U.change=function(){var a,b=this.dragType,c=this.options,d=this.canvas,e=this.container,f=this.cropBox,g=f.width,h=f.height,i=f.left,j=f.top,k=i+g,l=j+h,m=0,n=0,o=e.width,p=e.height,q=!0,r=c.aspectRatio,s={x:this.endX-this.startX,y:this.endY-this.startY};switch(c.strict&&(m=f.minLeft,n=f.minTop,o=m+O(e.width,d.width),p=n+O(e.height,d.height)),r&&(s.X=s.y*r,s.Y=s.x/r),b){case"all":i+=s.x,j+=s.y;break;case"e":if(s.x>=0&&(k>=o||r&&(n>=j||l>=p))){q=!1;break}g+=s.x,r&&(h=g/r,j-=s.Y/2),0>g&&(b="w",g=0);break;case"n":if(s.y<=0&&(n>=j||r&&(m>=i||k>=o))){q=!1;break}h-=s.y,j+=s.y,r&&(g=h*r,i+=s.X/2),0>h&&(b="s",h=0);break;case"w":if(s.x<=0&&(m>=i||r&&(n>=j||l>=p))){q=!1;break}g-=s.x,i+=s.x,r&&(h=g/r,j+=s.Y/2),0>g&&(b="e",g=0);break;case"s":if(s.y>=0&&(l>=p||r&&(m>=i||k>=o))){q=!1;break}h+=s.y,r&&(g=h*r,i-=s.X/2),0>h&&(b="n",h=0);break;case"ne":if(r){if(s.y<=0&&(n>=j||k>=o)){q=!1;break}h-=s.y,j+=s.y,g=h*r}else s.x>=0?o>k?g+=s.x:s.y<=0&&n>=j&&(q=!1):g+=s.x,s.y<=0?j>n&&(h-=s.y,j+=s.y):(h-=s.y,j+=s.y);0>g&&0>h?(b="sw",h=0,g=0):0>g?(b="nw",g=0):0>h&&(b="se",h=0);break;case"nw":if(r){if(s.y<=0&&(n>=j||m>=i)){q=!1;break}h-=s.y,j+=s.y,g=h*r,i+=s.X}else s.x<=0?i>m?(g-=s.x,i+=s.x):s.y<=0&&n>=j&&(q=!1):(g-=s.x,i+=s.x),s.y<=0?j>n&&(h-=s.y,j+=s.y):(h-=s.y,j+=s.y);0>g&&0>h?(b="se",h=0,g=0):0>g?(b="ne",g=0):0>h&&(b="sw",h=0);break;case"sw":if(r){if(s.x<=0&&(m>=i||l>=p)){q=!1;break}g-=s.x,i+=s.x,h=g/r}else s.x<=0?i>m?(g-=s.x,i+=s.x):s.y>=0&&l>=p&&(q=!1):(g-=s.x,i+=s.x),s.y>=0?p>l&&(h+=s.y):h+=s.y;0>g&&0>h?(b="ne",h=0,g=0):0>g?(b="se",g=0):0>h&&(b="nw",h=0);break;case"se":if(r){if(s.x>=0&&(k>=o||l>=p)){q=!1;break}g+=s.x,h=g/r}else s.x>=0?o>k?g+=s.x:s.y>=0&&l>=p&&(q=!1):g+=s.x,s.y>=0?p>l&&(h+=s.y):h+=s.y;0>g&&0>h?(b="nw",h=0,g=0):0>g?(b="sw",g=0):0>h&&(b="ne",h=0);break;case"move":d.left+=s.x,d.top+=s.y,this.renderCanvas(!0),q=!1;break;case"zoom":this.zoom(function(a,b,c,d){var e=N(a*a+b*b),f=N(c*c+d*d);return(f-e)/e}(Q(this.startX-this.startX2),Q(this.startY-this.startY2),Q(this.endX-this.endX2),Q(this.endY-this.endY2))),this.startX2=this.endX2,this.startY2=this.endY2,q=!1;break;case"crop":s.x&&s.y&&(a=this.$cropper.offset(),i=this.startX-a.left,j=this.startY-a.top,g=f.minWidth,h=f.minHeight,s.x>0?s.y>0?b="se":(b="ne",j-=h):s.y>0?(b="sw",i-=g):(b="nw",i-=g,j-=h),this.cropped||(this.cropped=!0,this.$cropBox.removeClass(t)))}q&&(f.width=g,f.height=h,f.left=i,f.top=j,this.dragType=b,this.renderCropBox()),this.startX=this.endX,this.startY=this.endY},a.extend(k.prototype,U),k.DEFAULTS={aspectRatio:NaN,autoCropArea:.8,crop:null,data:null,preview:"",strict:!0,responsive:!0,checkImageOrigin:!0,modal:!0,guides:!0,highlight:!0,background:!0,autoCrop:!0,dragCrop:!0,movable:!0,resizable:!0,rotatable:!0,zoomable:!0,touchDragZoom:!0,mouseWheelZoom:!0,minCanvasWidth:0,minCanvasHeight:0,minCropBoxWidth:0,minCropBoxHeight:0,minContainerWidth:200,minContainerHeight:100,build:null,built:null,dragstart:null,dragmove:null,dragend:null,zoomin:null,zoomout:null},k.setDefaults=function(b){a.extend(k.DEFAULTS,b)},k.TEMPLATE=function(a,b){return b=b.split(","),a.replace(/\d+/g,function(a){return b[a]})}('<0 6="5-container"><0 6="5-canvas"><0 6="5-2-9" 3-2="move"><0 6="5-crop-9"><1 6="5-view-9"><1 6="5-8 8-h"><1 6="5-8 8-v"><1 6="5-face" 3-2="all"><1 6="5-7 7-e" 3-2="e"><1 6="5-7 7-n" 3-2="n"><1 6="5-7 7-w" 3-2="w"><1 6="5-7 7-s" 3-2="s"><1 6="5-4 4-e" 3-2="e"><1 6="5-4 4-n" 3-2="n"><1 6="5-4 4-w" 3-2="w"><1 6="5-4 4-s" 3-2="s"><1 6="5-4 4-ne" 3-2="ne"><1 6="5-4 4-nw" 3-2="nw"><1 6="5-4 4-sw" 3-2="sw"><1 6="5-4 4-se" 3-2="se">',"div,span,drag,data,point,cropper,class,line,dashed,box"),k.other=a.fn.cropper,a.fn.cropper=function(b){var e,f=d(arguments,1);return this.each(function(){var c,d=a(this),g=d.data("cropper");g||d.data("cropper",g=new k(this,b)),"string"==typeof b&&a.isFunction(c=g[b])&&(e=c.apply(g,f))}),c(e)?this:e},a.fn.cropper.Constructor=k,a.fn.cropper.setDefaults=k.setDefaults,a.fn.cropper.noConflict=function(){return a.fn.cropper=k.other,this}}); \ No newline at end of file diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index b0ab15061..b532e5f1a 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -4,7 +4,7 @@ * the top of the compiled file, but it's generally better to create a new file per style scope. *= require_self *= require ./bootstrap.css - *= require ./cropper.min.css + *= require ./cropper.css *= require ./fineuploader.css *= require ./ui-lightness/jquery-ui-1.11.2.css *= require ./jquery.noty.css diff --git a/app/assets/stylesheets/cropper.css b/app/assets/stylesheets/cropper.css new file mode 100644 index 000000000..5bdefea32 --- /dev/null +++ b/app/assets/stylesheets/cropper.css @@ -0,0 +1,290 @@ +/*! + * Cropper v1.0.0-rc.1 + * https://github.com/fengyuanchen/cropper + * + * Copyright (c) 2014-2015 Fengyuan Chen and contributors + * Released under the MIT license + * + * Date: 2015-09-05T04:29:22.355Z + */ +.cropper-container { + position: relative; + overflow: hidden; + font-size: 0; + line-height: 0; + -ms-touch-action: none; + touch-action: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + direction: ltr !important; + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; +} +.cropper-container img { + display: block; + width: 100%; + min-width: 0 !important; + max-width: none !important; + height: 100%; + min-height: 0 !important; + max-height: none !important; + + image-orientation: 0deg !important; +} + +.cropper-canvas, +.cropper-drag-box, +.cropper-crop-box, +.cropper-modal { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.cropper-drag-box { + background-color: #fff; + filter: alpha(opacity=0); + opacity: 0; +} + +.cropper-modal { + background-color: #000; + filter: alpha(opacity=50); + opacity: .5; +} + +.cropper-view-box { + display: block; + width: 100%; + height: 100%; + overflow: hidden; + outline: 1px solid #69f; + outline-color: rgba(102, 153, 255, .75); +} + +.cropper-dashed { + position: absolute; + display: block; + filter: alpha(opacity=50); + border: 0 dashed #eee; + opacity: .5; +} +.cropper-dashed.dashed-h { + top: 33.33333%; + left: 0; + width: 100%; + height: 33.33333%; + border-top-width: 1px; + border-bottom-width: 1px; +} +.cropper-dashed.dashed-v { + top: 0; + left: 33.33333%; + width: 33.33333%; + height: 100%; + border-right-width: 1px; + border-left-width: 1px; +} + +.cropper-center { + position: absolute; + top: 50%; + left: 50%; + display: block; + width: 0; + height: 0; + filter: alpha(opacity=75); + opacity: .75; +} +.cropper-center:before, + .cropper-center:after { + position: absolute; + display: block; + content: " "; + background-color: #eee; +} +.cropper-center:before { + top: 0; + left: -3px; + width: 7px; + height: 1px; +} +.cropper-center:after { + top: -3px; + left: 0; + width: 1px; + height: 7px; +} + +.cropper-face, +.cropper-line, +.cropper-point { + position: absolute; + display: block; + width: 100%; + height: 100%; + filter: alpha(opacity=10); + opacity: .1; +} + +.cropper-face { + top: 0; + left: 0; + background-color: #fff; +} + +.cropper-line { + background-color: #69f; +} +.cropper-line.line-e { + top: 0; + right: -3px; + width: 5px; + cursor: e-resize; +} +.cropper-line.line-n { + top: -3px; + left: 0; + height: 5px; + cursor: n-resize; +} +.cropper-line.line-w { + top: 0; + left: -3px; + width: 5px; + cursor: w-resize; +} +.cropper-line.line-s { + bottom: -3px; + left: 0; + height: 5px; + cursor: s-resize; +} + +.cropper-point { + width: 5px; + height: 5px; + background-color: #69f; + filter: alpha(opacity=75); + opacity: .75; +} +.cropper-point.point-e { + top: 50%; + right: -3px; + margin-top: -3px; + cursor: e-resize; +} +.cropper-point.point-n { + top: -3px; + left: 50%; + margin-left: -3px; + cursor: n-resize; +} +.cropper-point.point-w { + top: 50%; + left: -3px; + margin-top: -3px; + cursor: w-resize; +} +.cropper-point.point-s { + bottom: -3px; + left: 50%; + margin-left: -3px; + cursor: s-resize; +} +.cropper-point.point-ne { + top: -3px; + right: -3px; + cursor: ne-resize; +} +.cropper-point.point-nw { + top: -3px; + left: -3px; + cursor: nw-resize; +} +.cropper-point.point-sw { + bottom: -3px; + left: -3px; + cursor: sw-resize; +} +.cropper-point.point-se { + right: -3px; + bottom: -3px; + width: 20px; + height: 20px; + cursor: se-resize; + filter: alpha(opacity=100); + opacity: 1; +} +.cropper-point.point-se:before { + position: absolute; + right: -50%; + bottom: -50%; + display: block; + width: 200%; + height: 200%; + content: " "; + background-color: #69f; + filter: alpha(opacity=0); + opacity: 0; +} +@media (min-width: 768px) { + .cropper-point.point-se { + width: 15px; + height: 15px; + } +} +@media (min-width: 992px) { + .cropper-point.point-se { + width: 10px; + height: 10px; + } +} +@media (min-width: 1200px) { + .cropper-point.point-se { + width: 5px; + height: 5px; + filter: alpha(opacity=75); + opacity: .75; + } +} + +.cropper-bg { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC"); +} + +.cropper-invisible { + filter: alpha(opacity=0); + opacity: 0; +} + +.cropper-hide { + position: absolute; + display: block; + width: 0; + height: 0; +} + +.cropper-hidden { + display: none !important; +} + +.cropper-move { + cursor: move; +} + +.cropper-crop { + cursor: crosshair; +} + +.cropper-disabled .cropper-drag-box, +.cropper-disabled .cropper-face, +.cropper-disabled .cropper-line, +.cropper-disabled .cropper-point { + cursor: not-allowed; +} diff --git a/app/assets/stylesheets/cropper.min.css b/app/assets/stylesheets/cropper.min.css deleted file mode 100755 index 531f9e638..000000000 --- a/app/assets/stylesheets/cropper.min.css +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * Cropper v0.9.3 - * https://github.com/fengyuanchen/cropper - * - * Copyright (c) 2014-2015 Fengyuan Chen and contributors - * Released under the MIT license - * - * Date: 2015-05-10T07:25:08.257Z - */.cropper-container{position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none}.cropper-container img{display:block;width:100%;min-width:0!important;max-width:none!important;height:100%;min-height:0!important;max-height:none!important;image-orientation:0deg!important}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal{position:absolute;top:0;right:0;bottom:0;left:0}.cropper-drag-box{background-color:#fff;filter:alpha(opacity=0);opacity:0}.cropper-modal{background-color:#000;filter:alpha(opacity=50);opacity:.5}.cropper-view-box{display:block;width:100%;height:100%;overflow:hidden;outline:1px solid #69f;outline-color:rgba(102,153,255,.75)}.cropper-dashed{position:absolute;display:block;filter:alpha(opacity=50);border:0 dashed #fff;opacity:.5}.cropper-dashed.dashed-h{top:33.33333333%;left:0;width:100%;height:33.33333333%;border-top-width:1px;border-bottom-width:1px}.cropper-dashed.dashed-v{top:0;left:33.33333333%;width:33.33333333%;height:100%;border-right-width:1px;border-left-width:1px}.cropper-face,.cropper-line,.cropper-point{position:absolute;display:block;width:100%;height:100%;filter:alpha(opacity=10);opacity:.1}.cropper-face{top:0;left:0;cursor:move;background-color:#fff}.cropper-line{background-color:#69f}.cropper-line.line-e{top:0;right:-3px;width:5px;cursor:e-resize}.cropper-line.line-n{top:-3px;left:0;height:5px;cursor:n-resize}.cropper-line.line-w{top:0;left:-3px;width:5px;cursor:w-resize}.cropper-line.line-s{bottom:-3px;left:0;height:5px;cursor:s-resize}.cropper-point{width:5px;height:5px;background-color:#69f;filter:alpha(opacity=75);opacity:.75}.cropper-point.point-e{top:50%;right:-3px;margin-top:-3px;cursor:e-resize}.cropper-point.point-n{top:-3px;left:50%;margin-left:-3px;cursor:n-resize}.cropper-point.point-w{top:50%;left:-3px;margin-top:-3px;cursor:w-resize}.cropper-point.point-s{bottom:-3px;left:50%;margin-left:-3px;cursor:s-resize}.cropper-point.point-ne{top:-3px;right:-3px;cursor:ne-resize}.cropper-point.point-nw{top:-3px;left:-3px;cursor:nw-resize}.cropper-point.point-sw{bottom:-3px;left:-3px;cursor:sw-resize}.cropper-point.point-se{right:-3px;bottom:-3px;width:20px;height:20px;cursor:se-resize;filter:alpha(opacity=100);opacity:1}.cropper-point.point-se:before{position:absolute;right:-50%;bottom:-50%;display:block;width:200%;height:200%;content:" ";background-color:#69f;filter:alpha(opacity=0);opacity:0}@media (min-width:768px){.cropper-point.point-se{width:15px;height:15px}}@media (min-width:992px){.cropper-point.point-se{width:10px;height:10px}}@media (min-width:1200px){.cropper-point.point-se{width:5px;height:5px;filter:alpha(opacity=75);opacity:.75}}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-invisible{filter:alpha(opacity=0);opacity:0}.cropper-hide{position:fixed;top:0;left:0;z-index:-1;width:auto!important;min-width:0!important;max-width:none!important;height:auto!important;min-height:0!important;max-height:none!important;filter:alpha(opacity=0);opacity:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed} \ No newline at end of file