/*! * 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 = [ '