/*!
* Cropper v0.3.2
* https://github.com/fengyuanchen/cropper
*
* Copyright 2014 Fengyuan Chen
* Released under the MIT license
*/
(function(factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as anonymous module.
define(["jquery"], factory);
} else {
// Browser globals.
factory(jQuery);
}
}(function($) {
"use strict";
var $window = $(window),
Cropper = function(element, options) {
options = $.isPlainObject(options) ? options : {};
this.$image = $(element);
this.defaults = $.extend({}, Cropper.defaults, this.$image.data(), options);
this.init();
};
Cropper.prototype = {
construstor: Cropper,
init: function() {
this.setAspectRatio(this.defaults.aspectRatio);
this.render();
},
render: function(callback) {
var that = this,
$image = this.$image,
$clone,
src;
if (this.active) {
return;
}
if (this.$clone) {
this.$clone.remove(); // Remove the old clone
}
src = $image.attr("src"); // Don't use "prop"
$clone = $('');
$clone.on("load", function() {
var image;
$clone.off("load");
if (this.naturalWidth && this.naturalHeight) {
image = {
naturalHeight: this.naturalHeight,
naturalWidth: this.naturalWidth
};
} else {
Cropper.fn.size($clone, {
height: "auto",
width: "auto"
});
image = Cropper.fn.size($clone);
image = {
naturalHeight: image.height,
naturalWidth: image.width
};
}
Cropper.fn.size($clone, {
height: "100%",
width: "100%"
});
image.aspectRatio = image.naturalWidth / image.naturalHeight;
that.src = src;
that.image = image;
that.active = true;
that.createCropper();
});
if ($.isFunction(callback)) {
$image.on("ready.cropper", callback);
}
this.$clone = $clone;
$image.after($clone);
},
unrender: function() {
if (this.active) {
this.active = false;
this.removeCropper();
this.src = "";
this.image = null;
this.cropper = null;
this.dragger = null;
}
},
rerender: function() {
this.unrender();
this.render();
},
resize: function() {
clearTimeout(this.resizing);
this.resizing = setTimeout($.proxy(this.rerender, this), 200);
},
createCropper: function() {
this.$cropper = $(Cropper.template);
this.$dragger = this.$cropper.find(".cropper-dragger");
Cropper.fn.toggle(this.$image);
this.$image.after(this.$cropper);
this.$cropper.prepend(this.$clone);
if (!this.defaults.modal) {
Cropper.fn.toggle(this.$cropper.find(".cropper-modal"));
}
this.setPreview();
this.addListener();
},
removeCropper: function() {
this.removeListener();
this.$preview = null;
this.$clone.remove();
this.$clone = null;
this.$dragger = null;
this.$cropper.remove();
this.$cropper = null;
Cropper.fn.toggle(this.$image);
},
addListener: function() {
this.$cropper.bind("mousedown touchstart", $.proxy(this.dragstart, this));
this.$cropper.bind("mousemove touchmove", $.proxy(this.dragmove, this));
this.$cropper.bind("mouseup mouseleave touchend touchleave", $.proxy(this.dragend, this));
$window.on("resize", $.proxy(this.resize, this));
},
removeListener: function() {
this.$cropper.unbind("mousedown touchstart", this.dragstart);
this.$cropper.unbind("mousemove touchmove", this.dragmove);
this.$cropper.unbind("mouseup mouseleave touchend touchleave", this.dragend);
$window.off("resize", this.resize);
},
setPreview: function() {
var preview = this.defaults.preview;
this.$preview = this.$cropper.find(".cropper-preview");
if (typeof preview === "string" && preview.length > 0) {
this.$preview = this.$preview.add(preview);
}
this.$preview.html('
');
this.setCropper();
},
setCropper: function() {
var $container = this.$image.parent(),
container = Cropper.fn.size($container),
image = this.image,
cropper;
if (((image.naturalWidth * container.height / image.naturalHeight) - container.width) >= 0) {
cropper = {
height: container.width / image.aspectRatio,
width: container.width,
left: 0
};
cropper.top = (container.height - cropper.height) / 2;
} else {
cropper = {
height: container.height,
width: container.height * image.aspectRatio,
top: 0
};
cropper.left = (container.width - cropper.width) / 2;
}
$.each(cropper, function(i, n) {
cropper[i] = Math.round(n);
});
image.height = cropper.height;
image.width = cropper.width;
image.ratio = image.width / image.naturalWidth;
Cropper.fn.position($container);
this.$cropper.css({
height: cropper.height,
left: cropper.left,
top: cropper.top,
width: cropper.width
});
this.cropper = cropper;
this.setDragger();
},
setDragger: function() {
var cropper = this.cropper,
// If not set, use the original aspect ratio of the image.
aspectRatio = this.defaults.aspectRatio || this.image.aspectRatio,
dragger;
if (((cropper.height * aspectRatio) - cropper.width) >= 0) {
dragger = {
height: cropper.width / aspectRatio,
width: cropper.width,
left: 0,
top: (cropper.height - (cropper.width / aspectRatio)) / 2,
maxWidth: cropper.width,
maxHeight: cropper.width / aspectRatio
};
} else {
dragger = {
height: cropper.height,
width: cropper.height * aspectRatio,
left: (cropper.width - (cropper.height * aspectRatio)) / 2,
top: 0,
maxHeight: cropper.height,
maxWidth: cropper.height * aspectRatio
};
}
dragger.height *= 0.8;
dragger.width *= 0.8;
dragger.left = (cropper.width - dragger.width) / 2;
dragger.top = (cropper.height - dragger.height) / 2;
this.dragger = Cropper.fn.round(dragger);
this.setData(this.defaults.data);
this.$image.trigger("ready.cropper").off("ready.cropper");
},
resetDragger: function() {
var dragger = this.dragger,
cropper = this.cropper;
dragger.width = dragger.width > dragger.maxWidth ? dragger.maxWidth : Math.abs(dragger.width);
dragger.height = dragger.height > dragger.maxHeight ? dragger.maxHeight : Math.abs(dragger.height);
dragger.maxLeft = cropper.width - dragger.width;
dragger.maxTop = cropper.height - dragger.height;
dragger.left = dragger.left < 0 ? 0 : dragger.left > dragger.maxLeft ? dragger.maxLeft : dragger.left;
dragger.top = dragger.top < 0 ? 0 : dragger.top > dragger.maxTop ? dragger.maxTop : dragger.top;
dragger = Cropper.fn.round(dragger);
this.$dragger.css({
height: dragger.height,
left: dragger.left,
top: dragger.top,
width: dragger.width
});
this.dragger = dragger;
this.preview();
this.output();
},
dragging: function() {
var direction = this.direction,
dragger = this.dragger,
aspectRatio = this.defaults.aspectRatio,
range = {
x: this.endX - this.startX,
y: this.endY - this.startY
};
if (aspectRatio) {
range.X = range.y * aspectRatio;
range.Y = range.x / aspectRatio;
}
switch (direction) {
// dragging
case "e":
dragger.width += range.x;
if (aspectRatio) {
dragger.height = dragger.width / aspectRatio;
dragger.top -= range.Y / 2;
}
if (dragger.width < 0) {
this.direction = "w";
dragger.width = 0;
}
break;
case "n":
dragger.height -= range.y;
dragger.top += range.y;
if (aspectRatio) {
dragger.width = dragger.height * aspectRatio;
dragger.left += range.X / 2;
}
if (dragger.height < 0) {
this.direction = "s";
dragger.height = 0;
}
break;
case "w":
dragger.width -= range.x;
dragger.left += range.x;
if (aspectRatio) {
dragger.height = dragger.width / aspectRatio;
dragger.top += range.Y / 2;
}
if (dragger.width < 0) {
this.direction = "e";
dragger.width = 0;
}
break;
case "s":
dragger.height += range.y;
if (aspectRatio) {
dragger.width = dragger.height * aspectRatio;
dragger.left -= range.X / 2;
}
if (dragger.height < 0) {
this.direction = "n";
dragger.height = 0;
}
break;
case "ne":
dragger.height -= range.y;
dragger.top += range.y;
if (aspectRatio) {
dragger.width = dragger.height * aspectRatio;
} else {
dragger.width += range.x;
}
if (dragger.height < 0) {
this.direction = "sw";
dragger.height = 0;
dragger.width = 0;
}
break;
case "nw":
dragger.height -= range.y;
dragger.top += range.y;
if (aspectRatio) {
dragger.width = dragger.height * aspectRatio;
dragger.left += range.X;
} else {
dragger.width -= range.x;
dragger.left += range.x;
}
if (dragger.height < 0) {
this.direction = "se";
dragger.height = 0;
dragger.width = 0;
}
break;
case "sw":
dragger.width -= range.x;
dragger.left += range.x;
if (aspectRatio) {
dragger.height = dragger.width / aspectRatio;
} else {
dragger.height += range.y;
}
if (dragger.width < 0) {
this.direction = "ne";
dragger.height = 0;
dragger.width = 0;
}
break;
case "se":
dragger.width += range.x;
if (aspectRatio) {
dragger.height = dragger.width / aspectRatio;
} else {
dragger.height += range.y;
}
if (dragger.width < 0) {
this.direction = "nw";
dragger.height = 0;
dragger.width = 0;
}
break;
// moving
default:
dragger.left += range.x;
dragger.top += range.y;
}
this.resetDragger();
this.startX = this.endX;
this.startY = this.endY;
},
output: function() {
this.defaults.done(this.getData());
},
preview: function() {
var that = this,
cropper = that.cropper,
dragger = that.dragger;
this.$preview.each(function() {
var $this = $(this),
ratio = $this.outerWidth() / dragger.width,
styles = {
height: cropper.height,
marginLeft: - dragger.left,
marginTop: - dragger.top,
width: cropper.width
};
$this.css({overflow: "hidden"});
$this.find("img").css(Cropper.fn.round(styles, function(n) {
return n * ratio;
}));
});
},
// Public methods
enable: function(callback) {
this.render(callback);
},
disable: function() {
this.unrender();
},
setAspectRatio: function(aspectRatio) {
if (aspectRatio === "auto" || ($.isNumeric(aspectRatio) && aspectRatio > 0)) {
this.defaults.aspectRatio = aspectRatio === "auto" ? NaN : aspectRatio;
if (this.active) {
this.setDragger();
}
}
},
setData: function(data) {
var cropper = this.cropper,
dragger = this.dragger,
aspectRatio = this.defaults.aspectRatio,
isNumber = function(n) {
return typeof n === "number";
};
if (!this.active) {
return;
}
if ($.isPlainObject(data) && !$.isEmptyObject(data)) {
data = Cropper.fn.transformData(data, this.image.ratio);
if (isNumber(data.x1) && data.x1 <= cropper.width) {
dragger.left = data.x1;
}
if (isNumber(data.y1) && data.y1 <= cropper.height) {
dragger.top = data.y1;
}
if (aspectRatio){
if (isNumber(data.width) && data.width <= cropper.width) {
dragger.width = data.width;
dragger.height = dragger.width / aspectRatio;
} else if (isNumber(data.height) && data.height <= cropper.height) {
dragger.height = data.height;
dragger.width = dragger.height * aspectRatio;
} else if (isNumber(data.x2) && data.x2 <= cropper.width) {
dragger.width = data.x2 - dragger.left;
dragger.height = dragger.width / aspectRatio;
} else if (isNumber(data.y2) && data.y2 <= cropper.height) {
dragger.height = data.y2 - dragger.top;
dragger.width = dragger.height * aspectRatio;
}
} else {
if (isNumber(data.width) && data.width <= cropper.width) {
dragger.width = data.width;
} else if (isNumber(data.x2) && data.x2 <= cropper.width) {
dragger.width = data.x2 - dragger.left;
}
if (isNumber(data.height) && data.height <= cropper.height) {
dragger.height = data.height;
} else if (isNumber(data.y2) && data.height <= cropper.height) {
dragger.height = data.y2 - dragger.top;
}
}
}
this.dragger = dragger;
this.resetDragger();
},
getData: function() {
var dragger = this.dragger,
data = {};
if (this.active) {
data = {
x1: dragger.left,
y1: dragger.top,
width: dragger.width,
height: dragger.height,
x2: dragger.left + dragger.width,
y2: dragger.top + dragger.height
};
data = Cropper.fn.transformData(data, (1 / this.image.ratio));
}
return data;
},
setImgSrc: function(src) {
if (typeof src === "string" && src.length > 0 && src !== this.src) {
this.$image.attr("src", src);
this.rerender();
}
},
getImgInfo: function() {
return this.image || {};
},
// Public events
dragstart: function(event) {
var touches = Cropper.fn.getOriginalEvent(event).touches,
e = event,
touching,
direction;
if (touches && touches.length === 1) {
e = touches[0];
this.touchId = e.identifier;
touching = true;
}
direction = $(e.target).data().direction;
if (Cropper.fn.isDirection(direction)) {
this.startX = e.pageX;
this.startY = e.pageY;
this.direction = direction;
this.$image.trigger("dragstart");
touching && event.preventDefault();
}
},
dragmove: function(event) {
var touches = Cropper.fn.getOriginalEvent(event).changedTouches,
e = event,
touching;
if (touches && touches.length === 1) {
e = touches[0];
touching = true;
if (e.identifier !== this.touchId) {
return;
}
}
if (this.direction) {
this.$image.trigger("dragmove");
touching && event.preventDefault();
this.endX = e.pageX;
this.endY = e.pageY;
this.dragging();
}
},
dragend: function(event) {
var touches = Cropper.fn.getOriginalEvent(event).changedTouches,
e = event,
touching;
if (touches && touches.length === 1) {
e = touches[0];
touching = true;
if (e.identifier !== this.touchId) {
return;
}
}
if (this.direction) {
this.direction = "";
this.$image.trigger("dragend");
touching && event.preventDefault();
}
}
};
// Common methods
Cropper.fn = {
toggle: function($e) {
$e.toggleClass("cropper-hidden");
},
position: function($e, option) {
var position = $e.css("position");
if (position === "static") {
$e.css("position", option || "relative");
}
},
size: function($e, options) {
if ($.isPlainObject(options)) {
$e.css(options);
} else {
return {
height: $e.height(),
width: $e.width()
};
}
},
round: function(data, fn) {
var value,
i;
for (i in data) {
value = data[i];
if (data.hasOwnProperty(i) && typeof value === "number") {
data[i] = Math.round($.isFunction(fn) ? fn(value) : value);
}
}
return data;
},
transformData: function(data, ratio) {
var that = this,
result = {};
$.each(data, function(i, n) {
if (that.isDataOption(i) && $.isNumeric(n) && n >= 0) {
result[i] = Math.round(n * ratio);
}
});
return result;
},
getOriginalEvent: function(event) {
if (event && typeof event.originalEvent !== "undefined") {
event = event.originalEvent;
}
return event;
},
isDataOption: function(s) {
return /^(x1|y1|x2|y2|width|height)$/i.test(s);
},
isDirection: function(s) {
return /^(\*|e|n|w|s|ne|nw|sw|se)$/i.test(s);
}
};
Cropper.template = [
'