1(function() { 2 var supportsCanvas = document.createElement('canvas'); 3 supportsCanvas = !!(supportsCanvas.getContext && supportsCanvas.getContext('2d')); 4 5 // helper functions 6 function is_touch_device() { 7 return 'ontouchstart' in window || // works on most browsers 8 'onmsgesturechange' in window; // works on ie10 9 } 10 11 function fill(value, target, container) { 12 if (value + target < container) 13 value = container - target; 14 return value > 0 ? 0 : value; 15 } 16 17 function uri2blob(dataURI) { 18 var uriComponents = dataURI.split(','); 19 var byteString = atob(uriComponents[1]); 20 var mimeString = uriComponents[0].split(':')[1].split(';')[0]; 21 var ab = new ArrayBuffer(byteString.length); 22 var ia = new Uint8Array(ab); 23 for (var i = 0; i < byteString.length; i++) 24 ia[i] = byteString.charCodeAt(i); 25 return new Blob([ab], { type: mimeString }); 26 } 27 28 var pluginName = 'cropbox'; 29 30 function factory($) { 31 function Crop($image, options, on_load) { 32 this.width = null; 33 this.height = null; 34 this.img_width = null; 35 this.img_height = null; 36 this.img_left = 0; 37 this.img_top = 0; 38 this.minPercent = null; 39 this.options = options; 40 this.$image = $image; 41 this.$image.hide().prop('draggable', false).addClass('cropImage').wrap('<div class="cropFrame" />'); // wrap image in frame; 42 this.$frame = this.$image.parent(); 43 this.on_load = on_load || function() {}; 44 this.init(); 45 } 46 47 Crop.prototype = { 48 init: function () { 49 var self = this; 50 51 var defaultControls = $('<div/>', { 'class' : 'cropControls' }) 52 .append($('<span>'+this.options.label+'</span>')) 53 .append($('<button/>', { 'class' : 'cropZoomIn', 'type':'button' }).on('click', $.proxy(this.zoomIn, this))) 54 .append($('<button/>', { 'class' : 'cropZoomOut', 'type':'button' }).on('click', $.proxy(this.zoomOut, this))); 55 56 this.$frame.append(this.options.controls || defaultControls); 57 this.updateOptions(); 58 59 if (typeof $.fn.hammer === 'function' || typeof Hammer !== 'undefined') { 60 var hammerit, dragData; 61 if (typeof $.fn.hammer === 'function') 62 hammerit = this.$image.hammer().data('hammer'); // Get the hammer instance after it has been created. 63 else 64 hammerit = Hammer(this.$image.get(0)); 65 // Enable panning in all directions without any threshold. 66 hammerit.get('pan').set({ direction: Hammer.DIRECTION_ALL, threshold: 0 }); 67 // Enable pinching. 68 hammerit.get('pinch').set({ enable: true }); 69 hammerit.on('panleft panright panup pandown', function(e) { 70 if (!dragData) 71 dragData = { 72 startX: self.img_left, 73 startY: self.img_top // Some IE versions complained about the extra comma. 74 }; 75 dragData.dx = e.deltaX; 76 dragData.dy = e.deltaY; 77 e.preventDefault(); 78 self.drag.call(self, dragData, true); 79 }).on('panend pancancel', function(e) { 80 e.preventDefault(); 81 dragData = null; 82 self.update.call(self); 83 }).on('doubletap', function(e) { 84 e.preventDefault(); 85 self.zoomIn.call(self); 86 }).on('pinchin', function (e) { 87 e.preventDefault(); 88 self.zoomOut.call(self); 89 }).on('pinchout', function (e) { 90 e.preventDefault(); 91 self.zoomIn.call(self); 92 }); 93 } else { 94 // prevent IE8's default drag functionality 95 this.$image.on("dragstart", function () { return false; }); 96 this.$image.on('mousedown.' + pluginName, function(e1) { 97 var dragData = { 98 startX: self.img_left, 99 startY: self.img_top 100 }; 101 e1.preventDefault(); 102 $(document).on('mousemove.' + pluginName, function (e2) { 103 dragData.dx = e2.pageX - e1.pageX; 104 dragData.dy = e2.pageY - e1.pageY; 105 self.drag.call(self, dragData, true); 106 }).on('mouseup.' + pluginName, function() { 107 self.update.call(self); 108 $(document).off('mouseup.' + pluginName); 109 $(document).off('mousemove.' + pluginName); 110 }); 111 }); 112 } 113 if ($.fn.mousewheel) { 114 this.$image.on('mousewheel.' + pluginName, function (e) { 115 e.preventDefault(); 116 if (e.deltaY < 0) 117 self.zoomIn.call(self); 118 else 119 self.zoomOut.call(self); 120 }); 121 } 122 }, 123 124 updateOptions: function () { 125 var self = this; 126 self.img_top = 0; 127 self.img_left = 0; 128 self.$image.css({width: '', left: self.img_left, top: self.img_top}); 129 self.$frame.width(self.options.width).height(self.options.height); 130 self.$frame.off('.' + pluginName); 131 self.$frame.removeClass('hover'); 132 if (self.options.showControls === 'always' || self.options.showControls === 'auto' && is_touch_device()
132) 133 self.$frame.addClass('hover'); 134 else if (self.options.showControls !== 'never') { 135 self.$frame.on('mouseenter.' + pluginName, function () { self.$frame.addClass('hover'); }); 136 self.$frame.on('mouseleave.' + pluginName, function () { self.$frame.removeClass('hover'); }); 137 } 138 139 // Image hack to get width and height on IE 140 var img = new Image(); 141 img.onload = function () { 142 self.width = img.width; 143 self.height = img.height; 144 img.src = ''; 145 img.onload = null; 146 self.percent = undefined; 147 self.fit.call(self); 148 if (self.options.result) 149 self.setCrop.call(self, self.options.result); 150 else 151 self.zoom.call(self, self.minPercent); 152 self.$image.fadeIn('fast'); 153 self.on_load.call(self); 154 }; 155 // onload has to be set before src for IE8 156 // otherwise it never fires 157 img.src = self.$image.attr('src'); 158 }, 159 160 remove: function () { 161 var hammerit; 162 if (typeof $.fn.hammer === 'function') 163 hammerit = this.$image.data('hammer'); // Get hammer instance object. 164 else if (typeof Hammer !== 'undefined') 165 hammerit = Hammer(this.$image.get(0)); 166 if (hammerit) 167 hammerit.off('panleft panright panup pandown panend pancancel doubletap pinchin pinchout'); 168 this.$frame.off('.' + pluginName); 169 this.$image.off('.' + pluginName); 170 this.$image.css({width: '', left: '', top: ''}); 171 this.$image.removeClass('cropImage'); 172 this.$image.removeData(pluginName); 173 this.$image.insertAfter(this.$frame); 174 this.$frame.removeClass('cropFrame'); 175 this.$frame.removeAttr('style'); 176 this.$frame.empty(); 177 this.$frame.remove(); 178 }, 179 180 fit: function () { 181 var widthRatio = this.options.width / this.width, 182 heightRatio = this.options.height / this.height; 183 this.minPercent = (widthRatio >= heightRatio) ? widthRatio : heightRatio; 184 }, 185 186 setCrop: function (result) { 187 this.percent = Math.max(this.options.width/result.cropW, this.options.height/result.cropH); 188 this.img_width = Math.ceil(this.width*this.percent); 189 this.img_height = Math.ceil(this.height*this.percent); 190 this.img_left = -Math.floor(result.cropX*this.percent); 191 this.img_top = -Math.floor(result.cropY*this.percent); 192 this.$image.css({ width: this.img_width, left: this.img_left, top: this.img_top }); 193 this.update(); 194 }, 195 196 zoom: function(percent) { 197 var old_percent = this.percent; 198 199 this.percent = Math.max(this.minPercent, Math.min(this.options.maxZoom, percent)); 200 this.img_width = Math.ceil(this.width * this.percent); 201 this.img_height = Math.ceil(this.height * this.percent); 202 203 if (old_percent) { 204 var zoomFactor = this.percent / old_percent; 205 this.img_left = fill((1 - zoomFactor) * this.options.width / 2 + zoomFactor * this.img_left, this.img_width, this.options.width); 206 this.img_top = fill((1 - zoomFactor) * this.options.height / 2 + zoomFactor * this.img_top, this.img_height, this.options.height); 207 } else { 208 this.img_left = fill((this.options.width - this.img_width) / 2, this.img_width, this.options.width); 209 this.img_top = fill((this.options.height - this.img_height) / 2, this.img_height, this.options.height); 210 } 211 212 this.$image.css({ width: this.img_width, left: this.img_left, top: this.img_top }); 213 this.update(); 214 }, 215 zoomIn: function() { 216 this.zoom(this.percent + (1 - this.minPercent) / (this.options.zoom - 1 || 1)); 217 }, 218 zoomOut: function() { 219 this.zoom(this.percent - (1 - this.minPercent) / (this.options.zoom - 1 || 1)); 220 }, 221 drag: function(data, skipupdate) { 222 this.img_left = fill(data.startX + data.dx, this.img_width, this.options.width); 223 this.img_top = fill(data.startY + data.dy, this.img_height, this.options.height); 224 this.$image.css({ left: this.img_left, top: this.img_top }); 225 if (!skipupdate) 226 this.update(); 227 }, 228 update: function() { 229 this.result = { 230 cropX: -Math.ceil(this.img_left / this.percent),
231 cropY: -Math.ceil(this.img_top / this.percent), 232 cropW: Math.floor(this.options.width / this.percent), 233 cropH: Math.floor(this.options.height / this.percent), 234 stretch: this.minPercent > 1 235 }; 236 237 this.$image.trigger(pluginName, [this.result, this]); 238 }, 239 getDataURL: function () { 240 if(!supportsCanvas) { 241 // return an empty string for browsers that don't support canvas. 242 // this allows it to fail gracefully. 243 return false; 244 } 245 var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); 246 canvas.width = this.options.width; 247 canvas.height = this.options.height; 248 ctx.drawImage(this.$image.get(0), this.result.cropX, this.result.cropY, this.result.cropW, this.result.cropH, 0, 0, this.options.width, this.options.height); 249 return canvas.toDataURL(); 250 }, 251 getBlob: function () { 252 return uri2blob(this.getDataURL()); 253 } 254 }; 255 256 $.fn[pluginName] = function(options, on_load) { 257 return this.each(function() { 258 var $this = $(this), inst = $this.data(pluginName); 259 if (!inst) { 260 var opts = $.extend({}, $.fn[pluginName].defaultOptions, options); 261 $this.data(pluginName, new Crop($this, opts, on_load)); 262 } else if (options) { 263 $.extend(inst.options, options); 264 inst.updateOptions(); 265 } 266 }); 267 }; 268 269 $.fn[pluginName].defaultOptions = { 270 width: 200, 271 height: 200, 272 zoom: 10, 273 maxZoom: 1, 274 controls: null, 275 showControls: 'auto', 276 label: 'Drag to crop' 277 }; 278 } 279 280 if (typeof require === "function" && typeof exports === "object" && typeof module === "object") 281 factory(require("jquery")); 282 else if (typeof define === "function" && define.amd) 283 define(["jquery"], factory); 284 else 285 factory(window.jQuery || window.Zepto); 286 287})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.