791 lines
19 KiB
Plaintext
791 lines
19 KiB
Plaintext
// Show the crop box manually
|
|
crop: function () {
|
|
if (!this.isBuilt || this.isDisabled) {
|
|
return;
|
|
}
|
|
|
|
if (!this.isCropped) {
|
|
this.isCropped = 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.isBuilt || this.isDisabled) {
|
|
return;
|
|
}
|
|
|
|
this.image = $.extend({}, this.initialImage);
|
|
this.canvas = $.extend({}, this.initialCanvas);
|
|
this.cropBox = $.extend({}, this.initialCropBox);
|
|
|
|
this.renderCanvas();
|
|
|
|
if (this.isCropped) {
|
|
this.renderCropBox();
|
|
}
|
|
},
|
|
|
|
// Clear the crop box
|
|
clear: function () {
|
|
if (!this.isCropped || this.isDisabled) {
|
|
return;
|
|
}
|
|
|
|
$.extend(this.cropBox, {
|
|
left: 0,
|
|
top: 0,
|
|
width: 0,
|
|
height: 0
|
|
});
|
|
|
|
this.isCropped = false;
|
|
this.renderCropBox();
|
|
|
|
this.limitCanvas(true, true);
|
|
|
|
// 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
|
|
* @param {Boolean} onlyColorChanged (optional)
|
|
*/
|
|
replace: function (url, onlyColorChanged) {
|
|
if (!this.isDisabled && url) {
|
|
if (this.isImg) {
|
|
this.$element.attr('src', url);
|
|
}
|
|
|
|
if (onlyColorChanged) {
|
|
this.url = url;
|
|
this.$clone.attr('src', url);
|
|
|
|
if (this.isBuilt) {
|
|
this.$preview.find('img').add(this.$clone2).attr('src', url);
|
|
}
|
|
} else {
|
|
if (this.isImg) {
|
|
this.isReplaced = true;
|
|
}
|
|
|
|
// Clear previous data
|
|
this.options.data = null;
|
|
this.load(url);
|
|
}
|
|
}
|
|
},
|
|
|
|
// Enable (unfreeze) the cropper
|
|
enable: function () {
|
|
if (this.isBuilt) {
|
|
this.isDisabled = false;
|
|
this.$cropper.removeClass(CLASS_DISABLED);
|
|
}
|
|
},
|
|
|
|
// Disable (freeze) the cropper
|
|
disable: function () {
|
|
if (this.isBuilt) {
|
|
this.isDisabled = true;
|
|
this.$cropper.addClass(CLASS_DISABLED);
|
|
}
|
|
},
|
|
|
|
// Destroy the cropper and remove the instance from the image
|
|
destroy: function () {
|
|
var $this = this.$element;
|
|
|
|
if (this.isLoaded) {
|
|
if (this.isImg && this.isReplaced) {
|
|
$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 with relative offsets
|
|
*
|
|
* @param {Number} offsetX
|
|
* @param {Number} offsetY (optional)
|
|
*/
|
|
move: function (offsetX, offsetY) {
|
|
var canvas = this.canvas;
|
|
|
|
this.moveTo(
|
|
isUndefined(offsetX) ? offsetX : canvas.left + num(offsetX),
|
|
isUndefined(offsetY) ? offsetY : canvas.top + num(offsetY)
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Move the canvas to an absolute point
|
|
*
|
|
* @param {Number} x
|
|
* @param {Number} y (optional)
|
|
*/
|
|
moveTo: function (x, y) {
|
|
var canvas = this.canvas;
|
|
var isChanged = false;
|
|
|
|
// If "y" is not present, its default value is "x"
|
|
if (isUndefined(y)) {
|
|
y = x;
|
|
}
|
|
|
|
x = num(x);
|
|
y = num(y);
|
|
|
|
if (this.isBuilt && !this.isDisabled && this.options.movable) {
|
|
if (isNumber(x)) {
|
|
canvas.left = x;
|
|
isChanged = true;
|
|
}
|
|
|
|
if (isNumber(y)) {
|
|
canvas.top = y;
|
|
isChanged = true;
|
|
}
|
|
|
|
if (isChanged) {
|
|
this.renderCanvas(true);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Zoom the canvas with a relative ratio
|
|
*
|
|
* @param {Number} ratio
|
|
* @param {jQuery Event} _event (private)
|
|
*/
|
|
zoom: function (ratio, _event) {
|
|
var canvas = this.canvas;
|
|
|
|
ratio = num(ratio);
|
|
|
|
if (ratio < 0) {
|
|
ratio = 1 / (1 - ratio);
|
|
} else {
|
|
ratio = 1 + ratio;
|
|
}
|
|
|
|
this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _event);
|
|
},
|
|
|
|
/**
|
|
* Zoom the canvas to an absolute ratio
|
|
*
|
|
* @param {Number} ratio
|
|
* @param {jQuery Event} _event (private)
|
|
*/
|
|
zoomTo: function (ratio, _event) {
|
|
var options = this.options;
|
|
var canvas = this.canvas;
|
|
var width = canvas.width;
|
|
var height = canvas.height;
|
|
var naturalWidth = canvas.naturalWidth;
|
|
var naturalHeight = canvas.naturalHeight;
|
|
var originalEvent;
|
|
var newWidth;
|
|
var newHeight;
|
|
var offset;
|
|
var center;
|
|
|
|
ratio = num(ratio);
|
|
|
|
if (ratio >= 0 && this.isBuilt && !this.isDisabled && options.zoomable) {
|
|
newWidth = naturalWidth * ratio;
|
|
newHeight = naturalHeight * ratio;
|
|
|
|
if (_event) {
|
|
originalEvent = _event.originalEvent;
|
|
}
|
|
|
|
if (this.trigger(EVENT_ZOOM, {
|
|
originalEvent: originalEvent,
|
|
oldRatio: width / naturalWidth,
|
|
ratio: newWidth / naturalWidth
|
|
}).isDefaultPrevented()) {
|
|
return;
|
|
}
|
|
|
|
if (originalEvent) {
|
|
offset = this.$cropper.offset();
|
|
center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : {
|
|
pageX: _event.pageX || originalEvent.pageX || 0,
|
|
pageY: _event.pageY || originalEvent.pageY || 0
|
|
};
|
|
|
|
// Zoom from the triggering point of the event
|
|
canvas.left -= (newWidth - width) * (
|
|
((center.pageX - offset.left) - canvas.left) / width
|
|
);
|
|
canvas.top -= (newHeight - height) * (
|
|
((center.pageY - offset.top) - canvas.top) / height
|
|
);
|
|
} else {
|
|
|
|
// Zoom from the center of the canvas
|
|
canvas.left -= (newWidth - width) / 2;
|
|
canvas.top -= (newHeight - height) / 2;
|
|
}
|
|
|
|
canvas.width = newWidth;
|
|
canvas.height = newHeight;
|
|
this.renderCanvas(true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Rotate the canvas with a relative degree
|
|
*
|
|
* @param {Number} degree
|
|
*/
|
|
rotate: function (degree) {
|
|
this.rotateTo((this.image.rotate || 0) + num(degree));
|
|
},
|
|
|
|
/**
|
|
* Rotate the canvas to an absolute degree
|
|
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
|
|
*
|
|
* @param {Number} degree
|
|
*/
|
|
rotateTo: function (degree) {
|
|
degree = num(degree);
|
|
|
|
if (isNumber(degree) && this.isBuilt && !this.isDisabled && this.options.rotatable) {
|
|
this.image.rotate = degree % 360;
|
|
this.isRotated = 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;
|
|
var isChanged = false;
|
|
|
|
// If "scaleY" is not present, its default value is "scaleX"
|
|
if (isUndefined(scaleY)) {
|
|
scaleY = scaleX;
|
|
}
|
|
|
|
scaleX = num(scaleX);
|
|
scaleY = num(scaleY);
|
|
|
|
if (this.isBuilt && !this.isDisabled && this.options.scalable) {
|
|
if (isNumber(scaleX)) {
|
|
image.scaleX = scaleX;
|
|
isChanged = true;
|
|
}
|
|
|
|
if (isNumber(scaleY)) {
|
|
image.scaleY = scaleY;
|
|
isChanged = true;
|
|
}
|
|
|
|
if (isChanged) {
|
|
this.renderImage(true);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Scale the abscissa of the image
|
|
*
|
|
* @param {Number} scaleX
|
|
*/
|
|
scaleX: function (scaleX) {
|
|
var scaleY = this.image.scaleY;
|
|
|
|
this.scale(scaleX, isNumber(scaleY) ? scaleY : 1);
|
|
},
|
|
|
|
/**
|
|
* Scale the ordinate of the image
|
|
*
|
|
* @param {Number} scaleY
|
|
*/
|
|
scaleY: function (scaleY) {
|
|
var scaleX = this.image.scaleX;
|
|
|
|
this.scale(isNumber(scaleX) ? scaleX : 1, scaleY);
|
|
},
|
|
|
|
/**
|
|
* Get the cropped area position and size data (base on the original image)
|
|
*
|
|
* @param {Boolean} isRounded (optional)
|
|
* @return {Object} data
|
|
*/
|
|
getData: function (isRounded) {
|
|
var options = this.options;
|
|
var image = this.image;
|
|
var canvas = this.canvas;
|
|
var cropBox = this.cropBox;
|
|
var ratio;
|
|
var data;
|
|
|
|
if (this.isBuilt && this.isCropped) {
|
|
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] = isRounded ? 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 options = this.options;
|
|
var image = this.image;
|
|
var canvas = this.canvas;
|
|
var cropBoxData = {};
|
|
var isRotated;
|
|
var isScaled;
|
|
var ratio;
|
|
|
|
if ($.isFunction(data)) {
|
|
data = data.call(this.element);
|
|
}
|
|
|
|
if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
|
|
if (options.rotatable) {
|
|
if (isNumber(data.rotate) && data.rotate !== image.rotate) {
|
|
image.rotate = data.rotate;
|
|
this.isRotated = isRotated = true;
|
|
}
|
|
}
|
|
|
|
if (options.scalable) {
|
|
if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {
|
|
image.scaleX = data.scaleX;
|
|
isScaled = true;
|
|
}
|
|
|
|
if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {
|
|
image.scaleY = data.scaleY;
|
|
isScaled = true;
|
|
}
|
|
}
|
|
|
|
if (isRotated) {
|
|
this.renderCanvas();
|
|
} else if (isScaled) {
|
|
this.renderImage();
|
|
}
|
|
|
|
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.isBuilt ? this.container : {};
|
|
},
|
|
|
|
/**
|
|
* Get the image position and size data
|
|
*
|
|
* @return {Object} data
|
|
*/
|
|
getImageData: function () {
|
|
return this.isLoaded ? this.image : {};
|
|
},
|
|
|
|
/**
|
|
* Get the canvas position and size data
|
|
*
|
|
* @return {Object} data
|
|
*/
|
|
getCanvasData: function () {
|
|
var canvas = this.canvas;
|
|
var data = {};
|
|
|
|
if (this.isBuilt) {
|
|
$.each([
|
|
'left',
|
|
'top',
|
|
'width',
|
|
'height',
|
|
'naturalWidth',
|
|
'naturalHeight'
|
|
], function (i, n) {
|
|
data[n] = canvas[n];
|
|
});
|
|
}
|
|
|
|
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.isBuilt && !this.isDisabled && $.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.isBuilt && this.isCropped) {
|
|
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 isWidthChanged;
|
|
var isHeightChanged;
|
|
|
|
if ($.isFunction(data)) {
|
|
data = data.call(this.$element);
|
|
}
|
|
|
|
if (this.isBuilt && this.isCropped && !this.isDisabled && $.isPlainObject(data)) {
|
|
|
|
if (isNumber(data.left)) {
|
|
cropBox.left = data.left;
|
|
}
|
|
|
|
if (isNumber(data.top)) {
|
|
cropBox.top = data.top;
|
|
}
|
|
|
|
if (isNumber(data.width)) {
|
|
isWidthChanged = true;
|
|
cropBox.width = data.width;
|
|
}
|
|
|
|
if (isNumber(data.height)) {
|
|
isHeightChanged = true;
|
|
cropBox.height = data.height;
|
|
}
|
|
|
|
if (aspectRatio) {
|
|
if (isWidthChanged) {
|
|
cropBox.height = cropBox.width / aspectRatio;
|
|
} else if (isHeightChanged) {
|
|
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.isBuilt || !SUPPORT_CANVAS) {
|
|
return;
|
|
}
|
|
|
|
if (!this.isCropped) {
|
|
return getSourceCanvas(this.$clone[0], this.image);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// The canvas element will use `Math.floor` on a float number, so floor first
|
|
canvasWidth = floor(scaledWidth || originalWidth);
|
|
canvasHeight = floor(scaledHeight || originalHeight);
|
|
|
|
canvas = $('<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 canvas = this.canvas;
|
|
var params = [source];
|
|
|
|
// Source canvas
|
|
var srcX = data.x + canvas.naturalWidth * (abs(data.scaleX || 1) - 1) / 2;
|
|
var srcY = data.y + canvas.naturalHeight * (abs(data.scaleY || 1) - 1) / 2;
|
|
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);
|
|
}
|
|
|
|
// All the numerical parameters should be integer for `drawImage` (#476)
|
|
params.push(floor(srcX), floor(srcY), floor(srcWidth), floor(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) {
|
|
params.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));
|
|
}
|
|
|
|
return params;
|
|
}).call(this));
|
|
|
|
return canvas;
|
|
},
|
|
|
|
/**
|
|
* Change the aspect ratio of the crop box
|
|
*
|
|
* @param {Number} aspectRatio
|
|
*/
|
|
setAspectRatio: function (aspectRatio) {
|
|
var options = this.options;
|
|
|
|
if (!this.isDisabled && !isUndefined(aspectRatio)) {
|
|
|
|
// 0 -> NaN
|
|
options.aspectRatio = max(0, aspectRatio) || NaN;
|
|
|
|
if (this.isBuilt) {
|
|
this.initCropBox();
|
|
|
|
if (this.isCropped) {
|
|
this.renderCropBox();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Change the drag mode
|
|
*
|
|
* @param {String} mode (optional)
|
|
*/
|
|
setDragMode: function (mode) {
|
|
var options = this.options;
|
|
var croppable;
|
|
var movable;
|
|
|
|
if (this.isLoaded && !this.isDisabled) {
|
|
croppable = mode === ACTION_CROP;
|
|
movable = options.movable && mode === ACTION_MOVE;
|
|
mode = (croppable || movable) ? mode : ACTION_NONE;
|
|
|
|
this.$dragBox.
|
|
data(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(DATA_ACTION, mode).
|
|
toggleClass(CLASS_CROP, croppable).
|
|
toggleClass(CLASS_MOVE, movable);
|
|
}
|
|
}
|
|
}
|
|
};
|