diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..074ab52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +package-lock.json +node_modules \ No newline at end of file diff --git a/dist/app.js b/dist/app.js new file mode 100644 index 0000000..5012397 --- /dev/null +++ b/dist/app.js @@ -0,0 +1,12 @@ +!function(t){var e={};function i(a){if(e[a])return e[a].exports;var n=e[a]={i:a,l:!1,exports:{}};return t[a].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.m=t,i.c=e,i.d=function(t,e,a){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(i.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)i.d(a,n,function(e){return t[e]}.bind(null,n));return a},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s="./src/app.js")}({"./node_modules/cropperjs/dist/cropper.js":function(t,e,i){ +/*! + * Cropper.js v1.5.1 + * https://fengyuanchen.github.io/cropperjs + * + * Copyright 2015-present Chen Fengyuan + * Released under the MIT license + * + * Date: 2019-03-10T09:55:53.729Z + */ +t.exports=function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e){for(var i=0;i0&&t<1/0};function B(t){return void 0===t}function L(e){return"object"===t(e)&&null!==e}var R=Object.prototype.hasOwnProperty;function N(t){if(!L(t))return!1;try{var e=t.constructor,i=e.prototype;return e&&i&&R.call(i,"isPrototypeOf")}catch(t){return!1}}function I(t){return"function"==typeof t}var _=Array.prototype.slice;function j(t){return Array.from?Array.from(t):_.call(t)}function A(t,e){return t&&I(e)&&(Array.isArray(t)||T(t.length)?j(t).forEach(function(i,a){e.call(t,i,a,t)}):L(t)&&Object.keys(t).forEach(function(i){e.call(t,t[i],i,t)})),t}var S=Object.assign||function(t){for(var e=arguments.length,i=new Array(e>1?e-1:0),a=1;a0&&i.forEach(function(e){L(e)&&Object.keys(e).forEach(function(i){t[i]=e[i]})}),t},W=/\.\d*(?:0|9){12}\d*$/;function z(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1e11;return W.test(t)?Math.round(t*e)/e:t}var H=/^width|height|left|top|marginLeft|marginTop$/;function $(t,e){var i=t.style;A(e,function(t,e){H.test(e)&&T(t)&&(t="".concat(t,"px")),i[e]=t})}function X(t,e){if(e)if(T(t.length))A(t,function(t){X(t,e)});else if(t.classList)t.classList.add(e);else{var i=t.className.trim();i?i.indexOf(e)<0&&(t.className="".concat(i," ").concat(e)):t.className=e}}function Y(t,e){e&&(T(t.length)?A(t,function(t){Y(t,e)}):t.classList?t.classList.remove(e):t.className.indexOf(e)>=0&&(t.className=t.className.replace(e,"")))}function U(t,e,i){e&&(T(t.length)?A(t,function(t){U(t,e,i)}):i?X(t,e):Y(t,e))}var P=/([a-z\d])([A-Z])/g;function F(t){return t.replace(P,"$1-$2").toLowerCase()}function q(t,e){return L(t[e])?t[e]:t.dataset?t.dataset[e]:t.getAttribute("data-".concat(F(e)))}function Q(t,e,i){L(i)?t[e]=i:t.dataset?t.dataset[e]=i:t.setAttribute("data-".concat(F(e)),i)}var J=/\s\s*/,V=function(){var t=!1;if(a){var e=!1,i=function(){},o=Object.defineProperty({},"once",{get:function(){return t=!0,e},set:function(t){e=t}});n.addEventListener("test",i,o),n.removeEventListener("test",i,o)}return t}();function G(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},n=i;e.trim().split(J).forEach(function(e){if(!V){var o=t.listeners;o&&o[e]&&o[e][i]&&(n=o[e][i],delete o[e][i],0===Object.keys(o[e]).length&&delete o[e],0===Object.keys(o).length&&delete t.listeners)}t.removeEventListener(e,n,a)})}function Z(t,e,i){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},n=i;e.trim().split(J).forEach(function(e){if(a.once&&!V){var o=t.listeners,r=void 0===o?{}:o;n=function(){delete r[e][i],t.removeEventListener(e,n,a);for(var o=arguments.length,s=new Array(o),h=0;h1&&void 0!==arguments[1]?arguments[1]:"contain",o=O(a),r=O(i);if(o&&r){var s=i*e;"contain"===n&&s>a||"cover"===n&&s=8&&(o=c+l)}}}if(o){var d,u,m=i.getUint16(o,a);for(u=0;ut.width?3===i?s=t.height*r:h=t.width/r:3===i?h=t.width/r:s=t.height*r;var c={aspectRatio:r,naturalWidth:n,naturalHeight:o,width:s,height:h};c.left=(t.width-s)/2,c.top=(t.height-h)/2,c.oldLeft=c.left,c.oldTop=c.top,this.canvasData=c,this.limited=1===i||2===i,this.limitCanvas(!0,!0),this.initialImageData=S({},e),this.initialCanvasData=S({},c)},limitCanvas:function(t,e){var i=this.options,a=this.containerData,n=this.canvasData,o=this.cropBoxData,r=i.viewMode,s=n.aspectRatio,h=this.cropped&&o;if(t){var c=Number(i.minCanvasWidth)||0,p=Number(i.minCanvasHeight)||0;r>1?(c=Math.max(c,a.width),p=Math.max(p,a.height),3===r&&(p*s>c?c=p*s:p=c/s)):r>0&&(c?c=Math.max(c,h?o.width:0):p?p=Math.max(p,h?o.height:0):h&&(c=o.width,(p=o.height)*s>c?c=p*s:p=c/s));var l=st({aspectRatio:s,width:c,height:p});c=l.width,p=l.height,n.minWidth=c,n.minHeight=p,n.maxWidth=1/0,n.maxHeight=1/0}if(e)if(r>(h?0:1)){var d=a.width-n.width,u=a.height-n.height;n.minLeft=Math.min(0,d),n.minTop=Math.min(0,u),n.maxLeft=Math.max(0,d),n.maxTop=Math.max(0,u),h&&this.limited&&(n.minLeft=Math.min(o.left,o.left+(o.width-n.width)),n.minTop=Math.min(o.top,o.top+(o.height-n.height)),n.maxLeft=o.left,n.maxTop=o.top,2===r&&(n.width>=a.width&&(n.minLeft=Math.min(0,d),n.maxLeft=Math.max(0,d)),n.height>=a.height&&(n.minTop=Math.min(0,u),n.maxTop=Math.max(0,u))))}else n.minLeft=-n.width,n.minTop=-n.height,n.maxLeft=a.width,n.maxTop=a.height},renderCanvas:function(t,e){var i=this.canvasData,a=this.imageData;if(e){var n=function(t){var e=t.width,i=t.height,a=t.degree;if(90==(a=Math.abs(a)%180))return{width:i,height:e};var n=a%90*Math.PI/180,o=Math.sin(n),r=Math.cos(n),s=e*r+i*o,h=e*o+i*r;return a>90?{width:h,height:s}:{width:s,height:h}}({width:a.naturalWidth*Math.abs(a.scaleX||1),height:a.naturalHeight*Math.abs(a.scaleY||1),degree:a.rotate||0}),o=n.width,r=n.height,s=i.width*(o/i.naturalWidth),h=i.height*(r/i.naturalHeight);i.left-=(s-i.width)/2,i.top-=(h-i.height)/2,i.width=s,i.height=h,i.aspectRatio=o/r,i.naturalWidth=o,i.naturalHeight=r,this.limitCanvas(!0,!1)}(i.width>i.maxWidth||i.widthi.maxHeight||i.heighte.width?n.height=n.width/i:n.width=n.height*i),this.cropBoxData=n,this.limitCropBox(!0,!0),n.width=Math.min(Math.max(n.width,n.minWidth),n.maxWidth),n.height=Math.min(Math.max(n.height,n.minHeight),n.maxHeight),n.width=Math.max(n.minWidth,n.width*a),n.height=Math.max(n.minHeight,n.height*a),n.left=e.left+(e.width-n.width)/2,n.top=e.top+(e.height-n.height)/2,n.oldLeft=n.left,n.oldTop=n.top,this.initialCropBoxData=S({},n)},limitCropBox:function(t,e){var i=this.options,a=this.containerData,n=this.canvasData,o=this.cropBoxData,r=this.limited,s=i.aspectRatio;if(t){var h=Number(i.minCropBoxWidth)||0,c=Number(i.minCropBoxHeight)||0,p=r?Math.min(a.width,n.width,n.width+n.left,a.width-n.left):a.width,l=r?Math.min(a.height,n.height,n.height+n.top,a.height-n.top):a.height;h=Math.min(h,a.width),c=Math.min(c,a.height),s&&(h&&c?c*s>h?c=h/s:h=c*s:h?c=h/s:c&&(h=c*s),l*s>p?l=p/s:p=l*s),o.minWidth=Math.min(h,p),o.minHeight=Math.min(c,l),o.maxWidth=p,o.maxHeight=l}e&&(r?(o.minLeft=Math.max(0,n.left),o.minTop=Math.max(0,n.top),o.maxLeft=Math.min(a.width,n.left+n.width)-o.width,o.maxTop=Math.min(a.height,n.top+n.height)-o.height):(o.minLeft=0,o.minTop=0,o.maxLeft=a.width-o.width,o.maxTop=a.height-o.height))},renderCropBox:function(){var t=this.options,e=this.containerData,i=this.cropBoxData;(i.width>i.maxWidth||i.widthi.maxHeight||i.height=e.width&&i.height>=e.height?"move":"all"),$(this.cropBox,S({width:i.width,height:i.height},ot({translateX:i.left,translateY:i.top}))),this.cropped&&this.limited&&this.limitCanvas(!0,!0),this.disabled||this.output()},output:function(){this.preview(),K(this.element,"crop",this.getData())}},dt={initPreview:function(){var t=this.crossOrigin,e=this.options.preview,i=t?this.crossOriginUrl:this.url,a=document.createElement("img");if(t&&(a.crossOrigin=t),a.src=i,this.viewBox.appendChild(a),this.viewBoxImage=a,e){var n=e;"string"==typeof e?n=this.element.ownerDocument.querySelectorAll(e):e.querySelector&&(n=[e]),this.previews=n,A(n,function(e){var a=document.createElement("img");Q(e,g,{width:e.offsetWidth,height:e.offsetHeight,html:e.innerHTML}),t&&(a.crossOrigin=t),a.src=i,a.style.cssText='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;"',e.innerHTML="",e.appendChild(a)})}},resetPreview:function(){A(this.previews,function(t){var e=q(t,g);$(t,{width:e.width,height:e.height}),t.innerHTML=e.html,function(t,e){if(L(t[e]))try{delete t[e]}catch(i){t[e]=void 0}else if(t.dataset)try{delete t.dataset[e]}catch(i){t.dataset[e]=void 0}else t.removeAttribute("data-".concat(F(e)))}(t,g)})},preview:function(){var t=this.imageData,e=this.canvasData,i=this.cropBoxData,a=i.width,n=i.height,o=t.width,r=t.height,s=i.left-e.left-t.left,h=i.top-e.top-t.top;this.cropped&&!this.disabled&&($(this.viewBoxImage,S({width:o,height:r},ot(S({translateX:-s,translateY:-h},t)))),A(this.previews,function(e){var i=q(e,g),c=i.width,p=i.height,l=c,d=p,u=1;a&&(d=n*(u=c/a)),n&&d>p&&(l=a*(u=p/n),d=p),$(e,{width:l,height:d}),$(e.getElementsByTagName("img")[0],S({width:o*u,height:r*u},ot(S({translateX:-s*u,translateY:-h*u},t))))}))}},ut={bind:function(){var t=this.element,e=this.options,i=this.cropper;I(e.cropstart)&&Z(t,"cropstart",e.cropstart),I(e.cropmove)&&Z(t,"cropmove",e.cropmove),I(e.cropend)&&Z(t,"cropend",e.cropend),I(e.crop)&&Z(t,"crop",e.crop),I(e.zoom)&&Z(t,"zoom",e.zoom),Z(i,b,this.onCropStart=this.cropStart.bind(this)),e.zoomable&&e.zoomOnWheel&&Z(i,"wheel",this.onWheel=this.wheel.bind(this),{passive:!1,capture:!0}),e.toggleDragModeOnDblclick&&Z(i,"dblclick",this.onDblclick=this.dblclick.bind(this)),Z(t.ownerDocument,y,this.onCropMove=this.cropMove.bind(this)),Z(t.ownerDocument,x,this.onCropEnd=this.cropEnd.bind(this)),e.responsive&&Z(window,"resize",this.onResize=this.resize.bind(this))},unbind:function(){var t=this.element,e=this.options,i=this.cropper;I(e.cropstart)&&G(t,"cropstart",e.cropstart),I(e.cropmove)&&G(t,"cropmove",e.cropmove),I(e.cropend)&&G(t,"cropend",e.cropend),I(e.crop)&&G(t,"crop",e.crop),I(e.zoom)&&G(t,"zoom",e.zoom),G(i,b,this.onCropStart),e.zoomable&&e.zoomOnWheel&&G(i,"wheel",this.onWheel,{passive:!1,capture:!0}),e.toggleDragModeOnDblclick&&G(i,"dblclick",this.onDblclick),G(t.ownerDocument,y,this.onCropMove),G(t.ownerDocument,x,this.onCropEnd),e.responsive&&G(window,"resize",this.onResize)}},mt={resize:function(){var t=this.options,e=this.container,i=this.containerData,a=Number(t.minContainerWidth)||200,n=Number(t.minContainerHeight)||100;if(!(this.disabled||i.width<=a||i.height<=n)){var o,r,s=e.offsetWidth/i.width;1===s&&e.offsetHeight===i.height||(t.restore&&(o=this.getCanvasData(),r=this.getCropBoxData()),this.render(),t.restore&&(this.setCanvasData(A(o,function(t,e){o[e]=t*s})),this.setCropBoxData(A(r,function(t,e){r[e]=t*s}))))}},dblclick:function(){var t,e;this.disabled||"none"===this.options.dragMode||this.setDragMode((t=this.dragBox,e=s,(t.classList?t.classList.contains(e):t.className.indexOf(e)>-1)?"move":"crop"))},wheel:function(t){var e=this,i=Number(this.options.wheelZoomRatio)||.1,a=1;this.disabled||(t.preventDefault(),this.wheeling||(this.wheeling=!0,setTimeout(function(){e.wheeling=!1},50),t.deltaY?a=t.deltaY>0?1:-1:t.wheelDelta?a=-t.wheelDelta/120:t.detail&&(a=t.detail>0?1:-1),this.zoom(-a*i,t)))},cropStart:function(t){var e=t.buttons,i=t.button;if(!(this.disabled||T(e)&&1!==e||T(i)&&0!==i||t.ctrlKey)){var a,n=this.options,o=this.pointers;t.changedTouches?A(t.changedTouches,function(t){o[t.identifier]=rt(t)}):o[t.pointerId||0]=rt(t),a=Object.keys(o).length>1&&n.zoomable&&n.zoomOnTouch?"zoom":q(t.target,m),M.test(a)&&!1!==K(this.element,"cropstart",{originalEvent:t,action:a})&&(t.preventDefault(),this.action=a,this.cropping=!1,"crop"===a&&(this.cropping=!0,X(this.dragBox,d)))}},cropMove:function(t){var e=this.action;if(!this.disabled&&e){var i=this.pointers;t.preventDefault(),!1!==K(this.element,"cropmove",{originalEvent:t,action:e})&&(t.changedTouches?A(t.changedTouches,function(t){S(i[t.identifier]||{},rt(t,!0))}):S(i[t.pointerId||0]||{},rt(t,!0)),this.change(t))}},cropEnd:function(t){if(!this.disabled){var e=this.action,i=this.pointers;t.changedTouches?A(t.changedTouches,function(t){delete i[t.identifier]}):delete i[t.pointerId||0],e&&(t.preventDefault(),Object.keys(i).length||(this.action=""),this.cropping&&(this.cropping=!1,U(this.dragBox,d,this.cropped&&this.options.modal)),K(this.element,"cropend",{originalEvent:t,action:e}))}}},gt={change:function(t){var e,i=this.options,a=this.canvasData,n=this.containerData,o=this.cropBoxData,r=this.pointers,s=this.action,h=i.aspectRatio,p=o.left,l=o.top,d=o.width,u=o.height,m=p+d,g=l+u,f=0,v=0,w=n.width,b=n.height,y=!0;!h&&t.shiftKey&&(h=d&&u?d/u:1),this.limited&&(f=o.minLeft,v=o.minTop,w=f+Math.min(n.width,a.width,a.left+a.width),b=v+Math.min(n.height,a.height,a.top+a.height));var x=r[Object.keys(r)[0]],M={x:x.endX-x.startX,y:x.endY-x.startY},D=function(t){switch(t){case"e":m+M.x>w&&(M.x=w-m);break;case"w":p+M.xb&&(M.y=b-g)}};switch(s){case"all":p+=M.x,l+=M.y;break;case"e":if(M.x>=0&&(m>=w||h&&(l<=v||g>=b))){y=!1;break}D("e"),(d+=M.x)<0&&(s="w",p-=d=-d),h&&(u=d/h,l+=(o.height-u)/2);break;case"n":if(M.y<=0&&(l<=v||h&&(p<=f||m>=w))){y=!1;break}D("n"),u-=M.y,l+=M.y,u<0&&(s="s",l-=u=-u),h&&(d=u*h,p+=(o.width-d)/2);break;case"w":if(M.x<=0&&(p<=f||h&&(l<=v||g>=b))){y=!1;break}D("w"),d-=M.x,p+=M.x,d<0&&(s="e",p-=d=-d),h&&(u=d/h,l+=(o.height-u)/2);break;case"s":if(M.y>=0&&(g>=b||h&&(p<=f||m>=w))){y=!1;break}D("s"),(u+=M.y)<0&&(s="n",l-=u=-u),h&&(d=u*h,p+=(o.width-d)/2);break;case"ne":if(h){if(M.y<=0&&(l<=v||m>=w)){y=!1;break}D("n"),u-=M.y,l+=M.y,d=u*h}else D("n"),D("e"),M.x>=0?mv&&(u-=M.y,l+=M.y):(u-=M.y,l+=M.y);d<0&&u<0?(s="sw",l-=u=-u,p-=d=-d):d<0?(s="nw",p-=d=-d):u<0&&(s="se",l-=u=-u);break;case"nw":if(h){if(M.y<=0&&(l<=v||p<=f)){y=!1;break}D("n"),u-=M.y,l+=M.y,d=u*h,p+=o.width-d}else D("n"),D("w"),M.x<=0?p>f?(d-=M.x,p+=M.x):M.y<=0&&l<=v&&(y=!1):(d-=M.x,p+=M.x),M.y<=0?l>v&&(u-=M.y,l+=M.y):(u-=M.y,l+=M.y);d<0&&u<0?(s="se",l-=u=-u,p-=d=-d):d<0?(s="ne",p-=d=-d):u<0&&(s="sw",l-=u=-u);break;case"sw":if(h){if(M.x<=0&&(p<=f||g>=b)){y=!1;break}D("w"),d-=M.x,p+=M.x,u=d/h}else D("s"),D("w"),M.x<=0?p>f?(d-=M.x,p+=M.x):M.y>=0&&g>=b&&(y=!1):(d-=M.x,p+=M.x),M.y>=0?g=0&&(m>=w||g>=b)){y=!1;break}D("e"),d+=M.x,u=d/h}else D("s"),D("e"),M.x>=0?m=0&&g>=b&&(y=!1):d+=M.x,M.y>=0?g0?s=M.y>0?"se":"ne":M.x<0&&(p-=d,s=M.y>0?"sw":"nw"),M.y<0&&(l-=u),this.cropped||(Y(this.cropBox,c),this.cropped=!0,this.limited&&this.limitCropBox(!0,!0))}y&&(o.width=d,o.height=u,o.left=p,o.top=l,this.action=s,this.renderCropBox()),A(r,function(t){t.startX=t.endX,t.startY=t.endY})}},ft={crop:function(){return!this.ready||this.cropped||this.disabled||(this.cropped=!0,this.limitCropBox(!0,!0),this.options.modal&&X(this.dragBox,d),Y(this.cropBox,c),this.setCropBoxData(this.initialCropBoxData)),this},reset:function(){return this.ready&&!this.disabled&&(this.imageData=S({},this.initialImageData),this.canvasData=S({},this.initialCanvasData),this.cropBoxData=S({},this.initialCropBoxData),this.renderCanvas(),this.cropped&&this.renderCropBox()),this},clear:function(){return this.cropped&&!this.disabled&&(S(this.cropBoxData,{left:0,top:0,width:0,height:0}),this.cropped=!1,this.renderCropBox(),this.limitCanvas(!0,!0),this.renderCanvas(),Y(this.dragBox,d),X(this.cropBox,c)),this},replace:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return!this.disabled&&t&&(this.isImg&&(this.element.src=t),e?(this.url=t,this.image.src=t,this.ready&&(this.viewBoxImage.src=t,A(this.previews,function(e){e.getElementsByTagName("img")[0].src=t}))):(this.isImg&&(this.replaced=!0),this.options.data=null,this.uncreate(),this.load(t))),this},enable:function(){return this.ready&&this.disabled&&(this.disabled=!1,Y(this.cropper,h)),this},disable:function(){return this.ready&&!this.disabled&&(this.disabled=!0,X(this.cropper,h)),this},destroy:function(){var t=this.element;return t.cropper?(t.cropper=void 0,this.isImg&&this.replaced&&(t.src=this.originalUrl),this.uncreate(),this):this},move:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:t,i=this.canvasData,a=i.left,n=i.top;return this.moveTo(B(t)?t:a+Number(t),B(e)?e:n+Number(e))},moveTo:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:t,i=this.canvasData,a=!1;return t=Number(t),e=Number(e),this.ready&&!this.disabled&&this.options.movable&&(T(t)&&(i.left=t,a=!0),T(e)&&(i.top=e,a=!0),a&&this.renderCanvas(!0)),this},zoom:function(t,e){var i=this.canvasData;return t=(t=Number(t))<0?1/(1-t):1+t,this.zoomTo(i.width*t/i.naturalWidth,null,e)},zoomTo:function(t,e,i){var a=this.options,n=this.canvasData,o=n.width,r=n.height,s=n.naturalWidth,h=n.naturalHeight;if((t=Number(t))>=0&&this.ready&&!this.disabled&&a.zoomable){var c=s*t,p=h*t;if(!1===K(this.element,"zoom",{ratio:t,oldRatio:o/s,originalEvent:i}))return this;if(i){var l=this.pointers,d=tt(this.cropper),u=l&&Object.keys(l).length?function(t){var e=0,i=0,a=0;return A(t,function(t){var n=t.startX,o=t.startY;e+=n,i+=o,a+=1}),{pageX:e/=a,pageY:i/=a}}(l):{pageX:i.pageX,pageY:i.pageY};n.left-=(c-o)*((u.pageX-d.left-n.left)/o),n.top-=(p-r)*((u.pageY-d.top-n.top)/r)}else N(e)&&T(e.x)&&T(e.y)?(n.left-=(c-o)*((e.x-n.left)/o),n.top-=(p-r)*((e.y-n.top)/r)):(n.left-=(c-o)/2,n.top-=(p-r)/2);n.width=c,n.height=p,this.renderCanvas(!0)}return this},rotate:function(t){return this.rotateTo((this.imageData.rotate||0)+Number(t))},rotateTo:function(t){return T(t=Number(t))&&this.ready&&!this.disabled&&this.options.rotatable&&(this.imageData.rotate=t%360,this.renderCanvas(!0,!0)),this},scaleX:function(t){var e=this.imageData.scaleY;return this.scale(t,T(e)?e:1)},scaleY:function(t){var e=this.imageData.scaleX;return this.scale(T(e)?e:1,t)},scale:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:t,i=this.imageData,a=!1;return t=Number(t),e=Number(e),this.ready&&!this.disabled&&this.options.scalable&&(T(t)&&(i.scaleX=t,a=!0),T(e)&&(i.scaleY=e,a=!0),a&&this.renderCanvas(!0,!0)),this},getData:function(){var t,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],i=this.options,a=this.imageData,n=this.canvasData,o=this.cropBoxData;if(this.ready&&this.cropped){t={x:o.left-n.left,y:o.top-n.top,width:o.width,height:o.height};var r=a.width/a.naturalWidth;if(A(t,function(e,i){t[i]=e/r}),e){var s=Math.round(t.y+t.height),h=Math.round(t.x+t.width);t.x=Math.round(t.x),t.y=Math.round(t.y),t.width=h-t.x,t.height=s-t.y}}else t={x:0,y:0,width:0,height:0};return i.rotatable&&(t.rotate=a.rotate||0),i.scalable&&(t.scaleX=a.scaleX||1,t.scaleY=a.scaleY||1),t},setData:function(t){var e=this.options,i=this.imageData,a=this.canvasData,n={};if(this.ready&&!this.disabled&&N(t)){var o=!1;e.rotatable&&T(t.rotate)&&t.rotate!==i.rotate&&(i.rotate=t.rotate,o=!0),e.scalable&&(T(t.scaleX)&&t.scaleX!==i.scaleX&&(i.scaleX=t.scaleX,o=!0),T(t.scaleY)&&t.scaleY!==i.scaleY&&(i.scaleY=t.scaleY,o=!0)),o&&this.renderCanvas(!0,!0);var r=i.width/i.naturalWidth;T(t.x)&&(n.left=t.x*r+a.left),T(t.y)&&(n.top=t.y*r+a.top),T(t.width)&&(n.width=t.width*r),T(t.height)&&(n.height=t.height*r),this.setCropBoxData(n)}return this},getContainerData:function(){return this.ready?S({},this.containerData):{}},getImageData:function(){return this.sized?S({},this.imageData):{}},getCanvasData:function(){var t=this.canvasData,e={};return this.ready&&A(["left","top","width","height","naturalWidth","naturalHeight"],function(i){e[i]=t[i]}),e},setCanvasData:function(t){var e=this.canvasData,i=e.aspectRatio;return this.ready&&!this.disabled&&N(t)&&(T(t.left)&&(e.left=t.left),T(t.top)&&(e.top=t.top),T(t.width)?(e.width=t.width,e.height=t.width/i):T(t.height)&&(e.height=t.height,e.width=t.height*i),this.renderCanvas(!0)),this},getCropBoxData:function(){var t,e=this.cropBoxData;return this.ready&&this.cropped&&(t={left:e.left,top:e.top,width:e.width,height:e.height}),t||{}},setCropBoxData:function(t){var e,i,a=this.cropBoxData,n=this.options.aspectRatio;return this.ready&&this.cropped&&!this.disabled&&N(t)&&(T(t.left)&&(a.left=t.left),T(t.top)&&(a.top=t.top),T(t.width)&&t.width!==a.width&&(e=!0,a.width=t.width),T(t.height)&&t.height!==a.height&&(i=!0,a.height=t.height),n&&(e?a.height=a.width/n:i&&(a.width=a.height*n)),this.renderCropBox()),this},getCroppedCanvas:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!this.ready||!window.HTMLCanvasElement)return null;var e=this.canvasData,a=function(t,e,a,n){var o=e.aspectRatio,r=e.naturalWidth,s=e.naturalHeight,h=e.rotate,c=void 0===h?0:h,p=e.scaleX,l=void 0===p?1:p,d=e.scaleY,u=void 0===d?1:d,m=a.aspectRatio,g=a.naturalWidth,f=a.naturalHeight,v=n.fillColor,w=void 0===v?"transparent":v,b=n.imageSmoothingEnabled,y=void 0===b||b,x=n.imageSmoothingQuality,M=void 0===x?"low":x,D=n.maxWidth,C=void 0===D?1/0:D,k=n.maxHeight,E=void 0===k?1/0:k,T=n.minWidth,O=void 0===T?0:T,B=n.minHeight,L=void 0===B?0:B,R=document.createElement("canvas"),N=R.getContext("2d"),I=st({aspectRatio:m,width:C,height:E}),_=st({aspectRatio:m,width:O,height:L},"cover"),j=Math.min(I.width,Math.max(_.width,g)),A=Math.min(I.height,Math.max(_.height,f)),S=st({aspectRatio:o,width:C,height:E}),W=st({aspectRatio:o,width:O,height:L},"cover"),H=Math.min(S.width,Math.max(W.width,r)),$=Math.min(S.height,Math.max(W.height,s)),X=[-H/2,-$/2,H,$];return R.width=z(j),R.height=z(A),N.fillStyle=w,N.fillRect(0,0,j,A),N.save(),N.translate(j/2,A/2),N.rotate(c*Math.PI/180),N.scale(l,u),N.imageSmoothingEnabled=y,N.imageSmoothingQuality=M,N.drawImage.apply(N,[t].concat(i(X.map(function(t){return Math.floor(z(t))})))),N.restore(),R}(this.image,this.imageData,e,t);if(!this.cropped)return a;var n=this.getData(),o=n.x,r=n.y,s=n.width,h=n.height,c=a.width/Math.floor(e.naturalWidth);1!==c&&(o*=c,r*=c,s*=c,h*=c);var p=s/h,l=st({aspectRatio:p,width:t.maxWidth||1/0,height:t.maxHeight||1/0}),d=st({aspectRatio:p,width:t.minWidth||0,height:t.minHeight||0},"cover"),u=st({aspectRatio:p,width:t.width||(1!==c?a.width:s),height:t.height||(1!==c?a.height:h)}),m=u.width,g=u.height;m=Math.min(l.width,Math.max(d.width,m)),g=Math.min(l.height,Math.max(d.height,g));var f=document.createElement("canvas"),v=f.getContext("2d");f.width=z(m),f.height=z(g),v.fillStyle=t.fillColor||"transparent",v.fillRect(0,0,m,g);var w=t.imageSmoothingEnabled,b=void 0===w||w,y=t.imageSmoothingQuality;v.imageSmoothingEnabled=b,y&&(v.imageSmoothingQuality=y);var x,M,D,C,k,E,T=a.width,O=a.height,B=o,L=r;B<=-s||B>T?(B=0,x=0,D=0,k=0):B<=0?(D=-B,B=0,x=Math.min(T,s+B),k=x):B<=T&&(D=0,x=Math.min(s,T-B),k=x),x<=0||L<=-h||L>O?(L=0,M=0,C=0,E=0):L<=0?(C=-L,L=0,M=Math.min(O,h+L),E=M):L<=O&&(C=0,M=Math.min(h,O-L),E=M);var R=[B,L,x,M];if(k>0&&E>0){var N=m/s;R.push(D*N,C*N,k*N,E*N)}return v.drawImage.apply(v,[a].concat(i(R.map(function(t){return Math.floor(z(t))})))),f},setAspectRatio:function(t){var e=this.options;return this.disabled||B(t)||(e.aspectRatio=Math.max(0,t)||NaN,this.ready&&(this.initCropBox(),this.cropped&&this.renderCropBox())),this},setDragMode:function(t){var e=this.options,i=this.dragBox,a=this.face;if(this.ready&&!this.disabled){var n="crop"===t,o=e.movable&&"move"===t;t=n||o?t:"none",e.dragMode=t,Q(i,m,t),U(i,s,n),U(i,u,o),e.cropBoxMovable||(Q(a,m,t),U(a,s,n),U(a,u,o))}return this}},vt=n.Cropper,wt=function(){function t(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),!e||!C.test(e.tagName))throw new Error("The first argument is required and must be an or element.");this.element=e,this.options=S({},k,N(i)&&i),this.cropped=!1,this.disabled=!1,this.pointers={},this.ready=!1,this.reloading=!1,this.replaced=!1,this.sized=!1,this.sizing=!1,this.init()}return i=t,o=[{key:"noConflict",value:function(){return window.Cropper=vt,t}},{key:"setDefaults",value:function(t){S(k,N(t)&&t)}}],(a=[{key:"init",value:function(){var t,e=this.element,i=e.tagName.toLowerCase();if(!e.cropper){if(e.cropper=this,"img"===i){if(this.isImg=!0,t=e.getAttribute("src")||"",this.originalUrl=t,!t)return;t=e.src}else"canvas"===i&&window.HTMLCanvasElement&&(t=e.toDataURL());this.load(t)}}},{key:"load",value:function(t){var e=this;if(t){this.url=t,this.imageData={};var i=this.element,a=this.options;if(a.rotatable||a.scalable||(a.checkOrientation=!1),a.checkOrientation&&window.ArrayBuffer)if(D.test(t))this.read((n=t.replace(ct,""),o=atob(n),r=new ArrayBuffer(o.length),A(s=new Uint8Array(r),function(t,e){s[e]=o.charCodeAt(e)}),r));else{var n,o,r,s,h=new XMLHttpRequest,c=this.clone.bind(this);this.reloading=!0,this.xhr=h,h.onabort=c,h.onerror=c,h.ontimeout=c,h.onprogress=function(){"image/jpeg"!==h.getResponseHeader("content-type")&&h.abort()},h.onload=function(){e.read(h.response)},h.onloadend=function(){e.reloading=!1,e.xhr=null},a.checkCrossOrigin&&at(t)&&i.crossOrigin&&(t=nt(t)),h.open("GET",t),h.responseType="arraybuffer",h.withCredentials="use-credentials"===i.crossOrigin,h.send()}else this.clone()}}},{key:"read",value:function(t){var e=this.options,i=this.imageData,a=pt(t),n=0,o=1,r=1;if(a>1){this.url=function(t,e){for(var i=[],a=new Uint8Array(t);a.length>0;)i.push(ht.apply(null,j(a.subarray(0,8192)))),a=a.subarray(8192);return"data:".concat(e,";base64,").concat(btoa(i.join("")))}(t,"image/jpeg");var s=function(t){var e=0,i=1,a=1;switch(t){case 2:i=-1;break;case 3:e=-180;break;case 4:a=-1;break;case 5:e=90,a=-1;break;case 6:e=90;break;case 7:e=90,i=-1;break;case 8:e=-90}return{rotate:e,scaleX:i,scaleY:a}}(a);n=s.rotate,o=s.scaleX,r=s.scaleY}e.rotatable&&(i.rotate=n),e.scalable&&(i.scaleX=o,i.scaleY=r),this.clone()}},{key:"clone",value:function(){var t,e,i=this.element,a=this.url;this.options.checkCrossOrigin&&at(a)&&((t=i.crossOrigin)?e=a:(t="anonymous",e=nt(a))),this.crossOrigin=t,this.crossOriginUrl=e;var n=document.createElement("img");t&&(n.crossOrigin=t),n.src=e||a,this.image=n,n.onload=this.start.bind(this),n.onerror=this.stop.bind(this),X(n,p),i.parentNode.insertBefore(n,i.nextSibling)}},{key:"start",value:function(){var t=this,e=this.isImg?this.element:this.image;e.onload=null,e.onerror=null,this.sizing=!0;var i=n.navigator&&/^(?:.(?!chrome|android))*safari/i.test(n.navigator.userAgent),a=function(e,i){S(t.imageData,{naturalWidth:e,naturalHeight:i,aspectRatio:e/i}),t.sizing=!1,t.sized=!0,t.build()};if(!e.naturalWidth||i){var o=document.createElement("img"),r=document.body||document.documentElement;this.sizingImage=o,o.onload=function(){a(o.width,o.height),i||r.removeChild(o)},o.src=e.src,i||(o.style.cssText="left:0;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;opacity:0;position:absolute;top:0;z-index:-1;",r.appendChild(o))}else a(e.naturalWidth,e.naturalHeight)}},{key:"stop",value:function(){var t=this.image;t.onload=null,t.onerror=null,t.parentNode.removeChild(t),this.image=null}},{key:"build",value:function(){if(this.sized&&!this.ready){var t=this.element,e=this.options,i=this.image,a=t.parentNode,n=document.createElement("div");n.innerHTML='
';var o=n.querySelector(".".concat("cropper","-container")),r=o.querySelector(".".concat("cropper","-canvas")),s=o.querySelector(".".concat("cropper","-drag-box")),h=o.querySelector(".".concat("cropper","-crop-box")),d=h.querySelector(".".concat("cropper","-face"));this.container=a,this.cropper=o,this.canvas=r,this.dragBox=s,this.cropBox=h,this.viewBox=o.querySelector(".".concat("cropper","-view-box")),this.face=d,r.appendChild(i),X(t,c),a.insertBefore(o,t.nextSibling),this.isImg||Y(i,p),this.initPreview(),this.bind(),e.initialAspectRatio=Math.max(0,e.initialAspectRatio)||NaN,e.aspectRatio=Math.max(0,e.aspectRatio)||NaN,e.viewMode=Math.max(0,Math.min(3,Math.round(e.viewMode)))||0,X(h,c),e.guides||X(h.getElementsByClassName("".concat("cropper","-dashed")),c),e.center||X(h.getElementsByClassName("".concat("cropper","-center")),c),e.background&&X(o,"".concat("cropper","-bg")),e.highlight||X(d,l),e.cropBoxMovable&&(X(d,u),Q(d,m,"all")),e.cropBoxResizable||(X(h.getElementsByClassName("".concat("cropper","-line")),c),X(h.getElementsByClassName("".concat("cropper","-point")),c)),this.render(),this.ready=!0,this.setDragMode(e.dragMode),e.autoCrop&&this.crop(),this.setData(e.data),I(e.ready)&&Z(t,"ready",e.ready,{once:!0}),K(t,"ready")}}},{key:"unbuild",value:function(){this.ready&&(this.ready=!1,this.unbind(),this.resetPreview(),this.cropper.parentNode.removeChild(this.cropper),Y(this.element,c))}},{key:"uncreate",value:function(){this.ready?(this.unbuild(),this.ready=!1,this.cropped=!1):this.sizing?(this.sizingImage.onload=null,this.sizing=!1,this.sized=!1):this.reloading?(this.xhr.onabort=null,this.xhr.abort()):this.image&&this.stop()}}])&&e(i.prototype,a),o&&e(i,o),t;var i,a,o}();return S(wt.prototype,lt,dt,ut,mt,gt,ft),wt}()},"./node_modules/font-awesome sync \\.(otf|eot|svg|ttf|woff|woff2)$":function(t,e){function i(t){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}i.keys=function(){return[]},i.resolve=i,t.exports=i,i.id="./node_modules/font-awesome sync \\.(otf|eot|svg|ttf|woff|woff2)$"},"./src/_events.js":function(t,e){t.exports={AJAX:"ajax-load",LOADED:"load",SET_TARGET_UPDATE:"set-target-update",RESTORE_FIELD:"restore-field",FORM_INIT_BASICS:"form-basics",FORM_INIT_STEPPED:"form-init-stepped",FORM_INIT_VALIDATE:"form-init-validate",FORM_INIT_VALIDATE_FIELD:"form-init-validate-field",FORM_INIT_STORAGE:"form-init-storage",FORM_VALIDATION_FAILED:"form-validation-failed",FORM_STEPPED_NEW_STEP:"form-new-step",FORM_STEPPED_FIRST_STEP:"form-first-step",FORM_STEPPED_LAST_STEP:"form-last-step"}},"./src/app.js":function(t,e,i){"use strict";i.r(e);i("./src/app.scss");var a=i("./src/_events.js"),n=i.n(a),o=i("jquery"),r=i.n(o),s=function(){function t(t,e){for(var i=0;i'),r.$image=r.$el.find("img.cropping-image"),r.original_image=r.$image[0],r.mask_img=!1,r.name=r.$input.attr("name"),r.width=r.$input.data("width"),r.height=r.$input.data("height"),r.options=o,r.cropper=!1,r.$el.data(i,r),r.$el.prepend('
'),r.$removeBtns=r.$el.find(".remove-masks"),r.masks=[],r.$input.on("change",function(t){var e=t.currentTarget.files;e&&e.length&&r.loadFile(e[0])}),r.$el.append('Обрезать'),r.$el.find(".act-crop").on("click",function(t){if(!r.cropper)return!0;t.preventDefault();var i=r.cropper.getCroppedCanvas({width:r.width,height:r.height});r.$image[0].src=i.toDataURL(),r.original_image=new Image,r.original_image.src=i.toDataURL(),r.cropper.destroy(),r.cropper=!1,r.$el.removeClass(e+"-cropping"),r.$el.addClass(e+"-cropped")}),r.$el.find(".masks .mask-item").on("click",function(e){e.preventDefault(),r.setMask(t(e.currentTarget))}),r.$form.on("submit",function(t){if(!r.cropper)return!0;h.show(),r.saveImage();var e=r.cropper.getCroppedCanvas({width:r.width,height:r.height});r.$image[0].src=e.toDataURL(),e.toBlob(function(t){r.uploadFile(t)}),t.preventDefault()})}return l(a,null,[{key:"dispose",value:function(){console.log("Destroying: "+e)}},{key:"_jQueryInterface",value:function(){return this.each(function(e,n){var o=t(n),r=o.data(i);r||(r=new a(n),o.data(i,r))})}}]),a}(),s=function(){var i=this;this.setMask=function(t){var e=i;e.mask_img&&e.addMask(e.getMask()),e.cropper&&(e.cropper.destroy(),e.cropper=!1,e.saveImage()),e.mask_img=new Image,e.mask_img.src=t.data("src"),e.mask_img.onload=function(){var i=e.mask_img;e.cropper=new p.a(e.$image[0],{aspectRatio:i.width/i.height,viewMode:0,guides:!0,center:!0,highlight:!0,cropBoxMovable:!0,cropBoxResizable:!0,movable:!1,rotatable:!1,zoomable:!1,ready:function(){e.$el.find(".cropper-face").css({"background-color":"transparent","background-image":"url("+e.mask_img.src+")",opacity:"0.8"}),e.$el.find(".cropper-face").data("current-mask",t)}})}},this.addMask=function(e){var a=i,n=Date.now();a.masks[n]=e;var o=t('Удалить #'+n+"");return a.$el.find(".masks").append(o),o.on("click",function(e){e.preventDefault();var i=t(e.currentTarget).data("id");a.removeMask(i)}),n},this.removeMask=function(t){var e=i;delete e.masks[t],e.$el.find('.masks [data-id="'+t+'"]').remove(),e.mask_img=!1,e.$el.find(".cropper-face").data("current-mask").click()},this.getMask=function(){var t=i,e=document.createElement("canvas"),a=e.getContext("2d"),n=t.cropper,o=n.getData().width,r=n.getData().height,s=n.getData().y,h=n.getData().x;n.getImageData().left,n.getImageData().top,n.getImageData().aspectRatio;return e.width=t.width,e.height=t.height,a.imageSmoothingEnabled=!0,{img:t.mask_img,left:h,top:s,width:o,height:r}},this.saveImage=function(){var t=i,e=document.createElement("canvas"),a=e.getContext("2d");for(var n in e.width=t.width,e.height=t.height,a.imageSmoothingEnabled=!0,a.drawImage(t.original_image,0,0,t.width,t.height),t.masks){var o=t.masks[n];console.log(o),a.drawImage(o.img,o.left,o.top,o.width,o.height)}return t.$image[0].src=e.toDataURL(),e},this.loadFile=function(t){var a=i;/^image\/\w+/.test(t.type)?(a.$image[0].src=URL.createObjectURL(t),a.cropper&&a.cropper.destroy(),a.cropper=new p.a(a.$image[0],a.options),a.$input[0].value=null,a.$el.addClass(e+"-cropping")):window.alert("Please choose an image file.")},this.uploadFile=function(e){console.log("Initializing uploading sequence!");var o=i,r=new FormData(o.$form[0]);r.delete("BackURL"),r.delete(o.name),r.append(o.name,e,o.name+"-image.png"),r.append("ajax","1"),t.ajax({url:o.$form.attr("action"),data:r,processData:!1,contentType:!1,type:o.$form.attr("method"),success:function(e){console.log("UPLOAD SUCCESS!"),h.hide(),t(a).trigger(n.a.AJAX)}})}};t.fn[e]=r._jQueryInterface,t.fn[e].Constructor=r,t.fn[e].noConflict=function(){return t.fn[e]=JQUERY_NO_CONFLICT,r._jQueryInterface},t(window).on(n.a.AJAX+" "+n.a.LOADED,function(){t(".field.croppie").jsCroppieUI()})}(r.a);var d=function(){function t(t,e){for(var i=0;i' + '
' + '' + '
' + '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '
' + '';\n\n /**\n * Check if the given value is not a number.\n */\n\n var isNaN = Number.isNaN || WINDOW.isNaN;\n /**\n * Check if the given value is a number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a number, else `false`.\n */\n\n function isNumber(value) {\n return typeof value === 'number' && !isNaN(value);\n }\n /**\n * Check if the given value is a positive number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a positive number, else `false`.\n */\n\n var isPositiveNumber = function isPositiveNumber(value) {\n return value > 0 && value < Infinity;\n };\n /**\n * Check if the given value is undefined.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is undefined, else `false`.\n */\n\n function isUndefined(value) {\n return typeof value === 'undefined';\n }\n /**\n * Check if the given value is an object.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is an object, else `false`.\n */\n\n function isObject(value) {\n return _typeof(value) === 'object' && value !== null;\n }\n var hasOwnProperty = Object.prototype.hasOwnProperty;\n /**\n * Check if the given value is a plain object.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.\n */\n\n function isPlainObject(value) {\n if (!isObject(value)) {\n return false;\n }\n\n try {\n var _constructor = value.constructor;\n var prototype = _constructor.prototype;\n return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');\n } catch (error) {\n return false;\n }\n }\n /**\n * Check if the given value is a function.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a function, else `false`.\n */\n\n function isFunction(value) {\n return typeof value === 'function';\n }\n var slice = Array.prototype.slice;\n /**\n * Convert array-like or iterable object to an array.\n * @param {*} value - The value to convert.\n * @returns {Array} Returns a new array.\n */\n\n function toArray(value) {\n return Array.from ? Array.from(value) : slice.call(value);\n }\n /**\n * Iterate the given data.\n * @param {*} data - The data to iterate.\n * @param {Function} callback - The process function for each element.\n * @returns {*} The original data.\n */\n\n function forEach(data, callback) {\n if (data && isFunction(callback)) {\n if (Array.isArray(data) || isNumber(data.length)\n /* array-like */\n ) {\n toArray(data).forEach(function (value, key) {\n callback.call(data, value, key, data);\n });\n } else if (isObject(data)) {\n Object.keys(data).forEach(function (key) {\n callback.call(data, data[key], key, data);\n });\n }\n }\n\n return data;\n }\n /**\n * Extend the given object.\n * @param {*} target - The target object to extend.\n * @param {*} args - The rest objects for merging to the target object.\n * @returns {Object} The extended object.\n */\n\n var assign = Object.assign || function assign(target) {\n for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n if (isObject(target) && args.length > 0) {\n args.forEach(function (arg) {\n if (isObject(arg)) {\n Object.keys(arg).forEach(function (key) {\n target[key] = arg[key];\n });\n }\n });\n }\n\n return target;\n };\n var REGEXP_DECIMALS = /\\.\\d*(?:0|9){12}\\d*$/;\n /**\n * Normalize decimal number.\n * Check out {@link http://0.30000000000000004.com/}\n * @param {number} value - The value to normalize.\n * @param {number} [times=100000000000] - The times for normalizing.\n * @returns {number} Returns the normalized number.\n */\n\n function normalizeDecimalNumber(value) {\n var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;\n return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;\n }\n var REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;\n /**\n * Apply styles to the given element.\n * @param {Element} element - The target element.\n * @param {Object} styles - The styles for applying.\n */\n\n function setStyle(element, styles) {\n var style = element.style;\n forEach(styles, function (value, property) {\n if (REGEXP_SUFFIX.test(property) && isNumber(value)) {\n value = \"\".concat(value, \"px\");\n }\n\n style[property] = value;\n });\n }\n /**\n * Check if the given element has a special class.\n * @param {Element} element - The element to check.\n * @param {string} value - The class to search.\n * @returns {boolean} Returns `true` if the special class was found.\n */\n\n function hasClass(element, value) {\n return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;\n }\n /**\n * Add classes to the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be added.\n */\n\n function addClass(element, value) {\n if (!value) {\n return;\n }\n\n if (isNumber(element.length)) {\n forEach(element, function (elem) {\n addClass(elem, value);\n });\n return;\n }\n\n if (element.classList) {\n element.classList.add(value);\n return;\n }\n\n var className = element.className.trim();\n\n if (!className) {\n element.className = value;\n } else if (className.indexOf(value) < 0) {\n element.className = \"\".concat(className, \" \").concat(value);\n }\n }\n /**\n * Remove classes from the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be removed.\n */\n\n function removeClass(element, value) {\n if (!value) {\n return;\n }\n\n if (isNumber(element.length)) {\n forEach(element, function (elem) {\n removeClass(elem, value);\n });\n return;\n }\n\n if (element.classList) {\n element.classList.remove(value);\n return;\n }\n\n if (element.className.indexOf(value) >= 0) {\n element.className = element.className.replace(value, '');\n }\n }\n /**\n * Add or remove classes from the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be toggled.\n * @param {boolean} added - Add only.\n */\n\n function toggleClass(element, value, added) {\n if (!value) {\n return;\n }\n\n if (isNumber(element.length)) {\n forEach(element, function (elem) {\n toggleClass(elem, value, added);\n });\n return;\n } // IE10-11 doesn't support the second parameter of `classList.toggle`\n\n\n if (added) {\n addClass(element, value);\n } else {\n removeClass(element, value);\n }\n }\n var REGEXP_CAMEL_CASE = /([a-z\\d])([A-Z])/g;\n /**\n * Transform the given string from camelCase to kebab-case\n * @param {string} value - The value to transform.\n * @returns {string} The transformed value.\n */\n\n function toParamCase(value) {\n return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();\n }\n /**\n * Get data from the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to get.\n * @returns {string} The data value.\n */\n\n function getData(element, name) {\n if (isObject(element[name])) {\n return element[name];\n }\n\n if (element.dataset) {\n return element.dataset[name];\n }\n\n return element.getAttribute(\"data-\".concat(toParamCase(name)));\n }\n /**\n * Set data to the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to set.\n * @param {string} data - The data value.\n */\n\n function setData(element, name, data) {\n if (isObject(data)) {\n element[name] = data;\n } else if (element.dataset) {\n element.dataset[name] = data;\n } else {\n element.setAttribute(\"data-\".concat(toParamCase(name)), data);\n }\n }\n /**\n * Remove data from the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to remove.\n */\n\n function removeData(element, name) {\n if (isObject(element[name])) {\n try {\n delete element[name];\n } catch (error) {\n element[name] = undefined;\n }\n } else if (element.dataset) {\n // #128 Safari not allows to delete dataset property\n try {\n delete element.dataset[name];\n } catch (error) {\n element.dataset[name] = undefined;\n }\n } else {\n element.removeAttribute(\"data-\".concat(toParamCase(name)));\n }\n }\n var REGEXP_SPACES = /\\s\\s*/;\n\n var onceSupported = function () {\n var supported = false;\n\n if (IS_BROWSER) {\n var once = false;\n\n var listener = function listener() {};\n\n var options = Object.defineProperty({}, 'once', {\n get: function get() {\n supported = true;\n return once;\n },\n\n /**\n * This setter can fix a `TypeError` in strict mode\n * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}\n * @param {boolean} value - The value to set\n */\n set: function set(value) {\n once = value;\n }\n });\n WINDOW.addEventListener('test', listener, options);\n WINDOW.removeEventListener('test', listener, options);\n }\n\n return supported;\n }();\n /**\n * Remove event listener from the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Function} listener - The event listener.\n * @param {Object} options - The event options.\n */\n\n\n function removeListener(element, type, listener) {\n var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n var handler = listener;\n type.trim().split(REGEXP_SPACES).forEach(function (event) {\n if (!onceSupported) {\n var listeners = element.listeners;\n\n if (listeners && listeners[event] && listeners[event][listener]) {\n handler = listeners[event][listener];\n delete listeners[event][listener];\n\n if (Object.keys(listeners[event]).length === 0) {\n delete listeners[event];\n }\n\n if (Object.keys(listeners).length === 0) {\n delete element.listeners;\n }\n }\n }\n\n element.removeEventListener(event, handler, options);\n });\n }\n /**\n * Add event listener to the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Function} listener - The event listener.\n * @param {Object} options - The event options.\n */\n\n function addListener(element, type, listener) {\n var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n var _handler = listener;\n type.trim().split(REGEXP_SPACES).forEach(function (event) {\n if (options.once && !onceSupported) {\n var _element$listeners = element.listeners,\n listeners = _element$listeners === void 0 ? {} : _element$listeners;\n\n _handler = function handler() {\n delete listeners[event][listener];\n element.removeEventListener(event, _handler, options);\n\n for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n args[_key2] = arguments[_key2];\n }\n\n listener.apply(element, args);\n };\n\n if (!listeners[event]) {\n listeners[event] = {};\n }\n\n if (listeners[event][listener]) {\n element.removeEventListener(event, listeners[event][listener], options);\n }\n\n listeners[event][listener] = _handler;\n element.listeners = listeners;\n }\n\n element.addEventListener(event, _handler, options);\n });\n }\n /**\n * Dispatch event on the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Object} data - The additional event data.\n * @returns {boolean} Indicate if the event is default prevented or not.\n */\n\n function dispatchEvent(element, type, data) {\n var event; // Event and CustomEvent on IE9-11 are global objects, not constructors\n\n if (isFunction(Event) && isFunction(CustomEvent)) {\n event = new CustomEvent(type, {\n detail: data,\n bubbles: true,\n cancelable: true\n });\n } else {\n event = document.createEvent('CustomEvent');\n event.initCustomEvent(type, true, true, data);\n }\n\n return element.dispatchEvent(event);\n }\n /**\n * Get the offset base on the document.\n * @param {Element} element - The target element.\n * @returns {Object} The offset data.\n */\n\n function getOffset(element) {\n var box = element.getBoundingClientRect();\n return {\n left: box.left + (window.pageXOffset - document.documentElement.clientLeft),\n top: box.top + (window.pageYOffset - document.documentElement.clientTop)\n };\n }\n var location = WINDOW.location;\n var REGEXP_ORIGINS = /^(\\w+:)\\/\\/([^:/?#]*):?(\\d*)/i;\n /**\n * Check if the given URL is a cross origin URL.\n * @param {string} url - The target URL.\n * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.\n */\n\n function isCrossOriginURL(url) {\n var parts = url.match(REGEXP_ORIGINS);\n return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);\n }\n /**\n * Add timestamp to the given URL.\n * @param {string} url - The target URL.\n * @returns {string} The result URL.\n */\n\n function addTimestamp(url) {\n var timestamp = \"timestamp=\".concat(new Date().getTime());\n return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;\n }\n /**\n * Get transforms base on the given object.\n * @param {Object} obj - The target object.\n * @returns {string} A string contains transform values.\n */\n\n function getTransforms(_ref) {\n var rotate = _ref.rotate,\n scaleX = _ref.scaleX,\n scaleY = _ref.scaleY,\n translateX = _ref.translateX,\n translateY = _ref.translateY;\n var values = [];\n\n if (isNumber(translateX) && translateX !== 0) {\n values.push(\"translateX(\".concat(translateX, \"px)\"));\n }\n\n if (isNumber(translateY) && translateY !== 0) {\n values.push(\"translateY(\".concat(translateY, \"px)\"));\n } // Rotate should come first before scale to match orientation transform\n\n\n if (isNumber(rotate) && rotate !== 0) {\n values.push(\"rotate(\".concat(rotate, \"deg)\"));\n }\n\n if (isNumber(scaleX) && scaleX !== 1) {\n values.push(\"scaleX(\".concat(scaleX, \")\"));\n }\n\n if (isNumber(scaleY) && scaleY !== 1) {\n values.push(\"scaleY(\".concat(scaleY, \")\"));\n }\n\n var transform = values.length ? values.join(' ') : 'none';\n return {\n WebkitTransform: transform,\n msTransform: transform,\n transform: transform\n };\n }\n /**\n * Get the max ratio of a group of pointers.\n * @param {string} pointers - The target pointers.\n * @returns {number} The result ratio.\n */\n\n function getMaxZoomRatio(pointers) {\n var pointers2 = assign({}, pointers);\n var ratios = [];\n forEach(pointers, function (pointer, pointerId) {\n delete pointers2[pointerId];\n forEach(pointers2, function (pointer2) {\n var x1 = Math.abs(pointer.startX - pointer2.startX);\n var y1 = Math.abs(pointer.startY - pointer2.startY);\n var x2 = Math.abs(pointer.endX - pointer2.endX);\n var y2 = Math.abs(pointer.endY - pointer2.endY);\n var z1 = Math.sqrt(x1 * x1 + y1 * y1);\n var z2 = Math.sqrt(x2 * x2 + y2 * y2);\n var ratio = (z2 - z1) / z1;\n ratios.push(ratio);\n });\n });\n ratios.sort(function (a, b) {\n return Math.abs(a) < Math.abs(b);\n });\n return ratios[0];\n }\n /**\n * Get a pointer from an event object.\n * @param {Object} event - The target event object.\n * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.\n * @returns {Object} The result pointer contains start and/or end point coordinates.\n */\n\n function getPointer(_ref2, endOnly) {\n var pageX = _ref2.pageX,\n pageY = _ref2.pageY;\n var end = {\n endX: pageX,\n endY: pageY\n };\n return endOnly ? end : assign({\n startX: pageX,\n startY: pageY\n }, end);\n }\n /**\n * Get the center point coordinate of a group of pointers.\n * @param {Object} pointers - The target pointers.\n * @returns {Object} The center point coordinate.\n */\n\n function getPointersCenter(pointers) {\n var pageX = 0;\n var pageY = 0;\n var count = 0;\n forEach(pointers, function (_ref3) {\n var startX = _ref3.startX,\n startY = _ref3.startY;\n pageX += startX;\n pageY += startY;\n count += 1;\n });\n pageX /= count;\n pageY /= count;\n return {\n pageX: pageX,\n pageY: pageY\n };\n }\n /**\n * Get the max sizes in a rectangle under the given aspect ratio.\n * @param {Object} data - The original sizes.\n * @param {string} [type='contain'] - The adjust type.\n * @returns {Object} The result sizes.\n */\n\n function getAdjustedSizes(_ref4) // or 'cover'\n {\n var aspectRatio = _ref4.aspectRatio,\n height = _ref4.height,\n width = _ref4.width;\n var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';\n var isValidWidth = isPositiveNumber(width);\n var isValidHeight = isPositiveNumber(height);\n\n if (isValidWidth && isValidHeight) {\n var adjustedWidth = height * aspectRatio;\n\n if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {\n height = width / aspectRatio;\n } else {\n width = height * aspectRatio;\n }\n } else if (isValidWidth) {\n height = width / aspectRatio;\n } else if (isValidHeight) {\n width = height * aspectRatio;\n }\n\n return {\n width: width,\n height: height\n };\n }\n /**\n * Get the new sizes of a rectangle after rotated.\n * @param {Object} data - The original sizes.\n * @returns {Object} The result sizes.\n */\n\n function getRotatedSizes(_ref5) {\n var width = _ref5.width,\n height = _ref5.height,\n degree = _ref5.degree;\n degree = Math.abs(degree) % 180;\n\n if (degree === 90) {\n return {\n width: height,\n height: width\n };\n }\n\n var arc = degree % 90 * Math.PI / 180;\n var sinArc = Math.sin(arc);\n var cosArc = Math.cos(arc);\n var newWidth = width * cosArc + height * sinArc;\n var newHeight = width * sinArc + height * cosArc;\n return degree > 90 ? {\n width: newHeight,\n height: newWidth\n } : {\n width: newWidth,\n height: newHeight\n };\n }\n /**\n * Get a canvas which drew the given image.\n * @param {HTMLImageElement} image - The image for drawing.\n * @param {Object} imageData - The image data.\n * @param {Object} canvasData - The canvas data.\n * @param {Object} options - The options.\n * @returns {HTMLCanvasElement} The result canvas.\n */\n\n function getSourceCanvas(image, _ref6, _ref7, _ref8) {\n var imageAspectRatio = _ref6.aspectRatio,\n imageNaturalWidth = _ref6.naturalWidth,\n imageNaturalHeight = _ref6.naturalHeight,\n _ref6$rotate = _ref6.rotate,\n rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,\n _ref6$scaleX = _ref6.scaleX,\n scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,\n _ref6$scaleY = _ref6.scaleY,\n scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;\n var aspectRatio = _ref7.aspectRatio,\n naturalWidth = _ref7.naturalWidth,\n naturalHeight = _ref7.naturalHeight;\n var _ref8$fillColor = _ref8.fillColor,\n fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor,\n _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,\n imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,\n _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,\n imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ,\n _ref8$maxWidth = _ref8.maxWidth,\n maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,\n _ref8$maxHeight = _ref8.maxHeight,\n maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,\n _ref8$minWidth = _ref8.minWidth,\n minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,\n _ref8$minHeight = _ref8.minHeight,\n minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;\n var canvas = document.createElement('canvas');\n var context = canvas.getContext('2d');\n var maxSizes = getAdjustedSizes({\n aspectRatio: aspectRatio,\n width: maxWidth,\n height: maxHeight\n });\n var minSizes = getAdjustedSizes({\n aspectRatio: aspectRatio,\n width: minWidth,\n height: minHeight\n }, 'cover');\n var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));\n var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as\n // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90\n\n var destMaxSizes = getAdjustedSizes({\n aspectRatio: imageAspectRatio,\n width: maxWidth,\n height: maxHeight\n });\n var destMinSizes = getAdjustedSizes({\n aspectRatio: imageAspectRatio,\n width: minWidth,\n height: minHeight\n }, 'cover');\n var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));\n var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));\n var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];\n canvas.width = normalizeDecimalNumber(width);\n canvas.height = normalizeDecimalNumber(height);\n context.fillStyle = fillColor;\n context.fillRect(0, 0, width, height);\n context.save();\n context.translate(width / 2, height / 2);\n context.rotate(rotate * Math.PI / 180);\n context.scale(scaleX, scaleY);\n context.imageSmoothingEnabled = imageSmoothingEnabled;\n context.imageSmoothingQuality = imageSmoothingQuality;\n context.drawImage.apply(context, [image].concat(_toConsumableArray(params.map(function (param) {\n return Math.floor(normalizeDecimalNumber(param));\n }))));\n context.restore();\n return canvas;\n }\n var fromCharCode = String.fromCharCode;\n /**\n * Get string from char code in data view.\n * @param {DataView} dataView - The data view for read.\n * @param {number} start - The start index.\n * @param {number} length - The read length.\n * @returns {string} The read result.\n */\n\n function getStringFromCharCode(dataView, start, length) {\n var str = '';\n length += start;\n\n for (var i = start; i < length; i += 1) {\n str += fromCharCode(dataView.getUint8(i));\n }\n\n return str;\n }\n var REGEXP_DATA_URL_HEAD = /^data:.*,/;\n /**\n * Transform Data URL to array buffer.\n * @param {string} dataURL - The Data URL to transform.\n * @returns {ArrayBuffer} The result array buffer.\n */\n\n function dataURLToArrayBuffer(dataURL) {\n var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n var binary = atob(base64);\n var arrayBuffer = new ArrayBuffer(binary.length);\n var uint8 = new Uint8Array(arrayBuffer);\n forEach(uint8, function (value, i) {\n uint8[i] = binary.charCodeAt(i);\n });\n return arrayBuffer;\n }\n /**\n * Transform array buffer to Data URL.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.\n * @param {string} mimeType - The mime type of the Data URL.\n * @returns {string} The result Data URL.\n */\n\n function arrayBufferToDataURL(arrayBuffer, mimeType) {\n var chunks = []; // Chunk Typed Array for better performance (#435)\n\n var chunkSize = 8192;\n var uint8 = new Uint8Array(arrayBuffer);\n\n while (uint8.length > 0) {\n // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9\n // eslint-disable-next-line prefer-spread\n chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));\n uint8 = uint8.subarray(chunkSize);\n }\n\n return \"data:\".concat(mimeType, \";base64,\").concat(btoa(chunks.join('')));\n }\n /**\n * Get orientation value from given array buffer.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to read.\n * @returns {number} The read orientation value.\n */\n\n function resetAndGetOrientation(arrayBuffer) {\n var dataView = new DataView(arrayBuffer);\n var orientation; // Ignores range error when the image does not have correct Exif information\n\n try {\n var littleEndian;\n var app1Start;\n var ifdStart; // Only handle JPEG image (start by 0xFFD8)\n\n if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n var length = dataView.byteLength;\n var offset = 2;\n\n while (offset + 1 < length) {\n if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n app1Start = offset;\n break;\n }\n\n offset += 1;\n }\n }\n\n if (app1Start) {\n var exifIDCode = app1Start + 4;\n var tiffOffset = app1Start + 10;\n\n if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n var endianness = dataView.getUint16(tiffOffset);\n littleEndian = endianness === 0x4949;\n\n if (littleEndian || endianness === 0x4D4D\n /* bigEndian */\n ) {\n if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n if (firstIFDOffset >= 0x00000008) {\n ifdStart = tiffOffset + firstIFDOffset;\n }\n }\n }\n }\n }\n\n if (ifdStart) {\n var _length = dataView.getUint16(ifdStart, littleEndian);\n\n var _offset;\n\n var i;\n\n for (i = 0; i < _length; i += 1) {\n _offset = ifdStart + i * 12 + 2;\n\n if (dataView.getUint16(_offset, littleEndian) === 0x0112\n /* Orientation */\n ) {\n // 8 is the offset of the current tag's value\n _offset += 8; // Get the original orientation value\n\n orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value\n\n dataView.setUint16(_offset, 1, littleEndian);\n break;\n }\n }\n }\n } catch (error) {\n orientation = 1;\n }\n\n return orientation;\n }\n /**\n * Parse Exif Orientation value.\n * @param {number} orientation - The orientation to parse.\n * @returns {Object} The parsed result.\n */\n\n function parseOrientation(orientation) {\n var rotate = 0;\n var scaleX = 1;\n var scaleY = 1;\n\n switch (orientation) {\n // Flip horizontal\n case 2:\n scaleX = -1;\n break;\n // Rotate left 180°\n\n case 3:\n rotate = -180;\n break;\n // Flip vertical\n\n case 4:\n scaleY = -1;\n break;\n // Flip vertical and rotate right 90°\n\n case 5:\n rotate = 90;\n scaleY = -1;\n break;\n // Rotate right 90°\n\n case 6:\n rotate = 90;\n break;\n // Flip horizontal and rotate right 90°\n\n case 7:\n rotate = 90;\n scaleX = -1;\n break;\n // Rotate left 90°\n\n case 8:\n rotate = -90;\n break;\n\n default:\n }\n\n return {\n rotate: rotate,\n scaleX: scaleX,\n scaleY: scaleY\n };\n }\n\n var render = {\n render: function render() {\n this.initContainer();\n this.initCanvas();\n this.initCropBox();\n this.renderCanvas();\n\n if (this.cropped) {\n this.renderCropBox();\n }\n },\n initContainer: function initContainer() {\n var element = this.element,\n options = this.options,\n container = this.container,\n cropper = this.cropper;\n addClass(cropper, CLASS_HIDDEN);\n removeClass(element, CLASS_HIDDEN);\n var containerData = {\n width: Math.max(container.offsetWidth, Number(options.minContainerWidth) || 200),\n height: Math.max(container.offsetHeight, Number(options.minContainerHeight) || 100)\n };\n this.containerData = containerData;\n setStyle(cropper, {\n width: containerData.width,\n height: containerData.height\n });\n addClass(element, CLASS_HIDDEN);\n removeClass(cropper, CLASS_HIDDEN);\n },\n // Canvas (image wrapper)\n initCanvas: function initCanvas() {\n var containerData = this.containerData,\n imageData = this.imageData;\n var viewMode = this.options.viewMode;\n var rotated = Math.abs(imageData.rotate) % 180 === 90;\n var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;\n var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;\n var aspectRatio = naturalWidth / naturalHeight;\n var canvasWidth = containerData.width;\n var canvasHeight = containerData.height;\n\n if (containerData.height * aspectRatio > containerData.width) {\n if (viewMode === 3) {\n canvasWidth = containerData.height * aspectRatio;\n } else {\n canvasHeight = containerData.width / aspectRatio;\n }\n } else if (viewMode === 3) {\n canvasHeight = containerData.width / aspectRatio;\n } else {\n canvasWidth = containerData.height * aspectRatio;\n }\n\n var canvasData = {\n aspectRatio: aspectRatio,\n naturalWidth: naturalWidth,\n naturalHeight: naturalHeight,\n width: canvasWidth,\n height: canvasHeight\n };\n canvasData.left = (containerData.width - canvasWidth) / 2;\n canvasData.top = (containerData.height - canvasHeight) / 2;\n canvasData.oldLeft = canvasData.left;\n canvasData.oldTop = canvasData.top;\n this.canvasData = canvasData;\n this.limited = viewMode === 1 || viewMode === 2;\n this.limitCanvas(true, true);\n this.initialImageData = assign({}, imageData);\n this.initialCanvasData = assign({}, canvasData);\n },\n limitCanvas: function limitCanvas(sizeLimited, positionLimited) {\n var options = this.options,\n containerData = this.containerData,\n canvasData = this.canvasData,\n cropBoxData = this.cropBoxData;\n var viewMode = options.viewMode;\n var aspectRatio = canvasData.aspectRatio;\n var cropped = this.cropped && cropBoxData;\n\n if (sizeLimited) {\n var minCanvasWidth = Number(options.minCanvasWidth) || 0;\n var minCanvasHeight = Number(options.minCanvasHeight) || 0;\n\n if (viewMode > 1) {\n minCanvasWidth = Math.max(minCanvasWidth, containerData.width);\n minCanvasHeight = Math.max(minCanvasHeight, containerData.height);\n\n if (viewMode === 3) {\n if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n minCanvasWidth = minCanvasHeight * aspectRatio;\n } else {\n minCanvasHeight = minCanvasWidth / aspectRatio;\n }\n }\n } else if (viewMode > 0) {\n if (minCanvasWidth) {\n minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);\n } else if (minCanvasHeight) {\n minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);\n } else if (cropped) {\n minCanvasWidth = cropBoxData.width;\n minCanvasHeight = cropBoxData.height;\n\n if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n minCanvasWidth = minCanvasHeight * aspectRatio;\n } else {\n minCanvasHeight = minCanvasWidth / aspectRatio;\n }\n }\n }\n\n var _getAdjustedSizes = getAdjustedSizes({\n aspectRatio: aspectRatio,\n width: minCanvasWidth,\n height: minCanvasHeight\n });\n\n minCanvasWidth = _getAdjustedSizes.width;\n minCanvasHeight = _getAdjustedSizes.height;\n canvasData.minWidth = minCanvasWidth;\n canvasData.minHeight = minCanvasHeight;\n canvasData.maxWidth = Infinity;\n canvasData.maxHeight = Infinity;\n }\n\n if (positionLimited) {\n if (viewMode > (cropped ? 0 : 1)) {\n var newCanvasLeft = containerData.width - canvasData.width;\n var newCanvasTop = containerData.height - canvasData.height;\n canvasData.minLeft = Math.min(0, newCanvasLeft);\n canvasData.minTop = Math.min(0, newCanvasTop);\n canvasData.maxLeft = Math.max(0, newCanvasLeft);\n canvasData.maxTop = Math.max(0, newCanvasTop);\n\n if (cropped && this.limited) {\n canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));\n canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));\n canvasData.maxLeft = cropBoxData.left;\n canvasData.maxTop = cropBoxData.top;\n\n if (viewMode === 2) {\n if (canvasData.width >= containerData.width) {\n canvasData.minLeft = Math.min(0, newCanvasLeft);\n canvasData.maxLeft = Math.max(0, newCanvasLeft);\n }\n\n if (canvasData.height >= containerData.height) {\n canvasData.minTop = Math.min(0, newCanvasTop);\n canvasData.maxTop = Math.max(0, newCanvasTop);\n }\n }\n }\n } else {\n canvasData.minLeft = -canvasData.width;\n canvasData.minTop = -canvasData.height;\n canvasData.maxLeft = containerData.width;\n canvasData.maxTop = containerData.height;\n }\n }\n },\n renderCanvas: function renderCanvas(changed, transformed) {\n var canvasData = this.canvasData,\n imageData = this.imageData;\n\n if (transformed) {\n var _getRotatedSizes = getRotatedSizes({\n width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),\n height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),\n degree: imageData.rotate || 0\n }),\n naturalWidth = _getRotatedSizes.width,\n naturalHeight = _getRotatedSizes.height;\n\n var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);\n var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);\n canvasData.left -= (width - canvasData.width) / 2;\n canvasData.top -= (height - canvasData.height) / 2;\n canvasData.width = width;\n canvasData.height = height;\n canvasData.aspectRatio = naturalWidth / naturalHeight;\n canvasData.naturalWidth = naturalWidth;\n canvasData.naturalHeight = naturalHeight;\n this.limitCanvas(true, false);\n }\n\n if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {\n canvasData.left = canvasData.oldLeft;\n }\n\n if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {\n canvasData.top = canvasData.oldTop;\n }\n\n canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);\n canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);\n this.limitCanvas(false, true);\n canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);\n canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);\n canvasData.oldLeft = canvasData.left;\n canvasData.oldTop = canvasData.top;\n setStyle(this.canvas, assign({\n width: canvasData.width,\n height: canvasData.height\n }, getTransforms({\n translateX: canvasData.left,\n translateY: canvasData.top\n })));\n this.renderImage(changed);\n\n if (this.cropped && this.limited) {\n this.limitCropBox(true, true);\n }\n },\n renderImage: function renderImage(changed) {\n var canvasData = this.canvasData,\n imageData = this.imageData;\n var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);\n var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);\n assign(imageData, {\n width: width,\n height: height,\n left: (canvasData.width - width) / 2,\n top: (canvasData.height - height) / 2\n });\n setStyle(this.image, assign({\n width: imageData.width,\n height: imageData.height\n }, getTransforms(assign({\n translateX: imageData.left,\n translateY: imageData.top\n }, imageData))));\n\n if (changed) {\n this.output();\n }\n },\n initCropBox: function initCropBox() {\n var options = this.options,\n canvasData = this.canvasData;\n var aspectRatio = options.aspectRatio || options.initialAspectRatio;\n var autoCropArea = Number(options.autoCropArea) || 0.8;\n var cropBoxData = {\n width: canvasData.width,\n height: canvasData.height\n };\n\n if (aspectRatio) {\n if (canvasData.height * aspectRatio > canvasData.width) {\n cropBoxData.height = cropBoxData.width / aspectRatio;\n } else {\n cropBoxData.width = cropBoxData.height * aspectRatio;\n }\n }\n\n this.cropBoxData = cropBoxData;\n this.limitCropBox(true, true); // Initialize auto crop area\n\n cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than \"minWidth/Height\"\n\n cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);\n cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);\n cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;\n cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;\n cropBoxData.oldLeft = cropBoxData.left;\n cropBoxData.oldTop = cropBoxData.top;\n this.initialCropBoxData = assign({}, cropBoxData);\n },\n limitCropBox: function limitCropBox(sizeLimited, positionLimited) {\n var options = this.options,\n containerData = this.containerData,\n canvasData = this.canvasData,\n cropBoxData = this.cropBoxData,\n limited = this.limited;\n var aspectRatio = options.aspectRatio;\n\n if (sizeLimited) {\n var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;\n var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;\n var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;\n var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height\n\n minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);\n minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);\n\n if (aspectRatio) {\n if (minCropBoxWidth && minCropBoxHeight) {\n if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n minCropBoxHeight = minCropBoxWidth / aspectRatio;\n } else {\n minCropBoxWidth = minCropBoxHeight * aspectRatio;\n }\n } else if (minCropBoxWidth) {\n minCropBoxHeight = minCropBoxWidth / aspectRatio;\n } else if (minCropBoxHeight) {\n minCropBoxWidth = minCropBoxHeight * aspectRatio;\n }\n\n if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n } else {\n maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n }\n } // The minWidth/Height must be less than maxWidth/Height\n\n\n cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);\n cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);\n cropBoxData.maxWidth = maxCropBoxWidth;\n cropBoxData.maxHeight = maxCropBoxHeight;\n }\n\n if (positionLimited) {\n if (limited) {\n cropBoxData.minLeft = Math.max(0, canvasData.left);\n cropBoxData.minTop = Math.max(0, canvasData.top);\n cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;\n cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;\n } else {\n cropBoxData.minLeft = 0;\n cropBoxData.minTop = 0;\n cropBoxData.maxLeft = containerData.width - cropBoxData.width;\n cropBoxData.maxTop = containerData.height - cropBoxData.height;\n }\n }\n },\n renderCropBox: function renderCropBox() {\n var options = this.options,\n containerData = this.containerData,\n cropBoxData = this.cropBoxData;\n\n if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {\n cropBoxData.left = cropBoxData.oldLeft;\n }\n\n if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {\n cropBoxData.top = cropBoxData.oldTop;\n }\n\n cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);\n this.limitCropBox(false, true);\n cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);\n cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);\n cropBoxData.oldLeft = cropBoxData.left;\n cropBoxData.oldTop = cropBoxData.top;\n\n if (options.movable && options.cropBoxMovable) {\n // Turn to move the canvas when the crop box is equal to the container\n setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);\n }\n\n setStyle(this.cropBox, assign({\n width: cropBoxData.width,\n height: cropBoxData.height\n }, getTransforms({\n translateX: cropBoxData.left,\n translateY: cropBoxData.top\n })));\n\n if (this.cropped && this.limited) {\n this.limitCanvas(true, true);\n }\n\n if (!this.disabled) {\n this.output();\n }\n },\n output: function output() {\n this.preview();\n dispatchEvent(this.element, EVENT_CROP, this.getData());\n }\n };\n\n var preview = {\n initPreview: function initPreview() {\n var crossOrigin = this.crossOrigin;\n var preview = this.options.preview;\n var url = crossOrigin ? this.crossOriginUrl : this.url;\n var image = document.createElement('img');\n\n if (crossOrigin) {\n image.crossOrigin = crossOrigin;\n }\n\n image.src = url;\n this.viewBox.appendChild(image);\n this.viewBoxImage = image;\n\n if (!preview) {\n return;\n }\n\n var previews = preview;\n\n if (typeof preview === 'string') {\n previews = this.element.ownerDocument.querySelectorAll(preview);\n } else if (preview.querySelector) {\n previews = [preview];\n }\n\n this.previews = previews;\n forEach(previews, function (el) {\n var img = document.createElement('img'); // Save the original size for recover\n\n setData(el, DATA_PREVIEW, {\n width: el.offsetWidth,\n height: el.offsetHeight,\n html: el.innerHTML\n });\n\n if (crossOrigin) {\n img.crossOrigin = crossOrigin;\n }\n\n img.src = url;\n /**\n * Override img element styles\n * Add `display:block` to avoid margin top issue\n * Add `height:auto` to override `height` attribute on IE8\n * (Occur only when margin-top <= -height)\n */\n\n img.style.cssText = '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;\"';\n el.innerHTML = '';\n el.appendChild(img);\n });\n },\n resetPreview: function resetPreview() {\n forEach(this.previews, function (element) {\n var data = getData(element, DATA_PREVIEW);\n setStyle(element, {\n width: data.width,\n height: data.height\n });\n element.innerHTML = data.html;\n removeData(element, DATA_PREVIEW);\n });\n },\n preview: function preview() {\n var imageData = this.imageData,\n canvasData = this.canvasData,\n cropBoxData = this.cropBoxData;\n var cropBoxWidth = cropBoxData.width,\n cropBoxHeight = cropBoxData.height;\n var width = imageData.width,\n height = imageData.height;\n var left = cropBoxData.left - canvasData.left - imageData.left;\n var top = cropBoxData.top - canvasData.top - imageData.top;\n\n if (!this.cropped || this.disabled) {\n return;\n }\n\n setStyle(this.viewBoxImage, assign({\n width: width,\n height: height\n }, getTransforms(assign({\n translateX: -left,\n translateY: -top\n }, imageData))));\n forEach(this.previews, function (element) {\n var data = getData(element, DATA_PREVIEW);\n var originalWidth = data.width;\n var originalHeight = data.height;\n var newWidth = originalWidth;\n var newHeight = originalHeight;\n var ratio = 1;\n\n if (cropBoxWidth) {\n ratio = originalWidth / cropBoxWidth;\n newHeight = cropBoxHeight * ratio;\n }\n\n if (cropBoxHeight && newHeight > originalHeight) {\n ratio = originalHeight / cropBoxHeight;\n newWidth = cropBoxWidth * ratio;\n newHeight = originalHeight;\n }\n\n setStyle(element, {\n width: newWidth,\n height: newHeight\n });\n setStyle(element.getElementsByTagName('img')[0], assign({\n width: width * ratio,\n height: height * ratio\n }, getTransforms(assign({\n translateX: -left * ratio,\n translateY: -top * ratio\n }, imageData))));\n });\n }\n };\n\n var events = {\n bind: function bind() {\n var element = this.element,\n options = this.options,\n cropper = this.cropper;\n\n if (isFunction(options.cropstart)) {\n addListener(element, EVENT_CROP_START, options.cropstart);\n }\n\n if (isFunction(options.cropmove)) {\n addListener(element, EVENT_CROP_MOVE, options.cropmove);\n }\n\n if (isFunction(options.cropend)) {\n addListener(element, EVENT_CROP_END, options.cropend);\n }\n\n if (isFunction(options.crop)) {\n addListener(element, EVENT_CROP, options.crop);\n }\n\n if (isFunction(options.zoom)) {\n addListener(element, EVENT_ZOOM, options.zoom);\n }\n\n addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));\n\n if (options.zoomable && options.zoomOnWheel) {\n addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {\n passive: false,\n capture: true\n });\n }\n\n if (options.toggleDragModeOnDblclick) {\n addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));\n }\n\n addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));\n addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));\n\n if (options.responsive) {\n addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));\n }\n },\n unbind: function unbind() {\n var element = this.element,\n options = this.options,\n cropper = this.cropper;\n\n if (isFunction(options.cropstart)) {\n removeListener(element, EVENT_CROP_START, options.cropstart);\n }\n\n if (isFunction(options.cropmove)) {\n removeListener(element, EVENT_CROP_MOVE, options.cropmove);\n }\n\n if (isFunction(options.cropend)) {\n removeListener(element, EVENT_CROP_END, options.cropend);\n }\n\n if (isFunction(options.crop)) {\n removeListener(element, EVENT_CROP, options.crop);\n }\n\n if (isFunction(options.zoom)) {\n removeListener(element, EVENT_ZOOM, options.zoom);\n }\n\n removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);\n\n if (options.zoomable && options.zoomOnWheel) {\n removeListener(cropper, EVENT_WHEEL, this.onWheel, {\n passive: false,\n capture: true\n });\n }\n\n if (options.toggleDragModeOnDblclick) {\n removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);\n }\n\n removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);\n removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);\n\n if (options.responsive) {\n removeListener(window, EVENT_RESIZE, this.onResize);\n }\n }\n };\n\n var handlers = {\n resize: function resize() {\n var options = this.options,\n container = this.container,\n containerData = this.containerData;\n var minContainerWidth = Number(options.minContainerWidth) || MIN_CONTAINER_WIDTH;\n var minContainerHeight = Number(options.minContainerHeight) || MIN_CONTAINER_HEIGHT;\n\n if (this.disabled || containerData.width <= minContainerWidth || containerData.height <= minContainerHeight) {\n return;\n }\n\n var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed\n\n if (ratio !== 1 || container.offsetHeight !== containerData.height) {\n var canvasData;\n var cropBoxData;\n\n if (options.restore) {\n canvasData = this.getCanvasData();\n cropBoxData = this.getCropBoxData();\n }\n\n this.render();\n\n if (options.restore) {\n this.setCanvasData(forEach(canvasData, function (n, i) {\n canvasData[i] = n * ratio;\n }));\n this.setCropBoxData(forEach(cropBoxData, function (n, i) {\n cropBoxData[i] = n * ratio;\n }));\n }\n }\n },\n dblclick: function dblclick() {\n if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {\n return;\n }\n\n this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);\n },\n wheel: function wheel(event) {\n var _this = this;\n\n var ratio = Number(this.options.wheelZoomRatio) || 0.1;\n var delta = 1;\n\n if (this.disabled) {\n return;\n }\n\n event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)\n\n if (this.wheeling) {\n return;\n }\n\n this.wheeling = true;\n setTimeout(function () {\n _this.wheeling = false;\n }, 50);\n\n if (event.deltaY) {\n delta = event.deltaY > 0 ? 1 : -1;\n } else if (event.wheelDelta) {\n delta = -event.wheelDelta / 120;\n } else if (event.detail) {\n delta = event.detail > 0 ? 1 : -1;\n }\n\n this.zoom(-delta * ratio, event);\n },\n cropStart: function cropStart(event) {\n var buttons = event.buttons,\n button = event.button;\n\n if (this.disabled // No primary button (Usually the left button)\n // Note that touch events have no `buttons` or `button` property\n || isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu\n || event.ctrlKey) {\n return;\n }\n\n var options = this.options,\n pointers = this.pointers;\n var action;\n\n if (event.changedTouches) {\n // Handle touch event\n forEach(event.changedTouches, function (touch) {\n pointers[touch.identifier] = getPointer(touch);\n });\n } else {\n // Handle mouse event and pointer event\n pointers[event.pointerId || 0] = getPointer(event);\n }\n\n if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {\n action = ACTION_ZOOM;\n } else {\n action = getData(event.target, DATA_ACTION);\n }\n\n if (!REGEXP_ACTIONS.test(action)) {\n return;\n }\n\n if (dispatchEvent(this.element, EVENT_CROP_START, {\n originalEvent: event,\n action: action\n }) === false) {\n return;\n } // This line is required for preventing page zooming in iOS browsers\n\n\n event.preventDefault();\n this.action = action;\n this.cropping = false;\n\n if (action === ACTION_CROP) {\n this.cropping = true;\n addClass(this.dragBox, CLASS_MODAL);\n }\n },\n cropMove: function cropMove(event) {\n var action = this.action;\n\n if (this.disabled || !action) {\n return;\n }\n\n var pointers = this.pointers;\n event.preventDefault();\n\n if (dispatchEvent(this.element, EVENT_CROP_MOVE, {\n originalEvent: event,\n action: action\n }) === false) {\n return;\n }\n\n if (event.changedTouches) {\n forEach(event.changedTouches, function (touch) {\n // The first parameter should not be undefined (#432)\n assign(pointers[touch.identifier] || {}, getPointer(touch, true));\n });\n } else {\n assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));\n }\n\n this.change(event);\n },\n cropEnd: function cropEnd(event) {\n if (this.disabled) {\n return;\n }\n\n var action = this.action,\n pointers = this.pointers;\n\n if (event.changedTouches) {\n forEach(event.changedTouches, function (touch) {\n delete pointers[touch.identifier];\n });\n } else {\n delete pointers[event.pointerId || 0];\n }\n\n if (!action) {\n return;\n }\n\n event.preventDefault();\n\n if (!Object.keys(pointers).length) {\n this.action = '';\n }\n\n if (this.cropping) {\n this.cropping = false;\n toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);\n }\n\n dispatchEvent(this.element, EVENT_CROP_END, {\n originalEvent: event,\n action: action\n });\n }\n };\n\n var change = {\n change: function change(event) {\n var options = this.options,\n canvasData = this.canvasData,\n containerData = this.containerData,\n cropBoxData = this.cropBoxData,\n pointers = this.pointers;\n var action = this.action;\n var aspectRatio = options.aspectRatio;\n var left = cropBoxData.left,\n top = cropBoxData.top,\n width = cropBoxData.width,\n height = cropBoxData.height;\n var right = left + width;\n var bottom = top + height;\n var minLeft = 0;\n var minTop = 0;\n var maxWidth = containerData.width;\n var maxHeight = containerData.height;\n var renderable = true;\n var offset; // Locking aspect ratio in \"free mode\" by holding shift key\n\n if (!aspectRatio && event.shiftKey) {\n aspectRatio = width && height ? width / height : 1;\n }\n\n if (this.limited) {\n minLeft = cropBoxData.minLeft;\n minTop = cropBoxData.minTop;\n maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);\n maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);\n }\n\n var pointer = pointers[Object.keys(pointers)[0]];\n var range = {\n x: pointer.endX - pointer.startX,\n y: pointer.endY - pointer.startY\n };\n\n var check = function check(side) {\n switch (side) {\n case ACTION_EAST:\n if (right + range.x > maxWidth) {\n range.x = maxWidth - right;\n }\n\n break;\n\n case ACTION_WEST:\n if (left + range.x < minLeft) {\n range.x = minLeft - left;\n }\n\n break;\n\n case ACTION_NORTH:\n if (top + range.y < minTop) {\n range.y = minTop - top;\n }\n\n break;\n\n case ACTION_SOUTH:\n if (bottom + range.y > maxHeight) {\n range.y = maxHeight - bottom;\n }\n\n break;\n\n default:\n }\n };\n\n switch (action) {\n // Move crop box\n case ACTION_ALL:\n left += range.x;\n top += range.y;\n break;\n // Resize crop box\n\n case ACTION_EAST:\n if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n renderable = false;\n break;\n }\n\n check(ACTION_EAST);\n width += range.x;\n\n if (width < 0) {\n action = ACTION_WEST;\n width = -width;\n left -= width;\n }\n\n if (aspectRatio) {\n height = width / aspectRatio;\n top += (cropBoxData.height - height) / 2;\n }\n\n break;\n\n case ACTION_NORTH:\n if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n renderable = false;\n break;\n }\n\n check(ACTION_NORTH);\n height -= range.y;\n top += range.y;\n\n if (height < 0) {\n action = ACTION_SOUTH;\n height = -height;\n top -= height;\n }\n\n if (aspectRatio) {\n width = height * aspectRatio;\n left += (cropBoxData.width - width) / 2;\n }\n\n break;\n\n case ACTION_WEST:\n if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n renderable = false;\n break;\n }\n\n check(ACTION_WEST);\n width -= range.x;\n left += range.x;\n\n if (width < 0) {\n action = ACTION_EAST;\n width = -width;\n left -= width;\n }\n\n if (aspectRatio) {\n height = width / aspectRatio;\n top += (cropBoxData.height - height) / 2;\n }\n\n break;\n\n case ACTION_SOUTH:\n if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n renderable = false;\n break;\n }\n\n check(ACTION_SOUTH);\n height += range.y;\n\n if (height < 0) {\n action = ACTION_NORTH;\n height = -height;\n top -= height;\n }\n\n if (aspectRatio) {\n width = height * aspectRatio;\n left += (cropBoxData.width - width) / 2;\n }\n\n break;\n\n case ACTION_NORTH_EAST:\n if (aspectRatio) {\n if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n renderable = false;\n break;\n }\n\n check(ACTION_NORTH);\n height -= range.y;\n top += range.y;\n width = height * aspectRatio;\n } else {\n check(ACTION_NORTH);\n check(ACTION_EAST);\n\n if (range.x >= 0) {\n if (right < maxWidth) {\n width += range.x;\n } else if (range.y <= 0 && top <= minTop) {\n renderable = false;\n }\n } else {\n width += range.x;\n }\n\n if (range.y <= 0) {\n if (top > minTop) {\n height -= range.y;\n top += range.y;\n }\n } else {\n height -= range.y;\n top += range.y;\n }\n }\n\n if (width < 0 && height < 0) {\n action = ACTION_SOUTH_WEST;\n height = -height;\n width = -width;\n top -= height;\n left -= width;\n } else if (width < 0) {\n action = ACTION_NORTH_WEST;\n width = -width;\n left -= width;\n } else if (height < 0) {\n action = ACTION_SOUTH_EAST;\n height = -height;\n top -= height;\n }\n\n break;\n\n case ACTION_NORTH_WEST:\n if (aspectRatio) {\n if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n renderable = false;\n break;\n }\n\n check(ACTION_NORTH);\n height -= range.y;\n top += range.y;\n width = height * aspectRatio;\n left += cropBoxData.width - width;\n } else {\n check(ACTION_NORTH);\n check(ACTION_WEST);\n\n if (range.x <= 0) {\n if (left > minLeft) {\n width -= range.x;\n left += range.x;\n } else if (range.y <= 0 && top <= minTop) {\n renderable = false;\n }\n } else {\n width -= range.x;\n left += range.x;\n }\n\n if (range.y <= 0) {\n if (top > minTop) {\n height -= range.y;\n top += range.y;\n }\n } else {\n height -= range.y;\n top += range.y;\n }\n }\n\n if (width < 0 && height < 0) {\n action = ACTION_SOUTH_EAST;\n height = -height;\n width = -width;\n top -= height;\n left -= width;\n } else if (width < 0) {\n action = ACTION_NORTH_EAST;\n width = -width;\n left -= width;\n } else if (height < 0) {\n action = ACTION_SOUTH_WEST;\n height = -height;\n top -= height;\n }\n\n break;\n\n case ACTION_SOUTH_WEST:\n if (aspectRatio) {\n if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n renderable = false;\n break;\n }\n\n check(ACTION_WEST);\n width -= range.x;\n left += range.x;\n height = width / aspectRatio;\n } else {\n check(ACTION_SOUTH);\n check(ACTION_WEST);\n\n if (range.x <= 0) {\n if (left > minLeft) {\n width -= range.x;\n left += range.x;\n } else if (range.y >= 0 && bottom >= maxHeight) {\n renderable = false;\n }\n } else {\n width -= range.x;\n left += range.x;\n }\n\n if (range.y >= 0) {\n if (bottom < maxHeight) {\n height += range.y;\n }\n } else {\n height += range.y;\n }\n }\n\n if (width < 0 && height < 0) {\n action = ACTION_NORTH_EAST;\n height = -height;\n width = -width;\n top -= height;\n left -= width;\n } else if (width < 0) {\n action = ACTION_SOUTH_EAST;\n width = -width;\n left -= width;\n } else if (height < 0) {\n action = ACTION_NORTH_WEST;\n height = -height;\n top -= height;\n }\n\n break;\n\n case ACTION_SOUTH_EAST:\n if (aspectRatio) {\n if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n renderable = false;\n break;\n }\n\n check(ACTION_EAST);\n width += range.x;\n height = width / aspectRatio;\n } else {\n check(ACTION_SOUTH);\n check(ACTION_EAST);\n\n if (range.x >= 0) {\n if (right < maxWidth) {\n width += range.x;\n } else if (range.y >= 0 && bottom >= maxHeight) {\n renderable = false;\n }\n } else {\n width += range.x;\n }\n\n if (range.y >= 0) {\n if (bottom < maxHeight) {\n height += range.y;\n }\n } else {\n height += range.y;\n }\n }\n\n if (width < 0 && height < 0) {\n action = ACTION_NORTH_WEST;\n height = -height;\n width = -width;\n top -= height;\n left -= width;\n } else if (width < 0) {\n action = ACTION_SOUTH_WEST;\n width = -width;\n left -= width;\n } else if (height < 0) {\n action = ACTION_NORTH_EAST;\n height = -height;\n top -= height;\n }\n\n break;\n // Move canvas\n\n case ACTION_MOVE:\n this.move(range.x, range.y);\n renderable = false;\n break;\n // Zoom canvas\n\n case ACTION_ZOOM:\n this.zoom(getMaxZoomRatio(pointers), event);\n renderable = false;\n break;\n // Create crop box\n\n case ACTION_CROP:\n if (!range.x || !range.y) {\n renderable = false;\n break;\n }\n\n offset = getOffset(this.cropper);\n left = pointer.startX - offset.left;\n top = pointer.startY - offset.top;\n width = cropBoxData.minWidth;\n height = cropBoxData.minHeight;\n\n if (range.x > 0) {\n action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n } else if (range.x < 0) {\n left -= width;\n action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n }\n\n if (range.y < 0) {\n top -= height;\n } // Show the crop box if is hidden\n\n\n if (!this.cropped) {\n removeClass(this.cropBox, CLASS_HIDDEN);\n this.cropped = true;\n\n if (this.limited) {\n this.limitCropBox(true, true);\n }\n }\n\n break;\n\n default:\n }\n\n if (renderable) {\n cropBoxData.width = width;\n cropBoxData.height = height;\n cropBoxData.left = left;\n cropBoxData.top = top;\n this.action = action;\n this.renderCropBox();\n } // Override\n\n\n forEach(pointers, function (p) {\n p.startX = p.endX;\n p.startY = p.endY;\n });\n }\n };\n\n var methods = {\n // Show the crop box manually\n crop: function crop() {\n if (this.ready && !this.cropped && !this.disabled) {\n this.cropped = true;\n this.limitCropBox(true, true);\n\n if (this.options.modal) {\n addClass(this.dragBox, CLASS_MODAL);\n }\n\n removeClass(this.cropBox, CLASS_HIDDEN);\n this.setCropBoxData(this.initialCropBoxData);\n }\n\n return this;\n },\n // Reset the image and crop box to their initial states\n reset: function reset() {\n if (this.ready && !this.disabled) {\n this.imageData = assign({}, this.initialImageData);\n this.canvasData = assign({}, this.initialCanvasData);\n this.cropBoxData = assign({}, this.initialCropBoxData);\n this.renderCanvas();\n\n if (this.cropped) {\n this.renderCropBox();\n }\n }\n\n return this;\n },\n // Clear the crop box\n clear: function clear() {\n if (this.cropped && !this.disabled) {\n assign(this.cropBoxData, {\n left: 0,\n top: 0,\n width: 0,\n height: 0\n });\n this.cropped = false;\n this.renderCropBox();\n this.limitCanvas(true, true); // Render canvas after crop box rendered\n\n this.renderCanvas();\n removeClass(this.dragBox, CLASS_MODAL);\n addClass(this.cropBox, CLASS_HIDDEN);\n }\n\n return this;\n },\n\n /**\n * Replace the image's src and rebuild the cropper\n * @param {string} url - The new URL.\n * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.\n * @returns {Cropper} this\n */\n replace: function replace(url) {\n var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (!this.disabled && url) {\n if (this.isImg) {\n this.element.src = url;\n }\n\n if (hasSameSize) {\n this.url = url;\n this.image.src = url;\n\n if (this.ready) {\n this.viewBoxImage.src = url;\n forEach(this.previews, function (element) {\n element.getElementsByTagName('img')[0].src = url;\n });\n }\n } else {\n if (this.isImg) {\n this.replaced = true;\n }\n\n this.options.data = null;\n this.uncreate();\n this.load(url);\n }\n }\n\n return this;\n },\n // Enable (unfreeze) the cropper\n enable: function enable() {\n if (this.ready && this.disabled) {\n this.disabled = false;\n removeClass(this.cropper, CLASS_DISABLED);\n }\n\n return this;\n },\n // Disable (freeze) the cropper\n disable: function disable() {\n if (this.ready && !this.disabled) {\n this.disabled = true;\n addClass(this.cropper, CLASS_DISABLED);\n }\n\n return this;\n },\n\n /**\n * Destroy the cropper and remove the instance from the image\n * @returns {Cropper} this\n */\n destroy: function destroy() {\n var element = this.element;\n\n if (!element[NAMESPACE]) {\n return this;\n }\n\n element[NAMESPACE] = undefined;\n\n if (this.isImg && this.replaced) {\n element.src = this.originalUrl;\n }\n\n this.uncreate();\n return this;\n },\n\n /**\n * Move the canvas with relative offsets\n * @param {number} offsetX - The relative offset distance on the x-axis.\n * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.\n * @returns {Cropper} this\n */\n move: function move(offsetX) {\n var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;\n var _this$canvasData = this.canvasData,\n left = _this$canvasData.left,\n top = _this$canvasData.top;\n return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));\n },\n\n /**\n * Move the canvas to an absolute point\n * @param {number} x - The x-axis coordinate.\n * @param {number} [y=x] - The y-axis coordinate.\n * @returns {Cropper} this\n */\n moveTo: function moveTo(x) {\n var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;\n var canvasData = this.canvasData;\n var changed = false;\n x = Number(x);\n y = Number(y);\n\n if (this.ready && !this.disabled && this.options.movable) {\n if (isNumber(x)) {\n canvasData.left = x;\n changed = true;\n }\n\n if (isNumber(y)) {\n canvasData.top = y;\n changed = true;\n }\n\n if (changed) {\n this.renderCanvas(true);\n }\n }\n\n return this;\n },\n\n /**\n * Zoom the canvas with a relative ratio\n * @param {number} ratio - The target ratio.\n * @param {Event} _originalEvent - The original event if any.\n * @returns {Cropper} this\n */\n zoom: function zoom(ratio, _originalEvent) {\n var canvasData = this.canvasData;\n ratio = Number(ratio);\n\n if (ratio < 0) {\n ratio = 1 / (1 - ratio);\n } else {\n ratio = 1 + ratio;\n }\n\n return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);\n },\n\n /**\n * Zoom the canvas to an absolute ratio\n * @param {number} ratio - The target ratio.\n * @param {Object} pivot - The zoom pivot point coordinate.\n * @param {Event} _originalEvent - The original event if any.\n * @returns {Cropper} this\n */\n zoomTo: function zoomTo(ratio, pivot, _originalEvent) {\n var options = this.options,\n canvasData = this.canvasData;\n var width = canvasData.width,\n height = canvasData.height,\n naturalWidth = canvasData.naturalWidth,\n naturalHeight = canvasData.naturalHeight;\n ratio = Number(ratio);\n\n if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {\n var newWidth = naturalWidth * ratio;\n var newHeight = naturalHeight * ratio;\n\n if (dispatchEvent(this.element, EVENT_ZOOM, {\n ratio: ratio,\n oldRatio: width / naturalWidth,\n originalEvent: _originalEvent\n }) === false) {\n return this;\n }\n\n if (_originalEvent) {\n var pointers = this.pointers;\n var offset = getOffset(this.cropper);\n var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {\n pageX: _originalEvent.pageX,\n pageY: _originalEvent.pageY\n }; // Zoom from the triggering point of the event\n\n canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);\n canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);\n } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {\n canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);\n canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);\n } else {\n // Zoom from the center of the canvas\n canvasData.left -= (newWidth - width) / 2;\n canvasData.top -= (newHeight - height) / 2;\n }\n\n canvasData.width = newWidth;\n canvasData.height = newHeight;\n this.renderCanvas(true);\n }\n\n return this;\n },\n\n /**\n * Rotate the canvas with a relative degree\n * @param {number} degree - The rotate degree.\n * @returns {Cropper} this\n */\n rotate: function rotate(degree) {\n return this.rotateTo((this.imageData.rotate || 0) + Number(degree));\n },\n\n /**\n * Rotate the canvas to an absolute degree\n * @param {number} degree - The rotate degree.\n * @returns {Cropper} this\n */\n rotateTo: function rotateTo(degree) {\n degree = Number(degree);\n\n if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {\n this.imageData.rotate = degree % 360;\n this.renderCanvas(true, true);\n }\n\n return this;\n },\n\n /**\n * Scale the image on the x-axis.\n * @param {number} scaleX - The scale ratio on the x-axis.\n * @returns {Cropper} this\n */\n scaleX: function scaleX(_scaleX) {\n var scaleY = this.imageData.scaleY;\n return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);\n },\n\n /**\n * Scale the image on the y-axis.\n * @param {number} scaleY - The scale ratio on the y-axis.\n * @returns {Cropper} this\n */\n scaleY: function scaleY(_scaleY) {\n var scaleX = this.imageData.scaleX;\n return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);\n },\n\n /**\n * Scale the image\n * @param {number} scaleX - The scale ratio on the x-axis.\n * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.\n * @returns {Cropper} this\n */\n scale: function scale(scaleX) {\n var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;\n var imageData = this.imageData;\n var transformed = false;\n scaleX = Number(scaleX);\n scaleY = Number(scaleY);\n\n if (this.ready && !this.disabled && this.options.scalable) {\n if (isNumber(scaleX)) {\n imageData.scaleX = scaleX;\n transformed = true;\n }\n\n if (isNumber(scaleY)) {\n imageData.scaleY = scaleY;\n transformed = true;\n }\n\n if (transformed) {\n this.renderCanvas(true, true);\n }\n }\n\n return this;\n },\n\n /**\n * Get the cropped area position and size data (base on the original image)\n * @param {boolean} [rounded=false] - Indicate if round the data values or not.\n * @returns {Object} The result cropped data.\n */\n getData: function getData() {\n var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n var options = this.options,\n imageData = this.imageData,\n canvasData = this.canvasData,\n cropBoxData = this.cropBoxData;\n var data;\n\n if (this.ready && this.cropped) {\n data = {\n x: cropBoxData.left - canvasData.left,\n y: cropBoxData.top - canvasData.top,\n width: cropBoxData.width,\n height: cropBoxData.height\n };\n var ratio = imageData.width / imageData.naturalWidth;\n forEach(data, function (n, i) {\n data[i] = n / ratio;\n });\n\n if (rounded) {\n // In case rounding off leads to extra 1px in right or bottom border\n // we should round the top-left corner and the dimension (#343).\n var bottom = Math.round(data.y + data.height);\n var right = Math.round(data.x + data.width);\n data.x = Math.round(data.x);\n data.y = Math.round(data.y);\n data.width = right - data.x;\n data.height = bottom - data.y;\n }\n } else {\n data = {\n x: 0,\n y: 0,\n width: 0,\n height: 0\n };\n }\n\n if (options.rotatable) {\n data.rotate = imageData.rotate || 0;\n }\n\n if (options.scalable) {\n data.scaleX = imageData.scaleX || 1;\n data.scaleY = imageData.scaleY || 1;\n }\n\n return data;\n },\n\n /**\n * Set the cropped area position and size with new data\n * @param {Object} data - The new data.\n * @returns {Cropper} this\n */\n setData: function setData(data) {\n var options = this.options,\n imageData = this.imageData,\n canvasData = this.canvasData;\n var cropBoxData = {};\n\n if (this.ready && !this.disabled && isPlainObject(data)) {\n var transformed = false;\n\n if (options.rotatable) {\n if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {\n imageData.rotate = data.rotate;\n transformed = true;\n }\n }\n\n if (options.scalable) {\n if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {\n imageData.scaleX = data.scaleX;\n transformed = true;\n }\n\n if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {\n imageData.scaleY = data.scaleY;\n transformed = true;\n }\n }\n\n if (transformed) {\n this.renderCanvas(true, true);\n }\n\n var ratio = imageData.width / imageData.naturalWidth;\n\n if (isNumber(data.x)) {\n cropBoxData.left = data.x * ratio + canvasData.left;\n }\n\n if (isNumber(data.y)) {\n cropBoxData.top = data.y * ratio + canvasData.top;\n }\n\n if (isNumber(data.width)) {\n cropBoxData.width = data.width * ratio;\n }\n\n if (isNumber(data.height)) {\n cropBoxData.height = data.height * ratio;\n }\n\n this.setCropBoxData(cropBoxData);\n }\n\n return this;\n },\n\n /**\n * Get the container size data.\n * @returns {Object} The result container data.\n */\n getContainerData: function getContainerData() {\n return this.ready ? assign({}, this.containerData) : {};\n },\n\n /**\n * Get the image position and size data.\n * @returns {Object} The result image data.\n */\n getImageData: function getImageData() {\n return this.sized ? assign({}, this.imageData) : {};\n },\n\n /**\n * Get the canvas position and size data.\n * @returns {Object} The result canvas data.\n */\n getCanvasData: function getCanvasData() {\n var canvasData = this.canvasData;\n var data = {};\n\n if (this.ready) {\n forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {\n data[n] = canvasData[n];\n });\n }\n\n return data;\n },\n\n /**\n * Set the canvas position and size with new data.\n * @param {Object} data - The new canvas data.\n * @returns {Cropper} this\n */\n setCanvasData: function setCanvasData(data) {\n var canvasData = this.canvasData;\n var aspectRatio = canvasData.aspectRatio;\n\n if (this.ready && !this.disabled && isPlainObject(data)) {\n if (isNumber(data.left)) {\n canvasData.left = data.left;\n }\n\n if (isNumber(data.top)) {\n canvasData.top = data.top;\n }\n\n if (isNumber(data.width)) {\n canvasData.width = data.width;\n canvasData.height = data.width / aspectRatio;\n } else if (isNumber(data.height)) {\n canvasData.height = data.height;\n canvasData.width = data.height * aspectRatio;\n }\n\n this.renderCanvas(true);\n }\n\n return this;\n },\n\n /**\n * Get the crop box position and size data.\n * @returns {Object} The result crop box data.\n */\n getCropBoxData: function getCropBoxData() {\n var cropBoxData = this.cropBoxData;\n var data;\n\n if (this.ready && this.cropped) {\n data = {\n left: cropBoxData.left,\n top: cropBoxData.top,\n width: cropBoxData.width,\n height: cropBoxData.height\n };\n }\n\n return data || {};\n },\n\n /**\n * Set the crop box position and size with new data.\n * @param {Object} data - The new crop box data.\n * @returns {Cropper} this\n */\n setCropBoxData: function setCropBoxData(data) {\n var cropBoxData = this.cropBoxData;\n var aspectRatio = this.options.aspectRatio;\n var widthChanged;\n var heightChanged;\n\n if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {\n if (isNumber(data.left)) {\n cropBoxData.left = data.left;\n }\n\n if (isNumber(data.top)) {\n cropBoxData.top = data.top;\n }\n\n if (isNumber(data.width) && data.width !== cropBoxData.width) {\n widthChanged = true;\n cropBoxData.width = data.width;\n }\n\n if (isNumber(data.height) && data.height !== cropBoxData.height) {\n heightChanged = true;\n cropBoxData.height = data.height;\n }\n\n if (aspectRatio) {\n if (widthChanged) {\n cropBoxData.height = cropBoxData.width / aspectRatio;\n } else if (heightChanged) {\n cropBoxData.width = cropBoxData.height * aspectRatio;\n }\n }\n\n this.renderCropBox();\n }\n\n return this;\n },\n\n /**\n * Get a canvas drawn the cropped image.\n * @param {Object} [options={}] - The config options.\n * @returns {HTMLCanvasElement} - The result canvas.\n */\n getCroppedCanvas: function getCroppedCanvas() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n if (!this.ready || !window.HTMLCanvasElement) {\n return null;\n }\n\n var canvasData = this.canvasData;\n var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.\n\n if (!this.cropped) {\n return source;\n }\n\n var _this$getData = this.getData(),\n initialX = _this$getData.x,\n initialY = _this$getData.y,\n initialWidth = _this$getData.width,\n initialHeight = _this$getData.height;\n\n var ratio = source.width / Math.floor(canvasData.naturalWidth);\n\n if (ratio !== 1) {\n initialX *= ratio;\n initialY *= ratio;\n initialWidth *= ratio;\n initialHeight *= ratio;\n }\n\n var aspectRatio = initialWidth / initialHeight;\n var maxSizes = getAdjustedSizes({\n aspectRatio: aspectRatio,\n width: options.maxWidth || Infinity,\n height: options.maxHeight || Infinity\n });\n var minSizes = getAdjustedSizes({\n aspectRatio: aspectRatio,\n width: options.minWidth || 0,\n height: options.minHeight || 0\n }, 'cover');\n\n var _getAdjustedSizes = getAdjustedSizes({\n aspectRatio: aspectRatio,\n width: options.width || (ratio !== 1 ? source.width : initialWidth),\n height: options.height || (ratio !== 1 ? source.height : initialHeight)\n }),\n width = _getAdjustedSizes.width,\n height = _getAdjustedSizes.height;\n\n width = Math.min(maxSizes.width, Math.max(minSizes.width, width));\n height = Math.min(maxSizes.height, Math.max(minSizes.height, height));\n var canvas = document.createElement('canvas');\n var context = canvas.getContext('2d');\n canvas.width = normalizeDecimalNumber(width);\n canvas.height = normalizeDecimalNumber(height);\n context.fillStyle = options.fillColor || 'transparent';\n context.fillRect(0, 0, width, height);\n var _options$imageSmoothi = options.imageSmoothingEnabled,\n imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,\n imageSmoothingQuality = options.imageSmoothingQuality;\n context.imageSmoothingEnabled = imageSmoothingEnabled;\n\n if (imageSmoothingQuality) {\n context.imageSmoothingQuality = imageSmoothingQuality;\n } // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n\n\n var sourceWidth = source.width;\n var sourceHeight = source.height; // Source canvas parameters\n\n var srcX = initialX;\n var srcY = initialY;\n var srcWidth;\n var srcHeight; // Destination canvas parameters\n\n var dstX;\n var dstY;\n var dstWidth;\n var dstHeight;\n\n if (srcX <= -initialWidth || srcX > sourceWidth) {\n srcX = 0;\n srcWidth = 0;\n dstX = 0;\n dstWidth = 0;\n } else if (srcX <= 0) {\n dstX = -srcX;\n srcX = 0;\n srcWidth = Math.min(sourceWidth, initialWidth + srcX);\n dstWidth = srcWidth;\n } else if (srcX <= sourceWidth) {\n dstX = 0;\n srcWidth = Math.min(initialWidth, sourceWidth - srcX);\n dstWidth = srcWidth;\n }\n\n if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {\n srcY = 0;\n srcHeight = 0;\n dstY = 0;\n dstHeight = 0;\n } else if (srcY <= 0) {\n dstY = -srcY;\n srcY = 0;\n srcHeight = Math.min(sourceHeight, initialHeight + srcY);\n dstHeight = srcHeight;\n } else if (srcY <= sourceHeight) {\n dstY = 0;\n srcHeight = Math.min(initialHeight, sourceHeight - srcY);\n dstHeight = srcHeight;\n }\n\n var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid \"IndexSizeError\"\n\n if (dstWidth > 0 && dstHeight > 0) {\n var scale = width / initialWidth;\n params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);\n } // All the numerical parameters should be integer for `drawImage`\n // https://github.com/fengyuanchen/cropper/issues/476\n\n\n context.drawImage.apply(context, [source].concat(_toConsumableArray(params.map(function (param) {\n return Math.floor(normalizeDecimalNumber(param));\n }))));\n return canvas;\n },\n\n /**\n * Change the aspect ratio of the crop box.\n * @param {number} aspectRatio - The new aspect ratio.\n * @returns {Cropper} this\n */\n setAspectRatio: function setAspectRatio(aspectRatio) {\n var options = this.options;\n\n if (!this.disabled && !isUndefined(aspectRatio)) {\n // 0 -> NaN\n options.aspectRatio = Math.max(0, aspectRatio) || NaN;\n\n if (this.ready) {\n this.initCropBox();\n\n if (this.cropped) {\n this.renderCropBox();\n }\n }\n }\n\n return this;\n },\n\n /**\n * Change the drag mode.\n * @param {string} mode - The new drag mode.\n * @returns {Cropper} this\n */\n setDragMode: function setDragMode(mode) {\n var options = this.options,\n dragBox = this.dragBox,\n face = this.face;\n\n if (this.ready && !this.disabled) {\n var croppable = mode === DRAG_MODE_CROP;\n var movable = options.movable && mode === DRAG_MODE_MOVE;\n mode = croppable || movable ? mode : DRAG_MODE_NONE;\n options.dragMode = mode;\n setData(dragBox, DATA_ACTION, mode);\n toggleClass(dragBox, CLASS_CROP, croppable);\n toggleClass(dragBox, CLASS_MOVE, movable);\n\n if (!options.cropBoxMovable) {\n // Sync drag mode to crop box when it is not movable\n setData(face, DATA_ACTION, mode);\n toggleClass(face, CLASS_CROP, croppable);\n toggleClass(face, CLASS_MOVE, movable);\n }\n }\n\n return this;\n }\n };\n\n var AnotherCropper = WINDOW.Cropper;\n\n var Cropper =\n /*#__PURE__*/\n function () {\n /**\n * Create a new Cropper.\n * @param {Element} element - The target element for cropping.\n * @param {Object} [options={}] - The configuration options.\n */\n function Cropper(element) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n _classCallCheck(this, Cropper);\n\n if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {\n throw new Error('The first argument is required and must be an or element.');\n }\n\n this.element = element;\n this.options = assign({}, DEFAULTS, isPlainObject(options) && options);\n this.cropped = false;\n this.disabled = false;\n this.pointers = {};\n this.ready = false;\n this.reloading = false;\n this.replaced = false;\n this.sized = false;\n this.sizing = false;\n this.init();\n }\n\n _createClass(Cropper, [{\n key: \"init\",\n value: function init() {\n var element = this.element;\n var tagName = element.tagName.toLowerCase();\n var url;\n\n if (element[NAMESPACE]) {\n return;\n }\n\n element[NAMESPACE] = this;\n\n if (tagName === 'img') {\n this.isImg = true; // e.g.: \"img/picture.jpg\"\n\n url = element.getAttribute('src') || '';\n this.originalUrl = url; // Stop when it's a blank image\n\n if (!url) {\n return;\n } // e.g.: \"http://example.com/img/picture.jpg\"\n\n\n url = element.src;\n } else if (tagName === 'canvas' && window.HTMLCanvasElement) {\n url = element.toDataURL();\n }\n\n this.load(url);\n }\n }, {\n key: \"load\",\n value: function load(url) {\n var _this = this;\n\n if (!url) {\n return;\n }\n\n this.url = url;\n this.imageData = {};\n var element = this.element,\n options = this.options;\n\n if (!options.rotatable && !options.scalable) {\n options.checkOrientation = false;\n } // Only IE10+ supports Typed Arrays\n\n\n if (!options.checkOrientation || !window.ArrayBuffer) {\n this.clone();\n return;\n } // Read ArrayBuffer from Data URL of JPEG images directly for better performance.\n\n\n if (REGEXP_DATA_URL_JPEG.test(url)) {\n this.read(dataURLToArrayBuffer(url));\n return;\n }\n\n var xhr = new XMLHttpRequest();\n var clone = this.clone.bind(this);\n this.reloading = true;\n this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:\n // http, https, data, chrome, chrome-extension.\n // 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy\n // in some browsers as IE11 and Safari.\n\n xhr.onabort = clone;\n xhr.onerror = clone;\n xhr.ontimeout = clone;\n\n xhr.onprogress = function () {\n if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {\n xhr.abort();\n }\n };\n\n xhr.onload = function () {\n _this.read(xhr.response);\n };\n\n xhr.onloadend = function () {\n _this.reloading = false;\n _this.xhr = null;\n }; // Bust cache when there is a \"crossOrigin\" property to avoid browser cache error\n\n\n if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {\n url = addTimestamp(url);\n }\n\n xhr.open('GET', url);\n xhr.responseType = 'arraybuffer';\n xhr.withCredentials = element.crossOrigin === 'use-credentials';\n xhr.send();\n }\n }, {\n key: \"read\",\n value: function read(arrayBuffer) {\n var options = this.options,\n imageData = this.imageData; // Reset the orientation value to its default value 1\n // as some iOS browsers will render image with its orientation\n\n var orientation = resetAndGetOrientation(arrayBuffer);\n var rotate = 0;\n var scaleX = 1;\n var scaleY = 1;\n\n if (orientation > 1) {\n // Generate a new URL which has the default orientation value\n this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);\n\n var _parseOrientation = parseOrientation(orientation);\n\n rotate = _parseOrientation.rotate;\n scaleX = _parseOrientation.scaleX;\n scaleY = _parseOrientation.scaleY;\n }\n\n if (options.rotatable) {\n imageData.rotate = rotate;\n }\n\n if (options.scalable) {\n imageData.scaleX = scaleX;\n imageData.scaleY = scaleY;\n }\n\n this.clone();\n }\n }, {\n key: \"clone\",\n value: function clone() {\n var element = this.element,\n url = this.url;\n var crossOrigin;\n var crossOriginUrl;\n\n if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {\n crossOrigin = element.crossOrigin;\n\n if (crossOrigin) {\n crossOriginUrl = url;\n } else {\n crossOrigin = 'anonymous'; // Bust cache when there is not a \"crossOrigin\" property\n\n crossOriginUrl = addTimestamp(url);\n }\n }\n\n this.crossOrigin = crossOrigin;\n this.crossOriginUrl = crossOriginUrl;\n var image = document.createElement('img');\n\n if (crossOrigin) {\n image.crossOrigin = crossOrigin;\n }\n\n image.src = crossOriginUrl || url;\n this.image = image;\n image.onload = this.start.bind(this);\n image.onerror = this.stop.bind(this);\n addClass(image, CLASS_HIDE);\n element.parentNode.insertBefore(image, element.nextSibling);\n }\n }, {\n key: \"start\",\n value: function start() {\n var _this2 = this;\n\n var image = this.isImg ? this.element : this.image;\n image.onload = null;\n image.onerror = null;\n this.sizing = true;\n var IS_SAFARI = WINDOW.navigator && /^(?:.(?!chrome|android))*safari/i.test(WINDOW.navigator.userAgent);\n\n var done = function done(naturalWidth, naturalHeight) {\n assign(_this2.imageData, {\n naturalWidth: naturalWidth,\n naturalHeight: naturalHeight,\n aspectRatio: naturalWidth / naturalHeight\n });\n _this2.sizing = false;\n _this2.sized = true;\n\n _this2.build();\n }; // Modern browsers (except Safari)\n\n\n if (image.naturalWidth && !IS_SAFARI) {\n done(image.naturalWidth, image.naturalHeight);\n return;\n }\n\n var sizingImage = document.createElement('img');\n var body = document.body || document.documentElement;\n this.sizingImage = sizingImage;\n\n sizingImage.onload = function () {\n done(sizingImage.width, sizingImage.height);\n\n if (!IS_SAFARI) {\n body.removeChild(sizingImage);\n }\n };\n\n sizingImage.src = image.src; // iOS Safari will convert the image automatically\n // with its orientation once append it into DOM (#279)\n\n if (!IS_SAFARI) {\n sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';\n body.appendChild(sizingImage);\n }\n }\n }, {\n key: \"stop\",\n value: function stop() {\n var image = this.image;\n image.onload = null;\n image.onerror = null;\n image.parentNode.removeChild(image);\n this.image = null;\n }\n }, {\n key: \"build\",\n value: function build() {\n if (!this.sized || this.ready) {\n return;\n }\n\n var element = this.element,\n options = this.options,\n image = this.image; // Create cropper elements\n\n var container = element.parentNode;\n var template = document.createElement('div');\n template.innerHTML = TEMPLATE;\n var cropper = template.querySelector(\".\".concat(NAMESPACE, \"-container\"));\n var canvas = cropper.querySelector(\".\".concat(NAMESPACE, \"-canvas\"));\n var dragBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-drag-box\"));\n var cropBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-crop-box\"));\n var face = cropBox.querySelector(\".\".concat(NAMESPACE, \"-face\"));\n this.container = container;\n this.cropper = cropper;\n this.canvas = canvas;\n this.dragBox = dragBox;\n this.cropBox = cropBox;\n this.viewBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-view-box\"));\n this.face = face;\n canvas.appendChild(image); // Hide the original image\n\n addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image\n\n container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden\n\n if (!this.isImg) {\n removeClass(image, CLASS_HIDE);\n }\n\n this.initPreview();\n this.bind();\n options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;\n options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;\n options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;\n addClass(cropBox, CLASS_HIDDEN);\n\n if (!options.guides) {\n addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-dashed\")), CLASS_HIDDEN);\n }\n\n if (!options.center) {\n addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-center\")), CLASS_HIDDEN);\n }\n\n if (options.background) {\n addClass(cropper, \"\".concat(NAMESPACE, \"-bg\"));\n }\n\n if (!options.highlight) {\n addClass(face, CLASS_INVISIBLE);\n }\n\n if (options.cropBoxMovable) {\n addClass(face, CLASS_MOVE);\n setData(face, DATA_ACTION, ACTION_ALL);\n }\n\n if (!options.cropBoxResizable) {\n addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-line\")), CLASS_HIDDEN);\n addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-point\")), CLASS_HIDDEN);\n }\n\n this.render();\n this.ready = true;\n this.setDragMode(options.dragMode);\n\n if (options.autoCrop) {\n this.crop();\n }\n\n this.setData(options.data);\n\n if (isFunction(options.ready)) {\n addListener(element, EVENT_READY, options.ready, {\n once: true\n });\n }\n\n dispatchEvent(element, EVENT_READY);\n }\n }, {\n key: \"unbuild\",\n value: function unbuild() {\n if (!this.ready) {\n return;\n }\n\n this.ready = false;\n this.unbind();\n this.resetPreview();\n this.cropper.parentNode.removeChild(this.cropper);\n removeClass(this.element, CLASS_HIDDEN);\n }\n }, {\n key: \"uncreate\",\n value: function uncreate() {\n if (this.ready) {\n this.unbuild();\n this.ready = false;\n this.cropped = false;\n } else if (this.sizing) {\n this.sizingImage.onload = null;\n this.sizing = false;\n this.sized = false;\n } else if (this.reloading) {\n this.xhr.onabort = null;\n this.xhr.abort();\n } else if (this.image) {\n this.stop();\n }\n }\n /**\n * Get the no conflict cropper class.\n * @returns {Cropper} The cropper class.\n */\n\n }], [{\n key: \"noConflict\",\n value: function noConflict() {\n window.Cropper = AnotherCropper;\n return Cropper;\n }\n /**\n * Change the default options.\n * @param {Object} options - The new default options.\n */\n\n }, {\n key: \"setDefaults\",\n value: function setDefaults(options) {\n assign(DEFAULTS, isPlainObject(options) && options);\n }\n }]);\n\n return Cropper;\n }();\n\n assign(Cropper.prototype, render, preview, events, handlers, change, methods);\n\n return Cropper;\n\n}));\n","function webpackEmptyContext(req) {\n\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\te.code = 'MODULE_NOT_FOUND';\n\tthrow e;\n}\nwebpackEmptyContext.keys = function() { return []; };\nwebpackEmptyContext.resolve = webpackEmptyContext;\nmodule.exports = webpackEmptyContext;\nwebpackEmptyContext.id = \"./node_modules/font-awesome sync \\\\.(otf|eot|svg|ttf|woff|woff2)$\";","/**\n * Add your global events here\n */\n\nmodule.exports = {\n AJAX: 'ajax-load',\n LOADED: 'load',\n SET_TARGET_UPDATE: 'set-target-update',\n RESTORE_FIELD: 'restore-field',\n FORM_INIT_BASICS: 'form-basics',\n FORM_INIT_STEPPED: 'form-init-stepped',\n FORM_INIT_VALIDATE: 'form-init-validate',\n FORM_INIT_VALIDATE_FIELD: 'form-init-validate-field',\n FORM_INIT_STORAGE: 'form-init-storage',\n FORM_VALIDATION_FAILED: 'form-validation-failed',\n FORM_STEPPED_NEW_STEP: 'form-new-step',\n FORM_STEPPED_FIRST_STEP: 'form-first-step',\n FORM_STEPPED_LAST_STEP: 'form-last-step',\n};\n","import $ from 'jquery';\n\nconst SpinnerUI = (($) => {\n class SpinnerUI {\n static show(callback) {\n $('#PageLoading').show(0, callback);\n }\n\n static hide(callback) {\n $('#PageLoading').hide('slow', callback);\n }\n }\n\n return SpinnerUI;\n})($);\n\nexport default SpinnerUI;\n","\"use strict\";\n\nimport $ from 'jquery';\n\nimport Events from './_events';\nimport SpinnerUI from './_ui.spinner';\n\nimport Cropper from 'cropperjs/dist/cropper.js'; //'cropperjs/src/index.js';\n\nconst CroppieUI = (($) => {\n\n const NAME = 'jsCroppieUI';\n const DATA_KEY = NAME;\n\n const G = window;\n const D = document;\n const DEFAULTS = {\n aspectRatio: 16 / 9,\n };\n\n class CroppieUI {\n\n constructor(element) {\n console.log(`Initializing: ${NAME}`);\n\n const ui = this;\n\n ui._element = element;\n\n ui.$el = $(ui._element);\n ui.$form = ui.$el.parents('form');\n ui.$input = ui.$el.find('input[type=\"file\"]');\n\n ui.$el.prepend('\"\"');\n ui.$image = ui.$el.find('img.cropping-image');\n ui.original_image = ui.$image[0];\n ui.mask_img = false;\n\n ui.name = ui.$input.attr('name');\n ui.width = ui.$input.data('width');\n ui.height = ui.$input.data('height');\n\n ui.options = DEFAULTS;\n ui.cropper = false;\n\n ui.$el.data(DATA_KEY, ui);\n\n ui.$el.prepend('
');\n ui.$removeBtns = ui.$el.find('.remove-masks');\n ui.masks = [];\n\n ui.$input.on('change', (e) => {\n const files = e.currentTarget.files;\n\n if (files && files.length) {\n ui.loadFile(files[0]);\n }\n });\n\n // actions\n ui.$el.append('Обрезать');\n\n // crop\n ui.$el.find('.act-crop').on('click', (e) => {\n if (!ui.cropper) {\n return true;\n }\n\n e.preventDefault();\n\n const canvas = ui.cropper.getCroppedCanvas({\n width: ui.width,\n height: ui.height,\n });\n\n ui.$image[0].src = canvas.toDataURL();\n\n ui.original_image = new Image();\n ui.original_image.src = canvas.toDataURL();\n\n ui.cropper.destroy();\n ui.cropper = false;\n\n ui.$el.removeClass(`${NAME}-cropping`);\n ui.$el.addClass(`${NAME}-cropped`);\n });\n\n // mask\n ui.$el.find('.masks .mask-item').on('click', (e) => {\n e.preventDefault();\n ui.setMask($(e.currentTarget));\n });\n\n // submit\n ui.$form.on('submit', (e) => {\n if (!ui.cropper) {\n return true;\n }\n\n SpinnerUI.show();\n\n ui.saveImage();\n\n const canvas = ui.cropper.getCroppedCanvas({\n width: ui.width,\n height: ui.height,\n });\n\n ui.$image[0].src = canvas.toDataURL();\n canvas.toBlob((blob) => {\n ui.uploadFile(blob);\n });\n\n e.preventDefault();\n });\n }\n\n setMask = ($el) => {\n const ui = this;\n\n // add current mask\n if (ui.mask_img) {\n ui.addMask(ui.getMask());\n }\n\n // update image storage\n if (ui.cropper) {\n ui.cropper.destroy();\n ui.cropper = false;\n\n ui.saveImage();\n }\n\n // add new image\n ui.mask_img = new Image();\n ui.mask_img.src = $el.data('src');\n\n ui.mask_img.onload = () => {\n const img = ui.mask_img;\n\n ui.cropper = new Cropper(ui.$image[0], {\n aspectRatio: img.width / img.height,\n viewMode: 0,\n guides: true,\n center: true,\n highlight: true,\n cropBoxMovable: true,\n cropBoxResizable: true,\n movable: false,\n rotatable: false,\n zoomable: false,\n ready: () => {\n ui.$el.find('.cropper-face').css({\n 'background-color': 'transparent',\n 'background-image': `url(${ui.mask_img.src})`,\n 'opacity': '0.8',\n });\n ui.$el.find('.cropper-face').data('current-mask', $el);\n }\n });\n };\n }\n\n // returns mask ID\n addMask = (mask) => {\n const ui = this;\n const id = Date.now();\n\n ui.masks[id] = mask;\n\n // draw removable button\n let $btn = $('Удалить #' + id + '');\n ui.$el.find('.masks').append($btn);\n\n /*ui.$removeBtns.prepend($btn);\n $btn = ui.$removeBtns.find('[data-id=\"' + id + '\"]');\n\n $btn.css({\n left: 100 * mask.left / ui.width + '%',\n top: 100 * mask.top / ui.width + '%',\n width: 100 * mask.width / ui.width + '%',\n height: 100 * mask.height / ui.height + '%',\n });*/\n $btn.on('click', (e) => {\n e.preventDefault();\n\n const $btn = $(e.currentTarget);\n const id = $btn.data('id');\n\n ui.removeMask(id);\n });\n return id;\n }\n\n removeMask = (id) => {\n const ui = this;\n delete ui.masks[id];\n ui.$el.find('.masks [data-id=\"' + id + '\"]').remove();\n\n ui.mask_img = false;\n ui.$el.find('.cropper-face').data('current-mask').click();\n }\n\n getMask = () => {\n const ui = this,\n canvas = document.createElement('canvas'),\n context = canvas.getContext('2d'),\n cropper = ui.cropper,\n maskWidth = cropper.getData().width,\n maskHeight = cropper.getData().height,\n maskTop = cropper.getData().y,\n maskLeft = cropper.getData().x,\n imageLeft = cropper.getImageData().left,\n imageTop = cropper.getImageData().top,\n imageAspect = cropper.getImageData().aspectRatio;\n\n canvas.width = ui.width;\n canvas.height = ui.height;\n context.imageSmoothingEnabled = true;\n\n return {\n img: ui.mask_img,\n left: maskLeft,\n top: maskTop,\n width: maskWidth,\n height: maskHeight\n };\n }\n\n saveImage = () => {\n const ui = this,\n canvas = document.createElement('canvas'),\n context = canvas.getContext('2d');\n\n canvas.width = ui.width;\n canvas.height = ui.height;\n context.imageSmoothingEnabled = true;\n\n context.drawImage(ui.original_image, 0, 0, ui.width, ui.height);\n\n for (let id in ui.masks) {\n const mask = ui.masks[id];\n console.log(mask);\n context.drawImage(mask.img, mask.left, mask.top, mask.width, mask.height);\n }\n\n ui.$image[0].src = canvas.toDataURL();\n\n return canvas;\n };\n\n loadFile = (file) => {\n const ui = this;\n\n if (/^image\\/\\w+/.test(file.type)) {\n\n ui.$image[0].src = URL.createObjectURL(file);\n\n if (ui.cropper) {\n ui.cropper.destroy();\n }\n\n ui.cropper = new Cropper(ui.$image[0], ui.options);\n ui.$input[0].value = null;\n\n ui.$el.addClass(`${NAME}-cropping`);\n } else {\n window.alert('Please choose an image file.');\n }\n };\n\n uploadFile = (blob) => {\n console.log('Initializing uploading sequence!');\n\n const ui = this;\n const data = new FormData(ui.$form[0]);\n\n data.delete('BackURL');\n data.delete(ui.name);\n data.append(ui.name, blob, ui.name + '-image.png');\n data.append('ajax', '1');\n\n $.ajax({\n url: ui.$form.attr('action'),\n data: data,\n processData: false,\n contentType: false,\n type: ui.$form.attr('method'),\n success: (data) => {\n console.log('UPLOAD SUCCESS!');\n\n SpinnerUI.hide();\n $(G).trigger(Events.AJAX);\n }\n });\n };\n\n static dispose() {\n console.log(`Destroying: ${NAME}`);\n }\n\n static _jQueryInterface() {\n return this.each((i, el) => {\n // attach functionality to element\n const $el = $(el);\n let data = $el.data(DATA_KEY);\n\n if (!data) {\n data = new CroppieUI(el);\n $el.data(DATA_KEY, data);\n }\n });\n }\n }\n\n // jQuery interface\n $.fn[NAME] = CroppieUI._jQueryInterface;\n $.fn[NAME].Constructor = CroppieUI;\n $.fn[NAME].noConflict = () => {\n $.fn[NAME] = JQUERY_NO_CONFLICT;\n return CroppieUI._jQueryInterface;\n };\n\n // auto-apply\n $(window).on(`${Events.AJAX} ${Events.LOADED}`, () => {\n $('.field.croppie').jsCroppieUI();\n });\n\n return CroppieUI;\n})($);\n\nexport default CroppieUI;\n","import 'app.scss';\nimport Events from './_events';\nimport Croppie from './_ui.form.croppie';\n\nfunction importAll(r) {\n return r.keys().map(r);\n}\n\nconst images = importAll(require.context('./img/', false, /\\.(png|jpe?g|svg)$/));\nconst fontAwesome = importAll(require.context('font-awesome', false, /\\.(otf|eot|svg|ttf|woff|woff2)$/));\n\nconst LayoutUI = (($) => {\n // Constants\n const W = window;\n const D = document;\n const $Body = $('body');\n\n const NAME = 'LayoutUI';\n\n class LayoutUI {\n static init() {\n const ui = this;\n ui.dispose();\n\n console.log(`Initializing: ${NAME}`);\n }\n\n static dispose() {\n console.log(`Destroying: ${NAME}`);\n }\n }\n\n $(W).on(`${Events.AJAX} ${Events.LOADED}`, () => {\n LayoutUI.init();\n });\n\n W.LayoutUI = LayoutUI;\n\n return LayoutUI;\n})($);\n\nexport default LayoutUI;\n","var map = {\n\t\"./bg.png\": \"./src/img/bg.png\",\n\t\"./photo1.png\": \"./src/img/photo1.png\",\n\t\"./photo2.jpg\": \"./src/img/photo2.jpg\",\n\t\"./photo3.svg\": \"./src/img/photo3.svg\"\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = \"./src/img sync \\\\.(png|jpe?g|svg)$\";","module.exports = \"../img/bg.png\";","module.exports = \"../img/photo1.png\";","module.exports = \"../img/photo2.jpg\";","module.exports = \"../fonts/photo3.svg\";","module.exports = jQuery;"],"sourceRoot":""} \ No newline at end of file diff --git a/dist/css/main.css b/dist/css/main.css new file mode 100644 index 0000000..262a3c9 --- /dev/null +++ b/dist/css/main.css @@ -0,0 +1,2 @@ +body{margin:0;padding:0;background-color:#dedede}fieldset{border:0}img{max-width:100%}.wrapper{position:relative;width:90%;padding:2rem 1rem;margin:2rem auto;background:#fff;border:1px solid red}.btn{display:inline-block;padding:.5rem 1rem;border:1px #dedede;background:#888;color:#000;text-decoration:none}.btn:focus,.btn:hover{opacity:.8}.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(../img/bg.png)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}.field{position:relative;margin:2rem 0}.field.croppie{position:relative;height:480px}.field.croppie .act-crop,.field.croppie .cropping-image,.field.croppie .mask-canvas,.field.croppie .masks{display:none}.field.croppie .cropper-face{background-repeat:no-repeat;background-size:contain}.field.croppie .masks h2{clear:both}.field.croppie .masks .mask-item{width:30%;float:left}.field.croppie .masks:after,.field.croppie .masks:before{content:"";clear:both}.field.croppie.jsCroppieUI-cropping .left,.field.croppie.jsCroppieUI-cropping .middle-column,.field.croppie.jsCroppieUI-cropping .right{display:none}.field.croppie.jsCroppieUI-cropping .act-crop{display:inline-block}.field.croppie.jsCroppieUI-cropped .left,.field.croppie.jsCroppieUI-cropped .middle-column,.field.croppie.jsCroppieUI-cropped .right{display:none}.field.croppie.jsCroppieUI-cropped .act-place,.field.croppie.jsCroppieUI-cropped .masks{display:block}.field.croppie .remove-mask{position:absolute} +/*# sourceMappingURL=main.css.map*/ \ No newline at end of file diff --git a/dist/css/main.css.map b/dist/css/main.css.map new file mode 100644 index 0000000..b5a5009 --- /dev/null +++ b/dist/css/main.css.map @@ -0,0 +1 @@ +{"version":3,"sources":[],"names":[],"mappings":"","file":"css/main.css","sourceRoot":""} \ No newline at end of file diff --git a/dist/fonts/photo3.svg b/dist/fonts/photo3.svg new file mode 100644 index 0000000..fadbe7c --- /dev/null +++ b/dist/fonts/photo3.svg @@ -0,0 +1 @@ +module.exports = "../img/photo3.svg"; \ No newline at end of file diff --git a/dist/img/bg.png b/dist/img/bg.png new file mode 100644 index 0000000..3c7056b Binary files /dev/null and b/dist/img/bg.png differ diff --git a/dist/img/photo1.png b/dist/img/photo1.png new file mode 100644 index 0000000..66674f1 Binary files /dev/null and b/dist/img/photo1.png differ diff --git a/dist/img/photo2.jpg b/dist/img/photo2.jpg new file mode 100644 index 0000000..6271cd7 Binary files /dev/null and b/dist/img/photo2.jpg differ diff --git a/dist/img/photo3.svg b/dist/img/photo3.svg new file mode 100644 index 0000000..7a9cb53 --- /dev/null +++ b/dist/img/photo3.svg @@ -0,0 +1,778 @@ + + + + + Hypnotoad + + + + + + + + + + + + + image/svg+xml + + Hypnotoad + Example of an SVG animation depicting Hypnotoad from the Futurama show. + + Futurama + + + Futurama + + + + + Ilya Sukhanov (dotCOMmie) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..afd03fd --- /dev/null +++ b/dist/index.html @@ -0,0 +1 @@ +JCROP

Ткни чтобы выбрать маску

Mask #1
Mask #2
\ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..7cff6c1 --- /dev/null +++ b/package.json @@ -0,0 +1,118 @@ +{ + "name": "ss-webpack-boilerplate", + "version": "1.0.0", + "description": "Lets you create SilverStripe faster", + "author": "Tony Air ", + "license": "MIT", + "private": true, + "engines": { + "yarn": ">= 1.0.0" + }, + "scripts": { + "prebuild": "rimraf build", + "start": "cross-env NODE_ENV=development webpack-dev-server --https -d --config webpack.config.js --mode development", + "build": "cross-env NODE_ENV=production webpack -p --config webpack.config.js --progress --mode production" + }, + "dependencies": { + "cropperjs": "^1.5.1", + "croppie": "^2.6.4", + "exif-js": "^2.3.0", + "font-awesome": "^4.7.0", + "jquery": "^3.4.1", + "yarn": "^1.16.0" + }, + "devDependencies": { + "autoprefixer": "^7.2.5", + "babel-core": "^6.26.3", + "babel-eslint": "^8.2.3", + "babel-loader": "^7.1.2", + "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "babel-preset-stage-2": "^6.24.1", + "browser-sync": "^2.24.5", + "browser-sync-webpack-plugin": "^2.2.2", + "copy-webpack-plugin": "^4.6.0", + "copyfiles": "^1.2.0", + "cross-env": "^5.1.6", + "css-loader": "^0.28.9", + "eslint": "^4.18.1", + "eslint-plugin-import": "^2.17.2", + "eslint-plugin-jquery": "^1.5.0", + "eslint-plugin-react": "^7.13.0", + "exports-loader": "^0.7.0", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "favicons-webpack-plugin": "0.0.9", + "file-loader": "^1.1.5", + "html-webpack-plugin": "^4.0.0-beta.5", + "laravel-mix": "^2.1.11", + "lost": "^8.2.0", + "node-sass": "^4.9.0", + "object-assign": "^4.1.1", + "optimize-css-assets-webpack-plugin": "^4.0.1", + "postcss-loader": "^2.1.5", + "react": "^16.3.2", + "react-dom": "^16.3.2", + "react-hot-loader": "^3.1.3", + "redux": "^3.7.2", + "redux-devtools-extension": "^2.13.2", + "resolve-url-loader": "^2.2.1", + "rimraf": "^2.6.2", + "sass-lint": "^1.12.1", + "sass-lint-fix": "^1.12.1", + "sass-loader": "^6.0.6", + "script-ext-html-webpack-plugin": "^2.1.3", + "style-loader": "^0.19.0", + "svg-url-loader": "^2.3.1", + "uglify-js": "git://github.com/mishoo/UglifyJS2.git#harmony-v2.8.22", + "url-loader": "^0.6.2", + "webpack": "^4.32.2", + "webpack-cli": "^3.3.2", + "webpack-dev-server": "^3.4.1", + "webpack-manifest-plugin": "^1.3.2", + "webpack-merge": "^4.1.1" + }, + "stylelint": { + "rules": { + "block-no-empty": null, + "color-no-invalid-hex": true, + "comment-empty-line-before": [ + "always", + { + "ignore": [ + "stylelint-commands", + "after-comment" + ] + } + ], + "declaration-colon-space-after": "always", + "indentation": [ + 4, + { + "except": [ + "value" + ] + } + ], + "max-empty-lines": 2, + "rule-empty-line-before": [ + "always", + { + "except": [ + "first-nested" + ], + "ignore": [ + "after-comment" + ] + } + ], + "unit-whitelist": [ + "em", + "rem", + "%", + "s", + "px" + ] + } + } +} diff --git a/src/_events.js b/src/_events.js new file mode 100755 index 0000000..d6a4ea2 --- /dev/null +++ b/src/_events.js @@ -0,0 +1,19 @@ +/** + * Add your global events here + */ + +module.exports = { + AJAX: 'ajax-load', + LOADED: 'load', + SET_TARGET_UPDATE: 'set-target-update', + RESTORE_FIELD: 'restore-field', + FORM_INIT_BASICS: 'form-basics', + FORM_INIT_STEPPED: 'form-init-stepped', + FORM_INIT_VALIDATE: 'form-init-validate', + FORM_INIT_VALIDATE_FIELD: 'form-init-validate-field', + FORM_INIT_STORAGE: 'form-init-storage', + FORM_VALIDATION_FAILED: 'form-validation-failed', + FORM_STEPPED_NEW_STEP: 'form-new-step', + FORM_STEPPED_FIRST_STEP: 'form-first-step', + FORM_STEPPED_LAST_STEP: 'form-last-step', +}; diff --git a/src/_ui.form.croppie.js b/src/_ui.form.croppie.js new file mode 100755 index 0000000..9c72a55 --- /dev/null +++ b/src/_ui.form.croppie.js @@ -0,0 +1,332 @@ +"use strict"; + +import $ from 'jquery'; + +import Events from './_events'; +import SpinnerUI from './_ui.spinner'; + +import Cropper from 'cropperjs/dist/cropper.js'; //'cropperjs/src/index.js'; + +const CroppieUI = (($) => { + + const NAME = 'jsCroppieUI'; + const DATA_KEY = NAME; + + const G = window; + const D = document; + const DEFAULTS = { + aspectRatio: 16 / 9, + }; + + class CroppieUI { + + constructor(element) { + console.log(`Initializing: ${NAME}`); + + const ui = this; + + ui._element = element; + + ui.$el = $(ui._element); + ui.$form = ui.$el.parents('form'); + ui.$input = ui.$el.find('input[type="file"]'); + + ui.$el.prepend(''); + ui.$image = ui.$el.find('img.cropping-image'); + ui.original_image = ui.$image[0]; + ui.mask_img = false; + + ui.name = ui.$input.attr('name'); + ui.width = ui.$input.data('width'); + ui.height = ui.$input.data('height'); + + ui.options = DEFAULTS; + ui.cropper = false; + + ui.$el.data(DATA_KEY, ui); + + ui.$el.prepend('
'); + ui.$removeBtns = ui.$el.find('.remove-masks'); + ui.masks = []; + + ui.$input.on('change', (e) => { + const files = e.currentTarget.files; + + if (files && files.length) { + ui.loadFile(files[0]); + } + }); + + // actions + ui.$el.append('Обрезать'); + + // crop + ui.$el.find('.act-crop').on('click', (e) => { + if (!ui.cropper) { + return true; + } + + e.preventDefault(); + + const canvas = ui.cropper.getCroppedCanvas({ + width: ui.width, + height: ui.height, + }); + + ui.$image[0].src = canvas.toDataURL(); + + ui.original_image = new Image(); + ui.original_image.src = canvas.toDataURL(); + + ui.cropper.destroy(); + ui.cropper = false; + + ui.$el.removeClass(`${NAME}-cropping`); + ui.$el.addClass(`${NAME}-cropped`); + }); + + // mask + ui.$el.find('.masks .mask-item').on('click', (e) => { + e.preventDefault(); + ui.setMask($(e.currentTarget)); + }); + + // submit + ui.$form.on('submit', (e) => { + if (!ui.cropper) { + return true; + } + + SpinnerUI.show(); + + ui.saveImage(); + + const canvas = ui.cropper.getCroppedCanvas({ + width: ui.width, + height: ui.height, + }); + + ui.$image[0].src = canvas.toDataURL(); + canvas.toBlob((blob) => { + ui.uploadFile(blob); + }); + + e.preventDefault(); + }); + } + + setMask = ($el) => { + const ui = this; + + // add current mask + if (ui.mask_img) { + ui.addMask(ui.getMask()); + } + + // update image storage + if (ui.cropper) { + ui.cropper.destroy(); + ui.cropper = false; + + ui.saveImage(); + } + + // add new image + ui.mask_img = new Image(); + ui.mask_img.src = $el.data('src'); + + ui.mask_img.onload = () => { + const img = ui.mask_img; + + ui.cropper = new Cropper(ui.$image[0], { + aspectRatio: img.width / img.height, + viewMode: 0, + guides: true, + center: true, + highlight: true, + cropBoxMovable: true, + cropBoxResizable: true, + movable: false, + rotatable: false, + zoomable: false, + ready: () => { + ui.$el.find('.cropper-face').css({ + 'background-color': 'transparent', + 'background-image': `url(${ui.mask_img.src})`, + 'opacity': '0.8', + }); + ui.$el.find('.cropper-face').data('current-mask', $el); + } + }); + }; + } + + // returns mask ID + addMask = (mask) => { + const ui = this; + const id = Date.now(); + + ui.masks[id] = mask; + + // draw removable button + let $btn = $('Удалить #' + id + ''); + ui.$el.find('.masks').append($btn); + + /*ui.$removeBtns.prepend($btn); + $btn = ui.$removeBtns.find('[data-id="' + id + '"]'); + + $btn.css({ + left: 100 * mask.left / ui.width + '%', + top: 100 * mask.top / ui.width + '%', + width: 100 * mask.width / ui.width + '%', + height: 100 * mask.height / ui.height + '%', + });*/ + $btn.on('click', (e) => { + e.preventDefault(); + + const $btn = $(e.currentTarget); + const id = $btn.data('id'); + + ui.removeMask(id); + }); + return id; + } + + removeMask = (id) => { + const ui = this; + delete ui.masks[id]; + ui.$el.find('.masks [data-id="' + id + '"]').remove(); + + ui.mask_img = false; + ui.$el.find('.cropper-face').data('current-mask').click(); + } + + getMask = () => { + const ui = this, + canvas = document.createElement('canvas'), + context = canvas.getContext('2d'), + cropper = ui.cropper, + maskWidth = cropper.getData().width, + maskHeight = cropper.getData().height, + maskTop = cropper.getData().y, + maskLeft = cropper.getData().x, + imageLeft = cropper.getImageData().left, + imageTop = cropper.getImageData().top, + imageAspect = cropper.getImageData().aspectRatio; + + canvas.width = ui.width; + canvas.height = ui.height; + context.imageSmoothingEnabled = true; + + return { + img: ui.mask_img, + left: maskLeft, + top: maskTop, + width: maskWidth, + height: maskHeight + }; + } + + saveImage = () => { + const ui = this, + canvas = document.createElement('canvas'), + context = canvas.getContext('2d'); + + canvas.width = ui.width; + canvas.height = ui.height; + context.imageSmoothingEnabled = true; + + context.drawImage(ui.original_image, 0, 0, ui.width, ui.height); + + for (let id in ui.masks) { + const mask = ui.masks[id]; + console.log(mask); + context.drawImage(mask.img, mask.left, mask.top, mask.width, mask.height); + } + + ui.$image[0].src = canvas.toDataURL(); + + return canvas; + }; + + loadFile = (file) => { + const ui = this; + + if (/^image\/\w+/.test(file.type)) { + + ui.$image[0].src = URL.createObjectURL(file); + + if (ui.cropper) { + ui.cropper.destroy(); + } + + ui.cropper = new Cropper(ui.$image[0], ui.options); + ui.$input[0].value = null; + + ui.$el.addClass(`${NAME}-cropping`); + } else { + window.alert('Please choose an image file.'); + } + }; + + uploadFile = (blob) => { + console.log('Initializing uploading sequence!'); + + const ui = this; + const data = new FormData(ui.$form[0]); + + data.delete('BackURL'); + data.delete(ui.name); + data.append(ui.name, blob, ui.name + '-image.png'); + data.append('ajax', '1'); + + $.ajax({ + url: ui.$form.attr('action'), + data: data, + processData: false, + contentType: false, + type: ui.$form.attr('method'), + success: (data) => { + console.log('UPLOAD SUCCESS!'); + + SpinnerUI.hide(); + $(G).trigger(Events.AJAX); + } + }); + }; + + static dispose() { + console.log(`Destroying: ${NAME}`); + } + + static _jQueryInterface() { + return this.each((i, el) => { + // attach functionality to element + const $el = $(el); + let data = $el.data(DATA_KEY); + + if (!data) { + data = new CroppieUI(el); + $el.data(DATA_KEY, data); + } + }); + } + } + + // jQuery interface + $.fn[NAME] = CroppieUI._jQueryInterface; + $.fn[NAME].Constructor = CroppieUI; + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT; + return CroppieUI._jQueryInterface; + }; + + // auto-apply + $(window).on(`${Events.AJAX} ${Events.LOADED}`, () => { + $('.field.croppie').jsCroppieUI(); + }); + + return CroppieUI; +})($); + +export default CroppieUI; diff --git a/src/_ui.form.croppie.scss b/src/_ui.form.croppie.scss new file mode 100755 index 0000000..9fe94d4 --- /dev/null +++ b/src/_ui.form.croppie.scss @@ -0,0 +1,286 @@ +.cropper { + &-container { + direction: ltr; + font-size: 0; + line-height: 0; + position: relative; + touch-action: none; + user-select: none; + + img { + display: block; + height: 100%; + image-orientation: 0deg; + max-height: none !important; + max-width: none !important; + min-height: 0 !important; + min-width: 0 !important; + width: 100%; + } + } + + &-wrap-box, + &-canvas, + &-drag-box, + &-crop-box, + &-modal { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + } + + &-wrap-box, + &-canvas { + overflow: hidden; + } + + &-drag-box { + background-color: #fff; + opacity: 0; + } + + &-modal { + background-color: #000; + opacity: 0.5; + } + + &-view-box { + display: block; + height: 100%; + outline: 1px solid #39f; + outline-color: rgba(51, 153, 255, 0.75); + overflow: hidden; + width: 100%; + } + + &-dashed { + border: 0 dashed #eee; + display: block; + opacity: 0.5; + position: absolute; + + &.dashed-h { + border-bottom-width: 1px; + border-top-width: 1px; + height: calc(100% / 3); + left: 0; + top: calc(100% / 3); + width: 100%; + } + + &.dashed-v { + border-left-width: 1px; + border-right-width: 1px; + height: 100%; + left: calc(100% / 3); + top: 0; + width: calc(100% / 3); + } + } + + &-center { + display: block; + height: 0; + left: 50%; + opacity: 0.75; + position: absolute; + top: 50%; + width: 0; + + &::before, + &::after { + background-color: #eee; + content: ' '; + display: block; + position: absolute; + } + + &::before { + height: 1px; + left: -3px; + top: 0; + width: 7px; + } + + &::after { + height: 7px; + left: 0; + top: -3px; + width: 1px; + } + } + + &-face, + &-line, + &-point { + display: block; + height: 100%; + opacity: 0.1; + position: absolute; + width: 100%; + } + + &-face { + background-color: #fff; + left: 0; + top: 0; + } + + &-line { + background-color: #39f; + + &.line-e { + cursor: ew-resize; + right: -3px; + top: 0; + width: 5px; + } + + &.line-n { + cursor: ns-resize; + height: 5px; + left: 0; + top: -3px; + } + + &.line-w { + cursor: ew-resize; + left: -3px; + top: 0; + width: 5px; + } + + &.line-s { + bottom: -3px; + cursor: ns-resize; + height: 5px; + left: 0; + } + } + + &-point { + background-color: #39f; + height: 5px; + opacity: 0.75; + width: 5px; + + &.point-e { + cursor: ew-resize; + margin-top: -3px; + right: -3px; + top: 50%; + } + + &.point-n { + cursor: ns-resize; + left: 50%; + margin-left: -3px; + top: -3px; + } + + &.point-w { + cursor: ew-resize; + left: -3px; + margin-top: -3px; + top: 50%; + } + + &.point-s { + bottom: -3px; + cursor: s-resize; + left: 50%; + margin-left: -3px; + } + + &.point-ne { + cursor: nesw-resize; + right: -3px; + top: -3px; + } + + &.point-nw { + cursor: nwse-resize; + left: -3px; + top: -3px; + } + + &.point-sw { + bottom: -3px; + cursor: nesw-resize; + left: -3px; + } + + &.point-se { + bottom: -3px; + cursor: nwse-resize; + height: 20px; + opacity: 1; + right: -3px; + width: 20px; + + @media (min-width: 768px) { + height: 15px; + width: 15px; + } + + @media (min-width: 992px) { + height: 10px; + width: 10px; + } + + @media (min-width: 1200px) { + height: 5px; + opacity: 0.75; + width: 5px; + } + } + + &.point-se::before { + background-color: #39f; + bottom: -50%; + content: ' '; + display: block; + height: 200%; + opacity: 0; + position: absolute; + right: -50%; + width: 200%; + } + } + + &-invisible { + opacity: 0; + } + + &-bg { + background-image: url("./img/bg.png"); + } + + &-hide { + display: block; + height: 0; + position: absolute; + width: 0; + } + + &-hidden { + display: none !important; + } + + &-move { + cursor: move; + } + + &-crop { + cursor: crosshair; + } + + &-disabled &-drag-box, + &-disabled &-face, + &-disabled &-line, + &-disabled &-point { + cursor: not-allowed; + } +} diff --git a/src/_ui.spinner.js b/src/_ui.spinner.js new file mode 100755 index 0000000..7cfc5ae --- /dev/null +++ b/src/_ui.spinner.js @@ -0,0 +1,17 @@ +import $ from 'jquery'; + +const SpinnerUI = (($) => { + class SpinnerUI { + static show(callback) { + $('#PageLoading').show(0, callback); + } + + static hide(callback) { + $('#PageLoading').hide('slow', callback); + } + } + + return SpinnerUI; +})($); + +export default SpinnerUI; diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..294e191 --- /dev/null +++ b/src/app.js @@ -0,0 +1,42 @@ +import 'app.scss'; +import Events from './_events'; +import Croppie from './_ui.form.croppie'; + +function importAll(r) { + return r.keys().map(r); +} + +const images = importAll(require.context('./img/', false, /\.(png|jpe?g|svg)$/)); +const fontAwesome = importAll(require.context('font-awesome', false, /\.(otf|eot|svg|ttf|woff|woff2)$/)); + +const LayoutUI = (($) => { + // Constants + const W = window; + const D = document; + const $Body = $('body'); + + const NAME = 'LayoutUI'; + + class LayoutUI { + static init() { + const ui = this; + ui.dispose(); + + console.log(`Initializing: ${NAME}`); + } + + static dispose() { + console.log(`Destroying: ${NAME}`); + } + } + + $(W).on(`${Events.AJAX} ${Events.LOADED}`, () => { + LayoutUI.init(); + }); + + W.LayoutUI = LayoutUI; + + return LayoutUI; +})($); + +export default LayoutUI; diff --git a/src/app.scss b/src/app.scss new file mode 100644 index 0000000..d09086b --- /dev/null +++ b/src/app.scss @@ -0,0 +1,109 @@ +// reset style +body { + margin: 0; + padding: 0; + background-color: #dedede; +} + +fieldset { + border: 0; +} + +img { + max-width: 100%; +} + +// main styling +.wrapper { + position: relative; + width: 90%; + padding: 2rem 1rem; + margin: 2rem auto; + background: #fff; + border: 1px solid red; +} + +.btn { + display: inline-block; + padding: .5rem 1rem; + border: 1px #dedede; + background: #888; + color: #000; + text-decoration: none; + + &:hover, + &:focus { + opacity: .8; + } +} + +// field styling +@import "./_ui.form.croppie.scss"; + +.field { + position: relative; + margin: 2rem 0; + + &.croppie { + position: relative; + height: 480px; + + .act-crop, + .cropping-image, + .masks, + .mask-canvas { + display: none; + } + + .cropper-face { + background-repeat: no-repeat; + background-size: contain; + } + + .masks { + h2 { + clear: both; + } + + .mask-item { + width: 30%; + float: left; + } + + &:before, + &:after { + content: ''; + clear: both; + } + } + + &.jsCroppieUI-cropping { + .left, + .right, + .middle-column { + display: none; + } + + .act-crop { + display: inline-block; + } + } + + &.jsCroppieUI-cropped { + .left, + .right, + .middle-column { + display: none; + } + + .masks, + .act-place { + display: block; + } + } + + .remove-mask { + position: absolute; + } + } +} diff --git a/src/img/bg.png b/src/img/bg.png new file mode 100644 index 0000000..3c7056b Binary files /dev/null and b/src/img/bg.png differ diff --git a/src/img/photo1.png b/src/img/photo1.png new file mode 100755 index 0000000..66674f1 Binary files /dev/null and b/src/img/photo1.png differ diff --git a/src/img/photo2.jpg b/src/img/photo2.jpg new file mode 100755 index 0000000..6271cd7 Binary files /dev/null and b/src/img/photo2.jpg differ diff --git a/src/img/photo3.svg b/src/img/photo3.svg new file mode 100644 index 0000000..7a9cb53 --- /dev/null +++ b/src/img/photo3.svg @@ -0,0 +1,778 @@ + + + + + Hypnotoad + + + + + + + + + + + + + image/svg+xml + + Hypnotoad + Example of an SVG animation depicting Hypnotoad from the Futurama show. + + Futurama + + + Futurama + + + + + Ilya Sukhanov (dotCOMmie) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..2a338d8 --- /dev/null +++ b/src/index.html @@ -0,0 +1,57 @@ + + + + JCROP + + + +
+ +
+
+
+
+
+

Ткни чтобы выбрать маску

+
+ Mask #1 +
+
+ Mask #2 +
+
+ +
+ + +
+ +
+
+
+ +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/upload.php b/src/upload.php new file mode 100644 index 0000000..6b202f3 --- /dev/null +++ b/src/upload.php @@ -0,0 +1,6 @@ += 11', + 'ie_mob >= 11', + 'Safari >= 10', + 'Android >= 4.4', + 'Chrome >= 44', // Retail + 'Samsung >= 4' + ] + }) + ] + } + }, { + loader: 'resolve-url-loader' + }, { + loader: 'sass-loader', + options: { + sourceMap: false + } + }, ] + }) + }, { + test: /fontawesome([^.]+).(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, + use: [{ + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts/', + publicPath: '../fonts/' + } + }] + }, { + test: /\.(ttf|otf|eot|svg|woff(2)?)$/, + use: [{ + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts/', + publicPath: '../fonts/' + } + }] + }, { + test: /\.(png|jpg|jpeg|gif|svg)$/, + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'img/', + publicPath: '../img/' + }, + }, ], + }, + resolve: { + modules: [ + path.resolve(__dirname, 'src'), + path.resolve(__dirname, 'node_modules'), + ], + alias: { + 'jquery': require.resolve('jquery'), + 'jQuery': require.resolve('jquery'), + }, + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify('production') + } + }), + new webpack.LoaderOptionsPlugin({ + minimize: true, + debug: false + }), + new ExtractTextPlugin({ + filename: 'css/[name].css', + allChunks: true + }), + new HtmlWebpackPlugin({ + template: './src/index.html' + }), + ], + + devServer: { + host: '127.0.0.1', + port: 8001, + historyApiFallback: true, + hot: false, + clientLogLevel: 'info', + contentBase: [ + path.resolve(__dirname, 'src'), + path.resolve(__dirname, 'node_modules'), + path.resolve(__dirname, 'dist'), + ], + //watchContentBase: true, + overlay: { + warnings: true, + errors: true + }, + headers: { + 'Access-Control-Allow-Origin': '*', + } + }, +};