camaleon_cms 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camaleon_cms might be problematic. Click here for more details.

Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/assets/javascripts/camaleon_cms/admin/_custom_fields.js +2 -2
  4. data/app/assets/javascripts/camaleon_cms/admin/_post.js +1 -1
  5. data/app/assets/javascripts/camaleon_cms/admin/form/cropper.min.js +2994 -1
  6. data/app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js.coffee +72 -33
  7. data/app/controllers/camaleon_cms/admin/media_controller.rb +1 -0
  8. data/app/controllers/camaleon_cms/admin/sessions_controller.rb +2 -2
  9. data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +11 -6
  10. data/app/controllers/camaleon_cms/admin/users_controller.rb +1 -1
  11. data/app/controllers/camaleon_cms/apps/plugins_front_controller.rb +1 -1
  12. data/app/controllers/concerns/camaleon_cms/frontend_concern.rb +1 -1
  13. data/app/decorators/camaleon_cms/site_decorator.rb +1 -1
  14. data/app/decorators/camaleon_cms/user_decorator.rb +1 -1
  15. data/app/decorators/camaleon_cms/user_role_decorator.rb +12 -0
  16. data/app/helpers/camaleon_cms/session_helper.rb +1 -1
  17. data/app/helpers/camaleon_cms/short_code_helper.rb +25 -7
  18. data/app/models/concerns/camaleon_cms/user_methods.rb +0 -7
  19. data/app/uploaders/camaleon_cms_aws_uploader.rb +2 -5
  20. data/app/uploaders/camaleon_cms_uploader.rb +6 -2
  21. data/app/views/camaleon_cms/admin/settings/custom_fields/_render.html.erb +1 -1
  22. data/app/views/camaleon_cms/admin/settings/custom_fields/form.html.erb +1 -1
  23. data/app/views/camaleon_cms/admin/user_roles/form.html.erb +5 -9
  24. data/app/views/camaleon_cms/admin/user_roles/index.html.erb +3 -3
  25. data/app/views/camaleon_cms/admin/users/form.html.erb +1 -1
  26. data/app/views/camaleon_cms/admin/users/index.html.erb +2 -2
  27. data/config/locales/camaleon_cms/admin/js.yml +2 -0
  28. data/db/migrate/20161215202255_drop_user_relationship_table.rb +5 -0
  29. data/lib/camaleon_cms/version.rb +1 -1
  30. data/spec/dummy/db/schema.rb +1 -11
  31. data/spec/helpers/short_code_helper_spec.rb +64 -0
  32. metadata +6 -3
  33. data/app/models/camaleon_cms/user_relationship.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad03c95897c5dc811f43a791b267f508f4d76bc1
4
- data.tar.gz: bb710c9fc0d1033051fe5401d49d4036b54361ec
3
+ metadata.gz: 8cb2fb3a08dbd2819598ef45a4667d9c0393d1a5
4
+ data.tar.gz: 238ae6a53554ed81d1051d2be04b53c749e6aab3
5
5
  SHA512:
6
- metadata.gz: e0757b58839198c2dd2bb7685f6d4e58b1cc3562d1fa2ee2845208ff060f368cc4f56f12861558b9bd9a8f63efec0f8b5157bf34c8be359727e6bb3537a43b8d
7
- data.tar.gz: 930b9358e5d68a396348120f85a39c1b9a101025799ad57a0e6210d1b6f7e7bdff40877390af7ae5ac63508ce3e9b5d0f48173cbabe9648fa40fee2fd28680b3
6
+ metadata.gz: 1bc85b56ad8e7d379924d9e86a91934dd934c62bfc997f3e3f9a9d15ced8f6cf50ef9744a2c26ffc8f1c1e04ff2ac2ab639d5998f81f3533ceabf44760f27b85
7
+ data.tar.gz: 6bd439b2121f04e8c5197b40d5882d28e3740d714d103b33eaac05e1f279f824aa8ae72a7959cf44f8b316238e2c1b3342b382abf210f51ec0d2f52e3fc90704
data/README.md CHANGED
@@ -39,7 +39,7 @@
39
39
  * Add the gem in your Gemfile
40
40
 
41
41
  ```
42
- gem "camaleon_cms", '>=2.3.7.2' # Stable versions 2.3.6, 2.2.1, 2.1.1, 2.1.0
42
+ gem "camaleon_cms", '>= 2.4.1' # Stable versions 2.3.6, 2.2.1, 2.1.1, 2.1.0
43
43
  # gem "camaleon_cms", github: 'owen2345/camaleon-cms' # current development version
44
44
  ```
45
45
  * Only Rails 5 support
@@ -92,7 +92,7 @@ http://camaleon.tuzitio.com/store/plugins
92
92
  ## Camaleon CMS is FREE and Open source
93
93
  It was released on July, 2015 and tested previously with more than 20 projects by 6 months and on august 22, 2015 was published as a gem.
94
94
 
