PageSourceSearch

https://www.renfrewshiregolfunion.co.uk/js/plugin/cropper.js

js renfrewshiregolfunion.co.uk collected 2026-09-27 15:22:04 UTC 42,849 bytes, 1,579 lines download raw bytes

1(function (factory) {
2  if (typeof define === "function" && define.amd) {
3    // AMD. Register as anonymous module.
4    define(["jquery"], factory);
5  } else if (typeof exports === "object") {
6    // Node / CommonJS
7    factory(require("jquery"));
8  } else {
9    // Browser globals.
10    factory(jQuery);
11  }
12})(function ($) {
13
14  "use strict";
15
16  var $window = $(window),
17      $document = $(document),
18      location = window.location,
19
20      // Constants
21      TRUE = true,
22      FALSE = false,
23      NULL = null,
24      NAN = NaN,
25      INFINITY = Infinity,
26      STRING_UNDEFINED = "undefined",
27      STRING_DIRECTIVE = "directive",
28      CROPPER_NAMESPACE = ".cropper",
29
30      // RegExps
31      REGEXP_DIRECTIVES = /^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/,
32      REGEXP_OPTIONS = /^(x|y|width|height)$/,
33      REGEXP_PROPERTIES = /^(naturalWidth|naturalHeight|width|height|aspectRatio|ratio|rotate)$/,
34
35      // Classes
36      CLASS_MODAL = "cropper-modal",
37      CLASS_HIDDEN = "cropper-hidden",
38      CLASS_INVISIBLE = "cropper-invisible",
39      CLASS_MOVE = "cropper-move",
40      CLASS_CROP = "cropper-crop",
41      CLASS_DISABLED = "cropper-disabled",
42
43      // Events
44      EVENT_MOUSE_DOWN = "mousedown touchstart",
45      EVENT_MOUSE_MOVE = "mousemove touchmove",
46      EVENT_MOUSE_UP = "mouseup mouseleave touchend touchleave touchcancel",
47      EVENT_WHEEL = "wheel mousewheel DOMMouseScroll",
48      EVENT_RESIZE = "resize" + CROPPER_NAMESPACE, // Bind to window with namespace
49      EVENT_DBLCLICK = "dblclick",
50      EVENT_BUILD = "build" + CROPPER_NAMESPACE,
51      EVENT_BUILT = "built" + CROPPER_NAMESPACE,
52      EVENT_DRAG_START = "dragstart" + CROPPER_NAMESPACE,
53      EVENT_DRAG_MOVE = "dragmove" + CROPPER_NAMESPACE,
54      EVENT_DRAG_END = "dragend" + CROPPER_NAMESPACE,
55
56      // Functions
57      isNumber = function (n) {
58        return typeof n === "number";
59      },
60
61      toArray = function (obj, offset) {
62        var args = [];
63
64        if (typeof offset === "number") { // It's necessary for IE8
65          args.push(offset);
66        }
67
68        return args.slice.apply(obj, args);
69      },
70
71      // Custom proxy to avoid jQuery's guid
72      proxy = function (fn, context) {
73        var args = toArray(arguments, 2);
74
75        return function () {
76          return fn.apply(context, args.concat(toArray(arguments)));
77        };
78      },
79
80      // Constructor
81      Cropper = function (element, options) {
82        this.element = element;
83        this.$element = $(element);
84        this.defaults = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) ? options : {});
85        this.$original = NULL;
86        this.ready = FALSE;
87        this.built = FALSE;
88        this.cropped = FALSE;
89        this.rotated = FALSE;
90        this.disabled = FALSE;
91        this.replaced = FALSE;
92        this.init();
93      },
94
95      // Others
96      sqrt = Math.sqrt,
97      min = Math.min,
98      max = Math.max,
99      abs = Math.abs,
100      sin = Math.sin,
101      cos = Math.cos,
102      num = parseFloat;
103
104  Cropper.prototype = {
105    constructor: Cropper,
106
107    support: {
108      canvas: $.isFunction($("<canvas>")[0].getContext)
109    },
110
111    init: function () {
112      var defaults = this.defaults;
113
114      $.each(defaults, function (i, n) {
115        switch (i) {
116          case "aspectRatio":
117            defaults[i] = abs(num(n)) || NAN; // 0 -> NaN
118            break;
119
120          case "autoCropArea":
121            defaults[i] = abs(num(n)) || 0.8; // 0 | NaN -> 0.8
122            break;
123
124          case "minWidth":
125          case "minHeight":
126            defaults[i] = abs(num(n)) || 0; // NaN -> 0
127            break;
128
129          case "maxWidth":
130          case "maxHeight":
131            defaults[i] = abs(num(n)) || INFINITY; // 0 | NaN -> Infinity
132            break;
133
134          // No default
135        }
136      });
137
138      // Set default image data
139      this.image = {
140        rotate: 0
141      };
142
143      this.load();
144    },
145
146    load: function () {
147      var _this = this,
148          $this = this.$element,
149          element = this.element,
150          image = this.image,
151          crossOrigin = "",
152          $clone,
153          url;
154
155      if ($this.is("img")) {
156        url = $this.prop("src");
157      } else if ($this.is("canvas") && this.support.canvas) {
158        url = element.toDataURL();
159      }
160
161      if (!url) {
162        return;
163      }
164
165      // Reset image rotate degree
166      if (this.replaced) {
167        image.rotate = 0;
168      }
169
170      if (this.defaults.checkImageOrigin) {
171        if ($this.prop("crossOrigin") || this.isCrossOriginURL(url)) {
172          crossOrigin = " crossOrigin";
173        }
174      }
175
176      this.$clone = ($clone = $("<img" + crossOrigin + ' src="' + url + '">'));
177
178      $clone.one("load", function () {
179        image.naturalWidth = this.naturalWidth || $clone.width();
180        image.naturalHeight = this.naturalHeight || $clone.height();
181        image.aspectRatio = image.naturalWidth / image.naturalHeight;
182
183        _this.url = url;
184        _this.ready = TRUE;
185        _this.build();
186      });
187
188      // Hide and prepend the clone iamge to the document body (Don't append to).
189      $clone.addClass(CLASS_INVISIBLE).prependTo("body");
190    },
191
192    isCrossOriginURL: function (url) {
193      var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
194
195      if ((parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port))) {
196        return TRUE;
197      }
198
199      return FALSE;
200    },
201
202    build: function () {
203      var $this = this.$element,
204          defaults = this.defaults,
205          buildEvent,
206          $cropper;
207
208      if (!this.ready) {
209        return;
210      }
211
212      if (this.built) {
213        this.unbuild();
214      }
215
216      $this.one(EVENT_BUILD, defaults.build); // Only trigger once
217      buildEvent = $.Event(EVENT_BUILD);
218      $this.trigger(buildEvent);
219
220      if (buildEvent.isDefaultPrevented()) {
221        return;
222      }
223
224      // Create cropper elements
225      this.$cropper = ($cropper = $(Cropper.TEMPLATE));
226
227      // Hide the original image
228      $this.addClass(CLASS_HIDDEN);
229
230      // Show and prepend the clone iamge to the cropper
231      this.$clone.removeClass(CLASS_INVISIBLE).prependTo($cropper);
232
233      // Save original image for rotation
234      if (!this.rotated) {
235        this.$original = this.$clone.clone();
236
237        // Append the image to document to avoid "NS_ERROR_NOT_AVAILABLE" error on Firefox when call the "drawImage" method.
238        this.$original.addClass(CLASS_HIDDEN).prependTo(this.$cropper);
239
240        this.originalImage = $.extend({}, this.image);
241      }
242
243      this.$container = $this.parent();
244      this.$container.append($cropper);
245
246      this.$canvas = $cropper.find(".cropper-canvas");
247      this.$dragger = $cropper.find(".cropper-dragger");
248      this.$viewer = $cropper.find(".cropper-viewer");
249
250      defaults.autoCrop ? (this.cropped = TRUE) : this.$dragger.addClass(CLASS_HIDDEN);
251      defaults.dragCrop && this.setDragMode("crop");
252      defaults.modal && this.$canvas.addClass(CLASS_MODAL);
253      !defaults.dashed && this.$dragger.find(".cropper-dashed").addClass(CLASS_HIDDEN);
254      !defaults.movable && this.$dragger.find(".cropper-face").data(STRING_DIRECTIVE, "move");
255      !defaults.resizable && this.$dragger.find(".cropper-line, .cropper-point").addClass(CLASS_HIDDEN);
256
257      this.addListeners();
258      this.initPreview();
259
260      this.built = TRUE; // Set `true` before update
261      this.update();
262      this.replaced = FALSE; // Reset to `false` after update
263
264      $this.one(EVENT_BUILT, defaults.built); // Only trigger once
265      $this.trigger(EVENT_BUILT);
266    },
267
268    unbuild: function () {
269      if (!this.built) {
270        return;
271      }
272
273      this.built = FALSE;
274      this.removeListeners();
275
276      this.$preview.empty();
277      this.$preview = NULL;
278
279      this.$dragger = NULL;
280      this.$canvas = NULL;
281      this.$container = NULL;
282
283      this.$cropper.remove();
284      this.$cropper = NULL;
285    },
286
287    update: function (data) {
288      this.initContainer();
289      this.initCropper();
290      this.initImage();
291      this.initDragger();
292
293      if (data) {
294        this.setData(data, TRUE);
295        this.setDragMode("crop");
296      } else {
297        this.setData(this.defaults.data);
298      }
299    },
300
301    resize: function () {
302      clearTimeout(this.resizing);
303      this.resizing = setTimeout($.proxy(this.update, this, this.getData()), 200);
304    },
305
306    preview: function () {
307      var image = this.image,
308          dragger = this.dragger,
309          width = image.width,
310          height = image.height,
311          left = dragger.left - image.left,
312          top = dragger.top - image.top;
313
314      this.$viewer.find("img").css({
315        width: width,
316        height: height,
317        marginLeft: -left,
318        marginTop: -top
319      });
320
321      this.$preview.each(function () {
322        var $this = $(this),
323            ratio = $this.width() / dragger.width;
324
325        $this.find("img").css({
326          width: width * ratio,
327          height: height * ratio,
328          marginLeft: -left * ratio,
329          marginTop: -top * ratio
330        });
331      });
332    },
333
334    addListeners: function () {
335      var defaults = this.defaults;
336
337      this.$element.on(EVENT_DRAG_START, defaults.dragstart).on(EVENT_DRAG_MOVE, defaults.dragmove).on(EVENT_DRAG_END, defaults.dragend);
338      this.$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.dragstart, this)).on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
339
340      if (defaults.zoomable) {
341        this.$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
342      }
343
344      if (defaults.multiple) {
345        this.$cropper.on(EVENT_MOUSE_MOVE, $.proxy(this.dragmove, this)).on(EVENT_MOUSE_UP, $.proxy(this.dragend, this));
346      } else {
347        $document.on(EVENT_MOUSE_MOVE, (this._dragmove = proxy(this.dragmove, this))).on(EVENT_MOUSE_UP, (this._dragend = proxy(this.dragend, this)));
348      }
349
350      $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
351    },
352
353    removeListeners: function () {
354      var defaults = this.defaults;
355
356      this.$element.off(EVENT_DRAG_START, defaults.dragstart).off(EVENT_DRAG_MOVE, defaults.dragmove).off(EVENT_DRAG_END, defaults.dragend);
357      this.$cropper.off(EVENT_MOUSE_DOWN, this.dragstart).off(EVENT_DBLCLICK, this.dblclick);
358
359      if (defaults.zoomable) {
360        this.$cropper.off(EVENT_WHEEL, this.wheel);
361      }
362
363      if (defaults.multiple) {
364        this.$cropper.off(EVENT_MOUSE_MOVE, this.dragmove).off(EVENT_MOUSE_UP, this.dragend);
365      } else {
366        $document.off(EVENT_MOUSE_MOVE, this._dragmove).off(EVENT_MOUSE_UP, this._dragend);
367      }
368
369      $window.off(EVENT_RESIZE, this._resize);
370    },
371
372    initPreview: function () {
373      var img = '<img src="' + this.url + '">';
374
375      this.$preview = $(this.defaults.preview);
376      this.$viewer.html(img);
377      this.$preview.html(img).find("img").css("cssText", "min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;");
378    },
379
380    initContainer: function () {
381      var $container = this.$container;
382
383      this.container = {
384        width: max($container.width(), 300),
385        height: max($container.height(), 150)
386      };
387    },
388
389    initCropper: function () {
390      var container = this.container,
391          image = this.image,
392          cropper;
393
394      if (((image.naturalWidth * container.height / image.naturalHeight) - container.width) >= 0) {
395        cropper = {
396          width: container.width,
397          height: container.width / image.aspectRatio,
398          left: 0
399        };
400
401        cropper.top = (container.height - cropper.height) / 2;
402      } else {
403        cropper = {
404          width: container.height * image.aspectRatio,
405          height: container.height,
406          top: 0
407        };
408
409        cropper.left = (container.width - cropper.width) / 2;
410      }
411
412      this.$cropper.css({
413        width: cropper.width,
414        height: cropper.height,
415        left: cropper.left,
416        top: cropper.top
417      });
418
419      this.cropper = cropper;
420    },
421
422    initImage: function () {
423      var image = this.image,
424          cropper = this.cropper,
425          defaultImage = {
426            _width: cropper.width,
427            _height: cropper.height,
428            width: cropper.width,
429            height: cropper.height,
430            left: 0,
431            top: 0,
432            ratio: cropper.width / image.naturalWidth
433          };
434
435      this.defaultImage = $.extend({}, image, defaultImage);
436
437      if (image._width !== cropper.width || image._height !== cropper.height) {
438        $.extend(image, defaultImage);
439      } else {
440        image = $.extend({}, defaultImage, image);
441
442        // Reset image ratio
443        if (this.replaced) {
444          image.ratio = defaultImage.ratio;
445        }
446      }
447
448      this.image = image;
449      this.renderImage();
450    },
451
452    renderImage: function (mode) {
453      var image = this.image;
454
455      if (mode === "zoom") {
456        image.left -= (image.width - image.oldWidth) / 2;
457        image.top -= (image.height - image.oldHeight) / 2;
458      }
459
460      image.left = min(max(image.left, image._width - image.width), 0);
461      image.top = min(max(image.top, image._height - image.height), 0);
462
463      this.$clone.css({
464        width: image.width,
465        height: image.height,
466        marginLeft: image.left,
467        marginTop: image.top
468      });
469
470      if (mode) {
471        this.defaults.done(this.getData());
472        this.preview();
473      }
474    },
475
476    initDragger: function () {
477      var defaults = this.defaults,
478          cropper = this.cropper,
479          // If not set, use the original aspect ratio of the image.
480          aspectRatio = defaults.aspectRatio || this.image.aspectRatio,
481          ratio = this.image.ratio,
482          autoCropDragger = {},
483          dragger;
484
485      if (((cropper.height * aspectRatio) - cropper.width) >= 0) {
486        dragger = {
487          height: cropper.width / aspectRatio,
488          width: cropper.width,
489          left: 0,
490          top: (cropper.height - (cropper.width / aspectRatio)) / 2,
491          maxWidth: cropper.width,
492          maxHeight: cropper.width / aspectRatio
493        };
494      } else {
495        dragger = {
496          height: cropper.height,
497          width: cropper.height * aspectRatio,
498          left: (cropper.width - (cropper.height * aspectRatio)) / 2,
499          top: 0,
500          maxWidth: cropper.height * aspectRatio,
501          maxHeight: cropper.height
502        };
503      }
504
505      dragger.minWidth = 0;
506      dragger.minHeight = 0;
507
508      if (defaults.aspectRatio) {
509        if (isFinite(defaults.maxWidth)) {
510          dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio);
511          dragger.maxHeight = dragger.maxWidth / aspectRatio;
512        } else if (isFinite(defaults.maxHeight)) {
513          dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio);
514          dragger.maxWidth = dragger.maxHeight * aspectRatio;
515        }
516
517        if (defaults.minWidth > 0) {
518          dragger.minWidth = max(0, defaults.minWidth * ratio);
519          dragger.minHeight = dragger.minWidth / aspectRatio;
520        } else if (defaults.minHeight > 0) {
521          dragger.minHeight = max(0, defaults.minHeight * ratio);
522          dragger.minWidth = dragger.minHeight * aspectRatio;
523        }
524      } else {
525        dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio);
526        dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio);
527        dragger.minWidth = max(0, defaults.minWidth * ratio);
528        dragger.minHeight = max(0, defaults.minHeight * ratio);
529      }
530
531      // minWidth can't be greater than maxWidth, and minHeight too.
532      dragger.minWidth = min(dragger.maxWidth, dragger.minWidth);
533      dragger.minHeight = min(dragger.maxHeight, dragger.minHeight);
534
535      // Center the dragger by default
536      autoCropDragger.height = dragger.height * defaults.autoCropArea;
537      autoCropDragger.width = dragger.width * defaults.autoCropArea;
538      autoCropDragger.left = (cropper.width - autoCropDragger.width) / 2;
539      autoCropDragger.top = (cropper.height - autoCropDragger.height) / 2;
540
541      autoCropDragger.oldLeft = dragger.oldLeft = dragger.left;
542      autoCropDragger.oldTop = dragger.oldTop = dragger.top;
543
544      this.autoCropDragger = autoCropDragger;
545      this.defaultDragger = $.extend({}, dragger);
546      this.dragger = dragger;
547    },
548
549    renderDragger: function () {
550      var dragger = this.dragger,
551          cropper = this.cropper;
552
553      if (dragger.width > dragger.maxWidth) {
554        dragger.width = dragger.maxWidth;
555        dragger.left = dragger.oldLeft;
556      } else if (dragger.width < dragger.minWidth) {
557        dragger.width = dragger.minWidth;
558        dragger.left = dragger.oldLeft;
559      }
560
561      if (dragger.height > dragger.maxHeight) {
562        dragger.height = dragger.maxHeight;
563        dragger.top = dragger.oldTop;
564      } else if (dragger.height < dragger.minHeight) {
565        dragger.height = dragger.minHeight;
566        dragger.top = dragger.oldTop;
567      }
568
569      dragger.left = min(max(dragger.left, 0), cropper.width - dragger.width);
570      dragger.top = min(max(dragger.top, 0), cropper.height - dragger.height);
571      dragger.oldLeft = dragger.left;
572      dragger.oldTop = dragger.top;
573
574      // Re-render the dragger
575      this.dragger = dragger;
576
577      if (!this.disabled) {
578        this.defaults.done(this.getData());
579      }
580
581      this.$dragger.css({
582        width: dragger.width,
583        height: dragger.height,
584        left: dragger.left,
585        top: dragger.top
586      });
587
588      this.preview();
589    },
590
591    reset: function (deep) {
592      if (!this.cropped) {
593        return;
594      }
595
596      if (deep) {
597        this.defaults.data = {};
598      }
599
600      this.image = $.extend({}, this.defaultImage);
601      this.renderImage();
602      this.dragger = $.extend({}, this.defaultDragger);
603      this.setData(this.defaults.data);
604    },
605
606    clear: function () {
607      if (!this.cropped) {
608        return;
609      }
610
611      this.cropped = FALSE;
612
613      this.setData({
614        x: 0,
615        y: 0,
616        width: 0,
617        height: 0
618      });
619
620      this.$canvas.removeClass(CLASS_MODAL);
621      this.$dragger.addClass(CLASS_HIDDEN);
622    },
623
624    destroy: function () {
625      var $this = this.$element;
626
627      if (!this.ready) {
628        return;
629      }
630
631      this.unbuild();
632      $this.removeClass(CLASS_HIDDEN).removeData("cropper");
633
634      if (this.rotated) {
635        $this.attr("src", this.$original.attr("src"));
636      }
637    },
638
639    replace: function (url, /*INTERNAL*/ rotated) {
640      var _this = this,
641          $this = this.$element,
642          element = this.element,
643          context;
644
645      if (url && url !== this.url && url !== $this.attr("src")) {
646        if (!rotated) {
647          this.rotated = FALSE;
648          this.replaced = TRUE;
649        }
650
651        if ($this.is("img")) {
652          $this.attr("src", url);
653          this.load();
654        } else if ($this.is("canvas") && this.support.canvas) {
655          context = element.getContext("2d");
656
657          $('<img src="' + url + '">').one("load", function () {
658            element.width = this.width;
659            element.height = this.height;
660            context.clearRect(0, 0, element.width, element.height);
661            context.drawImage(this, 0, 0);
662            _this.load();
663          });
664        }
665      }
666    },
667
668    setData: function (data, /*INTERNAL*/ once) {
669      var cropper = this.cropper,
670          dragger = this.dragger,
671          image = this.image,
672          aspectRatio = this.defaults.aspectRatio;
673
674      if (!this.built || typeof data === STRING_UNDEFINED) {
675        return;
676      }
677
678      if (data === NULL || $.isEmptyObject(data)) {
679        dragger = $.extend({}, this.autoCropDragger);
680      }
681
682      if ($.isPlainObject(data) && !$.isEmptyObject(data)) {
683
684        if (!once) {
685          this.defaults.data = data;
686        }
687
688        data = this.transformData(data);
689
690        if (isNumber(data.x) && data.x <= cropper.width - image.left) {
691          dragger.left = data.x + image.left;
692        }
693
694        if (isNumber(data.y) && data.y <= cropper.height - image.top) {
695          dragger.top = data.y + image.top;
696        }
697
698        if (aspectRatio) {
699          if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) {
700            dragger.width = data.width;
701            dragger.height = dragger.width / aspectRatio;
702          } else if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) {
703            dragger.height = data.height;
704            dragger.width = dragger.height * aspectRatio;
705          }
706        } else {
707          if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) {
708            dragger.width = data.width;
709          }
710
711          if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) {
712            dragger.height = data.height;
713          }
714        }
715      }
716
717      this.dragger = dragger;
718      this.renderDragger();
719    },
720
721    getData: function (rounded) {
722      var dragger = this.dragger,
723          image = this.image,
724          data = {};
725
726      if (this.built) {
727        data = {
728          x: dragger.left - image.left,
729          y: dragger.top - image.top,
730          width: dragger.width,
731          height: dragger.height
732        };
733
734        data = this.transformData(data, TRUE, rounded);
735      }
736
737      return data;
738    },
739
740    transformData: function (data, reversed, rounded) {
741      var ratio = this.image.ratio,
742          result = {};
743
744      $.each(data, function (i, n) {
745        n = num(n);
746
747        if (REGEXP_OPTIONS.test(i) && !isNaN(n)) {
748          result[i] = reversed ? (rounded ? Math.round(n / ratio) : n / ratio) : n * ratio;
749        }
750      });
751
752      return result;
753    },
754
755    setAspectRatio: function (aspectRatio) {
756      var freeRatio = aspectRatio === "auto";
757
758      aspectRatio = num(aspectRatio);
759
760      if (freeRatio || (!isNaN(aspectRatio) && aspectRatio > 0)) {
761        this.defaults.aspectRatio = freeRatio ? NAN : aspectRatio;
762
763        if (this.built) {
764          this.initDragger();
765          this.renderDragger();
766        }
767      }
768    },
769
770    getImageData: function () {
771      var data = {};
772
773      if (this.ready) {
774        $.each(this.image, function (name, value) {
775          if (REGEXP_PROPERTIES.test(name)) {
776            data[name] = value;
777          }
778        });
779      }
780
781      return data;
782    },
783
784    getDataURL: function (options, type, quality) {
785      var canvas = $("<canvas>")[0],
786          data = this.getData(),
787          dataURL = "",
788          context;
789
790      if (!$.isPlainObject(options)) {
791        quality = type;
792        type = options;
793        options = {};
794      }
795
796      options = $.extend({
797        width: data.width,
798        height: data.height
799      }, options);
800
801      if (this.cropped && this.support.canvas) {
802        canvas.width = options.width;
803        canvas.height = options.height;
804        context = canvas.getContext("2d");
805
806        const steps = (canvas.width / canvas.width) >> 1;
807        if (steps !== 0) { // This was causing [NS_ERROR_FAILURE] so if its at 0 then move one else set it
808          context.filter = `blur(${steps}px)`;
809        }         
810
811
812        if (type === "image/jpeg") {
813          context.fillStyle = "#fff";
814          context.fillRect(0, 0, options.width, options.height);
815        }
816
817        context.imageSmoothingQuality = 'high';
818        context.drawImage(this.$clone[0], data.x, data.y, data.width, data.height, 0, 0, options.width, options.height);
819        dataURL = canvas.toDataURL(type, quality);
820      }
821
822      return dataURL;
823    },
824
825    setDragMode: function (mode) {
826      var $canvas = this.$canvas,
827          defaults = this.defaults,
828          cropable = FALSE,
829          movable = FALSE;
830
831      if (!this.built || this.disabled) {
832        return;
833      }
834
835      switch (mode) {
836        case "crop":
837          if (defaults.dragCrop) {
838            cropable = TRUE;
839            $canvas.data(STRING_DIRECTIVE, mode);
840          }
841
842          break;
843
844        case "move":
845          movable = TRUE;
846          $canvas.data(STRING_DIRECTIVE, mode);
847
848          break;
849
850        default:
851          $canvas.removeData(STRING_DIRECTIVE);
852      }
853
854      $canvas.toggleClass(CLASS_CROP, cropable).toggleClass(CLASS_MOVE, movable);
855    },
856
857    enable: function () {
858      if (this.built) {
859        this.disabled = FALSE;
860        this.$cropper.removeClass(CLASS_DISABLED);
861      }
862    },
863
864    disable: function () {
865      if (this.built) {
866        this.disabled = TRUE;
867        this.$cropper.addClass(CLASS_DISABLED);
868      }
869    },
870
871    rotate: function (degree) {
872      var image = this.image;
873
874      degree = num(degree) || 0;
875
876      if (!this.built || degree === 0 || this.disabled || !this.defaults.rotatable || !this.support.canvas) {
877        return;
878      }
879
880      this.rotated = TRUE;
881      degree = (image.rotate = (image.rotate + degree) % 360);
882
883       // replace with "true" to prevent to override the original image
884      this.replace(this.getRotatedDataURL(degree), true);
885    },
886
887    getRotatedDataURL: function (degree) {
888      var canvas = $("<canvas>")[0],
889          context = canvas.getContext("2d"),
890          arc = degree * Math.PI / 180,
891          deg = abs(degree) % 180,
892          acuteAngle = deg > 90 ? (180 - deg) : deg,
893          acuteAngleArc = acuteAngle * Math.PI / 180,
894          originalImage = this.originalImage,
895          naturalWidth = originalImage.naturalWidth,
896          naturalHeight = originalImage.naturalHeight,
897          width = abs(naturalWidth * cos(acuteAngleArc) + naturalHeight * sin(acuteAngleArc)),
898          height = abs(naturalWidth * sin(acuteAngleArc) + naturalHeight * cos(acuteAngleArc));
899
900      canvas.width = width;
901      canvas.height = height;
902      context.save();
903      context.translate(width / 2, height / 2);
904      context.rotate(arc);
905      context.drawImage(this.$original[0], -naturalWidth / 2, -naturalHeight / 2, naturalWidth, naturalHeight);
906      context.restore();
907
908      return canvas.toDataURL();
909    },
910
911    zoom: function (delta) {
912      var image = this.image,
913          width,
914          height,
915          range;
916
917      delta = num(delta);
918
919      if (!this.built || !delta || this.disabled || !this.defaults.zoomable) {
920        return;
921      }
922
923      width = image.width * (1 + delta);
924      height = image.height * (1 + delta);
925      range = width / image._width;
926
927      if (range > 10) {
928        return;
929      }
930
931      if (range < 1) {
932        width = image._width;
933        height = image._height;
934      }
935
936      if (range <= 1) {
937        this.setDragMode("crop");
938      } else {
939        this.setDragMode("move");
940      }
941
942      image.oldWidth = image.width;
943      image.oldHeight = image.height;
944
945      image.width = width;
946      image.height = height;
947      image.ratio = image.width / image.naturalWidth;
948
949      this.renderImage("zoom");
950    },
951
952    dblclick: function () {
953      if (this.disabled) {
954        return;
955      }
956
957      if (this.$canvas.hasClass(CLASS_CROP)) {
958        this.setDragMode("move");
959      } else {
960        this.setDragMode("crop");
961      }
962    },
963
964    wheel: function (event) {
965      var e = event.originalEvent,
966          msDeltaY = 117.25, // IE
967          mozDelatY = 5, // Firefox
968          webkitDelatY = 166.66665649414062, // Chrome, Opera
969          zoomDelta = 0.1, // 10%
970          delta;
971
972      if (this.disabled) {
973        return;
974      }
975
976      event.preventDefault();
977
978      if (e.deltaY) {
979        delta = e.deltaY;
980        delta = delta % mozDelatY === 0 ? delta / mozDelatY : delta % msDeltaY === 0 ? delta / msDeltaY : delta / webkitDelatY;
981      } else {
982        delta = e.wheelDelta ? -e.wheelDelta / 120 : (e.detail ? e.detail / 3 : 0);
983      }
984
985      this.zoom(delta * zoomDelta);
986    },
987
988    dragstart: function (event) {
989      var touches = event.originalEvent.touches,
990          e = event,
991          directive,
992          dragStartEvent,
993          touchesLength;
994
995      if (this.disabled) {
996        return;
997      }
998
999      if (touches) {
1000        touchesLength = touches.length;
1001
1002        if (touchesLength > 1) {
1003          if (this.defaults.zoomable && touchesLength === 2) {
1004            e = touches[1];
1005            this.startX2 = e.pageX;
1006            this.startY2 = e.pageY;
1007            directive = "zoom";
1008          } else {
1009            return;
1010          }
1011        }
1012
1013        e = touches[0];
1014      }
1015
1016      directive = directive || $(e.target).data(STRING_DIRECTIVE);
1017
1018      if (REGEXP_DIRECTIVES.test(directive)) {
1019        event.preventDefault();
1020
1021        dragStartEvent = $.Event(EVENT_DRAG_START);
1022        this.$element.trigger(dragStartEvent);
1023
1024        if (dragStartEvent.isDefaultPrevented()) {
1025          return;
1026        }
1027
1028        this.directive = directive;
1029        this.cropping = FALSE;
1030        this.startX = e.pageX;
1031        this.startY = e.pageY;
1032
1033        if (directive === "crop") {
1034          this.cropping = TRUE;
1035          this.$canvas.addClass(CLASS_MODAL);
1036        }
1037      }
1038    },
1039
1040    dragmove: function (event) {
1041      var touches = event.originalEvent.touches,
1042          e = event,
1043          dragMoveEvent,
1044          touchesLength;
1045
1046      if (this.disabled) {
1047        return;
1048      }
1049
1050      if (touches) {
1051        touchesLength = touches.length;
1052
1053        if (touchesLength > 1) {
1054          if (this.defaults.zoomable && touchesLength === 2) {
1055            e = touches[1];
1056            this.endX2 = e.pageX;
1057            this.endY2 = e.pageY;
1058          } else {
1059            return;
1060          }
1061        }
1062
1063        e = touches[0];
1064      }
1065
1066      if (this.directive) {
1067        event.preventDefault();
1068
1069        dragMoveEvent = $.Event(EVENT_DRAG_MOVE);
1070        this.$element.trigger(dragMoveEvent);
1071
1072        if (dragMoveEvent.isDefaultPrevented()) {
1073          return;
1074        }
1075
1076        this.endX = e.pageX;
1077        this.endY = e.pageY;
1078
1079        this.dragging();
1080      }
1081    },
1082
1083    dragend: function (event) {
1084      var dragEndEvent;
1085
1086      if (this.disabled) {
1087        return;
1088      }
1089
1090      if (this.directive) {
1091        event.preventDefault();
1092
1093        dragEndEvent = $.Event(EVENT_DRAG_END);
1094        this.$element.trigger(dragEndEvent);
1095
1096        if (dragEndEvent.isDefaultPrevented()) {
1097          return;
1098        }
1099
1100        if (this.cropping) {
1101          this.cropping = FALSE;
1102          this.$canvas.toggleClass(CLASS_MODAL, this.cropped && this.defaults.modal);
1103        }
1104
1105        this.directive = "";
1106      }
1107    },
1108
1109    dragging: function () {
1110      var directive = this.directive,
1111          image = this.image,
1112          cropper = this.cropper,
1113          maxWidth = cropper.width,
1114          maxHeight = cropper.height,
1115          dragger = this.dragger,
1116          width = dragger.width,
1117          height = dragger.height,
1118          left = dragger.left,
1119          top = dragger.top,
1120          right = left + width,
1121          bottom = top + height,
1122          renderable = TRUE,
1123          defaults = this.defaults,
1124          aspectRatio = defaults.aspectRatio,
1125          range = {
1126            x: this.endX - this.startX,
1127            y: this.endY - this.startY
1128          },
1129          offset;
1130
1131      if (aspectRatio) {
1132        range.X = range.y * aspectRatio;
1133        range.Y = range.x / aspectRatio;
1134      }
1135
1136      switch (directive) {
1137        // Move dragger
1138        case "all":
1139          left += range.x;
1140          top += range.y;
1141
1142          break;
1143
1144        // Resize dragger
1145        case "e":
1146          if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= 0 || bottom >= maxHeight))) {
1147            renderable = FALSE;
1148            break;
1149          }
1150
1151          width += range.x;
1152
1153          if (aspectRatio) {
1154            height = width / aspectRatio;
1155            top -= range.Y / 2;
1156          }
1157
1158          if (width < 0) {
1159            directive = "w";
1160            width = 0;
1161          }
1162
1163          break;
1164
1165        case "n":
1166          if (range.y <= 0 && (top <= 0 || aspectRatio && (left <= 0 || right >= maxWidth))) {
1167            renderable = FALSE;
1168            break;
1169          }
1170
1171          height -= range.y;
1172          top += range.y;
1173
1174          if (aspectRatio) {
1175            width = height * aspectRatio;
1176            left += range.X / 2;
1177          }
1178
1179          if (height < 0) {
1180            directive = "s";
1181            height = 0;
1182          }
1183
1184          break;
1185
1186        case "w":
1187          if (range.x <= 0 && (left <= 0 || aspectRatio && (top <= 0 || bottom >= maxHeight))) {
1188            renderable = FALSE;
1189            break;
1190          }
1191
1192          width -= range.x;
1193          left += range.x;
1194
1195          if (aspectRatio) {
1196            height = width / aspectRatio;
1197            top += range.Y / 2;
1198          }
1199
1200          if (width < 0) {
1201            directive = "e";
1202            width = 0;
1203          }
1204
1205          break;
1206
1207        case "s":
1208          if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= 0 || right >= maxWidth))) {
1209            renderable = FALSE;
1210            break;
1211          }
1212
1213          height += range.y;
1214
1215          if (aspectRatio) {
1216            width = height * aspectRatio;
1217            left -= range.X / 2;
1218          }
1219
1220          if (height < 0) {
1221            directive = "n";
1222            height = 0;
1223          }
1224
1225          break;
1226
1227        case "ne":
1228          if (aspectRatio) {
1229            if (range.y <= 0 && (top <= 0 || right >= maxWidth)) {
1230              renderable = FALSE;
1231              break;
1232            }
1233
1234            height -= range.y;
1235            top += range.y;
1236            width = height * aspectRatio;
1237          } else {
1238            if (range.x >= 0) {
1239              if (right < maxWidth) {
1240                width += range.x;
1241              } else if (range.y <= 0 && top <= 0) {
1242                renderable = FALSE;
1243              }
1244            } else {
1245              width += range.x;
1246            }
1247
1248            if (range.y <= 0) {
1249              if (top > 0) {
1250                height -= range.y;
1251                top += range.y;
1252              }
1253            } else {
1254              height -= range.y;
1255              top += range.y;
1256            }
1257          }
1258
1259          if (height < 0) {
1260            directive = "sw";
1261            height = 0;
1262            width = 0;
1263          }
1264
1265          break;
1266
1267        case "nw":
1268          if (aspectRatio) {
1269            if (range.y <= 0 && (top <= 0 || left <= 0)) {
1270              renderable = FALSE;
1271              break;
1272            }
1273
1274            height -= range.y;
1275            top += range.y;
1276            width = height * aspectRatio;
1277            left += range.X;
1278          } else {
1279            if (range.x <= 0) {
1280              if (left > 0) {
1281                width -= range.x;
1282                left += range.x;
1283              } else if (range.y <= 0 && top <= 0) {
1284                renderable = FALSE;
1285              }
1286            } else {
1287              width -= range.x;
1288              left += range.x;
1289            }
1290
1291            if (range.y <= 0) {
1292              if (top > 0) {
1293                height -= range.y;
1294                top += range.y;
1295              }
1296            } else {
1297              height -= range.y;
1298              top += range.y;
1299            }
1300          }
1301
1302          if (height < 0) {
1303            directive = "se";
1304            height = 0;
1305            width = 0;
1306          }
1307
1308          break;
1309
1310        case "sw":
1311          if (aspectRatio) {
1312            if (range.x <= 0 && (left <= 0 || bottom >= maxHeight)) {
1313              renderable = FALSE;
1314              break;
1315            }
1316
1317            width -= range.x;
1318            left += range.x;
1319            height = width / aspectRatio;
1320          } else {
1321            if (range.x <= 0) {
1322              if (left > 0) {
1323                width -= range.x;
1324                left += range.x;
1325              } else if (range.y >= 0 && bottom >= maxHeight) {
1326                renderable = FALSE;
1327              }
1328            } else {
1329              width -= range.x;
1330              left += range.x;
1331            }
1332
1333            if (range.y >= 0) {
1334              if (bottom < maxHeight) {
1335                height += range.y;
1336              }
1337            } else {
1338              height += range.y;
1339            }
1340          }
1341
1342          if (width < 0) {
1343            directive = "ne";
1344            height = 0;
1345            width = 0;
1346          }
1347
1348          break;
1349
1350        case "se":
1351          if (aspectRatio) {
1352            if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
1353              renderable = FALSE;
1354              break;
1355            }
1356
1357            width += range.x;
1358            height = width / aspectRatio;
1359          } else {
1360            if (range.x >= 0) {
1361              if (right < maxWidth) {
1362                width += range.x;
1363              } else if (range.y >= 0 && bottom >= maxHeight) {
1364                renderable = FALSE;
1365              }
1366            } else {
1367              width += range.x;
1368            }
1369
1370            if (range.y >= 0) {
1371              if (bottom < maxHeight) {
1372                height += range.y;
1373              }
1374            } else {
1375              height += range.y;
1376            }
1377          }
1378
1379          if (width < 0) {
1380            directive = "nw";
1381            height = 0;
1382            width = 0;
1383          }
1384
1385          break;
1386
1387        // Move image
1388        case "move":
1389          image.left += range.x;
1390          image.top += range.y;
1391          this.renderImage("move");
1392          renderable = FALSE;
1393
1394          break;
1395
1396        // Scale image
1397        case "zoom":
1398          if (defaults.zoomable) {
1399            this.zoom(function (x, y, x1, y1, x2, y2) {
1400              return (sqrt(x2 * x2 + y2 * y2) - sqrt(x1 * x1 + y1 * y1)) / sqrt(x * x + y * y);
1401            }(
1402              image.width,
1403              image.height,
1404              abs(this.startX - this.startX2),
1405              abs(this.startY - this.startY2),
1406              abs(this.endX - this.endX2),
1407              abs(this.endY - this.endY2)
1408            ));
1409
1410            this.endX2 = this.startX2;
1411            this.endY2 = this.startY2;
1412          }
1413
1414          break;
1415
1416        // Crop image
1417        case "crop":
1418          if (range.x && range.y) {
1419            offset = this.$cropper.offset();
1420            left = this.startX - offset.left;
1421            top = this.startY - offset.top;
1422            width = dragger.minWidth;
1423            height = dragger.minHeight;
1424
1425            if (range.x > 0) {
1426              if (range.y > 0) {
1427                directive = "se";
1428              } else {
1429                directive = "ne";
1430                top -= height;
1431              }
1432            } else {
1433              if (range.y > 0) {
1434                directive = "sw";
1435                left -= width;
1436              } else {
1437                directive = "nw";
1438                left -= width;
1439                top -= height;
1440              }
1441            }
1442
1443            // Show the dragger if is hidden
1444            if (!this.cropped) {
1445              this.cropped = TRUE;
1446              this.$dragger.removeClass(CLASS_HIDDEN);
1447            }
1448          }
1449
1450          break;
1451
1452        // No default
1453      }
1454
1455      if (renderable) {
1456        dragger.width = width;
1457        dragger.height = height;
1458        dragger.left = left;
1459        dragger.top = top;
1460        this.directive = directive;
1461
1462        this.renderDragger();
1463      }
1464
1465      // Override
1466      this.startX = this.endX;
1467      this.startY = this.endY;
1468    }
1469  };
1470
1471  // Use the string compressor: Strmin (https://github.com/fengyuanchen/strmin)
1472  Cropper.TEMPLATE = (function (source, words) {
1473    words = words.split(",");
1474    return source.replace(/\d+/g, function (i) {
1475      return words[i];
1476    });
1477  })('<0 6="5-container"><0 6="5-canvas"></0><0 6="5-dragger"><1 6="5-viewer"></1><1 6="5-8 8-h"></1><1 6="5-8 8-v"></1><1 6="5-face" 3-2="all"></1><1 6="5-7 7-e" 3-2="e"></1><1 6="5-7 7-n" 3-2="n"></1><1 6="5-7 7-w" 3-2="w"></1><1 6="5-7 7-s" 3-2="s"></1><1 6="5-4 4-e" 3-2="e"></1><1 6="5-4 4-n" 3-2="n"></1><1 6="5-4 4-w" 3-2="w"></1><1 6="5-4 4-s" 3-2="s"></1><1 6="5-4 4-ne" 3-2="ne"></1><1 6="5-4 4-nw" 3-2="nw"></1><1 6="5-4 4-sw" 3-2="sw"></1><1 6="5-4 4-se" 3-2="se"></1></0></0>', "div,span,directive,data,point,cropper,class,line,dashed");
1478
1479  /* Template source:
1480  <div class="cropper-container">
1481    <div class="cropper-canvas"></div>
1482    <div class="cropper-dragger">
1483      <span class="cropper-viewer"></span>
1484      <span class="cropper-dashed dashed-h"></span>
1485      <span class="cropper-dashed dashed-v"></span>
1486      <span class="cropper-face" data-directive="all"></span>
1487      <span class="cropper-line line-e" data-directive="e"></span>
1488      <span class="cropper-line line-n" data-directive="n"></span>
1489      <span class="cropper-line line-w" data-directive="w"></span>
1490      <span class="cropper-line line-s" data-directive="s"></span>
1491      <span class="cropper-point point-e" data-directive="e"></span>
1492      <span class="cropper-point point-n" data-directive="n"></span>
1493      <span class="cropper-point point-w" data-directive="w"></span>
1494      <span class="cropper-point point-s" data-directive="s"></span>
1495      <span class="cropper-point point-ne" data-directive="ne"></span>
1496      <span class="cropper-point point-nw" data-directive="nw"></span>
1497      <span class="cropper-point point-sw" data-directive="sw"></span>
1498      <span class="cropper-point point-se" data-directive="se"></span>
1499    </div>
1500  </div>
1501  */
1502
1503  Cropper.DEFAULTS = {
1504    // Basic
1505    aspectRatio: "auto",
1506    autoCropArea: 0.8, // 80%
1507    data: {
1508      // x: 0,
1509      // y: 0,
1510      // width: 300,
1511      // height: 150
1512    },
1513    done: $.noop,
1514    preview: "",
1515
1516    // Toggles
1517    multiple: FALSE,
1518    autoCrop: TRUE,
1519    dragCrop: TRUE,
1520    dashed: TRUE,
1521    modal: TRUE,
1522    movable: TRUE,
1523    resizable: TRUE,
1524    zoomable: TRUE,
1525    rotatable: TRUE,
1526    checkImageOrigin: TRUE,
1527
1528    // Dimensions
1529    minWidth: 0,
1530    minHeight: 0,
1531    maxWidth: INFINITY,
1532    maxHeight: INFINITY,
1533
1534    // Events
1535    build: NULL,
1536    built: NULL,
1537    dragstart: NULL,
1538    dragmove: NULL,
1539    dragend: NULL
1540  };
1541
1542  Cropper.setDefaults = function (options) {
1543    $.extend(Cropper.DEFAULTS, options);
1544  };
1545
1546  // Save the other cropper
1547  Cropper.other = $.fn.cropper;
1548
1549  // Register as jQuery plugin
1550  $.fn.cropper = function (options) {
1551    var args = toArray(arguments, 1),
1552        result;
1553
1554    this.each(function () {
1555      var $this = $(this),
1556          data = $this.data("cropper"),
1557          fn;
1558
1559      if (!data) {
1560        $this.data("cropper", (data = new Cropper(this, options)));
1561      }
1562
1563      if (typeof options === "string" && $.isFunction((fn = data[options]))) {
1564        result = fn.apply(data, args);
1565      }
1566    });
1567
1568    return (typeof result !== STRING_UNDEFINED ? result : this);
1569  };
1570
1571  $.fn.cropper.Constructor = Cropper;
1572  $.fn.cropper.setDefaults = Cropper.setDefaults;
1573
1574  // No conflict
1575  $.fn.cropper.noConflict = function () {
1576    $.fn.cropper = Cropper.other;
1577    return this;
1578  };
1579});

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.