text: (function (factory) {
    if (typeof define === "function" && define.amd) {
      // AMD. Register as anonymous module.
      define(["jquery"], factory);
    } else if (typeof exports === "object") {
      // Node / CommonJS
      factory(require("jquery"));
    } else {
      // Browser globals.
      factory(jQuery);
    }
  })(function ($) {
  
    "use strict";
  
    var $window = $(window),
        $document = $(document),
        location = window.location,
  
        // Constants
        TRUE = true,
        FALSE = false,
        NULL = null,
        NAN = NaN,
        INFINITY = Infinity,
        STRING_UNDEFINED = "undefined",
        STRING_DIRECTIVE = "directive",
        CROPPER_NAMESPACE = ".cropper",
  
        // RegExps
        REGEXP_DIRECTIVES = /^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/,
        REGEXP_OPTIONS = /^(x|y|width|height)$/,
        REGEXP_PROPERTIES = /^(naturalWidth|naturalHeight|width|height|aspectRatio|ratio|rotate)$/,
  
        // Classes
        CLASS_MODAL = "cropper-modal",
        CLASS_HIDDEN = "cropper-hidden",
        CLASS_INVISIBLE = "cropper-invisible",
        CLASS_MOVE = "cropper-move",
        CLASS_CROP = "cropper-crop",
        CLASS_DISABLED = "cropper-disabled",
  
        // Events
        EVENT_MOUSE_DOWN = "mousedown touchstart",
        EVENT_MOUSE_MOVE = "mousemove touchmove",
        EVENT_MOUSE_UP = "mouseup mouseleave touchend touchleave touchcancel",
        EVENT_WHEEL = "wheel mousewheel DOMMouseScroll",
        EVENT_RESIZE = "resize" + CROPPER_NAMESPACE, // Bind to window with namespace
        EVENT_DBLCLICK = "dblclick",
        EVENT_BUILD = "build" + CROPPER_NAMESPACE,
        EVENT_BUILT = "built" + CROPPER_NAMESPACE,
        EVENT_DRAG_START = "dragstart" + CROPPER_NAMESPACE,
        EVENT_DRAG_MOVE = "dragmove" + CROPPER_NAMESPACE,
        EVENT_DRAG_END = "dragend" + CROPPER_NAMESPACE,
  
        // Functions
        isNumber = function (n) {
          return typeof n === "number";
        },
  
        toArray = function (obj, offset) {
          var args = [];
  
          if (typeof offset === "number") { // It's necessary for IE8
            args.push(offset);
          }
  
          return args.slice.apply(obj, args);
        },
  
        // Custom proxy to avoid jQuery's guid
        proxy = function (fn, context) {
          var args = toArray(arguments, 2);
  
          return function () {
            return fn.apply(context, args.concat(toArray(arguments)));
          };
        },
  
        addTimestamp = function (url) {
          var timestamp = "timestamp=" + (new Date()).getTime();
  
          return (url + (url.indexOf("?") === -1 ? "?" : "&") + timestamp);
        },
  
        // Constructor
        Cropper = function (element, options) {
          this.element = element;
          this.$element = $(element);
          this.defaults = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) ? options : {});
          this.$original = NULL;
          this.ready = FALSE;
          this.built = FALSE;
          this.cropped = FALSE;
          this.rotated = FALSE;
          this.disabled = FALSE;
          this.replaced = FALSE;
          this.init();
        },
  
        // Others
        sqrt = Math.sqrt,
        min = Math.min,
        max = Math.max,
        abs = Math.abs,
        sin = Math.sin,
        cos = Math.cos,
        num = parseFloat;
  
    Cropper.prototype = {
      constructor: Cropper,
  
      support: {
        canvas: $.isFunction($("<canvas>")[0].getContext)
      },
  
      init: function () {
        var defaults = this.defaults;
  
        $.each(defaults, function (i, n) {
          switch (i) {
            case "aspectRatio":
              defaults[i] = abs(num(n)) || NAN; // 0 -> NaN
              break;
  
            case "autoCropArea":
              defaults[i] = abs(num(n)) || 0.8; // 0 | NaN -> 0.8
              break;
  
            case "minWidth":
            case "minHeight":
              defaults[i] = abs(num(n)) || 0; // NaN -> 0
              break;
  
            case "maxWidth":
            case "maxHeight":
              defaults[i] = abs(num(n)) || INFINITY; // 0 | NaN -> Infinity
              break;
  
            // No default
          }
        });
  
        // Set default image data
        this.image = {
          rotate: 0
        };
  
        this.load();
      },
  
      load: function () {
        var _this = this,
            $this = this.$element,
            element = this.element,
            image = this.image,
            crossOrigin = "",
            $clone,
            url;
  
        if ($this.is("img")) {
          url = $this.prop("src");
        } else if ($this.is("canvas") && this.support.canvas) {
          url = element.toDataURL();
        }
  
        if (!url) {
          return;
        }
  
        // Reset image rotate degree
        if (this.replaced) {
          image.rotate = 0;
        }
  
        if (this.defaults.checkImageOrigin) {
          if ($this.prop("crossOrigin") || this.isCrossOriginURL(url)) {
            crossOrigin = " crossOrigin";
            url = addTimestamp(url); // Bust cache (#119, #148)
          }
        }
  
        this.$clone = ($clone = $("<img" + crossOrigin + ' src="' + url + '">'));
  
        $clone.one("load", function () {
          image.naturalWidth = this.naturalWidth || $clone.width();
          image.naturalHeight = this.naturalHeight || $clone.height();
          image.aspectRatio = image.naturalWidth / image.naturalHeight;
  
          _this.url = url;
          _this.ready = TRUE;
          _this.build();
        });
  
        // Hide and prepend the clone iamge to the document body (Don't append to).
        $clone.addClass(CLASS_INVISIBLE).prependTo("body");
      },
  
      isCrossOriginURL: function (url) {
        var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
  
        if ((parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port))) {
          return TRUE;
        }
  
        return FALSE;
      },
  
      build: function () {
        var $this = this.$element,
            defaults = this.defaults,
            buildEvent,
            $cropper;
  
        if (!this.ready) {
          return;
        }
  
        if (this.built) {
          this.unbuild();
        }
  
        $this.one(EVENT_BUILD, defaults.build); // Only trigger once
        buildEvent = $.Event(EVENT_BUILD);
        $this.trigger(buildEvent);
  
        if (buildEvent.isDefaultPrevented()) {
          return;
        }
  
        // Create cropper elements
        this.$cropper = ($cropper = $(Cropper.TEMPLATE));
  
        // Hide the original image
        $this.addClass(CLASS_HIDDEN);
  
        // Show and prepend the clone iamge to the cropper
        this.$clone.removeClass(CLASS_INVISIBLE).prependTo($cropper);
  
        // Save original image for rotation
        if (!this.rotated) {
          this.$original = this.$clone.clone();
  
          // Append the image to document to avoid "NS_ERROR_NOT_AVAILABLE" error on Firefox when call the "drawImage" method.
          this.$original.addClass(CLASS_HIDDEN).prependTo(this.$cropper);
  
          this.originalImage = $.extend({}, this.image);
        }
  
        this.$container = $this.parent();
        this.$container.append($cropper);
  
        this.$canvas = $cropper.find(".cropper-canvas");
        this.$dragger = $cropper.find(".cropper-dragger");
        this.$viewer = $cropper.find(".cropper-viewer");
  
        defaults.autoCrop ? (this.cropped = TRUE) : this.$dragger.addClass(CLASS_HIDDEN);
        defaults.modal && this.$canvas.addClass(CLASS_MODAL);
        !defaults.dashed && this.$dragger.find(".cropper-dashed").addClass(CLASS_HIDDEN);
        !defaults.movable && this.$dragger.find(".cropper-face").data(STRING_DIRECTIVE, "move");
        !defaults.resizable && this.$dragger.find(".cropper-line, .cropper-point").addClass(CLASS_HIDDEN);
  
        this.addListeners();
        this.initPreview();
  
        this.built = TRUE; // Set `true` before update
        defaults.dragCrop && this.setDragMode("crop"); // Set after built
        this.update();
        this.replaced = FALSE; // Reset to `false` after update
  
        $this.one(EVENT_BUILT, defaults.built); // Only trigger once
        $this.trigger(EVENT_BUILT);
      },
  
      unbuild: function () {
        if (!this.built) {
          return;
        }
  
        this.built = FALSE;
        this.removeListeners();
  
        this.$preview.empty();
        this.$preview = NULL;
  
        this.$dragger = NULL;
        this.$canvas = NULL;
        this.$container = NULL;
  
        this.$cropper.remove();
        this.$cropper = NULL;
      },
  
      update: function (data) {
        this.initContainer();
        this.initCropper();
        this.initImage();
        this.initDragger();
  
        if (data) {
          this.setData(data, TRUE);
          this.setDragMode("crop");
        } else {
          this.setData(this.defaults.data);
        }
      },
  
      resize: function () {
        clearTimeout(this.resizing);
        this.resizing = setTimeout($.proxy(this.update, this, this.getData()), 200);
      },
  
      preview: function () {
        var image = this.image,
            dragger = this.dragger,
            width = image.width,
            height = image.height,
            left = dragger.left - image.left,
            top = dragger.top - image.top;
  
        this.$viewer.find("img").css({
          width: width,
          height: height,
          marginLeft: -left,
          marginTop: -top
        });
  
        this.$preview.each(function () {
          var $this = $(this),
              ratio = $this.width() / dragger.width;
  
          $this.find("img").css({
            width: width * ratio,
            height: height * ratio,
            marginLeft: -left * ratio,
            marginTop: -top * ratio
          });
        });
      },
  
      addListeners: function () {
        var defaults = this.defaults;
  
        this.$element.on(EVENT_DRAG_START, defaults.dragstart).on(EVENT_DRAG_MOVE, defaults.dragmove).on(EVENT_DRAG_END, defaults.dragend);
        this.$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.dragstart, this)).on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
  
        if (defaults.zoomable) {
          this.$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
        }
  
        if (defaults.multiple) {
          this.$cropper.on(EVENT_MOUSE_MOVE, $.proxy(this.dragmove, this)).on(EVENT_MOUSE_UP, $.proxy(this.dragend, this));
        } else {
          $document.on(EVENT_MOUSE_MOVE, (this._dragmove = proxy(this.dragmove, this))).on(EVENT_MOUSE_UP, (this._dragend = proxy(this.dragend, this)));
        }
  
        $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
      },
  
      removeListeners: function () {
        var defaults = this.defaults;
  
        this.$element.off(EVENT_DRAG_START, defaults.dragstart).off(EVENT_DRAG_MOVE, defaults.dragmove).off(EVENT_DRAG_END, defaults.dragend);
        this.$cropper.off(EVENT_MOUSE_DOWN, this.dragstart).off(EVENT_DBLCLICK, this.dblclick);
  
        if (defaults.zoomable) {
          this.$cropper.off(EVENT_WHEEL, this.wheel);
        }
  
        if (defaults.multiple) {
          this.$cropper.off(EVENT_MOUSE_MOVE, this.dragmove).off(EVENT_MOUSE_UP, this.dragend);
        } else {
          $document.off(EVENT_MOUSE_MOVE, this._dragmove).off(EVENT_MOUSE_UP, this._dragend);
        }
  
        $window.off(EVENT_RESIZE, this._resize);
      },
  
      initPreview: function () {
        var img = '<img src="' + this.url + '">';
  
        this.$preview = $(this.defaults.preview);
        this.$viewer.html(img);
        this.$preview.html(img).find("img").css("cssText", "min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;");
      },
  
      initContainer: function () {
        var $container = this.$container;
  
        this.container = {
          width: max($container.width(), this.defaults.minContainerWidth),
          height: max($container.height(), this.defaults.minContainerHeight)
        };
      },
  
      initCropper: function () {
        var container = this.container,
            image = this.image,
            cropper;
  
        if (((image.naturalWidth * container.height / image.naturalHeight) - container.width) >= 0) {
          cropper = {
            width: container.width,
            height: container.width / image.aspectRatio,
            left: 0
          };
  
          cropper.top = (container.height - cropper.height) / 2;
        } else {
          cropper = {
            width: container.height * image.aspectRatio,
            height: container.height,
            top: 0
          };
  
          cropper.left = (container.width - cropper.width) / 2;
        }
  
        this.$cropper.css({
          width: cropper.width,
          height: cropper.height,
          left: cropper.left,
          top: cropper.top
        });
  
        this.cropper = cropper;
      },
  
      initImage: function () {
        var image = this.image,
            cropper = this.cropper,
            defaultImage = {
              _width: cropper.width,
              _height: cropper.height,
              width: cropper.width,
              height: cropper.height,
              left: 0,
              top: 0,
              ratio: cropper.width / image.naturalWidth
            };
  
        this.defaultImage = $.extend({}, image, defaultImage);
  
        if (image._width !== cropper.width || image._height !== cropper.height) {
          $.extend(image, defaultImage);
        } else {
          image = $.extend({}, defaultImage, image);
  
          // Reset image ratio
          if (this.replaced) {
            image.ratio = defaultImage.ratio;
          }
        }
  
        this.image = image;
        this.renderImage();
      },
  
      renderImage: function (mode) {
        var image = this.image;
  
        if (mode === "zoom") {
          image.left -= (image.width - image.oldWidth) / 2;
          image.top -= (image.height - image.oldHeight) / 2;
        }
  
        image.left = min(max(image.left, image._width - image.width), 0);
        image.top = min(max(image.top, image._height - image.height), 0);
  
        this.$clone.css({
          width: image.width,
          height: image.height,
          marginLeft: image.left,
          marginTop: image.top
        });
  
        if (mode) {
          this.defaults.done(this.getData());
          this.preview();
        }
      },
  
      initDragger: function () {
        var defaults = this.defaults,
            cropper = this.cropper,
            // If not set, use the original aspect ratio of the image.
            aspectRatio = defaults.aspectRatio || this.image.aspectRatio,
            ratio = this.image.ratio,
            autoCropDragger,
            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,
            maxWidth: cropper.height * aspectRatio,
            maxHeight: cropper.height
          };
        }
  
        dragger.minWidth = 0;
        dragger.minHeight = 0;
  
        if (defaults.aspectRatio) {
          if (isFinite(defaults.maxWidth)) {
            dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio);
            dragger.maxHeight = dragger.maxWidth / aspectRatio;
          } else if (isFinite(defaults.maxHeight)) {
            dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio);
            dragger.maxWidth = dragger.maxHeight * aspectRatio;
          }
  
          if (defaults.minWidth > 0) {
            dragger.minWidth = max(0, defaults.minWidth * ratio);
            dragger.minHeight = dragger.minWidth / aspectRatio;
          } else if (defaults.minHeight > 0) {
            dragger.minHeight = max(0, defaults.minHeight * ratio);
            dragger.minWidth = dragger.minHeight * aspectRatio;
          }
        } else {
          dragger.maxWidth = min(dragger.maxWidth, defaults.maxWidth * ratio);
          dragger.maxHeight = min(dragger.maxHeight, defaults.maxHeight * ratio);
          dragger.minWidth = max(0, defaults.minWidth * ratio);
          dragger.minHeight = max(0, defaults.minHeight * ratio);
        }
  
        // minWidth can't be greater than maxWidth, and minHeight too.
        dragger.minWidth = min(dragger.maxWidth, dragger.minWidth);
        dragger.minHeight = min(dragger.maxHeight, dragger.minHeight);
  
        // Center the dragger by default
        autoCropDragger = $.extend({}, dragger);
        autoCropDragger.height = dragger.height * defaults.autoCropArea;
        autoCropDragger.width = dragger.width * defaults.autoCropArea;
        autoCropDragger.left = (cropper.width - autoCropDragger.width) / 2;
        autoCropDragger.top = (cropper.height - autoCropDragger.height) / 2;
  
        autoCropDragger.oldLeft = dragger.oldLeft = dragger.left;
        autoCropDragger.oldTop = dragger.oldTop = dragger.top;
  
        this.autoCropDragger = autoCropDragger;
        this.defaultDragger = $.extend({}, dragger);
        this.dragger = dragger;
      },
  
      renderDragger: function () {
        var dragger = this.dragger,
            cropper = this.cropper;
  
        if (dragger.width > dragger.maxWidth) {
          dragger.width = dragger.maxWidth;
          dragger.left = dragger.oldLeft;
        } else if (dragger.width < dragger.minWidth) {
          dragger.width = dragger.minWidth;
          dragger.left = dragger.oldLeft;
        }
  
        if (dragger.height > dragger.maxHeight) {
          dragger.height = dragger.maxHeight;
          dragger.top = dragger.oldTop;
        } else if (dragger.height < dragger.minHeight) {
          dragger.height = dragger.minHeight;
          dragger.top = dragger.oldTop;
        }
  
        dragger.left = min(max(dragger.left, 0), cropper.width - dragger.width);
        dragger.top = min(max(dragger.top, 0), cropper.height - dragger.height);
        dragger.oldLeft = dragger.left;
        dragger.oldTop = dragger.top;
  
        // Re-render the dragger
        this.dragger = dragger;
  
        if (!this.disabled) {
          this.defaults.done(this.getData());
        }
  
        this.$dragger.css({
          width: dragger.width,
          height: dragger.height,
          left: dragger.left,
          top: dragger.top
        });
  
        this.preview();
      },
  
      reset: function (deep) {
        if (!this.cropped) {
          return;
        }
  
        if (deep) {
          this.defaults.data = {};
        }
  
        this.image = $.extend({}, this.defaultImage);
        this.renderImage();
        this.dragger = $.extend({}, this.defaultDragger);
        this.setData(this.defaults.data);
      },
  
      clear: function () {
        if (!this.cropped) {
          return;
        }
  
        this.cropped = FALSE;
  
        this.setData({
          x: 0,
          y: 0,
          width: 0,
          height: 0
        });
  
        this.$canvas.removeClass(CLASS_MODAL);
        this.$dragger.addClass(CLASS_HIDDEN);
      },
  
      destroy: function () {
        var $this = this.$element;
  
        if (!this.ready) {
          return;
        }
  
        this.unbuild();
        $this.removeClass(CLASS_HIDDEN).removeData("cropper");
  
        if (this.rotated) {
          $this.attr("src", this.$original.attr("src"));
        }
      },
  
      replace: function (url, /*INTERNAL*/ rotated) {
        var _this = this,
            $this = this.$element,
            element = this.element,
            context;
  
        if (url && url !== this.url && url !== $this.attr("src")) {
          if (!rotated) {
            this.rotated = FALSE;
            this.replaced = TRUE;
          }
  
          if ($this.is("img")) {
            $this.attr("src", url);
            this.load();
          } else if ($this.is("canvas") && this.support.canvas) {
            context = element.getContext("2d");
  
            $('<img src="' + url + '">').one("load", function () {
              element.width = this.width;
              element.height = this.height;
              context.clearRect(0, 0, element.width, element.height);
              context.drawImage(this, 0, 0);
              _this.load();
            });
          }
        }
      },
  
      setData: function (data, /*INTERNAL*/ once) {
        var cropper = this.cropper,
            dragger = this.dragger,
            image = this.image,
            aspectRatio = this.defaults.aspectRatio;
  
        if (!this.built || typeof data === STRING_UNDEFINED) {
          return;
        }
  
        if (data === NULL || $.isEmptyObject(data)) {
          dragger = $.extend({}, this.autoCropDragger);
        }
  
        if ($.isPlainObject(data) && !$.isEmptyObject(data)) {
  
          if (!once) {
            this.defaults.data = data;
          }
  
          data = this.transformData(data);
  
          if (isNumber(data.x) && data.x <= cropper.width - image.left) {
            dragger.left = data.x + image.left;
          }
  
          if (isNumber(data.y) && data.y <= cropper.height - image.top) {
            dragger.top = data.y + image.top;
          }
  
          if (aspectRatio) {
            if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) {
              dragger.width = data.width;
              dragger.height = dragger.width / aspectRatio;
            } else if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) {
              dragger.height = data.height;
              dragger.width = dragger.height * aspectRatio;
            }
          } else {
            if (isNumber(data.width) && data.width <= dragger.maxWidth && data.width >= dragger.minWidth) {
              dragger.width = data.width;
            }
  
            if (isNumber(data.height) && data.height <= dragger.maxHeight && data.height >= dragger.minHeight) {
              dragger.height = data.height;
            }
          }
        }
  
        this.dragger = dragger;
        this.renderDragger();
      },
  
      getData: function (rounded) {
        var dragger = this.dragger,
            image = this.image,
            data = {};
  
        if (this.built) {
          data = {
            x: dragger.left - image.left,
            y: dragger.top - image.top,
            width: dragger.width,
            height: dragger.height
          };
  
          data = this.transformData(data, TRUE, rounded);
        }
  
        return data;
      },
  
      transformData: function (data, reversed, rounded) {
        var ratio = this.image.ratio,
            result = {};
  
        $.each(data, function (i, n) {
          n = num(n);
  
          if (REGEXP_OPTIONS.test(i) && !isNaN(n)) {
            result[i] = reversed ? (rounded ? Math.round(n / ratio) : n / ratio) : n * ratio;
          }
        });
  
        return result;
      },
  
      setAspectRatio: function (aspectRatio) {
        var freeRatio = aspectRatio === "auto";
  
        aspectRatio = num(aspectRatio);
  
        if (freeRatio || (!isNaN(aspectRatio) && aspectRatio > 0)) {
          this.defaults.aspectRatio = freeRatio ? NAN : aspectRatio;
  
          if (this.built) {
            this.initDragger();
            this.renderDragger();
            this.setData(this.defaults.data); // Reset to initial state
          }
        }
      },
  
      getImageData: function () {
        var data = {};
  
        if (this.ready) {
          $.each(this.image, function (name, value) {
            if (REGEXP_PROPERTIES.test(name)) {
              data[name] = value;
            }
          });
        }
  
        return data;
      },
  
      getDataURL: function (options, type, quality) {
        var canvas = $("<canvas>")[0],
            data = this.getData(),
            dataURL = "",
            context;
  
        if (!$.isPlainObject(options)) {
          quality = type;
          type = options;
          options = {};
        }
  
        options = $.extend({
          width: data.width,
          height: data.height
        }, options);
  
        if (this.cropped && this.support.canvas) {
          canvas.width = options.width;
          canvas.height = options.height;
          context = canvas.getContext("2d");
  
          if (type === "image/jpeg") {
            context.fillStyle = "#fff";
            context.fillRect(0, 0, options.width, options.height);
          }
  
          context.drawImage(this.$clone[0], data.x, data.y, data.width, data.height, 0, 0, options.width, options.height);
          dataURL = canvas.toDataURL(type, quality);
        }
  
        return dataURL;
      },
  
      setDragMode: function (mode) {
        var $canvas = this.$canvas,
            defaults = this.defaults,
            cropable = FALSE,
            movable = FALSE;
  
        if (!this.built || this.disabled) {
          return;
        }
  
        switch (mode) {
          case "crop":
            if (defaults.dragCrop) {
              cropable = TRUE;
              $canvas.data(STRING_DIRECTIVE, mode);
            }
  
            break;
  
          case "move":
            movable = TRUE;
            $canvas.data(STRING_DIRECTIVE, mode);
  
            break;
  
          default:
            $canvas.removeData(STRING_DIRECTIVE);
        }
  
        $canvas.toggleClass(CLASS_CROP, cropable).toggleClass(CLASS_MOVE, movable);
      },
  
      enable: function () {
        if (this.built) {
          this.disabled = FALSE;
          this.$cropper.removeClass(CLASS_DISABLED);
        }
      },
  
      disable: function () {
        if (this.built) {
          this.disabled = TRUE;
          this.$cropper.addClass(CLASS_DISABLED);
        }
      },
  
      rotate: function (degree) {
        var image = this.image;
  
        degree = num(degree) || 0;
  
        if (!this.built || degree === 0 || this.disabled || !this.defaults.rotatable || !this.support.canvas) {
          return;
        }
  
        this.rotated = TRUE;
        degree = (image.rotate = (image.rotate + degree) % 360);
  
         // replace with "true" to prevent to override the original image
        this.replace(this.getRotatedDataURL(degree), true);
      },
  
      getRotatedDataURL: function (degree) {
        var canvas = $("<canvas>")[0],
            context = canvas.getContext("2d"),
            arc = degree * Math.PI / 180,
            deg = abs(degree) % 180,
            acuteAngle = deg > 90 ? (180 - deg) : deg,
            acuteAngleArc = acuteAngle * Math.PI / 180,
            originalImage = this.originalImage,
            naturalWidth = originalImage.naturalWidth,
            naturalHeight = originalImage.naturalHeight,
            width = abs(naturalWidth * cos(acuteAngleArc) + naturalHeight * sin(acuteAngleArc)),
            height = abs(naturalWidth * sin(acuteAngleArc) + naturalHeight * cos(acuteAngleArc));
  
        canvas.width = width;
        canvas.height = height;
        context.save();
        context.translate(width / 2, height / 2);
        context.rotate(arc);
        context.drawImage(this.$original[0], -naturalWidth / 2, -naturalHeight / 2, naturalWidth, naturalHeight);
        context.restore();
  
        return canvas.toDataURL();
      },
  
      zoom: function (delta) {
        var image = this.image,
            width,
            height,
            range;
  
        delta = num(delta);
  
        if (!this.built || !delta || this.disabled || !this.defaults.zoomable) {
          return;
        }
  
        width = image.width * (1 + delta);
        height = image.height * (1 + delta);
        range = width / image._width;
  
        if (range > 10) {
          return;
        }
  
        if (range < 1) {
          width = image._width;
          height = image._height;
        }
  
        if (range <= 1) {
          this.setDragMode("crop");
        } else {
          this.setDragMode("move");
        }
  
        image.oldWidth = image.width;
        image.oldHeight = image.height;
  
        image.width = width;
        image.height = height;
        image.ratio = image.width / image.naturalWidth;
  
        this.renderImage("zoom");
      },
  
      dblclick: function () {
        if (this.disabled) {
          return;
        }
  
        if (this.$canvas.hasClass(CLASS_CROP)) {
          this.setDragMode("move");
        } else {
          this.setDragMode("crop");
        }
      },
  
      wheel: function (event) {
        var e = event.originalEvent,
            msDeltaY = 117.25, // IE
            mozDelatY = 5, // Firefox
            webkitDelatY = 166.66665649414062, // Chrome, Opera
            zoomDelta = 0.1, // 10%
            delta;
  
        if (this.disabled) {
          return;
        }
  
        event.preventDefault();
  
        if (e.deltaY) {
          delta = e.deltaY;
          delta = delta % mozDelatY === 0 ? delta / mozDelatY : delta % msDeltaY === 0 ? delta / msDeltaY : delta / webkitDelatY;
        } else {
          delta = e.wheelDelta ? -e.wheelDelta / 120 : (e.detail ? e.detail / 3 : 0);
        }
  
        this.zoom(delta * zoomDelta);
      },
  
      dragstart: function (event) {
        var touches = event.originalEvent.touches,
            e = event,
            directive,
            dragStartEvent,
            touchesLength;
  
        if (this.disabled) {
          return;
        }
  
        if (touches) {
          touchesLength = touches.length;
  
          if (touchesLength > 1) {
            if (this.defaults.zoomable && touchesLength === 2) {
              e = touches[1];
              this.startX2 = e.pageX;
              this.startY2 = e.pageY;
              directive = "zoom";
            } else {
              return;
            }
          }
  
          e = touches[0];
        }
  
        directive = directive || $(e.target).data(STRING_DIRECTIVE);
  
        if (REGEXP_DIRECTIVES.test(directive)) {
          event.preventDefault();
  
          dragStartEvent = $.Event(EVENT_DRAG_START);
          this.$element.trigger(dragStartEvent);
  
          if (dragStartEvent.isDefaultPrevented()) {
            return;
          }
  
          this.directive = directive;
          this.cropping = FALSE;
          this.startX = e.pageX;
          this.startY = e.pageY;
  
          if (directive === "crop") {
            this.cropping = TRUE;
            this.$canvas.addClass(CLASS_MODAL);
          }
        }
      },
  
      dragmove: function (event) {
        var touches = event.originalEvent.touches,
            e = event,
            dragMoveEvent,
            touchesLength;
  
        if (this.disabled) {
          return;
        }
  
        if (touches) {
          touchesLength = touches.length;
  
          if (touchesLength > 1) {
            if (this.defaults.zoomable && touchesLength === 2) {
              e = touches[1];
              this.endX2 = e.pageX;
              this.endY2 = e.pageY;
            } else {
              return;
            }
          }
  
          e = touches[0];
        }
  
        if (this.directive) {
          event.preventDefault();
  
          dragMoveEvent = $.Event(EVENT_DRAG_MOVE);
          this.$element.trigger(dragMoveEvent);
  
          if (dragMoveEvent.isDefaultPrevented()) {
            return;
          }
  
          this.endX = e.pageX;
          this.endY = e.pageY;
  
          this.dragging();
        }
      },
  
      dragend: function (event) {
        var dragEndEvent;
  
        if (this.disabled) {
          return;
        }
  
        if (this.directive) {
          event.preventDefault();
  
          dragEndEvent = $.Event(EVENT_DRAG_END);
          this.$element.trigger(dragEndEvent);
  
          if (dragEndEvent.isDefaultPrevented()) {
            return;
          }
  
          if (this.cropping) {
            this.cropping = FALSE;
            this.$canvas.toggleClass(CLASS_MODAL, this.cropped && this.defaults.modal);
          }
  
          this.directive = "";
        }
      },
  
      dragging: function () {
        var directive = this.directive,
            image = this.image,
            cropper = this.cropper,
            maxWidth = cropper.width,
            maxHeight = cropper.height,
            dragger = this.dragger,
            width = dragger.width,
            height = dragger.height,
            left = dragger.left,
            top = dragger.top,
            right = left + width,
            bottom = top + height,
            renderable = TRUE,
            defaults = this.defaults,
            aspectRatio = defaults.aspectRatio,
            range = {
              x: this.endX - this.startX,
              y: this.endY - this.startY
            },
            offset;
  
        if (aspectRatio) {
          range.X = range.y * aspectRatio;
          range.Y = range.x / aspectRatio;
        }
  
        switch (directive) {
          // Move dragger
          case "all":
            left += range.x;
            top += range.y;
  
            break;
  
          // Resize dragger
          case "e":
            if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= 0 || bottom >= maxHeight))) {
              renderable = FALSE;
              break;
            }
  
            width += range.x;
  
            if (aspectRatio) {
              height = width / aspectRatio;
              top -= range.Y / 2;
            }
  
            if (width < 0) {
              directive = "w";
              width = 0;
            }
  
            break;
  
          case "n":
            if (range.y <= 0 && (top <= 0 || aspectRatio && (left <= 0 || right >= maxWidth))) {
              renderable = FALSE;
              break;
            }
  
            height -= range.y;
            top += range.y;
  
            if (aspectRatio) {
              width = height * aspectRatio;
              left += range.X / 2;
            }
  
            if (height < 0) {
              directive = "s";
              height = 0;
            }
  
            break;
  
          case "w":
            if (range.x <= 0 && (left <= 0 || aspectRatio && (top <= 0 || bottom >= maxHeight))) {
              renderable = FALSE;
              break;
            }
  
            width -= range.x;
            left += range.x;
  
            if (aspectRatio) {
              height = width / aspectRatio;
              top += range.Y / 2;
            }
  
            if (width < 0) {
              directive = "e";
              width = 0;
            }
  
            break;
  
          case "s":
            if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= 0 || right >= maxWidth))) {
              renderable = FALSE;
              break;
            }
  
            height += range.y;
  
            if (aspectRatio) {
              width = height * aspectRatio;
              left -= range.X / 2;
            }
  
            if (height < 0) {
              directive = "n";
              height = 0;
            }
  
            break;
  
          case "ne":
            if (aspectRatio) {
              if (range.y <= 0 && (top <= 0 || right >= maxWidth)) {
                renderable = FALSE;
                break;
              }
  
              height -= range.y;
              top += range.y;
              width = height * aspectRatio;
            } else {
              if (range.x >= 0) {
                if (right < maxWidth) {
                  width += range.x;
                } else if (range.y <= 0 && top <= 0) {
                  renderable = FALSE;
                }
              } else {
                width += range.x;
              }
  
              if (range.y <= 0) {
                if (top > 0) {
                  height -= range.y;
                  top += range.y;
                }
              } else {
                height -= range.y;
                top += range.y;
              }
            }
  
            if (height < 0) {
              directive = "sw";
              height = 0;
              width = 0;
            }
  
            break;
  
          case "nw":
            if (aspectRatio) {
              if (range.y <= 0 && (top <= 0 || left <= 0)) {
                renderable = FALSE;
                break;
              }
  
              height -= range.y;
              top += range.y;
              width = height * aspectRatio;
              left += range.X;
            } else {
              if (range.x <= 0) {
                if (left > 0) {
                  width -= range.x;
                  left += range.x;
                } else if (range.y <= 0 && top <= 0) {
                  renderable = FALSE;
                }
              } else {
                width -= range.x;
                left += range.x;
              }
  
              if (range.y <= 0) {
                if (top > 0) {
                  height -= range.y;
                  top += range.y;
                }
              } else {
                height -= range.y;
                top += range.y;
              }
            }
  
            if (height < 0) {
              directive = "se";
              height = 0;
              width = 0;
            }
  
            break;
  
          case "sw":
            if (aspectRatio) {
              if (range.x <= 0 && (left <= 0 || bottom >= maxHeight)) {
                renderable = FALSE;
                break;
              }
  
              width -= range.x;
              left += range.x;
              height = width / aspectRatio;
            } else {
              if (range.x <= 0) {
                if (left > 0) {
                  width -= range.x;
                  left += range.x;
                } else if (range.y >= 0 && bottom >= maxHeight) {
                  renderable = FALSE;
                }
              } else {
                width -= range.x;
                left += range.x;
              }
  
              if (range.y >= 0) {
                if (bottom < maxHeight) {
                  height += range.y;
                }
              } else {
                height += range.y;
              }
            }
  
            if (width < 0) {
              directive = "ne";
              height = 0;
              width = 0;
            }
  
            break;
  
          case "se":
            if (aspectRatio) {
              if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
                renderable = FALSE;
                break;
              }
  
              width += range.x;
              height = width / aspectRatio;
            } else {
              if (range.x >= 0) {
                if (right < maxWidth) {
                  width += range.x;
                } else if (range.y >= 0 && bottom >= maxHeight) {
                  renderable = FALSE;
                }
              } else {
                width += range.x;
              }
  
              if (range.y >= 0) {
                if (bottom < maxHeight) {
                  height += range.y;
                }
              } else {
                height += range.y;
              }
            }
  
            if (width < 0) {
              directive = "nw";
              height = 0;
              width = 0;
            }
  
            break;
  
          // Move image
          case "move":
            image.left += range.x;
            image.top += range.y;
            this.renderImage("move");
            renderable = FALSE;
  
            break;
  
          // Scale image
          case "zoom":
            if (defaults.zoomable) {
              this.zoom(function (x, y, x1, y1, x2, y2) {
                return (sqrt(x2 * x2 + y2 * y2) - sqrt(x1 * x1 + y1 * y1)) / sqrt(x * x + y * y);
              }(
                image.width,
                image.height,
                abs(this.startX - this.startX2),
                abs(this.startY - this.startY2),
                abs(this.endX - this.endX2),
                abs(this.endY - this.endY2)
              ));
  
              this.endX2 = this.startX2;
              this.endY2 = this.startY2;
            }
  
            break;
  
          // Crop image
          case "crop":
            if (range.x && range.y) {
              offset = this.$cropper.offset();
              left = this.startX - offset.left;
              top = this.startY - offset.top;
              width = dragger.minWidth;
              height = dragger.minHeight;
  
              if (range.x > 0) {
                if (range.y > 0) {
                  directive = "se";
                } else {
                  directive = "ne";
                  top -= height;
                }
              } else {
                if (range.y > 0) {
                  directive = "sw";
                  left -= width;
                } else {
                  directive = "nw";
                  left -= width;
                  top -= height;
                }
              }
  
              // Show the dragger if is hidden
              if (!this.cropped) {
                this.cropped = TRUE;
                this.$dragger.removeClass(CLASS_HIDDEN);
              }
            }
  
            break;
  
          // No default
        }
  
        if (renderable) {
          dragger.width = width;
          dragger.height = height;
          dragger.left = left;
          dragger.top = top;
          this.directive = directive;
  
          this.renderDragger();
        }
  
        // Override
        this.startX = this.endX;
        this.startY = this.endY;
      }
    };
  
    // Use the string compressor: Strmin (https://github.com/fengyuanchen/strmin)
    Cropper.TEMPLATE = (function (source, words) {
      words = words.split(",");
      return source.replace(/\d+/g, function (i) {
        return words[i];
      });
    })('<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");
  
    /* Template source:
    <div class="cropper-container">
      <div class="cropper-canvas"></div>
      <div class="cropper-dragger">
        <span class="cropper-viewer"></span>
        <span class="cropper-dashed dashed-h"></span>
        <span class="cropper-dashed dashed-v"></span>
        <span class="cropper-face" data-directive="all"></span>
        <span class="cropper-line line-e" data-directive="e"></span>
        <span class="cropper-line line-n" data-directive="n"></span>
        <span class="cropper-line line-w" data-directive="w"></span>
        <span class="cropper-line line-s" data-directive="s"></span>
        <span class="cropper-point point-e" data-directive="e"></span>
        <span class="cropper-point point-n" data-directive="n"></span>
        <span class="cropper-point point-w" data-directive="w"></span>
        <span class="cropper-point point-s" data-directive="s"></span>
        <span class="cropper-point point-ne" data-directive="ne"></span>
        <span class="cropper-point point-nw" data-directive="nw"></span>
        <span class="cropper-point point-sw" data-directive="sw"></span>
        <span class="cropper-point point-se" data-directive="se"></span>
      </div>
    </div>
    */
  
    Cropper.DEFAULTS = {
      // Basic
      aspectRatio: "auto",
      autoCropArea: 0.8, // 80%
      data: {
        // x: 0,
        // y: 0,
        // width: 300,
        // height: 150
      },
      done: $.noop,
      preview: "",
  
      // Toggles
      multiple: FALSE,
      autoCrop: TRUE,
      dragCrop: TRUE,
      dashed: TRUE,
      modal: TRUE,
      movable: TRUE,
      resizable: TRUE,
      zoomable: TRUE,
      rotatable: TRUE,
      checkImageOrigin: TRUE,
  
      // Dimensions
      minWidth: 0,
      minHeight: 0,
      maxWidth: INFINITY,
      maxHeight: INFINITY,
      minContainerWidth: 300,
      minContainerHeight: 150,
  
      // Events
      build: NULL,
      built: NULL,
      dragstart: NULL,
      dragmove: NULL,
      dragend: NULL
    };
  
    Cropper.setDefaults = function (options) {
      $.extend(Cropper.DEFAULTS, options);
    };
  
    // Save the other cropper
    Cropper.other = $.fn.cropper;
  
    // Register as jQuery plugin
    $.fn.cropper = function (options) {
      var args = toArray(arguments, 1),
          result;
  
      this.each(function () {
        var $this = $(this),
            data = $this.data("cropper"),
            fn;
  
        if (!data) {
          $this.data("cropper", (data = new Cropper(this, options)));
        }
  
        if (typeof options === "string" && $.isFunction((fn = data[options]))) {
          result = fn.apply(data, args);
        }
      });
  
      return (typeof result !== STRING_UNDEFINED ? result : this);
    };
  
    $.fn.cropper.Constructor = Cropper;
    $.fn.cropper.setDefaults = Cropper.setDefaults;
  
    // No conflict
    $.fn.cropper.noConflict = function () {
      $.fn.cropper = Cropper.other;
      return this;
    };
  });