95
- ![](http://camaleon.tuzitio.com/media/132/multi-language.png)
95
+ ![](screenshot.png)
96
96
 
97
97
  ## With Camaleon you can do:
98
98
  * Multiples sites in the same installation
@@ -1,5 +1,5 @@
1
1
  // build custom field groups with values recovered from DB received in field_values
2
- function build_custom_field_group(field_values, group_id, fields_data, is_repeat){
2
+ function build_custom_field_group(field_values, group_id, fields_data, is_repeat, field_name_group){
3
3
  if(field_values.length == 0) field_values = [{}];
4
4
  var group_panel = $('#custom_field_group_'+group_id);
5
5
  var group_panel_body = group_panel.find(' > .panel-body');
@@ -9,7 +9,7 @@ function build_custom_field_group(field_values, group_id, fields_data, is_repeat
9
9
 
10
10
  function add_group(values){
11
11
  var clone = group_clone.clone();
12
- clone.find('input, textarea, select').not('.code_style').each(function(){ $(this).attr('name', $(this).attr('name').replace('field_options', 'field_options['+field_group_counter+']')) });
12
+ clone.find('input, textarea, select').not('.code_style').each(function(){ $(this).attr('name', $(this).attr('name').replace(field_name_group, field_name_group+'['+field_group_counter+']')) });
13
13
  group_panel_body.append(clone);
14
14
  group_panel.trigger('update_custom_group_number');
15
15
  for(var k in fields_data){
@@ -198,7 +198,7 @@ function cama_init_post(obj) {
198
198
  return;
199
199
  }
200
200
  if ($(window).scrollTop() >= fixed_offset_top + 10) {
201
- fixed_position.css({position: "fixed", width: "279px", top: 0, "z-index": 4});
201
+ fixed_position.css({position: "fixed", width: panel_scroll.width()+'px', top: 0, "z-index": 4});
202
202
  panel_scroll.css("padding-top", fixed_position.height() + 20)
203
203
  } else {
204
204
  fixed_position.css({position: "", width: "auto"});
@@ -7,4 +7,2997 @@
7
7
  *
8
8
  * Date: 2016-09-03T05:50:45.412Z
9
9
  */
10
- !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof exports?require("jquery"):jQuery)}(function(t){"use strict";function i(t){return"number"==typeof t&&!isNaN(t)}function e(t){return"undefined"==typeof t}function s(t,e){var s=[];return i(e)&&s.push(e),s.slice.apply(t,s)}function a(t,i){var e=s(arguments,2);return function(){return t.apply(i,e.concat(s(arguments)))}}function o(t){var i=t.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);return i&&(i[1]!==C.protocol||i[2]!==C.hostname||i[3]!==C.port)}function h(t){var i="timestamp="+(new Date).getTime();return t+(t.indexOf("?")===-1?"?":"&")+i}function n(t){return t?' crossOrigin="'+t+'"':""}function r(t,i){var e;return t.naturalWidth&&!mt?i(t.naturalWidth,t.naturalHeight):(e=document.createElement("img"),e.onload=function(){i(this.width,this.height)},void(e.src=t.src))}function p(t){var e=[],s=t.rotate,a=t.scaleX,o=t.scaleY;return i(s)&&0!==s&&e.push("rotate("+s+"deg)"),i(a)&&1!==a&&e.push("scaleX("+a+")"),i(o)&&1!==o&&e.push("scaleY("+o+")"),e.length?e.join(" "):"none"}function l(t,i){var e,s,a=Ct(t.degree)%180,o=(a>90?180-a:a)*Math.PI/180,h=bt(o),n=Bt(o),r=t.width,p=t.height,l=t.aspectRatio;return i?(e=r/(n+h/l),s=e/l):(e=r*n+p*h,s=r*h+p*n),{width:e,height:s}}function c(e,s){var a,o,h,n=t("<canvas>")[0],r=n.getContext("2d"),p=0,c=0,d=s.naturalWidth,g=s.naturalHeight,u=s.rotate,f=s.scaleX,m=s.scaleY,v=i(f)&&i(m)&&(1!==f||1!==m),w=i(u)&&0!==u,x=w||v,C=d*Ct(f||1),b=g*Ct(m||1);return v&&(a=C/2,o=b/2),w&&(h=l({width:C,height:b,degree:u}),C=h.width,b=h.height,a=C/2,o=b/2),n.width=C,n.height=b,x&&(p=-d/2,c=-g/2,r.save(),r.translate(a,o)),w&&r.rotate(u*Math.PI/180),v&&r.scale(f,m),r.drawImage(e,$t(p),$t(c),$t(d),$t(g)),x&&r.restore(),n}function d(i){var e=i.length,s=0,a=0;return e&&(t.each(i,function(t,i){s+=i.pageX,a+=i.pageY}),s/=e,a/=e),{pageX:s,pageY:a}}function g(t,i,e){var s,a="";for(s=i,e+=i;s<e;s++)a+=Lt(t.getUint8(s));return a}function u(t){var i,e,s,a,o,h,n,r,p,l,c=new D(t),d=c.byteLength;if(255===c.getUint8(0)&&216===c.getUint8(1))for(p=2;p<d;){if(255===c.getUint8(p)&&225===c.getUint8(p+1)){n=p;break}p++}if(n&&(e=n+4,s=n+10,"Exif"===g(c,e,4)&&(h=c.getUint16(s),o=18761===h,(o||19789===h)&&42===c.getUint16(s+2,o)&&(a=c.getUint32(s+4,o),a>=8&&(r=s+a)))),r)for(d=c.getUint16(r,o),l=0;l<d;l++)if(p=r+12*l+2,274===c.getUint16(p,o)){p+=8,i=c.getUint16(p,o),mt&&c.setUint16(p,1,o);break}return i}function f(t){var i,e=t.replace(G,""),s=atob(e),a=s.length,o=new B(a),h=new y(o);for(i=0;i<a;i++)h[i]=s.charCodeAt(i);return o}function m(t){var i,e=new y(t),s=e.length,a="";for(i=0;i<s;i++)a+=Lt(e[i]);return"data:image/jpeg;base64,"+$(a)}function v(i,e){this.$element=t(i),this.options=t.extend({},v.DEFAULTS,t.isPlainObject(e)&&e),this.isLoaded=!1,this.isBuilt=!1,this.isCompleted=!1,this.isRotated=!1,this.isCropped=!1,this.isDisabled=!1,this.isReplaced=!1,this.isLimited=!1,this.wheeling=!1,this.isImg=!1,this.originalUrl="",this.canvas=null,this.cropBox=null,this.init()}var w=t(window),x=t(document),C=window.location,b=window.navigator,B=window.ArrayBuffer,y=window.Uint8Array,D=window.DataView,$=window.btoa,L="cropper",T="cropper-modal",X="cropper-hide",Y="cropper-hidden",k="cropper-invisible",M="cropper-move",W="cropper-crop",H="cropper-disabled",R="cropper-bg",z="mousedown touchstart pointerdown MSPointerDown",O="mousemove touchmove pointermove MSPointerMove",P="mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel",E="wheel mousewheel DOMMouseScroll",U="dblclick",I="load."+L,F="error."+L,j="resize."+L,A="build."+L,S="built."+L,N="cropstart."+L,_="cropmove."+L,q="cropend."+L,K="crop."+L,Z="zoom."+L,Q=/^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/,V=/^data:/,G=/^data:([^;]+);base64,/,J=/^data:image\/jpeg.*;base64,/,tt="preview",it="action",et="e",st="w",at="s",ot="n",ht="se",nt="sw",rt="ne",pt="nw",lt="all",ct="crop",dt="move",gt="zoom",ut="none",ft=t.isFunction(t("<canvas>")[0].getContext),mt=b&&/(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(b.userAgent),vt=Number,wt=Math.min,xt=Math.max,Ct=Math.abs,bt=Math.sin,Bt=Math.cos,yt=Math.sqrt,Dt=Math.round,$t=Math.floor,Lt=String.fromCharCode;v.prototype={constructor:v,init:function(){var t,i=this.$element;if(i.is("img")){if(this.isImg=!0,this.originalUrl=t=i.attr("src"),!t)return;t=i.prop("src")}else i.is("canvas")&&ft&&(t=i[0].toDataURL());this.load(t)},trigger:function(i,e){var s=t.Event(i,e);return this.$element.trigger(s),s},load:function(i){var e,s,a=this.options,n=this.$element;if(i&&(n.one(A,a.build),!this.trigger(A).isDefaultPrevented())){if(this.url=i,this.image={},!a.checkOrientation||!B)return this.clone();if(e=t.proxy(this.read,this),V.test(i))return J.test(i)?e(f(i)):this.clone();s=new XMLHttpRequest,s.onerror=s.onabort=t.proxy(function(){this.clone()},this),s.onload=function(){e(this.response)},a.checkCrossOrigin&&o(i)&&n.prop("crossOrigin")&&(i=h(i)),s.open("get",i),s.responseType="arraybuffer",s.send()}},read:function(t){var i=this.options,e=u(t),s=this.image,a=0,o=1,h=1;if(e>1)switch(this.url=m(t),e){case 2:o=-1;break;case 3:a=-180;break;case 4:h=-1;break;case 5:a=90,h=-1;break;case 6:a=90;break;case 7:a=90,o=-1;break;case 8:a=-90}i.rotatable&&(s.rotate=a),i.scalable&&(s.scaleX=o,s.scaleY=h),this.clone()},clone:function(){var i,e,s=this.options,a=this.$element,r=this.url,p="";s.checkCrossOrigin&&o(r)&&(p=a.prop("crossOrigin"),p?i=r:(p="anonymous",i=h(r))),this.crossOrigin=p,this.crossOriginUrl=i,this.$clone=e=t("<img"+n(p)+' src="'+(i||r)+'">'),this.isImg?a[0].complete?this.start():a.one(I,t.proxy(this.start,this)):e.one(I,t.proxy(this.start,this)).one(F,t.proxy(this.stop,this)).addClass(X).insertAfter(a)},start:function(){var i=this.$element,e=this.$clone;this.isImg||(e.off(F,this.stop),i=e),r(i[0],t.proxy(function(i,e){t.extend(this.image,{naturalWidth:i,naturalHeight:e,aspectRatio:i/e}),this.isLoaded=!0,this.build()},this))},stop:function(){this.$clone.remove(),this.$clone=null},build:function(){var i,e,s,a=this.options,o=this.$element,h=this.$clone;this.isLoaded&&(this.isBuilt&&this.unbuild(),this.$container=o.parent(),this.$cropper=i=t(v.TEMPLATE),this.$canvas=i.find(".cropper-canvas").append(h),this.$dragBox=i.find(".cropper-drag-box"),this.$cropBox=e=i.find(".cropper-crop-box"),this.$viewBox=i.find(".cropper-view-box"),this.$face=s=e.find(".cropper-face"),o.addClass(Y).after(i),this.isImg||h.removeClass(X),this.initPreview(),this.bind(),a.aspectRatio=xt(0,a.aspectRatio)||NaN,a.viewMode=xt(0,wt(3,Dt(a.viewMode)))||0,a.autoCrop?(this.isCropped=!0,a.modal&&this.$dragBox.addClass(T)):e.addClass(Y),a.guides||e.find(".cropper-dashed").addClass(Y),a.center||e.find(".cropper-center").addClass(Y),a.cropBoxMovable&&s.addClass(M).data(it,lt),a.highlight||s.addClass(k),a.background&&i.addClass(R),a.cropBoxResizable||e.find(".cropper-line, .cropper-point").addClass(Y),this.setDragMode(a.dragMode),this.render(),this.isBuilt=!0,this.setData(a.data),o.one(S,a.built),this.completing=setTimeout(t.proxy(function(){this.trigger(S),this.trigger(K,this.getData()),this.isCompleted=!0},this),0))},unbuild:function(){this.isBuilt&&(this.isCompleted||clearTimeout(this.completing),this.isBuilt=!1,this.isCompleted=!1,this.initialImage=null,this.initialCanvas=null,this.initialCropBox=null,this.container=null,this.canvas=null,this.cropBox=null,this.unbind(),this.resetPreview(),this.$preview=null,this.$viewBox=null,this.$cropBox=null,this.$dragBox=null,this.$canvas=null,this.$container=null,this.$cropper.remove(),this.$cropper=null)},render:function(){this.initContainer(),this.initCanvas(),this.initCropBox(),this.renderCanvas(),this.isCropped&&this.renderCropBox()},initContainer:function(){var t=this.options,i=this.$element,e=this.$container,s=this.$cropper;s.addClass(Y),i.removeClass(Y),s.css(this.container={width:xt(e.width(),vt(t.minContainerWidth)||200),height:xt(e.height(),vt(t.minContainerHeight)||100)}),i.addClass(Y),s.removeClass(Y)},initCanvas:function(){var i,e=this.options.viewMode,s=this.container,a=s.width,o=s.height,h=this.image,n=h.naturalWidth,r=h.naturalHeight,p=90===Ct(h.rotate),l=p?r:n,c=p?n:r,d=l/c,g=a,u=o;o*d>a?3===e?g=o*d:u=a/d:3===e?u=a/d:g=o*d,i={naturalWidth:l,naturalHeight:c,aspectRatio:d,width:g,height:u},i.oldLeft=i.left=(a-g)/2,i.oldTop=i.top=(o-u)/2,this.canvas=i,this.isLimited=1===e||2===e,this.limitCanvas(!0,!0),this.initialImage=t.extend({},h),this.initialCanvas=t.extend({},i)},limitCanvas:function(t,i){var e,s,a,o,h=this.options,n=h.viewMode,r=this.container,p=r.width,l=r.height,c=this.canvas,d=c.aspectRatio,g=this.cropBox,u=this.isCropped&&g;t&&(e=vt(h.minCanvasWidth)||0,s=vt(h.minCanvasHeight)||0,n&&(n>1?(e=xt(e,p),s=xt(s,l),3===n&&(s*d>e?e=s*d:s=e/d)):e?e=xt(e,u?g.width:0):s?s=xt(s,u?g.height:0):u&&(e=g.width,s=g.height,s*d>e?e=s*d:s=e/d)),e&&s?s*d>e?s=e/d:e=s*d:e?s=e/d:s&&(e=s*d),c.minWidth=e,c.minHeight=s,c.maxWidth=1/0,c.maxHeight=1/0),i&&(n?(a=p-c.width,o=l-c.height,c.minLeft=wt(0,a),c.minTop=wt(0,o),c.maxLeft=xt(0,a),c.maxTop=xt(0,o),u&&this.isLimited&&(c.minLeft=wt(g.left,g.left+g.width-c.width),c.minTop=wt(g.top,g.top+g.height-c.height),c.maxLeft=g.left,c.maxTop=g.top,2===n&&(c.width>=p&&(c.minLeft=wt(0,a),c.maxLeft=xt(0,a)),c.height>=l&&(c.minTop=wt(0,o),c.maxTop=xt(0,o))))):(c.minLeft=-c.width,c.minTop=-c.height,c.maxLeft=p,c.maxTop=l))},renderCanvas:function(t){var i,e,s=this.canvas,a=this.image,o=a.rotate,h=a.naturalWidth,n=a.naturalHeight;this.isRotated&&(this.isRotated=!1,e=l({width:a.width,height:a.height,degree:o}),i=e.width/e.height,i!==s.aspectRatio&&(s.left-=(e.width-s.width)/2,s.top-=(e.height-s.height)/2,s.width=e.width,s.height=e.height,s.aspectRatio=i,s.naturalWidth=h,s.naturalHeight=n,o%180&&(e=l({width:h,height:n,degree:o}),s.naturalWidth=e.width,s.naturalHeight=e.height),this.limitCanvas(!0,!1))),(s.width>s.maxWidth||s.width<s.minWidth)&&(s.left=s.oldLeft),(s.height>s.maxHeight||s.height<s.minHeight)&&(s.top=s.oldTop),s.width=wt(xt(s.width,s.minWidth),s.maxWidth),s.height=wt(xt(s.height,s.minHeight),s.maxHeight),this.limitCanvas(!1,!0),s.oldLeft=s.left=wt(xt(s.left,s.minLeft),s.maxLeft),s.oldTop=s.top=wt(xt(s.top,s.minTop),s.maxTop),this.$canvas.css({width:s.width,height:s.height,left:s.left,top:s.top}),this.renderImage(),this.isCropped&&this.isLimited&&this.limitCropBox(!0,!0),t&&this.output()},renderImage:function(i){var e,s=this.canvas,a=this.image;a.rotate&&(e=l({width:s.width,height:s.height,degree:a.rotate,aspectRatio:a.aspectRatio},!0)),t.extend(a,e?{width:e.width,height:e.height,left:(s.width-e.width)/2,top:(s.height-e.height)/2}:{width:s.width,height:s.height,left:0,top:0}),this.$clone.css({width:a.width,height:a.height,marginLeft:a.left,marginTop:a.top,transform:p(a)}),i&&this.output()},initCropBox:function(){var i=this.options,e=this.canvas,s=i.aspectRatio,a=vt(i.autoCropArea)||.8,o={width:e.width,height:e.height};s&&(e.height*s>e.width?o.height=o.width/s:o.width=o.height*s),this.cropBox=o,this.limitCropBox(!0,!0),o.width=wt(xt(o.width,o.minWidth),o.maxWidth),o.height=wt(xt(o.height,o.minHeight),o.maxHeight),o.width=xt(o.minWidth,o.width*a),o.height=xt(o.minHeight,o.height*a),o.oldLeft=o.left=e.left+(e.width-o.width)/2,o.oldTop=o.top=e.top+(e.height-o.height)/2,this.initialCropBox=t.extend({},o)},limitCropBox:function(t,i){var e,s,a,o,h=this.options,n=h.aspectRatio,r=this.container,p=r.width,l=r.height,c=this.canvas,d=this.cropBox,g=this.isLimited;t&&(e=vt(h.minCropBoxWidth)||0,s=vt(h.minCropBoxHeight)||0,e=wt(e,p),s=wt(s,l),a=wt(p,g?c.width:p),o=wt(l,g?c.height:l),n&&(e&&s?s*n>e?s=e/n:e=s*n:e?s=e/n:s&&(e=s*n),o*n>a?o=a/n:a=o*n),d.minWidth=wt(e,a),d.minHeight=wt(s,o),d.maxWidth=a,d.maxHeight=o),i&&(g?(d.minLeft=xt(0,c.left),d.minTop=xt(0,c.top),d.maxLeft=wt(p,c.left+c.width)-d.width,d.maxTop=wt(l,c.top+c.height)-d.height):(d.minLeft=0,d.minTop=0,d.maxLeft=p-d.width,d.maxTop=l-d.height))},renderCropBox:function(){var t=this.options,i=this.container,e=i.width,s=i.height,a=this.cropBox;(a.width>a.maxWidth||a.width<a.minWidth)&&(a.left=a.oldLeft),(a.height>a.maxHeight||a.height<a.minHeight)&&(a.top=a.oldTop),a.width=wt(xt(a.width,a.minWidth),a.maxWidth),a.height=wt(xt(a.height,a.minHeight),a.maxHeight),this.limitCropBox(!1,!0),a.oldLeft=a.left=wt(xt(a.left,a.minLeft),a.maxLeft),a.oldTop=a.top=wt(xt(a.top,a.minTop),a.maxTop),t.movable&&t.cropBoxMovable&&this.$face.data(it,a.width===e&&a.height===s?dt:lt),this.$cropBox.css({width:a.width,height:a.height,left:a.left,top:a.top}),this.isCropped&&this.isLimited&&this.limitCanvas(!0,!0),this.isDisabled||this.output()},output:function(){this.preview(),this.isCompleted&&this.trigger(K,this.getData())},initPreview:function(){var i,e=n(this.crossOrigin),s=e?this.crossOriginUrl:this.url;this.$preview=t(this.options.preview),this.$clone2=i=t("<img"+e+' src="'+s+'">'),this.$viewBox.html(i),this.$preview.each(function(){var i=t(this);i.data(tt,{width:i.width(),height:i.height(),html:i.html()}),i.html("<img"+e+' src="'+s+'" style="display:block;width:100%;height:auto;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation:0deg!important;">')})},resetPreview:function(){this.$preview.each(function(){var i=t(this),e=i.data(tt);i.css({width:e.width,height:e.height}).html(e.html).removeData(tt)})},preview:function(){var i=this.image,e=this.canvas,s=this.cropBox,a=s.width,o=s.height,h=i.width,n=i.height,r=s.left-e.left-i.left,l=s.top-e.top-i.top;this.isCropped&&!this.isDisabled&&(this.$clone2.css({width:h,height:n,marginLeft:-r,marginTop:-l,transform:p(i)}),this.$preview.each(function(){var e=t(this),s=e.data(tt),c=s.width,d=s.height,g=c,u=d,f=1;a&&(f=c/a,u=o*f),o&&u>d&&(f=d/o,g=a*f,u=d),e.css({width:g,height:u}).find("img").css({width:h*f,height:n*f,marginLeft:-r*f,marginTop:-l*f,transform:p(i)})}))},bind:function(){var i=this.options,e=this.$element,s=this.$cropper;t.isFunction(i.cropstart)&&e.on(N,i.cropstart),t.isFunction(i.cropmove)&&e.on(_,i.cropmove),t.isFunction(i.cropend)&&e.on(q,i.cropend),t.isFunction(i.crop)&&e.on(K,i.crop),t.isFunction(i.zoom)&&e.on(Z,i.zoom),s.on(z,t.proxy(this.cropStart,this)),i.zoomable&&i.zoomOnWheel&&s.on(E,t.proxy(this.wheel,this)),i.toggleDragModeOnDblclick&&s.on(U,t.proxy(this.dblclick,this)),x.on(O,this._cropMove=a(this.cropMove,this)).on(P,this._cropEnd=a(this.cropEnd,this)),i.responsive&&w.on(j,this._resize=a(this.resize,this))},unbind:function(){var i=this.options,e=this.$element,s=this.$cropper;t.isFunction(i.cropstart)&&e.off(N,i.cropstart),t.isFunction(i.cropmove)&&e.off(_,i.cropmove),t.isFunction(i.cropend)&&e.off(q,i.cropend),t.isFunction(i.crop)&&e.off(K,i.crop),t.isFunction(i.zoom)&&e.off(Z,i.zoom),s.off(z,this.cropStart),i.zoomable&&i.zoomOnWheel&&s.off(E,this.wheel),i.toggleDragModeOnDblclick&&s.off(U,this.dblclick),x.off(O,this._cropMove).off(P,this._cropEnd),i.responsive&&w.off(j,this._resize)},resize:function(){var i,e,s,a=this.options.restore,o=this.$container,h=this.container;!this.isDisabled&&h&&(s=o.width()/h.width,1===s&&o.height()===h.height||(a&&(i=this.getCanvasData(),e=this.getCropBoxData()),this.render(),a&&(this.setCanvasData(t.each(i,function(t,e){i[t]=e*s})),this.setCropBoxData(t.each(e,function(t,i){e[t]=i*s})))))},dblclick:function(){this.isDisabled||(this.$dragBox.hasClass(W)?this.setDragMode(dt):this.setDragMode(ct))},wheel:function(i){var e=i.originalEvent||i,s=vt(this.options.wheelZoomRatio)||.1,a=1;this.isDisabled||(i.preventDefault(),this.wheeling||(this.wheeling=!0,setTimeout(t.proxy(function(){this.wheeling=!1},this),50),e.deltaY?a=e.deltaY>0?1:-1:e.wheelDelta?a=-e.wheelDelta/120:e.detail&&(a=e.detail>0?1:-1),this.zoom(-a*s,i)))},cropStart:function(i){var e,s,a=this.options,o=i.originalEvent,h=o&&o.touches,n=i;if(!this.isDisabled){if(h){if(e=h.length,e>1){if(!a.zoomable||!a.zoomOnTouch||2!==e)return;n=h[1],this.startX2=n.pageX,this.startY2=n.pageY,s=gt}n=h[0]}if(s=s||t(n.target).data(it),Q.test(s)){if(this.trigger(N,{originalEvent:o,action:s}).isDefaultPrevented())return;i.preventDefault(),this.action=s,this.cropping=!1,this.startX=n.pageX||o&&o.pageX,this.startY=n.pageY||o&&o.pageY,s===ct&&(this.cropping=!0,this.$dragBox.addClass(T))}}},cropMove:function(t){var i,e=this.options,s=t.originalEvent,a=s&&s.touches,o=t,h=this.action;if(!this.isDisabled){if(a){if(i=a.length,i>1){if(!e.zoomable||!e.zoomOnTouch||2!==i)return;o=a[1],this.endX2=o.pageX,this.endY2=o.pageY}o=a[0]}if(h){if(this.trigger(_,{originalEvent:s,action:h}).isDefaultPrevented())return;t.preventDefault(),this.endX=o.pageX||s&&s.pageX,this.endY=o.pageY||s&&s.pageY,this.change(o.shiftKey,h===gt?t:null)}}},cropEnd:function(t){var i=t.originalEvent,e=this.action;this.isDisabled||e&&(t.preventDefault(),this.cropping&&(this.cropping=!1,this.$dragBox.toggleClass(T,this.isCropped&&this.options.modal)),this.action="",this.trigger(q,{originalEvent:i,action:e}))},change:function(t,i){var e,s,a=this.options,o=a.aspectRatio,h=this.action,n=this.container,r=this.canvas,p=this.cropBox,l=p.width,c=p.height,d=p.left,g=p.top,u=d+l,f=g+c,m=0,v=0,w=n.width,x=n.height,C=!0;switch(!o&&t&&(o=l&&c?l/c:1),this.isLimited&&(m=p.minLeft,v=p.minTop,w=m+wt(n.width,r.width,r.left+r.width),x=v+wt(n.height,r.height,r.top+r.height)),s={x:this.endX-this.startX,y:this.endY-this.startY},o&&(s.X=s.y*o,s.Y=s.x/o),h){case lt:d+=s.x,g+=s.y;break;case et:if(s.x>=0&&(u>=w||o&&(g<=v||f>=x))){C=!1;break}l+=s.x,o&&(c=l/o,g-=s.Y/2),l<0&&(h=st,l=0);break;case ot:if(s.y<=0&&(g<=v||o&&(d<=m||u>=w))){C=!1;break}c-=s.y,g+=s.y,o&&(l=c*o,d+=s.X/2),c<0&&(h=at,c=0);break;case st:if(s.x<=0&&(d<=m||o&&(g<=v||f>=x))){C=!1;break}l-=s.x,d+=s.x,o&&(c=l/o,g+=s.Y/2),l<0&&(h=et,l=0);break;case at:if(s.y>=0&&(f>=x||o&&(d<=m||u>=w))){C=!1;break}c+=s.y,o&&(l=c*o,d-=s.X/2),c<0&&(h=ot,c=0);break;case rt:if(o){if(s.y<=0&&(g<=v||u>=w)){C=!1;break}c-=s.y,g+=s.y,l=c*o}else s.x>=0?u<w?l+=s.x:s.y<=0&&g<=v&&(C=!1):l+=s.x,s.y<=0?g>v&&(c-=s.y,g+=s.y):(c-=s.y,g+=s.y);l<0&&c<0?(h=nt,c=0,l=0):l<0?(h=pt,l=0):c<0&&(h=ht,c=0);break;case pt:if(o){if(s.y<=0&&(g<=v||d<=m)){C=!1;break}c-=s.y,g+=s.y,l=c*o,d+=s.X}else s.x<=0?d>m?(l-=s.x,d+=s.x):s.y<=0&&g<=v&&(C=!1):(l-=s.x,d+=s.x),s.y<=0?g>v&&(c-=s.y,g+=s.y):(c-=s.y,g+=s.y);l<0&&c<0?(h=ht,c=0,l=0):l<0?(h=rt,l=0):c<0&&(h=nt,c=0);break;case nt:if(o){if(s.x<=0&&(d<=m||f>=x)){C=!1;break}l-=s.x,d+=s.x,c=l/o}else s.x<=0?d>m?(l-=s.x,d+=s.x):s.y>=0&&f>=x&&(C=!1):(l-=s.x,d+=s.x),s.y>=0?f<x&&(c+=s.y):c+=s.y;l<0&&c<0?(h=rt,c=0,l=0):l<0?(h=ht,l=0):c<0&&(h=pt,c=0);break;case ht:if(o){if(s.x>=0&&(u>=w||f>=x)){C=!1;break}l+=s.x,c=l/o}else s.x>=0?u<w?l+=s.x:s.y>=0&&f>=x&&(C=!1):l+=s.x,s.y>=0?f<x&&(c+=s.y):c+=s.y;l<0&&c<0?(h=pt,c=0,l=0):l<0?(h=nt,l=0):c<0&&(h=rt,c=0);break;case dt:this.move(s.x,s.y),C=!1;break;case gt:this.zoom(function(t,i,e,s){var a=yt(t*t+i*i),o=yt(e*e+s*s);return(o-a)/a}(Ct(this.startX-this.startX2),Ct(this.startY-this.startY2),Ct(this.endX-this.endX2),Ct(this.endY-this.endY2)),i),this.startX2=this.endX2,this.startY2=this.endY2,C=!1;break;case ct:if(!s.x||!s.y){C=!1;break}e=this.$cropper.offset(),d=this.startX-e.left,g=this.startY-e.top,l=p.minWidth,c=p.minHeight,s.x>0?h=s.y>0?ht:rt:s.x<0&&(d-=l,h=s.y>0?nt:pt),s.y<0&&(g-=c),this.isCropped||(this.$cropBox.removeClass(Y),this.isCropped=!0,this.isLimited&&this.limitCropBox(!0,!0))}C&&(p.width=l,p.height=c,p.left=d,p.top=g,this.action=h,this.renderCropBox()),this.startX=this.endX,this.startY=this.endY},crop:function(){this.isBuilt&&!this.isDisabled&&(this.isCropped||(this.isCropped=!0,this.limitCropBox(!0,!0),this.options.modal&&this.$dragBox.addClass(T),this.$cropBox.removeClass(Y)),this.setCropBoxData(this.initialCropBox))},reset:function(){this.isBuilt&&!this.isDisabled&&(this.image=t.extend({},this.initialImage),this.canvas=t.extend({},this.initialCanvas),this.cropBox=t.extend({},this.initialCropBox),this.renderCanvas(),this.isCropped&&this.renderCropBox())},clear:function(){this.isCropped&&!this.isDisabled&&(t.extend(this.cropBox,{left:0,top:0,width:0,height:0}),this.isCropped=!1,this.renderCropBox(),this.limitCanvas(!0,!0),this.renderCanvas(),this.$dragBox.removeClass(T),this.$cropBox.addClass(Y))},replace:function(t,i){!this.isDisabled&&t&&(this.isImg&&this.$element.attr("src",t),i?(this.url=t,this.$clone.attr("src",t),this.isBuilt&&this.$preview.find("img").add(this.$clone2).attr("src",t)):(this.isImg&&(this.isReplaced=!0),this.options.data=null,this.load(t)))},enable:function(){this.isBuilt&&(this.isDisabled=!1,this.$cropper.removeClass(H))},disable:function(){this.isBuilt&&(this.isDisabled=!0,this.$cropper.addClass(H))},destroy:function(){var t=this.$element;this.isLoaded?(this.isImg&&this.isReplaced&&t.attr("src",this.originalUrl),this.unbuild(),t.removeClass(Y)):this.isImg?t.off(I,this.start):this.$clone&&this.$clone.remove(),t.removeData(L)},move:function(t,i){var s=this.canvas;this.moveTo(e(t)?t:s.left+vt(t),e(i)?i:s.top+vt(i))},moveTo:function(t,s){var a=this.canvas,o=!1;e(s)&&(s=t),t=vt(t),s=vt(s),this.isBuilt&&!this.isDisabled&&this.options.movable&&(i(t)&&(a.left=t,o=!0),i(s)&&(a.top=s,o=!0),o&&this.renderCanvas(!0))},zoom:function(t,i){var e=this.canvas;t=vt(t),t=t<0?1/(1-t):1+t,this.zoomTo(e.width*t/e.naturalWidth,i)},zoomTo:function(t,i){var e,s,a,o,h,n=this.options,r=this.canvas,p=r.width,l=r.height,c=r.naturalWidth,g=r.naturalHeight;if(t=vt(t),t>=0&&this.isBuilt&&!this.isDisabled&&n.zoomable){if(s=c*t,a=g*t,i&&(e=i.originalEvent),this.trigger(Z,{originalEvent:e,oldRatio:p/c,ratio:s/c}).isDefaultPrevented())return;e?(o=this.$cropper.offset(),h=e.touches?d(e.touches):{pageX:i.pageX||e.pageX||0,pageY:i.pageY||e.pageY||0},r.left-=(s-p)*((h.pageX-o.left-r.left)/p),r.top-=(a-l)*((h.pageY-o.top-r.top)/l)):(r.left-=(s-p)/2,r.top-=(a-l)/2),r.width=s,r.height=a,this.renderCanvas(!0)}},rotate:function(t){this.rotateTo((this.image.rotate||0)+vt(t))},rotateTo:function(t){t=vt(t),i(t)&&this.isBuilt&&!this.isDisabled&&this.options.rotatable&&(this.image.rotate=t%360,this.isRotated=!0,this.renderCanvas(!0))},scale:function(t,s){var a=this.image,o=!1;e(s)&&(s=t),t=vt(t),s=vt(s),this.isBuilt&&!this.isDisabled&&this.options.scalable&&(i(t)&&(a.scaleX=t,o=!0),i(s)&&(a.scaleY=s,o=!0),o&&this.renderImage(!0))},scaleX:function(t){var e=this.image.scaleY;this.scale(t,i(e)?e:1)},scaleY:function(t){var e=this.image.scaleX;this.scale(i(e)?e:1,t)},getData:function(i){var e,s,a=this.options,o=this.image,h=this.canvas,n=this.cropBox;return this.isBuilt&&this.isCropped?(s={x:n.left-h.left,y:n.top-h.top,width:n.width,height:n.height},e=o.width/o.naturalWidth,t.each(s,function(t,a){a/=e,s[t]=i?Dt(a):a})):s={x:0,y:0,width:0,height:0},a.rotatable&&(s.rotate=o.rotate||0),a.scalable&&(s.scaleX=o.scaleX||1,s.scaleY=o.scaleY||1),s},setData:function(e){var s,a,o,h=this.options,n=this.image,r=this.canvas,p={};t.isFunction(e)&&(e=e.call(this.element)),this.isBuilt&&!this.isDisabled&&t.isPlainObject(e)&&(h.rotatable&&i(e.rotate)&&e.rotate!==n.rotate&&(n.rotate=e.rotate,this.isRotated=s=!0),h.scalable&&(i(e.scaleX)&&e.scaleX!==n.scaleX&&(n.scaleX=e.scaleX,a=!0),i(e.scaleY)&&e.scaleY!==n.scaleY&&(n.scaleY=e.scaleY,a=!0)),s?this.renderCanvas():a&&this.renderImage(),o=n.width/n.naturalWidth,i(e.x)&&(p.left=e.x*o+r.left),i(e.y)&&(p.top=e.y*o+r.top),i(e.width)&&(p.width=e.width*o),i(e.height)&&(p.height=e.height*o),this.setCropBoxData(p))},getContainerData:function(){return this.isBuilt?this.container:{}},getImageData:function(){return this.isLoaded?this.image:{}},getCanvasData:function(){var i=this.canvas,e={};return this.isBuilt&&t.each(["left","top","width","height","naturalWidth","naturalHeight"],function(t,s){e[s]=i[s]}),e},setCanvasData:function(e){var s=this.canvas,a=s.aspectRatio;t.isFunction(e)&&(e=e.call(this.$element)),this.isBuilt&&!this.isDisabled&&t.isPlainObject(e)&&(i(e.left)&&(s.left=e.left),i(e.top)&&(s.top=e.top),i(e.width)?(s.width=e.width,s.height=e.width/a):i(e.height)&&(s.height=e.height,s.width=e.height*a),this.renderCanvas(!0))},getCropBoxData:function(){var t,i=this.cropBox;return this.isBuilt&&this.isCropped&&(t={left:i.left,top:i.top,width:i.width,height:i.height}),t||{}},setCropBoxData:function(e){var s,a,o=this.cropBox,h=this.options.aspectRatio;t.isFunction(e)&&(e=e.call(this.$element)),this.isBuilt&&this.isCropped&&!this.isDisabled&&t.isPlainObject(e)&&(i(e.left)&&(o.left=e.left),i(e.top)&&(o.top=e.top),i(e.width)&&(s=!0,o.width=e.width),i(e.height)&&(a=!0,o.height=e.height),h&&(s?o.height=o.width/h:a&&(o.width=o.height*h)),this.renderCropBox())},getCroppedCanvas:function(i){var e,s,a,o,h,n,r,p,l,d,g;if(this.isBuilt&&ft)return this.isCropped?(t.isPlainObject(i)||(i={}),g=this.getData(),e=g.width,s=g.height,p=e/s,t.isPlainObject(i)&&(h=i.width,n=i.height,h?(n=h/p,r=h/e):n&&(h=n*p,r=n/s)),a=$t(h||e),o=$t(n||s),l=t("<canvas>")[0],l.width=a,l.height=o,d=l.getContext("2d"),i.fillColor&&(d.fillStyle=i.fillColor,d.fillRect(0,0,a,o)),d.drawImage.apply(d,function(){var t,i,a,o,h,n,p=c(this.$clone[0],this.image),l=p.width,d=p.height,u=this.canvas,f=[p],m=g.x+u.naturalWidth*(Ct(g.scaleX||1)-1)/2,v=g.y+u.naturalHeight*(Ct(g.scaleY||1)-1)/2;return m<=-e||m>l?m=t=a=h=0:m<=0?(a=-m,m=0,t=h=wt(l,e+m)):m<=l&&(a=0,t=h=wt(e,l-m)),t<=0||v<=-s||v>d?v=i=o=n=0:v<=0?(o=-v,v=0,i=n=wt(d,s+v)):v<=d&&(o=0,i=n=wt(s,d-v)),f.push($t(m),$t(v),$t(t),$t(i)),r&&(a*=r,o*=r,h*=r,n*=r),h>0&&n>0&&f.push($t(a),$t(o),$t(h),$t(n)),f}.call(this)),l):c(this.$clone[0],this.image)},setAspectRatio:function(t){var i=this.options;this.isDisabled||e(t)||(i.aspectRatio=xt(0,t)||NaN,this.isBuilt&&(this.initCropBox(),this.isCropped&&this.renderCropBox()))},setDragMode:function(t){var i,e,s=this.options;this.isLoaded&&!this.isDisabled&&(i=t===ct,e=s.movable&&t===dt,t=i||e?t:ut,this.$dragBox.data(it,t).toggleClass(W,i).toggleClass(M,e),s.cropBoxMovable||this.$face.data(it,t).toggleClass(W,i).toggleClass(M,e))}},v.DEFAULTS={viewMode:0,dragMode:"crop",aspectRatio:NaN,data:null,preview:"",responsive:!0,restore:!0,checkCrossOrigin:!0,checkOrientation:!0,modal:!0,guides:!0,center:!0,highlight:!0,background:!0,autoCrop:!0,autoCropArea:.8,movable:!0,rotatable:!0,scalable:!0,zoomable:!0,zoomOnTouch:!0,zoomOnWheel:!0,wheelZoomRatio:.1,cropBoxMovable:!0,cropBoxResizable:!0,toggleDragModeOnDblclick:!0,minCanvasWidth:0,minCanvasHeight:0,minCropBoxWidth:0,minCropBoxHeight:0,minContainerWidth:200,minContainerHeight:100,build:null,built:null,cropstart:null,cropmove:null,cropend:null,crop:null,zoom:null},v.setDefaults=function(i){t.extend(v.DEFAULTS,i)},v.TEMPLATE='<div class="cropper-container"><div class="cropper-wrap-box"><div class="cropper-canvas"></div></div><div class="cropper-drag-box"></div><div class="cropper-crop-box"><span class="cropper-view-box"></span><span class="cropper-dashed dashed-h"></span><span class="cropper-dashed dashed-v"></span><span class="cropper-center"></span><span class="cropper-face"></span><span class="cropper-line line-e" data-action="e"></span><span class="cropper-line line-n" data-action="n"></span><span class="cropper-line line-w" data-action="w"></span><span class="cropper-line line-s" data-action="s"></span><span class="cropper-point point-e" data-action="e"></span><span class="cropper-point point-n" data-action="n"></span><span class="cropper-point point-w" data-action="w"></span><span class="cropper-point point-s" data-action="s"></span><span class="cropper-point point-ne" data-action="ne"></span><span class="cropper-point point-nw" data-action="nw"></span><span class="cropper-point point-sw" data-action="sw"></span><span class="cropper-point point-se" data-action="se"></span></div></div>',v.other=t.fn.cropper,t.fn.cropper=function(i){var a,o=s(arguments,1);return this.each(function(){var e,s,h=t(this),n=h.data(L);if(!n){if(/destroy/.test(i))return;e=t.extend({},h.data(),t.isPlainObject(i)&&i),h.data(L,n=new v(this,e))}"string"==typeof i&&t.isFunction(s=n[i])&&(a=s.apply(n,o))}),e(a)?this:a},t.fn.cropper.Constructor=v,t.fn.cropper.setDefaults=v.setDefaults,t.fn.cropper.noConflict=function(){return t.fn.cropper=v.other,this}});
10
+
11
+ (function (factory) {
12
+ if (typeof define === 'function' && define.amd) {
13
+ // AMD. Register as anonymous module.
14
+ define(['jquery'], factory);
15
+ } else if (typeof exports === 'object') {
16
+ // Node / CommonJS
17
+ factory(require('jquery'));
18
+ } else {
19
+ // Browser globals.
20
+ factory(jQuery);
21
+ }
22
+ })(function ($) {
23
+
24
+ 'use strict';
25
+
26
+ // Globals
27
+ var $window = $(window);
28
+ var $document = $(document);
29
+ var location = window.location;
30
+ var navigator = window.navigator;
31
+ var ArrayBuffer = window.ArrayBuffer;
32
+ var Uint8Array = window.Uint8Array;
33
+ var DataView = window.DataView;
34
+ var btoa = window.btoa;
35
+
36
+ // Constants
37
+ var NAMESPACE = 'cropper';
38
+
39
+ // Classes
40
+ var CLASS_MODAL = 'cropper-modal';
41
+ var CLASS_HIDE = 'cropper-hide';
42
+ var CLASS_HIDDEN = 'cropper-hidden';
43
+ var CLASS_INVISIBLE = 'cropper-invisible';
44
+ var CLASS_MOVE = 'cropper-move';
45
+ var CLASS_CROP = 'cropper-crop';
46
+ var CLASS_DISABLED = 'cropper-disabled';
47
+ var CLASS_BG = 'cropper-bg';
48
+
49
+ // Events
50
+ var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown';
51
+ var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove';
52
+ var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel';
53
+ var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
54
+ var EVENT_DBLCLICK = 'dblclick';
55
+ var EVENT_LOAD = 'load.' + NAMESPACE;
56
+ var EVENT_ERROR = 'error.' + NAMESPACE;
57
+ var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace
58
+ var EVENT_BUILD = 'build.' + NAMESPACE;
59
+ var EVENT_BUILT = 'built.' + NAMESPACE;
60
+ var EVENT_CROP_START = 'cropstart.' + NAMESPACE;
61
+ var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE;
62
+ var EVENT_CROP_END = 'cropend.' + NAMESPACE;
63
+ var EVENT_CROP = 'crop.' + NAMESPACE;
64
+ var EVENT_ZOOM = 'zoom.' + NAMESPACE;
65
+
66
+ // RegExps
67
+ var REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;
68
+ var REGEXP_DATA_URL = /^data:/;
69
+ var REGEXP_DATA_URL_HEAD = /^data:([^;]+);base64,/;
70
+ var REGEXP_DATA_URL_JPEG = /^data:image\/jpeg.*;base64,/;
71
+
72
+ // Data keys
73
+ var DATA_PREVIEW = 'preview';
74
+ var DATA_ACTION = 'action';
75
+
76
+ // Actions
77
+ var ACTION_EAST = 'e';
78
+ var ACTION_WEST = 'w';
79
+ var ACTION_SOUTH = 's';
80
+ var ACTION_NORTH = 'n';
81
+ var ACTION_SOUTH_EAST = 'se';
82
+ var ACTION_SOUTH_WEST = 'sw';
83
+ var ACTION_NORTH_EAST = 'ne';
84
+ var ACTION_NORTH_WEST = 'nw';
85
+ var ACTION_ALL = 'all';
86
+ var ACTION_CROP = 'crop';
87
+ var ACTION_MOVE = 'move';
88
+ var ACTION_ZOOM = 'zoom';
89
+ var ACTION_NONE = 'none';
90
+
91
+ // Supports
92
+ var SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext);
93
+ var IS_SAFARI_OR_UIWEBVIEW = navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(navigator.userAgent);
94
+
95
+ // Maths
96
+ var num = Number;
97
+ var min = Math.min;
98
+ var max = Math.max;
99
+ var abs = Math.abs;
100
+ var sin = Math.sin;
101
+ var cos = Math.cos;
102
+ var sqrt = Math.sqrt;
103
+ var round = Math.round;
104
+ var floor = Math.floor;
105
+
106
+ // Utilities
107
+ var fromCharCode = String.fromCharCode;
108
+
109
+ function isNumber(n) {
110
+ return typeof n === 'number' && !isNaN(n);
111
+ }
112
+
113
+ function isUndefined(n) {
114
+ return typeof n === 'undefined';
115
+ }
116
+
117
+ function toArray(obj, offset) {
118
+ var args = [];
119
+
120
+ // This is necessary for IE8
121
+ if (isNumber(offset)) {
122
+ args.push(offset);
123
+ }
124
+
125
+ return args.slice.apply(obj, args);
126
+ }
127
+
128
+ // Custom proxy to avoid jQuery's guid
129
+ function proxy(fn, context) {
130
+ var args = toArray(arguments, 2);
131
+
132
+ return function () {
133
+ return fn.apply(context, args.concat(toArray(arguments)));
134
+ };
135
+ }
136
+
137
+ function isCrossOriginURL(url) {
138
+ var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
139
+
140
+ return parts && (
141
+ parts[1] !== location.protocol ||
142
+ parts[2] !== location.hostname ||
143
+ parts[3] !== location.port
144
+ );
145
+ }
146
+
147
+ function addTimestamp(url) {
148
+ var timestamp = 'timestamp=' + (new Date()).getTime();
149
+
150
+ return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);
151
+ }
152
+
153
+ function getCrossOrigin(crossOrigin) {
154
+ return crossOrigin ? ' crossOrigin="' + crossOrigin + '"' : '';
155
+ }
156
+
157
+ function getImageSize(image, callback) {
158
+ var newImage;
159
+
160
+ // Modern browsers (ignore Safari, #120 & #509)
161
+ if (image.naturalWidth && !IS_SAFARI_OR_UIWEBVIEW) {
162
+ return callback(image.naturalWidth, image.naturalHeight);
163
+ }
164
+
165
+ // IE8: Don't use `new Image()` here (#319)
166
+ newImage = document.createElement('img');
167
+
168
+ newImage.onload = function () {
169
+ callback(this.width, this.height);
170
+ };
171
+
172
+ newImage.src = image.src;
173
+ }
174
+
175
+ function getTransform(options) {
176
+ var transforms = [];
177
+ var rotate = options.rotate;
178
+ var scaleX = options.scaleX;
179
+ var scaleY = options.scaleY;
180
+
181
+ // Rotate should come first before scale to match orientation transform
182
+ if (isNumber(rotate) && rotate !== 0) {
183
+ transforms.push('rotate(' + rotate + 'deg)');
184
+ }
185
+
186
+ if (isNumber(scaleX) && scaleX !== 1) {
187
+ transforms.push('scaleX(' + scaleX + ')');
188
+ }
189
+
190
+ if (isNumber(scaleY) && scaleY !== 1) {
191
+ transforms.push('scaleY(' + scaleY + ')');
192
+ }
193
+
194
+ return transforms.length ? transforms.join(' ') : 'none';
195
+ }
196
+
197
+ function getRotatedSizes(data, isReversed) {
198
+ var deg = abs(data.degree) % 180;
199
+ var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180;
200
+ var sinArc = sin(arc);
201
+ var cosArc = cos(arc);
202
+ var width = data.width;
203
+ var height = data.height;
204
+ var aspectRatio = data.aspectRatio;
205
+ var newWidth;
206
+ var newHeight;
207
+
208
+ if (!isReversed) {
209
+ newWidth = width * cosArc + height * sinArc;
210
+ newHeight = width * sinArc + height * cosArc;
211
+ } else {
212
+ newWidth = width / (cosArc + sinArc / aspectRatio);
213
+ newHeight = newWidth / aspectRatio;
214
+ }
215
+
216
+ return {
217
+ width: newWidth,
218
+ height: newHeight
219
+ };
220
+ }
221
+
222
+ function getSourceCanvas(image, data) {
223
+ var canvas = $('<canvas>')[0];
224
+ var context = canvas.getContext('2d');
225
+ var dstX = 0;
226
+ var dstY = 0;
227
+ var dstWidth = data.naturalWidth;
228
+ var dstHeight = data.naturalHeight;
229
+ var rotate = data.rotate;
230
+ var scaleX = data.scaleX;
231
+ var scaleY = data.scaleY;
232
+ var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1);
233
+ var rotatable = isNumber(rotate) && rotate !== 0;
234
+ var advanced = rotatable || scalable;
235
+ var canvasWidth = dstWidth * abs(scaleX || 1);
236
+ var canvasHeight = dstHeight * abs(scaleY || 1);
237
+ var translateX;
238
+ var translateY;
239
+ var rotated;
240
+
241
+ if (scalable) {
242
+ translateX = canvasWidth / 2;
243
+ translateY = canvasHeight / 2;
244
+ }
245
+
246
+ if (rotatable) {
247
+ rotated = getRotatedSizes({
248
+ width: canvasWidth,
249
+ height: canvasHeight,
250
+ degree: rotate
251
+ });
252
+
253
+ canvasWidth = rotated.width;
254
+ canvasHeight = rotated.height;
255
+ translateX = canvasWidth / 2;
256
+ translateY = canvasHeight / 2;
257
+ }
258
+
259
+ canvas.width = canvasWidth;
260
+ canvas.height = canvasHeight;
261
+
262
+ if (advanced) {
263
+ dstX = -dstWidth / 2;
264
+ dstY = -dstHeight / 2;
265
+
266
+ context.save();
267
+ context.translate(translateX, translateY);
268
+ }
269
+
270
+ // Rotate should come first before scale as in the "getTransform" function
271
+ if (rotatable) {
272
+ context.rotate(rotate * Math.PI / 180);
273
+ }
274
+
275
+ if (scalable) {
276
+ context.scale(scaleX, scaleY);
277
+ }
278
+
279
+ context.drawImage(image, floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));
280
+
281
+ if (advanced) {
282
+ context.restore();
283
+ }
284
+
285
+ return canvas;
286
+ }
287
+
288
+ function getTouchesCenter(touches) {
289
+ var length = touches.length;
290
+ var pageX = 0;
291
+ var pageY = 0;
292
+
293
+ if (length) {
294
+ $.each(touches, function (i, touch) {
295
+ pageX += touch.pageX;
296
+ pageY += touch.pageY;
297
+ });
298
+
299
+ pageX /= length;
300
+ pageY /= length;
301
+ }
302
+
303
+ return {
304
+ pageX: pageX,
305
+ pageY: pageY
306
+ };
307
+ }
308
+
309
+ function getStringFromCharCode(dataView, start, length) {
310
+ var str = '';
311
+ var i;
312
+
313
+ for (i = start, length += start; i < length; i++) {
314
+ str += fromCharCode(dataView.getUint8(i));
315
+ }
316
+
317
+ return str;
318
+ }
319
+
320
+ function getOrientation(arrayBuffer) {
321
+ var dataView = new DataView(arrayBuffer);
322
+ var length = dataView.byteLength;
323
+ var orientation;
324
+ var exifIDCode;
325
+ var tiffOffset;
326
+ var firstIFDOffset;
327
+ var littleEndian;
328
+ var endianness;
329
+ var app1Start;
330
+ var ifdStart;
331
+ var offset;
332
+ var i;
333
+
334
+ // Only handle JPEG image (start by 0xFFD8)
335
+ if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
336
+ offset = 2;
337
+
338
+ while (offset < length) {
339
+ if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
340
+ app1Start = offset;
341
+ break;
342
+ }
343
+
344
+ offset++;
345
+ }
346
+ }
347
+
348
+ if (app1Start) {
349
+ exifIDCode = app1Start + 4;
350
+ tiffOffset = app1Start + 10;
351
+
352
+ if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
353
+ endianness = dataView.getUint16(tiffOffset);
354
+ littleEndian = endianness === 0x4949;
355
+
356
+ if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
357
+ if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
358
+ firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
359
+
360
+ if (firstIFDOffset >= 0x00000008) {
361
+ ifdStart = tiffOffset + firstIFDOffset;
362
+ }
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ if (ifdStart) {
369
+ length = dataView.getUint16(ifdStart, littleEndian);
370
+
371
+ for (i = 0; i < length; i++) {
372
+ offset = ifdStart + i * 12 + 2;
373
+
374
+ if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
375
+
376
+ // 8 is the offset of the current tag's value
377
+ offset += 8;
378
+
379
+ // Get the original orientation value
380
+ orientation = dataView.getUint16(offset, littleEndian);
381
+
382
+ // Override the orientation with its default value for Safari (#120)
383
+ if (IS_SAFARI_OR_UIWEBVIEW) {
384
+ dataView.setUint16(offset, 1, littleEndian);
385
+ }
386
+
387
+ break;
388
+ }
389
+ }
390
+ }
391
+
392
+ return orientation;
393
+ }
394
+
395
+ function dataURLToArrayBuffer(dataURL) {
396
+ var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
397
+ var binary = atob(base64);
398
+ var length = binary.length;
399
+ var arrayBuffer = new ArrayBuffer(length);
400
+ var dataView = new Uint8Array(arrayBuffer);
401
+ var i;
402
+
403
+ for (i = 0; i < length; i++) {
404
+ dataView[i] = binary.charCodeAt(i);
405
+ }
406
+
407
+ return arrayBuffer;
408
+ }
409
+
410
+ // Only available for JPEG image
411
+ function arrayBufferToDataURL(arrayBuffer) {
412
+ var dataView = new Uint8Array(arrayBuffer);
413
+ var length = dataView.length;
414
+ var base64 = '';
415
+ var i;
416
+
417
+ for (i = 0; i < length; i++) {
418
+ base64 += fromCharCode(dataView[i]);
419
+ }
420
+
421
+ return 'data:image/jpeg;base64,' + btoa(base64);
422
+ }
423
+
424
+ function Cropper(element, options) {
425
+ this.$element = $(element);
426
+ this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options);
427
+ this.isLoaded = false;
428
+ this.isBuilt = false;
429
+ this.isCompleted = false;
430
+ this.isRotated = false;
431
+ this.isCropped = false;
432
+ this.isDisabled = false;
433
+ this.isReplaced = false;
434
+ this.isLimited = false;
435
+ this.wheeling = false;
436
+ this.isImg = false;
437
+ this.originalUrl = '';
438
+ this.canvas = null;
439
+ this.cropBox = null;
440
+ this.init();
441
+ }
442
+
443
+ Cropper.prototype = {
444
+ constructor: Cropper,
445
+
446
+ init: function () {
447
+ var $this = this.$element;
448
+ var url;
449
+
450
+ if ($this.is('img')) {
451
+ this.isImg = true;
452
+
453
+ // Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
454
+ this.originalUrl = url = $this.attr('src');
455
+
456
+ // Stop when it's a blank image
457
+ if (!url) {
458
+ return;
459
+ }
460
+
461
+ // Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
462
+ url = $this.prop('src');
463
+ } else if ($this.is('canvas') && SUPPORT_CANVAS) {
464
+ url = $this[0].toDataURL();
465
+ }
466
+
467
+ this.load(url);
468
+ },
469
+
470
+ // A shortcut for triggering custom events
471
+ trigger: function (type, data) {
472
+ var e = $.Event(type, data);
473
+
474
+ this.$element.trigger(e);
475
+
476
+ return e;
477
+ },
478
+
479
+ load: function (url) {
480
+ var options = this.options;
481
+ var $this = this.$element;
482
+ var read;
483
+ var xhr;
484
+
485
+ if (!url) {
486
+ return;
487
+ }
488
+
489
+ // Trigger build event first
490
+ $this.one(EVENT_BUILD, options.build);
491
+
492
+ if (this.trigger(EVENT_BUILD).isDefaultPrevented()) {
493
+ return;
494
+ }
495
+
496
+ this.url = url;
497
+ this.image = {};
498
+
499
+ if (!options.checkOrientation || !ArrayBuffer) {
500
+ return this.clone();
501
+ }
502
+
503
+ read = $.proxy(this.read, this);
504
+
505
+ // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
506
+ if (REGEXP_DATA_URL.test(url)) {
507
+ return REGEXP_DATA_URL_JPEG.test(url) ?
508
+ read(dataURLToArrayBuffer(url)) :
509
+ this.clone();
510
+ }
511
+
512
+ xhr = new XMLHttpRequest();
513
+
514
+ xhr.onerror = xhr.onabort = $.proxy(function () {
515
+ this.clone();
516
+ }, this);
517
+
518
+ xhr.onload = function () {
519
+ read(this.response);
520
+ };
521
+
522
+ if (options.checkCrossOrigin && isCrossOriginURL(url) && $this.prop('crossOrigin')) {
523
+ url = addTimestamp(url);
524
+ }
525
+
526
+ xhr.open('get', url);
527
+ xhr.responseType = 'arraybuffer';
528
+ xhr.send();
529
+ },
530
+
531
+ read: function (arrayBuffer) {
532
+ var options = this.options;
533
+ var orientation = getOrientation(arrayBuffer);
534
+ var image = this.image;
535
+ var rotate = 0;
536
+ var scaleX = 1;
537
+ var scaleY = 1;
538
+
539
+ if (orientation > 1) {
540
+ this.url = arrayBufferToDataURL(arrayBuffer);
541
+
542
+ switch (orientation) {
543
+
544
+ // flip horizontal
545
+ case 2:
546
+ scaleX = -1;
547
+ break;
548
+
549
+ // rotate left 180°
550
+ case 3:
551
+ rotate = -180;
552
+ break;
553
+
554
+ // flip vertical
555
+ case 4:
556
+ scaleY = -1;
557
+ break;
558
+
559
+ // flip vertical + rotate right 90°
560
+ case 5:
561
+ rotate = 90;
562
+ scaleY = -1;
563
+ break;
564
+
565
+ // rotate right 90°
566
+ case 6:
567
+ rotate = 90;
568
+ break;
569
+
570
+ // flip horizontal + rotate right 90°
571
+ case 7:
572
+ rotate = 90;
573
+ scaleX = -1;
574
+ break;
575
+
576
+ // rotate left 90°
577
+ case 8:
578
+ rotate = -90;
579
+ break;
580
+ }
581
+ }
582
+
583
+ if (options.rotatable) {
584
+ image.rotate = rotate;
585
+ }
586
+
587
+ if (options.scalable) {
588
+ image.scaleX = scaleX;
589
+ image.scaleY = scaleY;
590
+ }
591
+
592
+ this.clone();
593
+ },
594
+
595
+ clone: function () {
596
+ var options = this.options;
597
+ var $this = this.$element;
598
+ var url = this.url;
599
+ var crossOrigin = '';
600
+ var crossOriginUrl;
601
+ var $clone;
602
+
603
+ if (options.checkCrossOrigin && isCrossOriginURL(url)) {
604
+ crossOrigin = $this.prop('crossOrigin');
605
+
606
+ if (crossOrigin) {
607
+ crossOriginUrl = url;
608
+ } else {
609
+ crossOrigin = 'anonymous';
610
+
611
+ // Bust cache (#148) when there is not a "crossOrigin" property
612
+ crossOriginUrl = addTimestamp(url);
613
+ }
614
+ }
615
+
616
+ this.crossOrigin = crossOrigin;
617
+ this.crossOriginUrl = crossOriginUrl;
618
+ this.$clone = $clone = $('<img' + getCrossOrigin(crossOrigin) + ' src="' + (crossOriginUrl || url) + '">');
619
+
620
+ if (this.isImg) {
621
+ if ($this[0].complete) {
622
+ this.start();
623
+ } else {
624
+ $this.one(EVENT_LOAD, $.proxy(this.start, this));
625
+ }
626
+ } else {
627
+ $clone.
628
+ one(EVENT_LOAD, $.proxy(this.start, this)).
629
+ one(EVENT_ERROR, $.proxy(this.stop, this)).
630
+ addClass(CLASS_HIDE).
631
+ insertAfter($this);
632
+ }
633
+ },
634
+
635
+ start: function () {
636
+ var $image = this.$element;
637
+ var $clone = this.$clone;
638
+
639
+ if (!this.isImg) {
640
+ $clone.off(EVENT_ERROR, this.stop);
641
+ $image = $clone;
642
+ }
643
+
644
+ getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) {
645
+ $.extend(this.image, {
646
+ naturalWidth: naturalWidth,
647
+ naturalHeight: naturalHeight,
648
+ aspectRatio: naturalWidth / naturalHeight
649
+ });
650
+
651
+ this.isLoaded = true;
652
+ this.build();
653
+ }, this));
654
+ },
655
+
656
+ stop: function () {
657
+ this.$clone.remove();
658
+ this.$clone = null;
659
+ },
660
+
661
+ build: function () {
662
+ var options = this.options;
663
+ var $this = this.$element;
664
+ var $clone = this.$clone;
665
+ var $cropper;
666
+ var $cropBox;
667
+ var $face;
668
+
669
+ if (!this.isLoaded) {
670
+ return;
671
+ }
672
+
673
+ // Unbuild first when replace
674
+ if (this.isBuilt) {
675
+ this.unbuild();
676
+ }
677
+
678
+ // Create cropper elements
679
+ this.$container = $this.parent();
680
+ this.$cropper = $cropper = $(Cropper.TEMPLATE);
681
+ this.$canvas = $cropper.find('.cropper-canvas').append($clone);
682
+ this.$dragBox = $cropper.find('.cropper-drag-box');
683
+ this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
684
+ this.$viewBox = $cropper.find('.cropper-view-box');
685
+ this.$face = $face = $cropBox.find('.cropper-face');
686
+
687
+ // Hide the original image
688
+ $this.addClass(CLASS_HIDDEN).after($cropper);
689
+
690
+ // Show the clone image if is hidden
691
+ if (!this.isImg) {
692
+ $clone.removeClass(CLASS_HIDE);
693
+ }
694
+
695
+ this.initPreview();
696
+ this.bind();
697
+
698
+ options.aspectRatio = max(0, options.aspectRatio) || NaN;
699
+ options.viewMode = max(0, min(3, round(options.viewMode))) || 0;
700
+
701
+ if (options.autoCrop) {
702
+ this.isCropped = true;
703
+
704
+ if (options.modal) {
705
+ this.$dragBox.addClass(CLASS_MODAL);
706
+ }
707
+ } else {
708
+ $cropBox.addClass(CLASS_HIDDEN);
709
+ }
710
+
711
+ if (!options.guides) {
712
+ $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
713
+ }
714
+
715
+ if (!options.center) {
716
+ $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);
717
+ }
718
+
719
+ if (options.cropBoxMovable) {
720
+ $face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL);
721
+ }
722
+
723
+ if (!options.highlight) {
724
+ $face.addClass(CLASS_INVISIBLE);
725
+ }
726
+
727
+ if (options.background) {
728
+ $cropper.addClass(CLASS_BG);
729
+ }
730
+
731
+ if (!options.cropBoxResizable) {
732
+ $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
733
+ }
734
+
735
+ this.setDragMode(options.dragMode);
736
+ this.render();
737
+ this.isBuilt = true;
738
+ this.setData(options.data);
739
+ $this.one(EVENT_BUILT, options.built);
740
+
741
+ // Trigger the built event asynchronously to keep `data('cropper')` is defined
742
+ this.completing = setTimeout($.proxy(function () {
743
+ this.trigger(EVENT_BUILT);
744
+ this.trigger(EVENT_CROP, this.getData());
745
+ this.isCompleted = true;
746
+ }, this), 0);
747
+ },
748
+
749
+ unbuild: function () {
750
+ if (!this.isBuilt) {
751
+ return;
752
+ }
753
+
754
+ if (!this.isCompleted) {
755
+ clearTimeout(this.completing);
756
+ }
757
+
758
+ this.isBuilt = false;
759
+ this.isCompleted = false;
760
+ this.initialImage = null;
761
+
762
+ // Clear `initialCanvas` is necessary when replace
763
+ this.initialCanvas = null;
764
+ this.initialCropBox = null;
765
+ this.container = null;
766
+ this.canvas = null;
767
+
768
+ // Clear `cropBox` is necessary when replace
769
+ this.cropBox = null;
770
+ this.unbind();
771
+
772
+ this.resetPreview();
773
+ this.$preview = null;
774
+
775
+ this.$viewBox = null;
776
+ this.$cropBox = null;
777
+ this.$dragBox = null;
778
+ this.$canvas = null;
779
+ this.$container = null;
780
+
781
+ this.$cropper.remove();
782
+ this.$cropper = null;
783
+ },
784
+
785
+ render: function () {
786
+ this.initContainer();
787
+ this.initCanvas();
788
+ this.initCropBox();
789
+
790
+ this.renderCanvas();
791
+
792
+ if (this.isCropped) {
793
+ this.renderCropBox();
794
+ }
795
+ },
796
+
797
+ initContainer: function () {
798
+ var options = this.options;
799
+ var $this = this.$element;
800
+ var $container = this.$container;
801
+ var $cropper = this.$cropper;
802
+
803
+ $cropper.addClass(CLASS_HIDDEN);
804
+ $this.removeClass(CLASS_HIDDEN);
805
+
806
+ $cropper.css((this.container = {
807
+ width: max($container.width(), num(options.minContainerWidth) || 200),
808
+ height: max($container.height(), num(options.minContainerHeight) || 100)
809
+ }));
810
+
811
+ $this.addClass(CLASS_HIDDEN);
812
+ $cropper.removeClass(CLASS_HIDDEN);
813
+ },
814
+
815
+ // Canvas (image wrapper)
816
+ initCanvas: function () {
817
+ var viewMode = this.options.viewMode;
818
+ var container = this.container;
819
+ var containerWidth = container.width;
820
+ var containerHeight = container.height;
821
+ var image = this.image;
822
+ var imageNaturalWidth = image.naturalWidth;
823
+ var imageNaturalHeight = image.naturalHeight;
824
+ var is90Degree = abs(image.rotate) === 90;
825
+ var naturalWidth = is90Degree ? imageNaturalHeight : imageNaturalWidth;
826
+ var naturalHeight = is90Degree ? imageNaturalWidth : imageNaturalHeight;
827
+ var aspectRatio = naturalWidth / naturalHeight;
828
+ var canvasWidth = containerWidth;
829
+ var canvasHeight = containerHeight;
830
+ var canvas;
831
+
832
+ if (containerHeight * aspectRatio > containerWidth) {
833
+ if (viewMode === 3) {
834
+ canvasWidth = containerHeight * aspectRatio;
835
+ } else {
836
+ canvasHeight = containerWidth / aspectRatio;
837
+ }
838
+ } else {
839
+ if (viewMode === 3) {
840
+ canvasHeight = containerWidth / aspectRatio;
841
+ } else {
842
+ canvasWidth = containerHeight * aspectRatio;
843
+ }
844
+ }
845
+
846
+ canvas = {
847
+ naturalWidth: naturalWidth,
848
+ naturalHeight: naturalHeight,
849
+ aspectRatio: aspectRatio,
850
+ width: canvasWidth,
851
+ height: canvasHeight
852
+ };
853
+
854
+ canvas.oldLeft = canvas.left = (containerWidth - canvasWidth) / 2;
855
+ canvas.oldTop = canvas.top = (containerHeight - canvasHeight) / 2;
856
+
857
+ this.canvas = canvas;
858
+ this.isLimited = (viewMode === 1 || viewMode === 2);
859
+ this.limitCanvas(true, true);
860
+ this.initialImage = $.extend({}, image);
861
+ this.initialCanvas = $.extend({}, canvas);
862
+ },
863
+
864
+ limitCanvas: function (isSizeLimited, isPositionLimited) {
865
+ var options = this.options;
866
+ var viewMode = options.viewMode;
867
+ var container = this.container;
868
+ var containerWidth = container.width;
869
+ var containerHeight = container.height;
870
+ var canvas = this.canvas;
871
+ var aspectRatio = canvas.aspectRatio;
872
+ var cropBox = this.cropBox;
873
+ var isCropped = this.isCropped && cropBox;
874
+ var minCanvasWidth;
875
+ var minCanvasHeight;
876
+ var newCanvasLeft;
877
+ var newCanvasTop;
878
+
879
+ if (isSizeLimited) {
880
+ minCanvasWidth = num(options.minCanvasWidth) || 0;
881
+ minCanvasHeight = num(options.minCanvasHeight) || 0;
882
+
883
+ if (viewMode) {
884
+ if (viewMode > 1) {
885
+ minCanvasWidth = max(minCanvasWidth, containerWidth);
886
+ minCanvasHeight = max(minCanvasHeight, containerHeight);
887
+
888
+ if (viewMode === 3) {
889
+ if (minCanvasHeight * aspectRatio > minCanvasWidth) {
890
+ minCanvasWidth = minCanvasHeight * aspectRatio;
891
+ } else {
892
+ minCanvasHeight = minCanvasWidth / aspectRatio;
893
+ }
894
+ }
895
+ } else {
896
+ if (minCanvasWidth) {
897
+ minCanvasWidth = max(minCanvasWidth, isCropped ? cropBox.width : 0);
898
+ } else if (minCanvasHeight) {
899
+ minCanvasHeight = max(minCanvasHeight, isCropped ? cropBox.height : 0);
900
+ } else if (isCropped) {
901
+ minCanvasWidth = cropBox.width;
902
+ minCanvasHeight = cropBox.height;
903
+
904
+ if (minCanvasHeight * aspectRatio > minCanvasWidth) {
905
+ minCanvasWidth = minCanvasHeight * aspectRatio;
906
+ } else {
907
+ minCanvasHeight = minCanvasWidth / aspectRatio;
908
+ }
909
+ }
910
+ }
911
+ }
912
+
913
+ if (minCanvasWidth && minCanvasHeight) {
914
+ if (minCanvasHeight * aspectRatio > minCanvasWidth) {
915
+ minCanvasHeight = minCanvasWidth / aspectRatio;
916
+ } else {
917
+ minCanvasWidth = minCanvasHeight * aspectRatio;
918
+ }
919
+ } else if (minCanvasWidth) {
920
+ minCanvasHeight = minCanvasWidth / aspectRatio;
921
+ } else if (minCanvasHeight) {
922
+ minCanvasWidth = minCanvasHeight * aspectRatio;
923
+ }
924
+
925
+ canvas.minWidth = minCanvasWidth;
926
+ canvas.minHeight = minCanvasHeight;
927
+ canvas.maxWidth = Infinity;
928
+ canvas.maxHeight = Infinity;
929
+ }
930
+
931
+ if (isPositionLimited) {
932
+ if (viewMode) {
933
+ newCanvasLeft = containerWidth - canvas.width;
934
+ newCanvasTop = containerHeight - canvas.height;
935
+
936
+ canvas.minLeft = min(0, newCanvasLeft);
937
+ canvas.minTop = min(0, newCanvasTop);
938
+ canvas.maxLeft = max(0, newCanvasLeft);
939
+ canvas.maxTop = max(0, newCanvasTop);
940
+
941
+ if (isCropped && this.isLimited) {
942
+ canvas.minLeft = min(
943
+ cropBox.left,
944
+ cropBox.left + cropBox.width - canvas.width
945
+ );
946
+ canvas.minTop = min(
947
+ cropBox.top,
948
+ cropBox.top + cropBox.height - canvas.height
949
+ );
950
+ canvas.maxLeft = cropBox.left;
951
+ canvas.maxTop = cropBox.top;
952
+
953
+ if (viewMode === 2) {
954
+ if (canvas.width >= containerWidth) {
955
+ canvas.minLeft = min(0, newCanvasLeft);
956
+ canvas.maxLeft = max(0, newCanvasLeft);
957
+ }
958
+
959
+ if (canvas.height >= containerHeight) {
960
+ canvas.minTop = min(0, newCanvasTop);
961
+ canvas.maxTop = max(0, newCanvasTop);
962
+ }
963
+ }
964
+ }
965
+ } else {
966
+ canvas.minLeft = -canvas.width;
967
+ canvas.minTop = -canvas.height;
968
+ canvas.maxLeft = containerWidth;
969
+ canvas.maxTop = containerHeight;
970
+ }
971
+ }
972
+ },
973
+
974
+ renderCanvas: function (isChanged) {
975
+ var canvas = this.canvas;
976
+ var image = this.image;
977
+ var rotate = image.rotate;
978
+ var naturalWidth = image.naturalWidth;
979
+ var naturalHeight = image.naturalHeight;
980
+ var aspectRatio;
981
+ var rotated;
982
+
983
+ if (this.isRotated) {
984
+ this.isRotated = false;
985
+
986
+ // Computes rotated sizes with image sizes
987
+ rotated = getRotatedSizes({
988
+ width: image.width,
989
+ height: image.height,
990
+ degree: rotate
991
+ });
992
+
993
+ aspectRatio = rotated.width / rotated.height;
994
+
995
+ if (aspectRatio !== canvas.aspectRatio) {
996
+ canvas.left -= (rotated.width - canvas.width) / 2;
997
+ canvas.top -= (rotated.height - canvas.height) / 2;
998
+ canvas.width = rotated.width;
999
+ canvas.height = rotated.height;
1000
+ canvas.aspectRatio = aspectRatio;
1001
+ canvas.naturalWidth = naturalWidth;
1002
+ canvas.naturalHeight = naturalHeight;
1003
+
1004
+ // Computes rotated sizes with natural image sizes
1005
+ if (rotate % 180) {
1006
+ rotated = getRotatedSizes({
1007
+ width: naturalWidth,
1008
+ height: naturalHeight,
1009
+ degree: rotate
1010
+ });
1011
+
1012
+ canvas.naturalWidth = rotated.width;
1013
+ canvas.naturalHeight = rotated.height;
1014
+ }
1015
+
1016
+ this.limitCanvas(true, false);
1017
+ }
1018
+ }
1019
+
1020
+ if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {
1021
+ canvas.left = canvas.oldLeft;
1022
+ }
1023
+
1024
+ if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {
1025
+ canvas.top = canvas.oldTop;
1026
+ }
1027
+
1028
+ canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);
1029
+ canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight);
1030
+
1031
+ this.limitCanvas(false, true);
1032
+
1033
+ canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);
1034
+ canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop);
1035
+
1036
+ this.$canvas.css({
1037
+ width: canvas.width,
1038
+ height: canvas.height,
1039
+ left: canvas.left,
1040
+ top: canvas.top
1041
+ });
1042
+
1043
+ this.renderImage();
1044
+
1045
+ if (this.isCropped && this.isLimited) {
1046
+ this.limitCropBox(true, true);
1047
+ }
1048
+
1049
+ if (isChanged) {
1050
+ this.output();
1051
+ }
1052
+ },
1053
+
1054
+ renderImage: function (isChanged) {
1055
+ var canvas = this.canvas;
1056
+ var image = this.image;
1057
+ var reversed;
1058
+
1059
+ if (image.rotate) {
1060
+ reversed = getRotatedSizes({
1061
+ width: canvas.width,
1062
+ height: canvas.height,
1063
+ degree: image.rotate,
1064
+ aspectRatio: image.aspectRatio
1065
+ }, true);
1066
+ }
1067
+
1068
+ $.extend(image, reversed ? {
1069
+ width: reversed.width,
1070
+ height: reversed.height,
1071
+ left: (canvas.width - reversed.width) / 2,
1072
+ top: (canvas.height - reversed.height) / 2
1073
+ } : {
1074
+ width: canvas.width,
1075
+ height: canvas.height,
1076
+ left: 0,
1077
+ top: 0
1078
+ });
1079
+
1080
+ this.$clone.css({
1081
+ width: image.width,
1082
+ height: image.height,
1083
+ marginLeft: image.left,
1084
+ marginTop: image.top,
1085
+ transform: getTransform(image)
1086
+ });
1087
+
1088
+ if (isChanged) {
1089
+ this.output();
1090
+ }
1091
+ },
1092
+
1093
+ initCropBox: function () {
1094
+ var options = this.options;
1095
+ var canvas = this.canvas;
1096
+ var aspectRatio = options.aspectRatio;
1097
+ var autoCropArea = num(options.autoCropArea) || 0.8;
1098
+ var cropBox = {
1099
+ width: canvas.width,
1100
+ height: canvas.height
1101
+ };
1102
+
1103
+ if (aspectRatio) {
1104
+ if (canvas.height * aspectRatio > canvas.width) {
1105
+ cropBox.height = cropBox.width / aspectRatio;
1106
+ } else {
1107
+ cropBox.width = cropBox.height * aspectRatio;
1108
+ }
1109
+ }
1110
+
1111
+ this.cropBox = cropBox;
1112
+ this.limitCropBox(true, true);
1113
+
1114
+ // Initialize auto crop area
1115
+ cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
1116
+ cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
1117
+
1118
+ // The width of auto crop area must large than "minWidth", and the height too. (#164)
1119
+ cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);
1120
+ cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);
1121
+ cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;
1122
+ cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;
1123
+
1124
+ this.initialCropBox = $.extend({}, cropBox);
1125
+ },
1126
+
1127
+ limitCropBox: function (isSizeLimited, isPositionLimited) {
1128
+ var options = this.options;
1129
+ var aspectRatio = options.aspectRatio;
1130
+ var container = this.container;
1131
+ var containerWidth = container.width;
1132
+ var containerHeight = container.height;
1133
+ var canvas = this.canvas;
1134
+ var cropBox = this.cropBox;
1135
+ var isLimited = this.isLimited;
1136
+ var minCropBoxWidth;
1137
+ var minCropBoxHeight;
1138
+ var maxCropBoxWidth;
1139
+ var maxCropBoxHeight;
1140
+
1141
+ if (isSizeLimited) {
1142
+ minCropBoxWidth = num(options.minCropBoxWidth) || 0;
1143
+ minCropBoxHeight = num(options.minCropBoxHeight) || 0;
1144
+
1145
+ // The min/maxCropBoxWidth/Height must be less than containerWidth/Height
1146
+ minCropBoxWidth = min(minCropBoxWidth, containerWidth);
1147
+ minCropBoxHeight = min(minCropBoxHeight, containerHeight);
1148
+ maxCropBoxWidth = min(containerWidth, isLimited ? canvas.width : containerWidth);
1149
+ maxCropBoxHeight = min(containerHeight, isLimited ? canvas.height : containerHeight);
1150
+
1151
+ if (aspectRatio) {
1152
+ if (minCropBoxWidth && minCropBoxHeight) {
1153
+ if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
1154
+ minCropBoxHeight = minCropBoxWidth / aspectRatio;
1155
+ } else {
1156
+ minCropBoxWidth = minCropBoxHeight * aspectRatio;
1157
+ }
1158
+ } else if (minCropBoxWidth) {
1159
+ minCropBoxHeight = minCropBoxWidth / aspectRatio;
1160
+ } else if (minCropBoxHeight) {
1161
+ minCropBoxWidth = minCropBoxHeight * aspectRatio;
1162
+ }
1163
+
1164
+ if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
1165
+ maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
1166
+ } else {
1167
+ maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
1168
+ }
1169
+ }
1170
+
1171
+ // The minWidth/Height must be less than maxWidth/Height
1172
+ cropBox.minWidth = min(minCropBoxWidth, maxCropBoxWidth);
1173
+ cropBox.minHeight = min(minCropBoxHeight, maxCropBoxHeight);
1174
+ cropBox.maxWidth = maxCropBoxWidth;
1175
+ cropBox.maxHeight = maxCropBoxHeight;
1176
+ }
1177
+
1178
+ if (isPositionLimited) {
1179
+ if (isLimited) {
1180
+ cropBox.minLeft = max(0, canvas.left);
1181
+ cropBox.minTop = max(0, canvas.top);
1182
+ cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;
1183
+ cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;
1184
+ } else {
1185
+ cropBox.minLeft = 0;
1186
+ cropBox.minTop = 0;
1187
+ cropBox.maxLeft = containerWidth - cropBox.width;
1188
+ cropBox.maxTop = containerHeight - cropBox.height;
1189
+ }
1190
+ }
1191
+ },
1192
+
1193
+ renderCropBox: function () {
1194
+ var options = this.options;
1195
+ var container = this.container;
1196
+ var containerWidth = container.width;
1197
+ var containerHeight = container.height;
1198
+ var cropBox = this.cropBox;
1199
+
1200
+ if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {
1201
+ cropBox.left = cropBox.oldLeft;
1202
+ }
1203
+
1204
+ if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {
1205
+ cropBox.top = cropBox.oldTop;
1206
+ }
1207
+
1208
+ cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
1209
+ cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
1210
+
1211
+ this.limitCropBox(false, true);
1212
+
1213
+ cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);
1214
+ cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop);
1215
+
1216
+ if (options.movable && options.cropBoxMovable) {
1217
+
1218
+ // Turn to move the canvas when the crop box is equal to the container
1219
+ this.$face.data(DATA_ACTION, (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL);
1220
+ }
1221
+
1222
+ this.$cropBox.css({
1223
+ width: cropBox.width,
1224
+ height: cropBox.height,
1225
+ left: cropBox.left,
1226
+ top: cropBox.top
1227
+ });
1228
+
1229
+ if (this.isCropped && this.isLimited) {
1230
+ this.limitCanvas(true, true);
1231
+ }
1232
+
1233
+ if (!this.isDisabled) {
1234
+ this.output();
1235
+ }
1236
+ },
1237
+
1238
+ output: function () {
1239
+ this.preview();
1240
+
1241
+ if (this.isCompleted) {
1242
+ this.trigger(EVENT_CROP, this.getData());
1243
+ }
1244
+ },
1245
+
1246
+ initPreview: function () {
1247
+ var crossOrigin = getCrossOrigin(this.crossOrigin);
1248
+ var url = crossOrigin ? this.crossOriginUrl : this.url;
1249
+ var $clone2;
1250
+
1251
+ this.$preview = $(this.options.preview);
1252
+ this.$clone2 = $clone2 = $('<img' + crossOrigin + ' src="' + url + '">');
1253
+ this.$viewBox.html($clone2);
1254
+ this.$preview.each(function () {
1255
+ var $this = $(this);
1256
+
1257
+ // Save the original size for recover
1258
+ $this.data(DATA_PREVIEW, {
1259
+ width: $this.width(),
1260
+ height: $this.height(),
1261
+ html: $this.html()
1262
+ });
1263
+
1264
+ /**
1265
+ * Override img element styles
1266
+ * Add `display:block` to avoid margin top issue
1267
+ * (Occur only when margin-top <= -height)
1268
+ */
1269
+ $this.html(
1270
+ '<img' + crossOrigin + ' src="' + url + '" style="' +
1271
+ 'display:block;width:100%;height:auto;' +
1272
+ 'min-width:0!important;min-height:0!important;' +
1273
+ 'max-width:none!important;max-height:none!important;' +
1274
+ 'image-orientation:0deg!important;">'
1275
+ );
1276
+ });
1277
+ },
1278
+
1279
+ resetPreview: function () {
1280
+ this.$preview.each(function () {
1281
+ var $this = $(this);
1282
+ var data = $this.data(DATA_PREVIEW);
1283
+
1284
+ $this.css({
1285
+ width: data.width,
1286
+ height: data.height
1287
+ }).html(data.html).removeData(DATA_PREVIEW);
1288
+ });
1289
+ },
1290
+
1291
+ preview: function () {
1292
+ var image = this.image;
1293
+ var canvas = this.canvas;
1294
+ var cropBox = this.cropBox;
1295
+ var cropBoxWidth = cropBox.width;
1296
+ var cropBoxHeight = cropBox.height;
1297
+ var width = image.width;
1298
+ var height = image.height;
1299
+ var left = cropBox.left - canvas.left - image.left;
1300
+ var top = cropBox.top - canvas.top - image.top;
1301
+
1302
+ if (!this.isCropped || this.isDisabled) {
1303
+ return;
1304
+ }
1305
+
1306
+ this.$clone2.css({
1307
+ width: width,
1308
+ height: height,
1309
+ marginLeft: -left,
1310
+ marginTop: -top,
1311
+ transform: getTransform(image)
1312
+ });
1313
+
1314
+ this.$preview.each(function () {
1315
+ var $this = $(this);
1316
+ var data = $this.data(DATA_PREVIEW);
1317
+ var originalWidth = data.width;
1318
+ var originalHeight = data.height;
1319
+ var newWidth = originalWidth;
1320
+ var newHeight = originalHeight;
1321
+ var ratio = 1;
1322
+
1323
+ if (cropBoxWidth) {
1324
+ ratio = originalWidth / cropBoxWidth;
1325
+ newHeight = cropBoxHeight * ratio;
1326
+ }
1327
+
1328
+ if (cropBoxHeight && newHeight > originalHeight) {
1329
+ ratio = originalHeight / cropBoxHeight;
1330
+ newWidth = cropBoxWidth * ratio;
1331
+ newHeight = originalHeight;
1332
+ }
1333
+
1334
+ $this.css({
1335
+ width: newWidth,
1336
+ height: newHeight
1337
+ }).find('img').css({
1338
+ width: width * ratio,
1339
+ height: height * ratio,
1340
+ marginLeft: -left * ratio,
1341
+ marginTop: -top * ratio,
1342
+ transform: getTransform(image)
1343
+ });
1344
+ });
1345
+ },
1346
+
1347
+ bind: function () {
1348
+ var options = this.options;
1349
+ var $this = this.$element;
1350
+ var $cropper = this.$cropper;
1351
+
1352
+ if ($.isFunction(options.cropstart)) {
1353
+ $this.on(EVENT_CROP_START, options.cropstart);
1354
+ }
1355
+
1356
+ if ($.isFunction(options.cropmove)) {
1357
+ $this.on(EVENT_CROP_MOVE, options.cropmove);
1358
+ }
1359
+
1360
+ if ($.isFunction(options.cropend)) {
1361
+ $this.on(EVENT_CROP_END, options.cropend);
1362
+ }
1363
+
1364
+ if ($.isFunction(options.crop)) {
1365
+ $this.on(EVENT_CROP, options.crop);
1366
+ }
1367
+
1368
+ if ($.isFunction(options.zoom)) {
1369
+ $this.on(EVENT_ZOOM, options.zoom);
1370
+ }
1371
+
1372
+ $cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this));
1373
+
1374
+ if (options.zoomable && options.zoomOnWheel) {
1375
+ $cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
1376
+ }
1377
+
1378
+ if (options.toggleDragModeOnDblclick) {
1379
+ $cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
1380
+ }
1381
+
1382
+ $document.
1383
+ on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))).
1384
+ on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this)));
1385
+
1386
+ if (options.responsive) {
1387
+ $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
1388
+ }
1389
+ },
1390
+
1391
+ unbind: function () {
1392
+ var options = this.options;
1393
+ var $this = this.$element;
1394
+ var $cropper = this.$cropper;
1395
+
1396
+ if ($.isFunction(options.cropstart)) {
1397
+ $this.off(EVENT_CROP_START, options.cropstart);
1398
+ }
1399
+
1400
+ if ($.isFunction(options.cropmove)) {
1401
+ $this.off(EVENT_CROP_MOVE, options.cropmove);
1402
+ }
1403
+
1404
+ if ($.isFunction(options.cropend)) {
1405
+ $this.off(EVENT_CROP_END, options.cropend);
1406
+ }
1407
+
1408
+ if ($.isFunction(options.crop)) {
1409
+ $this.off(EVENT_CROP, options.crop);
1410
+ }
1411
+
1412
+ if ($.isFunction(options.zoom)) {
1413
+ $this.off(EVENT_ZOOM, options.zoom);
1414
+ }
1415
+
1416
+ $cropper.off(EVENT_MOUSE_DOWN, this.cropStart);
1417
+
1418
+ if (options.zoomable && options.zoomOnWheel) {
1419
+ $cropper.off(EVENT_WHEEL, this.wheel);
1420
+ }
1421
+
1422
+ if (options.toggleDragModeOnDblclick) {
1423
+ $cropper.off(EVENT_DBLCLICK, this.dblclick);
1424
+ }
1425
+
1426
+ $document.
1427
+ off(EVENT_MOUSE_MOVE, this._cropMove).
1428
+ off(EVENT_MOUSE_UP, this._cropEnd);
1429
+
1430
+ if (options.responsive) {
1431
+ $window.off(EVENT_RESIZE, this._resize);
1432
+ }
1433
+ },
1434
+
1435
+ resize: function () {
1436
+ var restore = this.options.restore;
1437
+ var $container = this.$container;
1438
+ var container = this.container;
1439
+ var canvasData;
1440
+ var cropBoxData;
1441
+ var ratio;
1442
+
1443
+ // Check `container` is necessary for IE8
1444
+ if (this.isDisabled || !container) {
1445
+ return;
1446
+ }
1447
+
1448
+ ratio = $container.width() / container.width;
1449
+
1450
+ // Resize when width changed or height changed
1451
+ if (ratio !== 1 || $container.height() !== container.height) {
1452
+ if (restore) {
1453
+ canvasData = this.getCanvasData();
1454
+ cropBoxData = this.getCropBoxData();
1455
+ }
1456
+
1457
+ this.render();
1458
+
1459
+ if (restore) {
1460
+ this.setCanvasData($.each(canvasData, function (i, n) {
1461
+ canvasData[i] = n * ratio;
1462
+ }));
1463
+ this.setCropBoxData($.each(cropBoxData, function (i, n) {
1464
+ cropBoxData[i] = n * ratio;
1465
+ }));
1466
+ }
1467
+ }
1468
+ },
1469
+
1470
+ dblclick: function () {
1471
+ if (this.isDisabled) {
1472
+ return;
1473
+ }
1474
+
1475
+ if (this.$dragBox.hasClass(CLASS_CROP)) {
1476
+ this.setDragMode(ACTION_MOVE);
1477
+ } else {
1478
+ this.setDragMode(ACTION_CROP);
1479
+ }
1480
+ },
1481
+
1482
+ wheel: function (event) {
1483
+ var e = event.originalEvent || event;
1484
+ var ratio = num(this.options.wheelZoomRatio) || 0.1;
1485
+ var delta = 1;
1486
+
1487
+ if (this.isDisabled) {
1488
+ return;
1489
+ }
1490
+
1491
+ event.preventDefault();
1492
+
1493
+ // Limit wheel speed to prevent zoom too fast
1494
+ if (this.wheeling) {
1495
+ return;
1496
+ }
1497
+
1498
+ this.wheeling = true;
1499
+
1500
+ setTimeout($.proxy(function () {
1501
+ this.wheeling = false;
1502
+ }, this), 50);
1503
+
1504
+ if (e.deltaY) {
1505
+ delta = e.deltaY > 0 ? 1 : -1;
1506
+ } else if (e.wheelDelta) {
1507
+ delta = -e.wheelDelta / 120;
1508
+ } else if (e.detail) {
1509
+ delta = e.detail > 0 ? 1 : -1;
1510
+ }
1511
+
1512
+ this.zoom(-delta * ratio, event);
1513
+ },
1514
+
1515
+ cropStart: function (event) {
1516
+ var options = this.options;
1517
+ var originalEvent = event.originalEvent;
1518
+ var touches = originalEvent && originalEvent.touches;
1519
+ var e = event;
1520
+ var touchesLength;
1521
+ var action;
1522
+
1523
+ if (this.isDisabled) {
1524
+ return;
1525
+ }
1526
+
1527
+ if (touches) {
1528
+ touchesLength = touches.length;
1529
+
1530
+ if (touchesLength > 1) {
1531
+ if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {
1532
+ e = touches[1];
1533
+ this.startX2 = e.pageX;
1534
+ this.startY2 = e.pageY;
1535
+ action = ACTION_ZOOM;
1536
+ } else {
1537
+ return;
1538
+ }
1539
+ }
1540
+
1541
+ e = touches[0];
1542
+ }
1543
+
1544
+ action = action || $(e.target).data(DATA_ACTION);
1545
+
1546
+ if (REGEXP_ACTIONS.test(action)) {
1547
+ if (this.trigger(EVENT_CROP_START, {
1548
+ originalEvent: originalEvent,
1549
+ action: action
1550
+ }).isDefaultPrevented()) {
1551
+ return;
1552
+ }
1553
+
1554
+ event.preventDefault();
1555
+
1556
+ this.action = action;
1557
+ this.cropping = false;
1558
+
1559
+ // IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`
1560
+ // IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`
1561
+ this.startX = e.pageX || originalEvent && originalEvent.pageX;
1562
+ this.startY = e.pageY || originalEvent && originalEvent.pageY;
1563
+
1564
+ if (action === ACTION_CROP) {
1565
+ this.cropping = true;
1566
+ this.$dragBox.addClass(CLASS_MODAL);
1567
+ }
1568
+ }
1569
+ },
1570
+
1571
+ cropMove: function (event) {
1572
+ var options = this.options;
1573
+ var originalEvent = event.originalEvent;
1574
+ var touches = originalEvent && originalEvent.touches;
1575
+ var e = event;
1576
+ var action = this.action;
1577
+ var touchesLength;
1578
+
1579
+ if (this.isDisabled) {
1580
+ return;
1581
+ }
1582
+
1583
+ if (touches) {
1584
+ touchesLength = touches.length;
1585
+
1586
+ if (touchesLength > 1) {
1587
+ if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {
1588
+ e = touches[1];
1589
+ this.endX2 = e.pageX;
1590
+ this.endY2 = e.pageY;
1591
+ } else {
1592
+ return;
1593
+ }
1594
+ }
1595
+
1596
+ e = touches[0];
1597
+ }
1598
+
1599
+ if (action) {
1600
+ if (this.trigger(EVENT_CROP_MOVE, {
1601
+ originalEvent: originalEvent,
1602
+ action: action
1603
+ }).isDefaultPrevented()) {
1604
+ return;
1605
+ }
1606
+
1607
+ event.preventDefault();
1608
+
1609
+ this.endX = e.pageX || originalEvent && originalEvent.pageX;
1610
+ this.endY = e.pageY || originalEvent && originalEvent.pageY;
1611
+
1612
+ this.change(e.shiftKey, action === ACTION_ZOOM ? event : null);
1613
+ }
1614
+ },
1615
+
1616
+ cropEnd: function (event) {
1617
+ var originalEvent = event.originalEvent;
1618
+ var action = this.action;
1619
+
1620
+ if (this.isDisabled) {
1621
+ return;
1622
+ }
1623
+
1624
+ if (action) {
1625
+ event.preventDefault();
1626
+
1627
+ if (this.cropping) {
1628
+ this.cropping = false;
1629
+ this.$dragBox.toggleClass(CLASS_MODAL, this.isCropped && this.options.modal);
1630
+ }
1631
+
1632
+ this.action = '';
1633
+
1634
+ this.trigger(EVENT_CROP_END, {
1635
+ originalEvent: originalEvent,
1636
+ action: action
1637
+ });
1638
+ }
1639
+ },
1640
+
1641
+ change: function (shiftKey, event) {
1642
+ var options = this.options;
1643
+ var aspectRatio = options.aspectRatio;
1644
+ var action = this.action;
1645
+ var container = this.container;
1646
+ var canvas = this.canvas;
1647
+ var cropBox = this.cropBox;
1648
+ var width = cropBox.width;
1649
+ var height = cropBox.height;
1650
+ var left = cropBox.left;
1651
+ var top = cropBox.top;
1652
+ var right = left + width;
1653
+ var bottom = top + height;
1654
+ var minLeft = 0;
1655
+ var minTop = 0;
1656
+ var maxWidth = container.width;
1657
+ var maxHeight = container.height;
1658
+ var renderable = true;
1659
+ var offset;
1660
+ var range;
1661
+
1662
+ // Locking aspect ratio in "free mode" by holding shift key (#259)
1663
+ if (!aspectRatio && shiftKey) {
1664
+ aspectRatio = width && height ? width / height : 1;
1665
+ }
1666
+
1667
+ if (this.isLimited) {
1668
+ minLeft = cropBox.minLeft;
1669
+ minTop = cropBox.minTop;
1670
+ maxWidth = minLeft + min(container.width, canvas.width, canvas.left + canvas.width);
1671
+ maxHeight = minTop + min(container.height, canvas.height, canvas.top + canvas.height);
1672
+ }
1673
+
1674
+ range = {
1675
+ x: this.endX - this.startX,
1676
+ y: this.endY - this.startY
1677
+ };
1678
+
1679
+ if (aspectRatio) {
1680
+ range.X = range.y * aspectRatio;
1681
+ range.Y = range.x / aspectRatio;
1682
+ }
1683
+
1684
+ switch (action) {
1685
+ // Move crop box
1686
+ case ACTION_ALL:
1687
+ left += range.x;
1688
+ top += range.y;
1689
+ break;
1690
+
1691
+ // Resize crop box
1692
+ case ACTION_EAST:
1693
+ if (range.x >= 0 && (right >= maxWidth || aspectRatio &&
1694
+ (top <= minTop || bottom >= maxHeight))) {
1695
+
1696
+ renderable = false;
1697
+ break;
1698
+ }
1699
+
1700
+ width += range.x;
1701
+
1702
+ if (aspectRatio) {
1703
+ height = width / aspectRatio;
1704
+ top -= range.Y / 2;
1705
+ }
1706
+
1707
+ if (width < 0) {
1708
+ action = ACTION_WEST;
1709
+ width = 0;
1710
+ }
1711
+
1712
+ break;
1713
+
1714
+ case ACTION_NORTH:
1715
+ if (range.y <= 0 && (top <= minTop || aspectRatio &&
1716
+ (left <= minLeft || right >= maxWidth))) {
1717
+
1718
+ renderable = false;
1719
+ break;
1720
+ }
1721
+
1722
+ height -= range.y;
1723
+ top += range.y;
1724
+
1725
+ if (aspectRatio) {
1726
+ width = height * aspectRatio;
1727
+ left += range.X / 2;
1728
+ }
1729
+
1730
+ if (height < 0) {
1731
+ action = ACTION_SOUTH;
1732
+ height = 0;
1733
+ }
1734
+
1735
+ break;
1736
+
1737
+ case ACTION_WEST:
1738
+ if (range.x <= 0 && (left <= minLeft || aspectRatio &&
1739
+ (top <= minTop || bottom >= maxHeight))) {
1740
+
1741
+ renderable = false;
1742
+ break;
1743
+ }
1744
+
1745
+ width -= range.x;
1746
+ left += range.x;
1747
+
1748
+ if (aspectRatio) {
1749
+ height = width / aspectRatio;
1750
+ top += range.Y / 2;
1751
+ }
1752
+
1753
+ if (width < 0) {
1754
+ action = ACTION_EAST;
1755
+ width = 0;
1756
+ }
1757
+
1758
+ break;
1759
+
1760
+ case ACTION_SOUTH:
1761
+ if (range.y >= 0 && (bottom >= maxHeight || aspectRatio &&
1762
+ (left <= minLeft || right >= maxWidth))) {
1763
+
1764
+ renderable = false;
1765
+ break;
1766
+ }
1767
+
1768
+ height += range.y;
1769
+
1770
+ if (aspectRatio) {
1771
+ width = height * aspectRatio;
1772
+ left -= range.X / 2;
1773
+ }
1774
+
1775
+ if (height < 0) {
1776
+ action = ACTION_NORTH;
1777
+ height = 0;
1778
+ }
1779
+
1780
+ break;
1781
+
1782
+ case ACTION_NORTH_EAST:
1783
+ if (aspectRatio) {
1784
+ if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
1785
+ renderable = false;
1786
+ break;
1787
+ }
1788
+
1789
+ height -= range.y;
1790
+ top += range.y;
1791
+ width = height * aspectRatio;
1792
+ } else {
1793
+ if (range.x >= 0) {
1794
+ if (right < maxWidth) {
1795
+ width += range.x;
1796
+ } else if (range.y <= 0 && top <= minTop) {
1797
+ renderable = false;
1798
+ }
1799
+ } else {
1800
+ width += range.x;
1801
+ }
1802
+
1803
+ if (range.y <= 0) {
1804
+ if (top > minTop) {
1805
+ height -= range.y;
1806
+ top += range.y;
1807
+ }
1808
+ } else {
1809
+ height -= range.y;
1810
+ top += range.y;
1811
+ }
1812
+ }
1813
+
1814
+ if (width < 0 && height < 0) {
1815
+ action = ACTION_SOUTH_WEST;
1816
+ height = 0;
1817
+ width = 0;
1818
+ } else if (width < 0) {
1819
+ action = ACTION_NORTH_WEST;
1820
+ width = 0;
1821
+ } else if (height < 0) {
1822
+ action = ACTION_SOUTH_EAST;
1823
+ height = 0;
1824
+ }
1825
+
1826
+ break;
1827
+
1828
+ case ACTION_NORTH_WEST:
1829
+ if (aspectRatio) {
1830
+ if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
1831
+ renderable = false;
1832
+ break;
1833
+ }
1834
+
1835
+ height -= range.y;
1836
+ top += range.y;
1837
+ width = height * aspectRatio;
1838
+ left += range.X;
1839
+ } else {
1840
+ if (range.x <= 0) {
1841
+ if (left > minLeft) {
1842
+ width -= range.x;
1843
+ left += range.x;
1844
+ } else if (range.y <= 0 && top <= minTop) {
1845
+ renderable = false;
1846
+ }
1847
+ } else {
1848
+ width -= range.x;
1849
+ left += range.x;
1850
+ }
1851
+
1852
+ if (range.y <= 0) {
1853
+ if (top > minTop) {
1854
+ height -= range.y;
1855
+ top += range.y;
1856
+ }
1857
+ } else {
1858
+ height -= range.y;
1859
+ top += range.y;
1860
+ }
1861
+ }
1862
+
1863
+ if (width < 0 && height < 0) {
1864
+ action = ACTION_SOUTH_EAST;
1865
+ height = 0;
1866
+ width = 0;
1867
+ } else if (width < 0) {
1868
+ action = ACTION_NORTH_EAST;
1869
+ width = 0;
1870
+ } else if (height < 0) {
1871
+ action = ACTION_SOUTH_WEST;
1872
+ height = 0;
1873
+ }
1874
+
1875
+ break;
1876
+
1877
+ case ACTION_SOUTH_WEST:
1878
+ if (aspectRatio) {
1879
+ if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
1880
+ renderable = false;
1881
+ break;
1882
+ }
1883
+
1884
+ width -= range.x;
1885
+ left += range.x;
1886
+ height = width / aspectRatio;
1887
+ } else {
1888
+ if (range.x <= 0) {
1889
+ if (left > minLeft) {
1890
+ width -= range.x;
1891
+ left += range.x;
1892
+ } else if (range.y >= 0 && bottom >= maxHeight) {
1893
+ renderable = false;
1894
+ }
1895
+ } else {
1896
+ width -= range.x;
1897
+ left += range.x;
1898
+ }
1899
+
1900
+ if (range.y >= 0) {
1901
+ if (bottom < maxHeight) {
1902
+ height += range.y;
1903
+ }
1904
+ } else {
1905
+ height += range.y;
1906
+ }
1907
+ }
1908
+
1909
+ if (width < 0 && height < 0) {
1910
+ action = ACTION_NORTH_EAST;
1911
+ height = 0;
1912
+ width = 0;
1913
+ } else if (width < 0) {
1914
+ action = ACTION_SOUTH_EAST;
1915
+ width = 0;
1916
+ } else if (height < 0) {
1917
+ action = ACTION_NORTH_WEST;
1918
+ height = 0;
1919
+ }
1920
+
1921
+ break;
1922
+
1923
+ case ACTION_SOUTH_EAST:
1924
+ if (aspectRatio) {
1925
+ if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
1926
+ renderable = false;
1927
+ break;
1928
+ }
1929
+
1930
+ width += range.x;
1931
+ height = width / aspectRatio;
1932
+ } else {
1933
+ if (range.x >= 0) {
1934
+ if (right < maxWidth) {
1935
+ width += range.x;
1936
+ } else if (range.y >= 0 && bottom >= maxHeight) {
1937
+ renderable = false;
1938
+ }
1939
+ } else {
1940
+ width += range.x;
1941
+ }
1942
+
1943
+ if (range.y >= 0) {
1944
+ if (bottom < maxHeight) {
1945
+ height += range.y;
1946
+ }
1947
+ } else {
1948
+ height += range.y;
1949
+ }
1950
+ }
1951
+
1952
+ if (width < 0 && height < 0) {
1953
+ action = ACTION_NORTH_WEST;
1954
+ height = 0;
1955
+ width = 0;
1956
+ } else if (width < 0) {
1957
+ action = ACTION_SOUTH_WEST;
1958
+ width = 0;
1959
+ } else if (height < 0) {
1960
+ action = ACTION_NORTH_EAST;
1961
+ height = 0;
1962
+ }
1963
+
1964
+ break;
1965
+
1966
+ // Move canvas
1967
+ case ACTION_MOVE:
1968
+ this.move(range.x, range.y);
1969
+ renderable = false;
1970
+ break;
1971
+
1972
+ // Zoom canvas
1973
+ case ACTION_ZOOM:
1974
+ this.zoom((function (x1, y1, x2, y2) {
1975
+ var z1 = sqrt(x1 * x1 + y1 * y1);
1976
+ var z2 = sqrt(x2 * x2 + y2 * y2);
1977
+
1978
+ return (z2 - z1) / z1;
1979
+ })(
1980
+ abs(this.startX - this.startX2),
1981
+ abs(this.startY - this.startY2),
1982
+ abs(this.endX - this.endX2),
1983
+ abs(this.endY - this.endY2)
1984
+ ), event);
1985
+ this.startX2 = this.endX2;
1986
+ this.startY2 = this.endY2;
1987
+ renderable = false;
1988
+ break;
1989
+
1990
+ // Create crop box
1991
+ case ACTION_CROP:
1992
+ if (!range.x || !range.y) {
1993
+ renderable = false;
1994
+ break;
1995
+ }
1996
+
1997
+ offset = this.$cropper.offset();
1998
+ left = this.startX - offset.left;
1999
+ top = this.startY - offset.top;
2000
+ width = cropBox.minWidth;
2001
+ height = cropBox.minHeight;
2002
+
2003
+ if (range.x > 0) {
2004
+ action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
2005
+ } else if (range.x < 0) {
2006
+ left -= width;
2007
+ action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
2008
+ }
2009
+
2010
+ if (range.y < 0) {
2011
+ top -= height;
2012
+ }
2013
+
2014
+ // Show the crop box if is hidden
2015
+ if (!this.isCropped) {
2016
+ this.$cropBox.removeClass(CLASS_HIDDEN);
2017
+ this.isCropped = true;
2018
+
2019
+ if (this.isLimited) {
2020
+ this.limitCropBox(true, true);
2021
+ }
2022
+ }
2023
+
2024
+ break;
2025
+
2026
+ // No default
2027
+ }
2028
+
2029
+ if (renderable) {
2030
+ cropBox.width = width;
2031
+ cropBox.height = height;
2032
+ cropBox.left = left;
2033
+ cropBox.top = top;
2034
+ this.action = action;
2035
+
2036
+ this.renderCropBox();
2037
+ }
2038
+
2039
+ // Override
2040
+ this.startX = this.endX;
2041
+ this.startY = this.endY;
2042
+ },
2043
+
2044
+ // Show the crop box manually
2045
+ crop: function () {
2046
+ if (!this.isBuilt || this.isDisabled) {
2047
+ return;
2048
+ }
2049
+
2050
+ if (!this.isCropped) {
2051
+ this.isCropped = true;
2052
+ this.limitCropBox(true, true);
2053
+
2054
+ if (this.options.modal) {
2055
+ this.$dragBox.addClass(CLASS_MODAL);
2056
+ }
2057
+
2058
+ this.$cropBox.removeClass(CLASS_HIDDEN);
2059
+ }
2060
+
2061
+ this.setCropBoxData(this.initialCropBox);
2062
+ },
2063
+
2064
+ // Reset the image and crop box to their initial states
2065
+ reset: function () {
2066
+ if (!this.isBuilt || this.isDisabled) {
2067
+ return;
2068
+ }
2069
+
2070
+ this.image = $.extend({}, this.initialImage);
2071
+ this.canvas = $.extend({}, this.initialCanvas);
2072
+ this.cropBox = $.extend({}, this.initialCropBox);
2073
+
2074
+ this.renderCanvas();
2075
+
2076
+ if (this.isCropped) {
2077
+ this.renderCropBox();
2078
+ }
2079
+ },
2080
+
2081
+ // Clear the crop box
2082
+ clear: function () {
2083
+ if (!this.isCropped || this.isDisabled) {
2084
+ return;
2085
+ }
2086
+
2087
+ $.extend(this.cropBox, {
2088
+ left: 0,
2089
+ top: 0,
2090
+ width: 0,
2091
+ height: 0
2092
+ });
2093
+
2094
+ this.isCropped = false;
2095
+ this.renderCropBox();
2096
+
2097
+ this.limitCanvas(true, true);
2098
+
2099
+ // Render canvas after crop box rendered
2100
+ this.renderCanvas();
2101
+
2102
+ this.$dragBox.removeClass(CLASS_MODAL);
2103
+ this.$cropBox.addClass(CLASS_HIDDEN);
2104
+ },
2105
+
2106
+ /**
2107
+ * Replace the image's src and rebuild the cropper
2108
+ *
2109
+ * @param {String} url
2110
+ * @param {Boolean} onlyColorChanged (optional)
2111
+ */
2112
+ replace: function (url, onlyColorChanged) {
2113
+ if (!this.isDisabled && url) {
2114
+ if (this.isImg) {
2115
+ this.$element.attr('src', url);
2116
+ }
2117
+
2118
+ if (onlyColorChanged) {
2119
+ this.url = url;
2120
+ this.$clone.attr('src', url);
2121
+
2122
+ if (this.isBuilt) {
2123
+ this.$preview.find('img').add(this.$clone2).attr('src', url);
2124
+ }
2125
+ } else {
2126
+ if (this.isImg) {
2127
+ this.isReplaced = true;
2128
+ }
2129
+
2130
+ // Clear previous data
2131
+ this.options.data = null;
2132
+ this.load(url);
2133
+ }
2134
+ }
2135
+ },
2136
+
2137
+ // Enable (unfreeze) the cropper
2138
+ enable: function () {
2139
+ if (this.isBuilt) {
2140
+ this.isDisabled = false;
2141
+ this.$cropper.removeClass(CLASS_DISABLED);
2142
+ }
2143
+ },
2144
+
2145
+ // Disable (freeze) the cropper
2146
+ disable: function () {
2147
+ if (this.isBuilt) {
2148
+ this.isDisabled = true;
2149
+ this.$cropper.addClass(CLASS_DISABLED);
2150
+ }
2151
+ },
2152
+
2153
+ // Destroy the cropper and remove the instance from the image
2154
+ destroy: function () {
2155
+ var $this = this.$element;
2156
+
2157
+ if (this.isLoaded) {
2158
+ if (this.isImg && this.isReplaced) {
2159
+ $this.attr('src', this.originalUrl);
2160
+ }
2161
+
2162
+ this.unbuild();
2163
+ $this.removeClass(CLASS_HIDDEN);
2164
+ } else {
2165
+ if (this.isImg) {
2166
+ $this.off(EVENT_LOAD, this.start);
2167
+ } else if (this.$clone) {
2168
+ this.$clone.remove();
2169
+ }
2170
+ }
2171
+
2172
+ $this.removeData(NAMESPACE);
2173
+ },
2174
+
2175
+ /**
2176
+ * Move the canvas with relative offsets
2177
+ *
2178
+ * @param {Number} offsetX
2179
+ * @param {Number} offsetY (optional)
2180
+ */
2181
+ move: function (offsetX, offsetY) {
2182
+ var canvas = this.canvas;
2183
+
2184
+ this.moveTo(
2185
+ isUndefined(offsetX) ? offsetX : canvas.left + num(offsetX),
2186
+ isUndefined(offsetY) ? offsetY : canvas.top + num(offsetY)
2187
+ );
2188
+ },
2189
+
2190
+ /**
2191
+ * Move the canvas to an absolute point
2192
+ *
2193
+ * @param {Number} x
2194
+ * @param {Number} y (optional)
2195
+ */
2196
+ moveTo: function (x, y) {
2197
+ var canvas = this.canvas;
2198
+ var isChanged = false;
2199
+
2200
+ // If "y" is not present, its default value is "x"
2201
+ if (isUndefined(y)) {
2202
+ y = x;
2203
+ }
2204
+
2205
+ x = num(x);
2206
+ y = num(y);
2207
+
2208
+ if (this.isBuilt && !this.isDisabled && this.options.movable) {
2209
+ if (isNumber(x)) {
2210
+ canvas.left = x;
2211
+ isChanged = true;
2212
+ }
2213
+
2214
+ if (isNumber(y)) {
2215
+ canvas.top = y;
2216
+ isChanged = true;
2217
+ }
2218
+
2219
+ if (isChanged) {
2220
+ this.renderCanvas(true);
2221
+ }
2222
+ }
2223
+ },
2224
+
2225
+ /**
2226
+ * Zoom the canvas with a relative ratio
2227
+ *
2228
+ * @param {Number} ratio
2229
+ * @param {jQuery Event} _event (private)
2230
+ */
2231
+ zoom: function (ratio, _event) {
2232
+ var canvas = this.canvas;
2233
+
2234
+ ratio = num(ratio);
2235
+
2236
+ if (ratio < 0) {
2237
+ ratio = 1 / (1 - ratio);
2238
+ } else {
2239
+ ratio = 1 + ratio;
2240
+ }
2241
+
2242
+ this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _event);
2243
+ },
2244
+
2245
+ /**
2246
+ * Zoom the canvas to an absolute ratio
2247
+ *
2248
+ * @param {Number} ratio
2249
+ * @param {jQuery Event} _event (private)
2250
+ */
2251
+ zoomTo: function (ratio, _event) {
2252
+ var options = this.options;
2253
+ var canvas = this.canvas;
2254
+ var width = canvas.width;
2255
+ var height = canvas.height;
2256
+ var naturalWidth = canvas.naturalWidth;
2257
+ var naturalHeight = canvas.naturalHeight;
2258
+ var originalEvent;
2259
+ var newWidth;
2260
+ var newHeight;
2261
+ var offset;
2262
+ var center;
2263
+
2264
+ ratio = num(ratio);
2265
+
2266
+ if (ratio >= 0 && this.isBuilt && !this.isDisabled && options.zoomable) {
2267
+ newWidth = naturalWidth * ratio;
2268
+ newHeight = naturalHeight * ratio;
2269
+
2270
+ if (_event) {
2271
+ originalEvent = _event.originalEvent;
2272
+ }
2273
+
2274
+ if (this.trigger(EVENT_ZOOM, {
2275
+ originalEvent: originalEvent,
2276
+ oldRatio: width / naturalWidth,
2277
+ ratio: newWidth / naturalWidth
2278
+ }).isDefaultPrevented()) {
2279
+ return;
2280
+ }
2281
+
2282
+ if (originalEvent) {
2283
+ offset = this.$cropper.offset();
2284
+ center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : {
2285
+ pageX: _event.pageX || originalEvent.pageX || 0,
2286
+ pageY: _event.pageY || originalEvent.pageY || 0
2287
+ };
2288
+
2289
+ // Zoom from the triggering point of the event
2290
+ canvas.left -= (newWidth - width) * (
2291
+ ((center.pageX - offset.left) - canvas.left) / width
2292
+ );
2293
+ canvas.top -= (newHeight - height) * (
2294
+ ((center.pageY - offset.top) - canvas.top) / height
2295
+ );
2296
+ } else {
2297
+
2298
+ // Zoom from the center of the canvas
2299
+ canvas.left -= (newWidth - width) / 2;
2300
+ canvas.top -= (newHeight - height) / 2;
2301
+ }
2302
+
2303
+ canvas.width = newWidth;
2304
+ canvas.height = newHeight;
2305
+ this.renderCanvas(true);
2306
+ }
2307
+ },
2308
+
2309
+ /**
2310
+ * Rotate the canvas with a relative degree
2311
+ *
2312
+ * @param {Number} degree
2313
+ */
2314
+ rotate: function (degree) {
2315
+ this.rotateTo((this.image.rotate || 0) + num(degree));
2316
+ },
2317
+
2318
+ /**
2319
+ * Rotate the canvas to an absolute degree
2320
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
2321
+ *
2322
+ * @param {Number} degree
2323
+ */
2324
+ rotateTo: function (degree) {
2325
+ degree = num(degree);
2326
+
2327
+ if (isNumber(degree) && this.isBuilt && !this.isDisabled && this.options.rotatable) {
2328
+ this.image.rotate = degree % 360;
2329
+ this.isRotated = true;
2330
+ this.renderCanvas(true);
2331
+ }
2332
+ },
2333
+
2334
+ /**
2335
+ * Scale the image
2336
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
2337
+ *
2338
+ * @param {Number} scaleX
2339
+ * @param {Number} scaleY (optional)
2340
+ */
2341
+ scale: function (scaleX, scaleY) {
2342
+ var image = this.image;
2343
+ var isChanged = false;
2344
+
2345
+ // If "scaleY" is not present, its default value is "scaleX"
2346
+ if (isUndefined(scaleY)) {
2347
+ scaleY = scaleX;
2348
+ }
2349
+
2350
+ scaleX = num(scaleX);
2351
+ scaleY = num(scaleY);
2352
+
2353
+ if (this.isBuilt && !this.isDisabled && this.options.scalable) {
2354
+ if (isNumber(scaleX)) {
2355
+ image.scaleX = scaleX;
2356
+ isChanged = true;
2357
+ }
2358
+
2359
+ if (isNumber(scaleY)) {
2360
+ image.scaleY = scaleY;
2361
+ isChanged = true;
2362
+ }
2363
+
2364
+ if (isChanged) {
2365
+ this.renderImage(true);
2366
+ }
2367
+ }
2368
+ },
2369
+
2370
+ /**
2371
+ * Scale the abscissa of the image
2372
+ *
2373
+ * @param {Number} scaleX
2374
+ */
2375
+ scaleX: function (scaleX) {
2376
+ var scaleY = this.image.scaleY;
2377
+
2378
+ this.scale(scaleX, isNumber(scaleY) ? scaleY : 1);
2379
+ },
2380
+
2381
+ /**
2382
+ * Scale the ordinate of the image
2383
+ *
2384
+ * @param {Number} scaleY
2385
+ */
2386
+ scaleY: function (scaleY) {
2387
+ var scaleX = this.image.scaleX;
2388
+
2389
+ this.scale(isNumber(scaleX) ? scaleX : 1, scaleY);
2390
+ },
2391
+
2392
+ /**
2393
+ * Get the cropped area position and size data (base on the original image)
2394
+ *
2395
+ * @param {Boolean} isRounded (optional)
2396
+ * @return {Object} data
2397
+ */
2398
+ getData: function (isRounded) {
2399
+ var options = this.options;
2400
+ var image = this.image;
2401
+ var canvas = this.canvas;
2402
+ var cropBox = this.cropBox;
2403
+ var ratio;
2404
+ var data;
2405
+
2406
+ if (this.isBuilt && this.isCropped) {
2407
+ data = {
2408
+ x: cropBox.left - canvas.left,
2409
+ y: cropBox.top - canvas.top,
2410
+ width: cropBox.width,
2411
+ height: cropBox.height
2412
+ };
2413
+
2414
+ ratio = image.width / image.naturalWidth;
2415
+
2416
+ $.each(data, function (i, n) {
2417
+ n = n / ratio;
2418
+ data[i] = isRounded ? round(n) : n;
2419
+ });
2420
+
2421
+ } else {
2422
+ data = {
2423
+ x: 0,
2424
+ y: 0,
2425
+ width: 0,
2426
+ height: 0
2427
+ };
2428
+ }
2429
+
2430
+ if (options.rotatable) {
2431
+ data.rotate = image.rotate || 0;
2432
+ }
2433
+
2434
+ if (options.scalable) {
2435
+ data.scaleX = image.scaleX || 1;
2436
+ data.scaleY = image.scaleY || 1;
2437
+ }
2438
+
2439
+ return data;
2440
+ },
2441
+
2442
+ /**
2443
+ * Set the cropped area position and size with new data
2444
+ *
2445
+ * @param {Object} data
2446
+ */
2447
+ setData: function (data) {
2448
+ var options = this.options;
2449
+ var image = this.image;
2450
+ var canvas = this.canvas;
2451
+ var cropBoxData = {};
2452
+ var isRotated;
2453
+ var isScaled;
2454
+ var ratio;
2455
+
2456
+ if ($.isFunction(data)) {
2457
+ data = data.call(this.element);
2458
+ }
2459
+
2460
+ if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
2461
+ if (options.rotatable) {
2462
+ if (isNumber(data.rotate) && data.rotate !== image.rotate) {
2463
+ image.rotate = data.rotate;
2464
+ this.isRotated = isRotated = true;
2465
+ }
2466
+ }
2467
+
2468
+ if (options.scalable) {
2469
+ if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {
2470
+ image.scaleX = data.scaleX;
2471
+ isScaled = true;
2472
+ }
2473
+
2474
+ if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {
2475
+ image.scaleY = data.scaleY;
2476
+ isScaled = true;
2477
+ }
2478
+ }
2479
+
2480
+ if (isRotated) {
2481
+ this.renderCanvas();
2482
+ } else if (isScaled) {
2483
+ this.renderImage();
2484
+ }
2485
+
2486
+ ratio = image.width / image.naturalWidth;
2487
+
2488
+ if (isNumber(data.x)) {
2489
+ cropBoxData.left = data.x * ratio + canvas.left;
2490
+ }
2491
+
2492
+ if (isNumber(data.y)) {
2493
+ cropBoxData.top = data.y * ratio + canvas.top;
2494
+ }
2495
+
2496
+ if (isNumber(data.width)) {
2497
+ cropBoxData.width = data.width * ratio;
2498
+ }
2499
+
2500
+ if (isNumber(data.height)) {
2501
+ cropBoxData.height = data.height * ratio;
2502
+ }
2503
+
2504
+ this.setCropBoxData(cropBoxData);
2505
+ }
2506
+ },
2507
+
2508
+ /**
2509
+ * Get the container size data
2510
+ *
2511
+ * @return {Object} data
2512
+ */
2513
+ getContainerData: function () {
2514
+ return this.isBuilt ? this.container : {};
2515
+ },
2516
+
2517
+ /**
2518
+ * Get the image position and size data
2519
+ *
2520
+ * @return {Object} data
2521
+ */
2522
+ getImageData: function () {
2523
+ return this.isLoaded ? this.image : {};
2524
+ },
2525
+
2526
+ /**
2527
+ * Get the canvas position and size data
2528
+ *
2529
+ * @return {Object} data
2530
+ */
2531
+ getCanvasData: function () {
2532
+ var canvas = this.canvas;
2533
+ var data = {};
2534
+
2535
+ if (this.isBuilt) {
2536
+ $.each([
2537
+ 'left',
2538
+ 'top',
2539
+ 'width',
2540
+ 'height',
2541
+ 'naturalWidth',
2542
+ 'naturalHeight'
2543
+ ], function (i, n) {
2544
+ data[n] = canvas[n];
2545
+ });
2546
+ }
2547
+
2548
+ return data;
2549
+ },
2550
+
2551
+ /**
2552
+ * Set the canvas position and size with new data
2553
+ *
2554
+ * @param {Object} data
2555
+ */
2556
+ setCanvasData: function (data) {
2557
+ var canvas = this.canvas;
2558
+ var aspectRatio = canvas.aspectRatio;
2559
+
2560
+ if ($.isFunction(data)) {
2561
+ data = data.call(this.$element);
2562
+ }
2563
+
2564
+ if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
2565
+ if (isNumber(data.left)) {
2566
+ canvas.left = data.left;
2567
+ }
2568
+
2569
+ if (isNumber(data.top)) {
2570
+ canvas.top = data.top;
2571
+ }
2572
+
2573
+ if (isNumber(data.width)) {
2574
+ canvas.width = data.width;
2575
+ canvas.height = data.width / aspectRatio;
2576
+ } else if (isNumber(data.height)) {
2577
+ canvas.height = data.height;
2578
+ canvas.width = data.height * aspectRatio;
2579
+ }
2580
+
2581
+ this.renderCanvas(true);
2582
+ }
2583
+ },
2584
+
2585
+ /**
2586
+ * Get the crop box position and size data
2587
+ *
2588
+ * @return {Object} data
2589
+ */
2590
+ getCropBoxData: function () {
2591
+ var cropBox = this.cropBox;
2592
+ var data;
2593
+
2594
+ if (this.isBuilt && this.isCropped) {
2595
+ data = {
2596
+ left: cropBox.left,
2597
+ top: cropBox.top,
2598
+ width: cropBox.width,
2599
+ height: cropBox.height
2600
+ };
2601
+ }
2602
+
2603
+ return data || {};
2604
+ },
2605
+
2606
+ /**
2607
+ * Set the crop box position and size with new data
2608
+ *
2609
+ * @param {Object} data
2610
+ */
2611
+ setCropBoxData: function (data) {
2612
+ var cropBox = this.cropBox;
2613
+ var aspectRatio = this.options.aspectRatio;
2614
+ var isWidthChanged;
2615
+ var isHeightChanged;
2616
+
2617
+ if ($.isFunction(data)) {
2618
+ data = data.call(this.$element);
2619
+ }
2620
+
2621
+ if (this.isBuilt && this.isCropped && !this.isDisabled && $.isPlainObject(data)) {
2622
+
2623
+ if (isNumber(data.left)) {
2624
+ cropBox.left = data.left;
2625
+ }
2626
+
2627
+ if (isNumber(data.top)) {
2628
+ cropBox.top = data.top;
2629
+ }
2630
+
2631
+ if (isNumber(data.width)) {
2632
+ isWidthChanged = true;
2633
+ cropBox.width = data.width;
2634
+ }
2635
+
2636
+ if (isNumber(data.height)) {
2637
+ isHeightChanged = true;
2638
+ cropBox.height = data.height;
2639
+ }
2640
+
2641
+ if (aspectRatio) {
2642
+ if (isWidthChanged) {
2643
+ cropBox.height = cropBox.width / aspectRatio;
2644
+ } else if (isHeightChanged) {
2645
+ cropBox.width = cropBox.height * aspectRatio;
2646
+ }
2647
+ }
2648
+
2649
+ this.renderCropBox();
2650
+ }
2651
+ },
2652
+
2653
+ /**
2654
+ * Get a canvas drawn the cropped image
2655
+ *
2656
+ * @param {Object} options (optional)
2657
+ * @return {HTMLCanvasElement} canvas
2658
+ */
2659
+ getCroppedCanvas: function (options) {
2660
+ var originalWidth;
2661
+ var originalHeight;
2662
+ var canvasWidth;
2663
+ var canvasHeight;
2664
+ var scaledWidth;
2665
+ var scaledHeight;
2666
+ var scaledRatio;
2667
+ var aspectRatio;
2668
+ var canvas;
2669
+ var context;
2670
+ var data;
2671
+
2672
+ if (!this.isBuilt || !SUPPORT_CANVAS) {
2673
+ return;
2674
+ }
2675
+
2676
+ if (!this.isCropped) {
2677
+ return getSourceCanvas(this.$clone[0], this.image);
2678
+ }
2679
+
2680
+ if (!$.isPlainObject(options)) {
2681
+ options = {};
2682
+ }
2683
+
2684
+ data = this.getData();
2685
+ originalWidth = data.width;
2686
+ originalHeight = data.height;
2687
+ aspectRatio = originalWidth / originalHeight;
2688
+
2689
+ if ($.isPlainObject(options)) {
2690
+ scaledWidth = options.width;
2691
+ scaledHeight = options.height;
2692
+ if(scaledWidth && scaledHeight){
2693
+ scaledRatio = scaledWidth / originalWidth;
2694
+ }else if (scaledWidth) {
2695
+ scaledHeight = scaledWidth / aspectRatio;
2696
+ scaledRatio = scaledWidth / originalWidth;
2697
+ } else if (scaledHeight) {
2698
+ scaledWidth = scaledHeight * aspectRatio;
2699
+ scaledRatio = scaledHeight / originalHeight;
2700
+ }
2701
+ }
2702
+
2703
+ // The canvas element will use `Math.floor` on a float number, so floor first
2704
+ canvasWidth = floor(scaledWidth || originalWidth);
2705
+ canvasHeight = floor(scaledHeight || originalHeight);
2706
+
2707
+ canvas = $('<canvas>')[0];
2708
+ canvas.width = canvasWidth;
2709
+ canvas.height = canvasHeight;
2710
+ context = canvas.getContext('2d');
2711
+ if (options.fillColor) {
2712
+ context.fillStyle = options.fillColor;
2713
+ context.fillRect(0, 0, canvasWidth, canvasHeight);
2714
+ }
2715
+
2716
+ // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
2717
+ context.drawImage.apply(context, (function () {
2718
+ var source = getSourceCanvas(this.$clone[0], this.image);
2719
+ var sourceWidth = source.width;
2720
+ var sourceHeight = source.height;
2721
+ var canvas = this.canvas;
2722
+ var params = [source];
2723
+
2724
+ // Source canvas
2725
+ var srcX = data.x + canvas.naturalWidth * (abs(data.scaleX || 1) - 1) / 2;
2726
+ var srcY = data.y + canvas.naturalHeight * (abs(data.scaleY || 1) - 1) / 2;
2727
+ var srcWidth;
2728
+ var srcHeight;
2729
+
2730
+ // Destination canvas
2731
+ var dstX;
2732
+ var dstY;
2733
+ var dstWidth;
2734
+ var dstHeight;
2735
+
2736
+ if (srcX <= -originalWidth || srcX > sourceWidth) {
2737
+ srcX = srcWidth = dstX = dstWidth = 0;
2738
+ } else if (srcX <= 0) {
2739
+ dstX = -srcX;
2740
+ srcX = 0;
2741
+ srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);
2742
+ } else if (srcX <= sourceWidth) {
2743
+ dstX = 0;
2744
+ srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);
2745
+ }
2746
+
2747
+ if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {
2748
+ srcY = srcHeight = dstY = dstHeight = 0;
2749
+ } else if (srcY <= 0) {
2750
+ dstY = -srcY;
2751
+ srcY = 0;
2752
+ srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);
2753
+ } else if (srcY <= sourceHeight) {
2754
+ dstY = 0;
2755
+ srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);
2756
+ }
2757
+
2758
+ // All the numerical parameters should be integer for `drawImage` (#476)
2759
+ params.push(floor(srcX), floor(srcY), floor(srcWidth), floor(srcHeight));
2760
+
2761
+ // Scale destination sizes
2762
+ if (scaledRatio) {
2763
+ dstX *= scaledRatio;
2764
+ dstY *= scaledRatio;
2765
+ dstWidth *= scaledRatio;
2766
+ dstHeight *= scaledRatio;
2767
+ }
2768
+
2769
+ // Avoid "IndexSizeError" in IE and Firefox
2770
+ if (dstWidth > 0 && dstHeight > 0) {
2771
+ params.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));
2772
+ }
2773
+ return params;
2774
+ }).call(this));
2775
+ return canvas;
2776
+ },
2777
+
2778
+ /**
2779
+ * Change the aspect ratio of the crop box
2780
+ *
2781
+ * @param {Number} aspectRatio
2782
+ */
2783
+ setAspectRatio: function (aspectRatio) {
2784
+ var options = this.options;
2785
+
2786
+ if (!this.isDisabled && !isUndefined(aspectRatio)) {
2787
+
2788
+ // 0 -> NaN
2789
+ options.aspectRatio = max(0, aspectRatio) || NaN;
2790
+
2791
+ if (this.isBuilt) {
2792
+ this.initCropBox();
2793
+
2794
+ if (this.isCropped) {
2795
+ this.renderCropBox();
2796
+ }
2797
+ }
2798
+ }
2799
+ },
2800
+
2801
+ /**
2802
+ * Change the drag mode
2803
+ *
2804
+ * @param {String} mode (optional)
2805
+ */
2806
+ setDragMode: function (mode) {
2807
+ var options = this.options;
2808
+ var croppable;
2809
+ var movable;
2810
+
2811
+ if (this.isLoaded && !this.isDisabled) {
2812
+ croppable = mode === ACTION_CROP;
2813
+ movable = options.movable && mode === ACTION_MOVE;
2814
+ mode = (croppable || movable) ? mode : ACTION_NONE;
2815
+
2816
+ this.$dragBox.
2817
+ data(DATA_ACTION, mode).
2818
+ toggleClass(CLASS_CROP, croppable).
2819
+ toggleClass(CLASS_MOVE, movable);
2820
+
2821
+ if (!options.cropBoxMovable) {
2822
+
2823
+ // Sync drag mode to crop box when it is not movable(#300)
2824
+ this.$face.
2825
+ data(DATA_ACTION, mode).
2826
+ toggleClass(CLASS_CROP, croppable).
2827
+ toggleClass(CLASS_MOVE, movable);
2828
+ }
2829
+ }
2830
+ }
2831
+ };
2832
+
2833
+ Cropper.DEFAULTS = {
2834
+
2835
+ // Define the view mode of the cropper
2836
+ viewMode: 0, // 0, 1, 2, 3
2837
+
2838
+ // Define the dragging mode of the cropper
2839
+ dragMode: 'crop', // 'crop', 'move' or 'none'
2840
+
2841
+ // Define the aspect ratio of the crop box
2842
+ aspectRatio: NaN,
2843
+
2844
+ // An object with the previous cropping result data
2845
+ data: null,
2846
+
2847
+ // A jQuery selector for adding extra containers to preview
2848
+ preview: '',
2849
+
2850
+ // Re-render the cropper when resize the window
2851
+ responsive: true,
2852
+
2853
+ // Restore the cropped area after resize the window
2854
+ restore: true,
2855
+
2856
+ // Check if the current image is a cross-origin image
2857
+ checkCrossOrigin: true,
2858
+
2859
+ // Check the current image's Exif Orientation information
2860
+ checkOrientation: true,
2861
+
2862
+ // Show the black modal
2863
+ modal: true,
2864
+
2865
+ // Show the dashed lines for guiding
2866
+ guides: true,
2867
+
2868
+ // Show the center indicator for guiding
2869
+ center: true,
2870
+
2871
+ // Show the white modal to highlight the crop box
2872
+ highlight: true,
2873
+
2874
+ // Show the grid background
2875
+ background: true,
2876
+
2877
+ // Enable to crop the image automatically when initialize
2878
+ autoCrop: true,
2879
+
2880
+ // Define the percentage of automatic cropping area when initializes
2881
+ autoCropArea: 0.8,
2882
+
2883
+ // Enable to move the image
2884
+ movable: true,
2885
+
2886
+ // Enable to rotate the image
2887
+ rotatable: true,
2888
+
2889
+ // Enable to scale the image
2890
+ scalable: true,
2891
+
2892
+ // Enable to zoom the image
2893
+ zoomable: true,
2894
+
2895
+ // Enable to zoom the image by dragging touch
2896
+ zoomOnTouch: true,
2897
+
2898
+ // Enable to zoom the image by wheeling mouse
2899
+ zoomOnWheel: true,
2900
+
2901
+ // Define zoom ratio when zoom the image by wheeling mouse
2902
+ wheelZoomRatio: 0.1,
2903
+
2904
+ // Enable to move the crop box
2905
+ cropBoxMovable: true,
2906
+
2907
+ // Enable to resize the crop box
2908
+ cropBoxResizable: true,
2909
+
2910
+ // Toggle drag mode between "crop" and "move" when click twice on the cropper
2911
+ toggleDragModeOnDblclick: true,
2912
+
2913
+ // Size limitation
2914
+ minCanvasWidth: 0,
2915
+ minCanvasHeight: 0,
2916
+ minCropBoxWidth: 0,
2917
+ minCropBoxHeight: 0,
2918
+ minContainerWidth: 200,
2919
+ minContainerHeight: 100,
2920
+
2921
+ // Shortcuts of events
2922
+ build: null,
2923
+ built: null,
2924
+ cropstart: null,
2925
+ cropmove: null,
2926
+ cropend: null,
2927
+ crop: null,
2928
+ zoom: null
2929
+ };
2930
+
2931
+ Cropper.setDefaults = function (options) {
2932
+ $.extend(Cropper.DEFAULTS, options);
2933
+ };
2934
+
2935
+ Cropper.TEMPLATE = (
2936
+ '<div class="cropper-container">' +
2937
+ '<div class="cropper-wrap-box">' +
2938
+ '<div class="cropper-canvas"></div>' +
2939
+ '</div>' +
2940
+ '<div class="cropper-drag-box"></div>' +
2941
+ '<div class="cropper-crop-box">' +
2942
+ '<span class="cropper-view-box"></span>' +
2943
+ '<span class="cropper-dashed dashed-h"></span>' +
2944
+ '<span class="cropper-dashed dashed-v"></span>' +
2945
+ '<span class="cropper-center"></span>' +
2946
+ '<span class="cropper-face"></span>' +
2947
+ '<span class="cropper-line line-e" data-action="e"></span>' +
2948
+ '<span class="cropper-line line-n" data-action="n"></span>' +
2949
+ '<span class="cropper-line line-w" data-action="w"></span>' +
2950
+ '<span class="cropper-line line-s" data-action="s"></span>' +
2951
+ '<span class="cropper-point point-e" data-action="e"></span>' +
2952
+ '<span class="cropper-point point-n" data-action="n"></span>' +
2953
+ '<span class="cropper-point point-w" data-action="w"></span>' +
2954
+ '<span class="cropper-point point-s" data-action="s"></span>' +
2955
+ '<span class="cropper-point point-ne" data-action="ne"></span>' +
2956
+ '<span class="cropper-point point-nw" data-action="nw"></span>' +
2957
+ '<span class="cropper-point point-sw" data-action="sw"></span>' +
2958
+ '<span class="cropper-point point-se" data-action="se"></span>' +
2959
+ '</div>' +
2960
+ '</div>'
2961
+ );
2962
+
2963
+ // Save the other cropper
2964
+ Cropper.other = $.fn.cropper;
2965
+
2966
+ // Register as jQuery plugin
2967
+ $.fn.cropper = function (option) {
2968
+ var args = toArray(arguments, 1);
2969
+ var result;
2970
+
2971
+ this.each(function () {
2972
+ var $this = $(this);
2973
+ var data = $this.data(NAMESPACE);
2974
+ var options;
2975
+ var fn;
2976
+
2977
+ if (!data) {
2978
+ if (/destroy/.test(option)) {
2979
+ return;
2980
+ }
2981
+
2982
+ options = $.extend({}, $this.data(), $.isPlainObject(option) && option);
2983
+ $this.data(NAMESPACE, (data = new Cropper(this, options)));
2984
+ }
2985
+
2986
+ if (typeof option === 'string' && $.isFunction(fn = data[option])) {
2987
+ result = fn.apply(data, args);
2988
+ }
2989
+ });
2990
+
2991
+ return isUndefined(result) ? this : result;
2992
+ };
2993
+
2994
+ $.fn.cropper.Constructor = Cropper;
2995
+ $.fn.cropper.setDefaults = Cropper.setDefaults;
2996
+
2997
+ // No conflict
2998
+ $.fn.cropper.noConflict = function () {
2999
+ $.fn.cropper = Cropper.other;
3000
+ return this;
3001
+ };
3002
+
3003
+ });