camaleon_cms 2.4.0 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.

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
+ });