(function(factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as anonymous module.
define(["jquery"], factory);
} else {
// Browser globals.
factory(scriptJquery);
}
}(function($) {
"use strict";
var $document = $(document),
Cropper = function(element, options) {
options = $.isPlainObject(options) ? options : {};
this.$element = $(element);
this.defaults = $.extend({}, Cropper.defaults, this.$element.data(), options);
this.init();
};
Cropper.prototype = {
construstor: Cropper,
init: function() {
var ratio = this.defaults.aspectRatio;
ratio = typeof ratio !== "number" ? parseInt(ratio, 10) : ratio;
this.defaults.aspectRatio = ratio && ratio > 0 ? ratio : 1;
this.enable();
},
enable: function(url) {
var $element = this.$element;
if (this.active) {
return;
}
url = url || $element.prop("src");
if (!url) {
throw new Error("Invalid image!");
}
this.url = url;
this.$cropper = $(Cropper.template);
this.$dragger = this.$cropper.find(".cropper-dragger");
Cropper.fn.toggle($element);
$element.after(this.$cropper);
if (!this.defaults.modal) {
Cropper.fn.toggle(this.$cropper.find(".cropper-modal"));
}
this.setPreview();
this.setImage();
this.addListener();
this.active = true;
},
disable: function() {
if (!this.active) {
return;
}
this.removeListener();
this.$cropper.empty().remove();
Cropper.fn.toggle(this.$element);
this.$cropper = null;
this.$dragger = null;
this.$preview = null;
this.cropper = null;
this.dragger = null;
this.image = null;
this.url = "";
this.active = false;
},
addListener: function() {
this.$element.on("load", $.proxy(this.load, this));
this.$dragger.on("mousedown", $.proxy(this.mousedown, this));
$document.on("mousemove", $.proxy(this.mousemove, this));
$document.on("mouseup", $.proxy(this.mouseup, this));
},
removeListener: function() {
this.$element.off("load", this.load);
this.$dragger.off("mousedown", this.mousedown);
$document.off("mousemove", this.mousemove);
$document.off("mouseup", this.mouseup);
},
load: function() {
var url;
if (this.active) {
url = this.$element.prop("src");
if (url && url !== this.url) {
this.disable();
this.enable(url);
}
return;
}
this.enable();
},
mousedown: function(e) {
var direction = $(e.target).data().direction;
if (typeof direction === "string" && direction.length > 0) {
this.mouseX1 = e.clientX;
this.mouseY1 = e.clientY;
this.direction = direction;
}
},
mousemove: function(e) {
if (this.direction) {
this.mouseX2 = e.clientX;
this.mouseY2 = e.clientY;
this.dragging();
}
},
mouseup: function() {
this.direction = "";
},
setImage: function() {
var that = this,
$image = $('<img src="' + this.url + '">');
$image.on("load", function() {
var $this = $(this),
image;
if (this.naturalWidth && this.naturalHeight) {
image = {
naturalHeight: this.naturalHeight,
naturalWidth: this.naturalWidth
};
} else {
Cropper.fn.size($this, {
height: "auto",
width: "auto"
});
image = Cropper.fn.size($this);
image = {
naturalHeight: image.height,
naturalWidth: image.width
};
}
Cropper.fn.size($this, {
height: "100%",
width: "100%"
});
that.image = image;
that.setCropper();
});
this.$cropper.prepend($image);
},
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('<img src="' + this.url + '">');
},
setCropper: function() {
var $container = this.$element.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.naturalHeight / image.naturalWidth,
width: container.width,
left: 0
};
cropper.top = (container.height - cropper.height) / 2;
} else {
cropper = {
height: container.height,
width: container.height * image.naturalWidth / image.naturalHeight,
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,
ratio = this.defaults.aspectRatio,
dragger;
if (((cropper.height * ratio) - cropper.width) >= 0) {
dragger = {
height: cropper.width / ratio,
width: cropper.width,
left: 0,
top: (cropper.height - (cropper.width / ratio)) / 2,
maxWidth: cropper.width,
maxHeight: cropper.width / ratio
};
} else {
dragger = {
height: cropper.height,
width: cropper.height * ratio,
left: (cropper.width - (cropper.height * ratio)) / 2,
top: 0,
maxHeight: cropper.height,
maxWidth: cropper.height * ratio
};
}
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.resetDragger();
},
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,
ratio = this.defaults.aspectRatio,
range = {
x: this.mouseX2 - this.mouseX1,
y: this.mouseY2 - this.mouseY1
};
range.X = range.y * ratio;
range.Y = range.x / ratio;
switch (direction) {
// dragging
case "e":
dragger.width += range.x;
dragger.height = dragger.width / ratio;
dragger.top -= range.Y / 2;
if (dragger.width < 0) {
this.direction = "w";
dragger.width = 0;
}
break;
case "n":
dragger.height -= range.y;
dragger.width = dragger.height * ratio;
dragger.left += range.X / 2;
dragger.top += range.y;
if (dragger.height < 0) {
this.direction = "s";
dragger.height = 0;
}
break;
case "w":
dragger.width -= range.x;
dragger.height = dragger.width / ratio;
dragger.left += range.x;
dragger.top += range.Y / 2;
if (dragger.width < 0) {
this.direction = "e";
dragger.width = 0;
}
break;
case "s":
dragger.height += range.y;
dragger.width = dragger.height * ratio;
dragger.left -= range.X / 2;
if (dragger.height < 0) {
this.direction = "n";
dragger.height = 0;
}
break;
case "ne":
dragger.height -= range.y;
dragger.width = dragger.height * ratio;
dragger.top += range.y;
if (dragger.height < 0) {
this.direction = "sw";
dragger.height = 0;
dragger.width = 0;
}
break;
case "nw":
dragger.height -= range.y;
dragger.width = dragger.height * ratio;
dragger.left += range.X;
dragger.top += range.y;
if (dragger.height < 0) {
this.direction = "se";
dragger.height = 0;
dragger.width = 0;
}
break;
case "sw":
dragger.width -= range.x;
dragger.height = dragger.width / ratio;
dragger.left += range.x;
if (dragger.width < 0) {
this.direction = "ne";
dragger.height = 0;
dragger.width = 0;
}
break;
case "se":
dragger.width += range.x;
dragger.height = dragger.width / ratio;
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.mouseX1 = this.mouseX2;
this.mouseY1 = this.mouseY2;
},
output: function() {
var ratio = this.image.ratio,
dragger = this.dragger,
data = {
x1: dragger.left,
y1: dragger.top,
x2: dragger.left + dragger.width,
y2: dragger.top + dragger.height,
height: dragger.height,
width: dragger.width
};
this.defaults.done(Cropper.fn.round(data, function(n) {
return n / ratio;
}));
},
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;
}));
});
}
};
// 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;
}
};
Cropper.template = [
'<div class="cropper-container">',
'<div class="cropper-modal"></div>',
'<div class="cropper-dragger">',
'<span class="cropper-preview"></span>',
'<span class="cropper-dashed dashed-h"></span>',
'<span class="cropper-dashed dashed-v"></span>',
'<span class="cropper-face" data-direction="*"></span>',
'<span class="cropper-line line-e" data-direction="e"></span>',
'<span class="cropper-line line-n" data-direction="n"></span>',
'<span class="cropper-line line-w" data-direction="w"></span>',
'<span class="cropper-line line-s" data-direction="s"></span>',
'<span class="cropper-point point-e" data-direction="e"></span>',
'<span class="cropper-point point-n" data-direction="n"></span>',
'<span class="cropper-point point-w" data-direction="w"></span>',
'<span class="cropper-point point-s" data-direction="s"></span>',
'<span class="cropper-point point-ne" data-direction="ne"></span>',
'<span class="cropper-point point-nw" data-direction="nw"></span>',
'<span class="cropper-point point-sw" data-direction="sw"></span>',
'<span class="cropper-point point-se" data-direction="se"></span>',
'</div>',
'</div>'
].join("");
Cropper.defaults = {
aspectRatio: 1,
done: function(/* data */) {},
modal: true,
preview: ""
};
Cropper.setDefaults = function(options) {
$.extend(Cropper.defaults, options);
};
// Register as jQuery plugin
$.fn.cropper = function(options) {
return this.each(function() {
var $this = $(this),
data = $this.data("cropper");
if (!data) {
data = new Cropper(this, options);
$this.data("cropper", data);
}
if (typeof options === "string" && $.isFunction(data[options])) {
data[options]();
}
});
};
$.fn.cropper.Constructor = Cropper;
$.fn.cropper.setDefaults = Cropper.setDefaults;
$(function() {
$("img[cropper]").cropper();
});
}));