cropt2 2.0.18 → 2.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # Changelog
2
+ ## [2.0.19] - 2026-03-31
3
+ - Backfixes from Devtheorem's version.
4
+
2
5
  ## [2.0.18] - 2026-01-08
3
6
  - Adjusting viewport default size to be container - 60px (instead of 220px by 220px)
4
7
 
package/README.md CHANGED
@@ -1,11 +1,17 @@
1
1
  # Cropt v2 - lightweight JavaScript image cropper
2
2
  [Github](https://github.com/mindflowgo/cropt/)
3
3
 
4
- Originally based on [Foliotek/Croppie](https://github.com/Foliotek/Croppie), but rewritten as a modern ES module with a simpler API, higher quality image scaling, and numerous other improvements by
5
- [Devtheorem](https://devtheorem.github.io/cropt/).
4
+ **Significant** code cleanup, and feature build-out from the original cropt by **Devtheorem**,without breaking backward compatibility, nor significantly increasing size.
6
5
 
7
- It was extensively enhanced (but backwards compatible with v1) to include adjustable viewport, rotation,
8
- keyboard handling, and various optimizations and bug fixes. And packed to work as browser install, commonJS, esm package, etc.
6
+ **Includes distribution version for esm, commonjs, and browser install unlike the original.**
7
+
8
+ Core new features:
9
+ - we have **lots** more demos than the original
10
+ - viewport adjustment (adjust the size of the crop image)
11
+ - rotation (rotate the image)
12
+ - rotate buttons (buttons to rotate the image)
13
+
14
+ Originally based on [Foliotek/Croppie](https://github.com/Foliotek/Croppie), but rewritten as a modern ES module with a simpler API, higher quality image scaling, and numerous other improvements by [Devtheorem](https://devtheorem.github.io/cropt/).
9
15
 
10
16
 
11
17
  ## Quick Start
@@ -20,6 +26,10 @@ npm install cropt2
20
26
  ```
21
27
 
22
28
  ## Running Demo
29
+ See the same demos on github: [https://mindflowgo.github.io/cropt/](https://mindflowgo.github.io/cropt/).
30
+
31
+ 1) [Optional] The run build prepares the distribution files if you modify the src files.
32
+ 2) To see the demos running locally `npm start`.
23
33
 
24
34
  ```
25
35
  npm run build
@@ -1 +1 @@
1
- function t(){return{boundary:document.createElement("div"),viewport:document.createElement("div"),preview:document.createElement("img"),overlay:document.createElement("div"),controls:document.createElement("div"),resizeHandleRight:document.createElement("div"),resizeHandleBottom:document.createElement("div"),toolBar:document.createElement("div"),zoomer:document.createElement("input"),rotateLeft:document.createElement("button"),rotateRight:document.createElement("button")}}function e(t,e,i){return Math.round(Math.max(Math.min(t,e),i))}export default class{element;elements;options={mouseWheelZoom:"on",viewport:{width:0,height:0,borderRadius:"0px"},zoomerInputClass:"cr-slider",enableZoomSlider:!0,enableKeypress:!1,resizeBars:!1,enableRotateBtns:!1};#t=.85;#e=50;#i=50;#o=1;#s=0;#n=!1;#r="#fff";#a=new AbortController;#l=function(t){let e;return(...i)=>{clearTimeout(e),e=setTimeout(()=>t(...i),100)}}(()=>{this.#h()});constructor(e,i){if(e.classList.contains("cropt-container"))throw new Error("Cropt appears initialized (element has 'cropt-container' class). Aborting.");i.viewport&&(i.viewport={...this.options.viewport,...i.viewport},(!i.viewport?.width||i.viewport.width<100)&&(i.viewport.width=Math.max(100,e.clientWidth-60)),(!i.viewport?.height||i.viewport.height<100)&&(i.viewport.height=Math.max(100,e.clientHeight-60))),this.options={...this.options,...i},this.options.transparencyColor&&(this.#r=this.options.transparencyColor),this.element=e,this.element.classList.add("cropt-container"),this.elements=t(),this.elements.toolBar.classList.add("cr-toolbar-wrap"),this.elements.boundary.classList.add("cr-boundary"),this.elements.viewport.classList.add("cr-viewport"),this.elements.overlay.classList.add("cr-overlay"),this.elements.controls.classList.add("cr-controls"),this.elements.viewport.setAttribute("tabindex","0"),this.#d(this.elements.preview),this.elements.boundary.appendChild(this.elements.preview),this.elements.boundary.appendChild(this.elements.viewport),this.elements.boundary.appendChild(this.elements.overlay),this.elements.boundary.appendChild(this.elements.controls),this.#m(),this.options.enableRotateBtns&&(this.elements.rotateLeft.type="button",this.elements.rotateLeft.innerHTML="↺",this.elements.rotateLeft.setAttribute("aria-label","rotate left"),this.elements.rotateLeft.classList.add("cr-rotate-btn","cr-rotate-left"),this.elements.rotateRight.type="button",this.elements.rotateRight.innerHTML="↻",this.elements.rotateRight.setAttribute("aria-label","rotate right"),this.elements.rotateRight.classList.add("cr-rotate-btn","cr-rotate-right"),this.elements.toolBar.appendChild(this.elements.rotateLeft),this.elements.toolBar.appendChild(this.elements.rotateRight)),this.elements.zoomer.type="range",this.elements.zoomer.step="0.001",this.elements.zoomer.value="1",this.options.enableZoomSlider&&(this.elements.zoomer.className=this.options.zoomerInputClass,this.elements.zoomer.setAttribute("aria-label","zoom"),this.elements.toolBar.appendChild(this.elements.zoomer)),this.element.appendChild(this.elements.boundary),this.element.appendChild(this.elements.toolBar),this.elements.toolBar.childNodes.length?this.element.style.setProperty("--cropt-toolbar","32px"):this.element.style.setProperty("--cropt-toolbar","0px"),this.#p(),this.#c(),this.#g(),this.#u()}bind(t,e){if(!t)throw new Error("src cannot be empty");return function(t){return new Promise((e,i)=>{const o=new Image;o.crossOrigin="anonymous",o.onload=()=>{e(o)},o.onerror=i,o.src=t})}(t).then(async t=>{if(this.#w(t),"object"==typeof e&&e?.transform)e?.viewport&&this.setOptions({viewport:e.viewport}),setTimeout(async()=>{e.transform?.rotate&&await this.setRotation(e.transform.rotate);const t=e.transform?.scale||null;this.#v(t),this.#b(e.transform),this.#h()},0);else{const t=Number(e)||null;this.#f(t)}})}#y(){const t=t=>Math.round(Math.max(0,t/this.#o)),e=this.elements.preview.getBoundingClientRect(),i=this.elements.viewport.getBoundingClientRect(),o=this.elements.viewport.offsetWidth,s=this.elements.viewport.offsetHeight,n=(i.width-o)/2,r=(i.height-s)/2,a=i.left-e.left,l=i.top-e.top;return{left:t(a),top:t(l),right:t(a+o+n),bottom:t(l+s+r),width:t(o+n),height:t(s+r)}}get(){const t=this.#y();let e={x:t.left,y:t.top,width:t.width,height:t.height};const i=this.elements.preview.naturalWidth,o=this.elements.preview.naturalHeight;return 90===this.#s||270===this.#s?(e.width=t.height,e.height=t.width,90===this.#s?(e.x=t.top,e.y=i-t.left-t.width):(e.x=o-t.top-t.height,e.y=t.left)):180===this.#s&&(e.x=i-t.left-t.width,e.y=o-t.top-t.height),e.x=Math.max(0,e.x),e.y=Math.max(0,e.y),{crop:e,transform:this.#b(),viewport:{width:Math.round(this.options.viewport.width),height:Math.round(this.options.viewport.height),borderRadius:this.options.viewport.borderRadius}}}toCanvas(t=null,e=""){const i=this.#y(),o=t&&t<0;t&&o&&(t=-t);let s=i.width,n=i.height;if(t&&(!o||s>t||n>t)){const e=this.elements.viewport.getBoundingClientRect(),i=e.width/e.height;i>1?(s=t,n=t/i):(n=t,s=t*i)}return Promise.resolve(this.#C(i,s,n,e))}toBlob(t=null,e="image/webp",i=1){return"image/webp"===e&&i<1&&!document.createElement("canvas").toDataURL("image/webp").startsWith("data:image/webp")&&(e="image/jpeg"),new Promise((o,s)=>{this.toCanvas(t,e).then(t=>{t.toBlob(e=>{null===e?s("Canvas blob is null"):(e.width=t.width,e.height=t.height,o(e))},e,i)})})}refresh(){this.#f()}setOptions(t){const e=this.options.viewport.width,i=this.options.viewport.height,o=this.options.viewport;t.viewport&&(t.viewport={...o,...t.viewport}),this.options={...this.options,...t},this.#p(),o.width===e&&o.height===i||this.#v()}setZoom(t){const e=this.elements.zoomer,i=parseFloat(e.min),o=parseFloat(e.max);e.value=Math.max(i,Math.min(o,t)).toFixed(3),this.#x()}async setRotation(t){if(void 0===t)return;const e=(t%360+360)%360,i=e-this.#s;0!==i&&(this.#s=e,await this.#R(i),this.#f())}async#R(t){const e=this.elements.preview,i=await createImageBitmap(e),o=90===Math.abs(t%180),s=document.createElement("canvas");s.width=o?i.height:i.width,s.height=o?i.width:i.height;const n=s.getContext("2d");if(!n)throw new Error("Could not get canvas context");n.translate(s.width/2,s.height/2),n.rotate(t*Math.PI/180),n.drawImage(i,-i.width/2,-i.height/2),i.close();const r=await new Promise((t,e)=>{s.toBlob(i=>i?t(i):e(new Error("Failed to create blob")),"image/webp",1)});this.#n&&URL.revokeObjectURL(e.src),e.src=URL.createObjectURL(r),await e.decode(),this.#n=!0}destroy(){this.#a.abort(),this.#n&&URL.revokeObjectURL(this.elements.preview.src),this.element.removeChild(this.elements.boundary),this.element.classList.remove("cropt-container"),this.element.removeChild(this.elements.toolBar),this.elements=t()}#b(t){const e=this.elements.preview,i=()=>{const[t,i]=(e.style.transformOrigin||"0px 0px").split(" ");return{x:parseFloat(t)||0,y:parseFloat(i)||0}};if(void 0!==t){const{x:o=0,y:s=0,scale:n=1}=t;if(e.style.transform=`translate(${o}px, ${s}px) scale(${n})`,void 0!==t.origin){const i=t.origin.x??0,o=t.origin.y??0;e.style.transformOrigin=`${i}px ${o}px`}return{x:o,y:s,scale:n,rotate:this.#s,origin:i()}}const o=e.style.transform||"";let s=0,n=0,r=1;for(const t of["translate","scale"]){const e=new RegExp(`${t}s*\\(([^)]+)\\)`),i=o.match(e);if(i){const e=i[1].trim();if("translate"===t){const[t,i]=e.split(",").map(t=>t.trim());s=Math.round(parseFloat(t))||0,n=void 0!==i?Math.round(parseFloat(i))||0:s}else"scale"===t&&(r=parseFloat(e)||1)}}return{x:s,y:n,scale:r,rotate:this.#s,origin:i()}}#p(){if(!this.elements?.viewport)return;const t=this.elements.viewport;t.style.borderRadius=this.options.viewport?.borderRadius||"50%",t.style.width=(this.options.viewport?.width||100)+"px",t.style.height=(this.options.viewport?.height||100)+"px",this.#L()}#m(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e}=this.elements;t.classList.add("cr-resize-handle","cr-resize-handle-right");const i=document.createElement("div");i.classList.add("cr-resize-handle-grabber"),t.appendChild(i),e.classList.add("cr-resize-handle","cr-resize-handle-bottom");const o=document.createElement("div");o.classList.add("cr-resize-handle-grabber"),e.appendChild(o),this.elements.controls.appendChild(t),this.elements.controls.appendChild(e),this.#E(),setTimeout(()=>this.#L(),200)}#L(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e,viewport:i,boundary:o}=this.elements,s=this.options.viewport.width,n=this.options.viewport.height,r=i.getBoundingClientRect(),a=o.getBoundingClientRect(),l=r.left-a.left,h=r.top-a.top;t.style.left=l+s-22+"px",t.style.top=h+n/2-22+"px",e.style.left=l+s/2-22+"px",e.style.top=h+n-22+"px"}#E(){let t=0,e=0;const i=i=>{i.preventDefault();const o=i.pageX-t,s=Math.floor(.95*this.elements.boundary.clientWidth),n=Math.min(s,Math.max(this.#e,e+o));this.options.viewport.width=n,this.#p()},o=()=>{document.removeEventListener("pointermove",i),document.removeEventListener("pointerup",o)};this.elements.resizeHandleRight.addEventListener("pointerdown",s=>{0===s.button&&(s.preventDefault(),s.stopPropagation(),t=s.pageX,e=this.options.viewport.width,document.addEventListener("pointermove",i,{signal:this.#a.signal}),document.addEventListener("pointerup",o,{signal:this.#a.signal}))},{signal:this.#a.signal});let s=0,n=0;const r=t=>{t.preventDefault();const e=t.pageY-s,i=Math.floor(.95*this.elements.boundary.clientHeight),o=Math.min(i,Math.max(this.#i,n+e));this.options.viewport.height=o,this.#p()},a=()=>{document.removeEventListener("pointermove",r),document.removeEventListener("pointerup",a)};this.elements.resizeHandleBottom.addEventListener("pointerdown",t=>{0===t.button&&(t.preventDefault(),t.stopPropagation(),s=t.pageY,n=this.options.viewport.height,document.addEventListener("pointermove",r,{signal:this.#a.signal}),document.addEventListener("pointerup",a,{signal:this.#a.signal}))},{signal:this.#a.signal})}#B(t){const e=document.createElement("canvas"),i=e.getContext("2d");if(null===i)throw new Error("Canvas context cannot be null");e.width=t.width,e.height=t.height;const o=this.elements.preview;return i.drawImage(o,t.left,t.top,t.width,t.height,0,0,e.width,e.height),e}#C(t,e,i,o){const s=this.#B(t),n=s.getContext("2d"),r=document.createElement("canvas"),a=r.getContext("2d"),l=document.createElement("canvas"),h=l.getContext("2d");if(l.width=e,l.height=i,null===h||null===n||null===a)throw new Error("Canvas context cannot be null");let d={width:s.width,height:s.height};for(;d.width>2*l.width;){let t=d.width,e=d.height;r.width=t,r.height=e,a.clearRect(0,0,r.width,r.height),a.drawImage(s,0,0),d={width:Math.floor(t/2),height:Math.floor(e/2)},n.clearRect(0,0,t,e),n.drawImage(r,0,0,t,e,0,0,d.width,d.height)}return"image/jpeg"===o&&(h.fillStyle=this.#r,h.fillRect(0,0,l.width,l.height)),h.drawImage(s,0,0,d.width,d.height,0,0,l.width,l.height),s.width=s.height=0,r.width=r.height=0,l}#z(){const t=this.#o,e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.clientWidth/2,o=this.elements.boundary.clientHeight/2,s=this.elements.preview.getBoundingClientRect(),n=e.width/2,r=e.height/2,a=-1*(n/t-i),l=-1*(r/t-o),h=1/t*n,d=1/t*r;return{translate:{maxX:a,minX:a-(s.width*(1/t)-e.width*(1/t)),maxY:l,minY:l-(s.height*(1/t)-e.height*(1/t))},origin:{maxX:s.width*(1/t)-h,minX:h,maxY:s.height*(1/t)-d,minY:d}}}#M(t,i){const o=this.elements.preview.getBoundingClientRect(),s=this.elements.viewport.getBoundingClientRect(),n=this.#b();n.y+=e(s.top-o.top,i,s.bottom-o.bottom),n.x+=e(s.left-o.left,t,s.right-o.right),this.#P(n),this.#l()}#c(){let t=0,e=0,i=[],o=0,s=s=>{s.preventDefault();const n=i.findIndex(t=>t.pointerId===s.pointerId);if(-1!==n){if(i[n]=s,2===i.length){let t=i[0],e=i[1],s=Math.hypot(t.pageX-e.pageX,t.pageY-e.pageY);return 0===o&&(o=s/this.#o),void this.setZoom(s/o)}0===o&&(this.#M(s.pageX-t,s.pageY-e),t=s.pageX,e=s.pageY)}},n=t=>{const e=i.findIndex(e=>e.pointerId===t.pointerId);-1!==e&&i.splice(e,1),0===i.length&&(this.elements.overlay.removeEventListener("pointermove",s),this.elements.overlay.removeEventListener("pointerup",n),this.elements.overlay.removeEventListener("pointerout",n),this.#I(!1,this.elements.preview),o=0)};this.elements.overlay.addEventListener("pointerdown",o=>{o.button||(o.preventDefault(),i.push(o),this.elements.overlay.setPointerCapture(o.pointerId),i.length>1||(t=o.pageX,e=o.pageY,this.#I(!0,this.elements.preview),this.elements.overlay.addEventListener("pointermove",s,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerup",n,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerout",n,{signal:this.#a.signal})))},{signal:this.#a.signal}),this.options.enableKeypress&&document.addEventListener("keydown",t=>{if(document.activeElement&&["INPUT","TEXTAREA","SELECT","BUTTON"].includes(document.activeElement.nodeName))return;const e=function(t){switch(t){case"ArrowLeft":return[2,0];case"ArrowUp":return[0,2];case"ArrowRight":return[-2,0];case"ArrowDown":return[0,-2];default:return null}}(t.key);if(null!==e)if(t.shiftKey&&e[1]){t.preventDefault();const i=parseFloat(this.elements.zoomer.value);this.setZoom(i+.005*e[1])}else t.preventDefault(),this.#M(e[0],e[1])},{signal:this.#a.signal})}#g(){this.options.enableZoomSlider&&this.elements.zoomer.addEventListener("input",()=>this.#x(),{signal:this.#a.signal}),"off"!==this.options.mouseWheelZoom&&this.elements.boundary.addEventListener("wheel",t=>{let e=0;("ctrl"!==this.options.mouseWheelZoom||t.ctrlKey)&&(t.deltaY&&(e=-1*t.deltaY/2e3),t.preventDefault(),this.setZoom(this.#o+e*this.#o))},{signal:this.#a.signal})}#x(){const t=this.#b();this.#o=parseFloat(this.elements.zoomer.value),t.scale=this.#o;const e=this.#z(),i=e.translate,o=e.origin;t.x>=i.maxX&&(t.origin.x=o.minX,t.x=i.maxX),t.x<=i.minX&&(t.origin.x=o.maxX,t.x=i.minX),t.y>=i.maxY&&(t.origin.y=o.minY,t.y=i.maxY),t.y<=i.minY&&(t.origin.y=o.maxY,t.y=i.minY),this.#b(t),this.#l()}#u(){this.options.enableRotateBtns&&(this.elements.rotateLeft.addEventListener("click",()=>this.setRotation(this.#s-90),{signal:this.#a.signal}),this.elements.rotateRight.addEventListener("click",()=>this.setRotation(this.#s+90),{signal:this.#a.signal}))}#w(t){this.#d(t),this.elements.preview.parentNode&&this.elements.preview.parentNode.replaceChild(t,this.elements.preview),this.elements.preview=t}#d(t){t.classList.add("cr-image"),t.style.background=this.#r,t.setAttribute("alt","preview"),this.#I(!1,t)}#I(t,e){e.setAttribute("aria-grabbed",t.toString()),this.elements.boundary.setAttribute("aria-dropeffect",t?"move":"none")}#H(){return null!==this.elements.preview.offsetParent}#h(){const t=this.elements.boundary.getBoundingClientRect(),e=this.elements.preview.getBoundingClientRect(),i=this.elements.overlay;i.style.width=e.width+"px",i.style.height=e.height+"px",i.style.top=e.top-t.top+"px",i.style.left=e.left-t.left+"px"}#f(t=null){if(!this.#H())return;const e={x:0,y:0,scale:1,origin:{x:0,y:0}};this.#b(e),this.#v(t),e.scale=this.#o,this.#b(e),this.#Z(),this.#h()}#P(t){const e=this.elements.viewport.getBoundingClientRect(),i=this.elements.preview.getBoundingClientRect(),{origin:o}=this.#b(),s=e.top-i.top+e.height/2,n=e.left-i.left+e.width/2,r={x:Math.round(n/this.#o),y:Math.round(s/this.#o)};t.x=Math.round(t.x-(r.x-(o.x??0))*(1-this.#o)),t.y=Math.round(t.y-(r.y-(o.y??0))*(1-this.#o)),this.#b({...t,origin:r})}#v(t=null){const e=this.elements.preview,i=this.elements.viewport.getBoundingClientRect(),o=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight);let s=this.#t;if(o>=s&&(s+=o),this.elements.zoomer.min=o.toFixed(3),this.elements.zoomer.max=s.toFixed(3),null===t){const i=this.elements.boundary.getBoundingClientRect();t=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight)}this.setZoom(t)}#Z(){const t=this.elements.preview.getBoundingClientRect(),e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.getBoundingClientRect(),o=e.left-i.left,s=e.top-i.top,n=o-(t.width-e.width)/2,r=s-(t.height-e.height)/2;this.#P({x:n,y:r,scale:this.#o})}}
1
+ function t(){return{boundary:document.createElement("div"),viewport:document.createElement("div"),preview:document.createElement("img"),overlay:document.createElement("div"),controls:document.createElement("div"),resizeHandleRight:document.createElement("div"),resizeHandleBottom:document.createElement("div"),toolBar:document.createElement("div"),zoomer:document.createElement("input"),rotateLeft:document.createElement("button"),rotateRight:document.createElement("button")}}function e(t,e,i){return Math.round(Math.max(Math.min(t,e),i))}export default class{element;elements;options={mouseWheelZoom:"on",viewport:{width:0,height:0,borderRadius:"0px"},zoomerInputClass:"cr-slider",enableZoomSlider:!0,enableKeypress:!1,resizeBars:!1,enableRotateBtns:!1};#t=.85;#e=50;#i=50;#o=1;#n=0;#s=!1;#r="#fff";#a=new AbortController;#l=function(t){let e;return(...i)=>{window.clearTimeout(e),e=window.setTimeout(()=>t(...i),100)}}(()=>{this.#h()});constructor(e,i){if(e.classList.contains("cropt-container"))throw new Error("Cropt appears initialized (element has 'cropt-container' class). Aborting.");i.viewport&&(i.viewport={...this.options.viewport,...i.viewport},(!i.viewport?.width||i.viewport.width<100)&&(i.viewport.width=Math.max(100,e.clientWidth-60)),(!i.viewport?.height||i.viewport.height<100)&&(i.viewport.height=Math.max(100,e.clientHeight-60))),this.options={...this.options,...i},this.options.transparencyColor&&(this.#r=this.options.transparencyColor),this.element=e,this.element.classList.add("cropt-container"),this.elements=t(),this.elements.toolBar.classList.add("cr-toolbar-wrap"),this.elements.boundary.classList.add("cr-boundary"),this.elements.viewport.classList.add("cr-viewport"),this.elements.overlay.classList.add("cr-overlay"),this.elements.controls.classList.add("cr-controls"),this.elements.viewport.setAttribute("tabindex","0"),this.#d(this.elements.preview),this.elements.boundary.appendChild(this.elements.preview),this.elements.boundary.appendChild(this.elements.viewport),this.elements.boundary.appendChild(this.elements.overlay),this.elements.boundary.appendChild(this.elements.controls),this.#m(),this.options.enableRotateBtns&&(this.elements.rotateLeft.type="button",this.elements.rotateLeft.innerHTML="↺",this.elements.rotateLeft.setAttribute("aria-label","rotate left"),this.elements.rotateLeft.classList.add("cr-rotate-btn","cr-rotate-left"),this.elements.rotateRight.type="button",this.elements.rotateRight.innerHTML="↻",this.elements.rotateRight.setAttribute("aria-label","rotate right"),this.elements.rotateRight.classList.add("cr-rotate-btn","cr-rotate-right"),this.elements.toolBar.appendChild(this.elements.rotateLeft),this.elements.toolBar.appendChild(this.elements.rotateRight)),this.elements.zoomer.type="range",this.elements.zoomer.step="0.0001",this.elements.zoomer.value="1",this.options.enableZoomSlider&&(this.elements.zoomer.className=this.options.zoomerInputClass,this.elements.zoomer.setAttribute("aria-label","zoom"),this.elements.toolBar.appendChild(this.elements.zoomer)),this.element.appendChild(this.elements.boundary),this.element.appendChild(this.elements.toolBar),this.elements.toolBar.childNodes.length?this.element.style.setProperty("--cropt-toolbar","32px"):this.element.style.setProperty("--cropt-toolbar","0px"),this.#p(),this.#c(),this.#g(),this.#u()}bind(t,e){if(!t)throw new Error("src cannot be empty");return function(t){return new Promise((e,i)=>{const o=new Image;o.crossOrigin="anonymous",o.onload=()=>{e(o)},o.onerror=i,o.src=t})}(t).then(async t=>{if(this.#w(t),"object"==typeof e&&e?.transform)e?.viewport&&this.setOptions({viewport:e.viewport}),setTimeout(async()=>{e.transform?.rotate&&await this.setRotation(e.transform.rotate);const t=e.transform?.scale||null;this.#v(t),this.#b(e.transform),this.#h()},0);else{const t=Number(e)||null;this.#f(t)}})}#y(){const t=t=>Math.round(Math.max(0,t/this.#o)),e=this.elements.preview.getBoundingClientRect(),i=this.elements.viewport.getBoundingClientRect(),o=this.elements.viewport.offsetWidth,n=this.elements.viewport.offsetHeight,s=(i.width-o)/2,r=(i.height-n)/2,a=i.left-e.left,l=i.top-e.top;return{left:t(a),top:t(l),right:t(a+o+s),bottom:t(l+n+r),width:t(o+s),height:t(n+r)}}get(){const t=this.#y();let e={x:t.left,y:t.top,width:t.width,height:t.height};const i=this.elements.preview.naturalWidth,o=this.elements.preview.naturalHeight;return 90===this.#n||270===this.#n?(e.width=t.height,e.height=t.width,90===this.#n?(e.x=t.top,e.y=i-t.left-t.width):(e.x=o-t.top-t.height,e.y=t.left)):180===this.#n&&(e.x=i-t.left-t.width,e.y=o-t.top-t.height),e.x=Math.max(0,e.x),e.y=Math.max(0,e.y),{crop:e,transform:this.#b(),viewport:{width:Math.round(this.options.viewport.width),height:Math.round(this.options.viewport.height),borderRadius:this.options.viewport.borderRadius}}}toCanvas(t=null,e=""){const i=this.#y(),o=t&&t<0;t&&o&&(t=-t);let n=i.width,s=i.height;if(t&&(!o||n>t||s>t)){const e=this.elements.viewport.getBoundingClientRect(),i=e.width/e.height;i>1?(n=t,s=t/i):(s=t,n=t*i)}return Promise.resolve(this.#C(i,n,s,e))}toBlob(t=null,e="image/webp",i=1){return"image/webp"===e&&i<1&&!document.createElement("canvas").toDataURL("image/webp").startsWith("data:image/webp")&&(e="image/jpeg"),new Promise((o,n)=>{this.toCanvas(t,e).then(t=>{t.toBlob(e=>{null===e?n("Canvas blob is null"):(e.width=t.width,e.height=t.height,o(e))},e,i)})})}refresh(){this.#f()}setOptions(t){const e=this.options.viewport.width,i=this.options.viewport.height,o=this.options.viewport;t.viewport&&(t.viewport={...o,...t.viewport}),this.options={...this.options,...t},this.#p(),o.width===e&&o.height===i||this.#v()}setZoom(t){const e=this.elements.zoomer,i=parseFloat(e.min),o=parseFloat(e.max);e.value=Math.max(i,Math.min(o,t)).toFixed(4),this.#x()}async setRotation(t){if(void 0===t)return;const e=(t%360+360)%360,i=e-this.#n;0!==i&&(this.#n=e,await this.#R(i),this.#f())}async#R(t){const e=this.elements.preview,i=await createImageBitmap(e),o=90===Math.abs(t%180),n=document.createElement("canvas");n.width=o?i.height:i.width,n.height=o?i.width:i.height;const s=n.getContext("2d");if(!s)throw new Error("Could not get canvas context");s.translate(n.width/2,n.height/2),s.rotate(t*Math.PI/180),s.drawImage(i,-i.width/2,-i.height/2),i.close();const r=await new Promise((t,e)=>{n.toBlob(i=>i?t(i):e(new Error("Failed to create blob")),"image/webp",1)});this.#s&&URL.revokeObjectURL(e.src),e.src=URL.createObjectURL(r),await e.decode(),this.#s=!0}destroy(){this.#a.abort(),this.#s&&URL.revokeObjectURL(this.elements.preview.src),this.element.removeChild(this.elements.boundary),this.element.classList.remove("cropt-container"),this.element.removeChild(this.elements.toolBar),this.elements=t()}#b(t){const e=this.elements.preview,i=()=>{const[t,i]=(e.style.transformOrigin||"0px 0px").split(" ");return{x:parseFloat(t)||0,y:parseFloat(i)||0}};if(void 0!==t){const{x:o=0,y:n=0,scale:s=1}=t;if(e.style.transform=`translate(${o}px, ${n}px) scale(${s})`,void 0!==t.origin){const i=t.origin.x??0,o=t.origin.y??0;e.style.transformOrigin=`${i}px ${o}px`}return{x:o,y:n,scale:s,rotate:this.#n,origin:i()}}const o=e.style.transform||"";let n=0,s=0,r=1;for(const t of["translate","scale"]){const e=new RegExp(`${t}s*\\(([^)]+)\\)`),i=o.match(e);if(i){const e=i[1].trim();if("translate"===t){const[t,i]=e.split(",").map(t=>t.trim());n=Math.round(parseFloat(t))||0,s=void 0!==i?Math.round(parseFloat(i))||0:n}else"scale"===t&&(r=parseFloat(e)||1)}}return{x:n,y:s,scale:r,rotate:this.#n,origin:i()}}#p(){if(!this.elements?.viewport)return;const t=this.elements.viewport;t.style.borderRadius=this.options.viewport?.borderRadius||"50%",t.style.width=(this.options.viewport?.width||100)+"px",t.style.height=(this.options.viewport?.height||100)+"px",this.#L()}#m(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e}=this.elements;t.classList.add("cr-resize-handle","cr-resize-handle-right");const i=document.createElement("div");i.classList.add("cr-resize-handle-grabber"),t.appendChild(i),e.classList.add("cr-resize-handle","cr-resize-handle-bottom");const o=document.createElement("div");o.classList.add("cr-resize-handle-grabber"),e.appendChild(o),this.elements.controls.appendChild(t),this.elements.controls.appendChild(e),this.#E(),setTimeout(()=>this.#L(),200)}#L(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e,viewport:i,boundary:o}=this.elements,n=this.options.viewport.width,s=this.options.viewport.height,r=i.getBoundingClientRect(),a=o.getBoundingClientRect(),l=r.left-a.left,h=r.top-a.top;t.style.left=l+n-22+"px",t.style.top=h+s/2-22+"px",e.style.left=l+n/2-22+"px",e.style.top=h+s-22+"px"}#E(){let t=0,e=0;const i=i=>{i.preventDefault();const o=i.pageX-t,n=Math.floor(.95*this.elements.boundary.clientWidth),s=Math.min(n,Math.max(this.#e,e+o));this.options.viewport.width=s,this.#p()},o=()=>{document.removeEventListener("pointermove",i),document.removeEventListener("pointerup",o)};this.elements.resizeHandleRight.addEventListener("pointerdown",n=>{0===n.button&&(n.preventDefault(),n.stopPropagation(),t=n.pageX,e=this.options.viewport.width,document.addEventListener("pointermove",i,{signal:this.#a.signal}),document.addEventListener("pointerup",o,{signal:this.#a.signal}))},{signal:this.#a.signal});let n=0,s=0;const r=t=>{t.preventDefault();const e=t.pageY-n,i=Math.floor(.95*this.elements.boundary.clientHeight),o=Math.min(i,Math.max(this.#i,s+e));this.options.viewport.height=o,this.#p()},a=()=>{document.removeEventListener("pointermove",r),document.removeEventListener("pointerup",a)};this.elements.resizeHandleBottom.addEventListener("pointerdown",t=>{0===t.button&&(t.preventDefault(),t.stopPropagation(),n=t.pageY,s=this.options.viewport.height,document.addEventListener("pointermove",r,{signal:this.#a.signal}),document.addEventListener("pointerup",a,{signal:this.#a.signal}))},{signal:this.#a.signal})}#B(t){const e=document.createElement("canvas"),i=e.getContext("2d");if(null===i)throw new Error("Canvas context cannot be null");e.width=t.width,e.height=t.height;const o=this.elements.preview;return i.drawImage(o,t.left,t.top,t.width,t.height,0,0,e.width,e.height),e}#C(t,e,i,o){const n=this.#B(t),s=n.getContext("2d"),r=document.createElement("canvas"),a=r.getContext("2d"),l=document.createElement("canvas"),h=l.getContext("2d");if(l.width=e,l.height=i,null===h||null===s||null===a)throw new Error("Canvas context cannot be null");let d={width:n.width,height:n.height};for(;d.width>2*l.width;){let t=d.width,e=d.height;r.width=t,r.height=e,a.clearRect(0,0,r.width,r.height),a.drawImage(n,0,0),d={width:Math.floor(t/2),height:Math.floor(e/2)},s.clearRect(0,0,t,e),s.drawImage(r,0,0,t,e,0,0,d.width,d.height)}return"image/jpeg"===o&&(h.fillStyle=this.#r,h.fillRect(0,0,l.width,l.height)),h.drawImage(n,0,0,d.width,d.height,0,0,l.width,l.height),n.width=n.height=0,r.width=r.height=0,l}#z(){const t=this.#o,e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.clientWidth/2,o=this.elements.boundary.clientHeight/2,n=this.elements.preview.getBoundingClientRect(),s=e.width/2,r=e.height/2,a=-1*(s/t-i),l=-1*(r/t-o),h=1/t*s,d=1/t*r;return{translate:{maxX:a,minX:a-(n.width*(1/t)-e.width*(1/t)),maxY:l,minY:l-(n.height*(1/t)-e.height*(1/t))},origin:{maxX:n.width*(1/t)-h,minX:h,maxY:n.height*(1/t)-d,minY:d}}}#M(t,i){const o=this.elements.preview.getBoundingClientRect(),n=this.elements.viewport.getBoundingClientRect(),s=this.#b();s.y+=e(n.top-o.top,i,n.bottom-o.bottom),s.x+=e(n.left-o.left,t,n.right-o.right),this.#P(s),this.#l()}#c(){let t=0,e=0,i=[],o=0,n=n=>{n.preventDefault();const s=i.findIndex(t=>t.pointerId===n.pointerId);if(-1!==s){if(i[s]=n,2===i.length){let t=i[0],e=i[1],n=Math.hypot(t.pageX-e.pageX,t.pageY-e.pageY);return 0===o&&(o=n/this.#o),void this.setZoom(n/o)}0===o&&(this.#M(n.pageX-t,n.pageY-e),t=n.pageX,e=n.pageY)}},s=t=>{const e=i.findIndex(e=>e.pointerId===t.pointerId);-1!==e&&i.splice(e,1),0===i.length&&(this.elements.overlay.removeEventListener("pointermove",n),this.elements.overlay.removeEventListener("pointerup",s),this.elements.overlay.removeEventListener("pointerout",s),this.#I(!1,this.elements.preview),o=0)};this.elements.overlay.addEventListener("pointerdown",o=>{o.button||(o.preventDefault(),i.push(o),this.elements.overlay.setPointerCapture(o.pointerId),i.length>1||(t=o.pageX,e=o.pageY,this.#I(!0,this.elements.preview),this.elements.overlay.addEventListener("pointermove",n,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerup",s,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerout",s,{signal:this.#a.signal})))},{signal:this.#a.signal}),this.options.enableKeypress&&document.addEventListener("keydown",t=>{if(document.activeElement&&["INPUT","TEXTAREA","SELECT","BUTTON"].includes(document.activeElement.nodeName))return;const e=function(t){switch(t){case"ArrowLeft":return[2,0];case"ArrowUp":return[0,2];case"ArrowRight":return[-2,0];case"ArrowDown":return[0,-2];default:return null}}(t.key);if(null!==e)if(t.shiftKey&&e[1]){t.preventDefault();const i=parseFloat(this.elements.zoomer.value);this.setZoom(i+.005*e[1])}else t.preventDefault(),this.#M(e[0],e[1])},{signal:this.#a.signal})}#g(){this.options.enableZoomSlider&&this.elements.zoomer.addEventListener("input",()=>this.#x(),{signal:this.#a.signal}),"off"!==this.options.mouseWheelZoom&&this.elements.boundary.addEventListener("wheel",t=>{let e=0;("ctrl"!==this.options.mouseWheelZoom||t.ctrlKey)&&(t.deltaY&&(e=-1*t.deltaY/2e3),t.preventDefault(),this.setZoom(this.#o+e*this.#o))},{signal:this.#a.signal})}#x(){const t=this.#b();this.#o=parseFloat(this.elements.zoomer.value),t.scale=this.#o;const e=this.#z(),i=e.translate,o=e.origin;t.x>=i.maxX&&(t.origin.x=o.minX,t.x=i.maxX),t.x<=i.minX&&(t.origin.x=o.maxX,t.x=i.minX),t.y>=i.maxY&&(t.origin.y=o.minY,t.y=i.maxY),t.y<=i.minY&&(t.origin.y=o.maxY,t.y=i.minY),this.#b(t),this.#l()}#u(){this.options.enableRotateBtns&&(this.elements.rotateLeft.addEventListener("click",()=>this.setRotation(this.#n-90),{signal:this.#a.signal}),this.elements.rotateRight.addEventListener("click",()=>this.setRotation(this.#n+90),{signal:this.#a.signal}))}#w(t){this.#d(t),this.elements.preview.parentNode&&this.elements.preview.parentNode.replaceChild(t,this.elements.preview),this.elements.preview=t}#d(t){t.classList.add("cr-image"),t.style.background=this.#r,t.setAttribute("alt","preview"),this.#I(!1,t)}#I(t,e){e.setAttribute("aria-grabbed",t.toString()),this.elements.boundary.setAttribute("aria-dropeffect",t?"move":"none")}#H(){return null!==this.elements.preview.offsetParent}#h(){const t=this.elements.boundary.getBoundingClientRect(),e=this.elements.preview.getBoundingClientRect(),i=this.elements.overlay;i.style.width=e.width+"px",i.style.height=e.height+"px",i.style.top=e.top-t.top+"px",i.style.left=e.left-t.left+"px"}#f(t=null){if(!this.#H())return;const e={x:0,y:0,scale:1,origin:{x:0,y:0}};this.#b(e),this.#v(t),e.scale=this.#o,this.#b(e),this.#Z(),this.#h()}#P(t){const e=this.elements.viewport.getBoundingClientRect(),i=this.elements.preview.getBoundingClientRect(),{origin:o}=this.#b(),n=e.top-i.top+e.height/2,s=e.left-i.left+e.width/2,r={x:Math.round(s/this.#o),y:Math.round(n/this.#o)};t.x=Math.round(t.x-(r.x-(o.x??0))*(1-this.#o)),t.y=Math.round(t.y-(r.y-(o.y??0))*(1-this.#o)),this.#b({...t,origin:r})}#v(t=null){const e=this.elements.preview,i=this.elements.viewport.getBoundingClientRect(),o=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight);let n=this.#t;if(o>=n&&(n+=o),this.elements.zoomer.min=o.toFixed(3),this.elements.zoomer.max=n.toFixed(3),null===t){const i=this.elements.boundary.getBoundingClientRect();t=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight)}this.setZoom(t)}#Z(){const t=this.elements.preview.getBoundingClientRect(),e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.getBoundingClientRect(),o=e.left-i.left,n=e.top-i.top,s=o-(t.width-e.width)/2,r=n-(t.height-e.height)/2;this.#P({x:s,y:r,scale:this.#o})}}
package/dist/cropt.min.js CHANGED
@@ -1 +1 @@
1
- function t(){return{boundary:document.createElement("div"),viewport:document.createElement("div"),preview:document.createElement("img"),overlay:document.createElement("div"),controls:document.createElement("div"),resizeHandleRight:document.createElement("div"),resizeHandleBottom:document.createElement("div"),toolBar:document.createElement("div"),zoomer:document.createElement("input"),rotateLeft:document.createElement("button"),rotateRight:document.createElement("button")}}function e(t,e,i){return Math.round(Math.max(Math.min(t,e),i))}class Cropt{element;elements;options={mouseWheelZoom:"on",viewport:{width:0,height:0,borderRadius:"0px"},zoomerInputClass:"cr-slider",enableZoomSlider:!0,enableKeypress:!1,resizeBars:!1,enableRotateBtns:!1};#t=.85;#e=50;#i=50;#o=1;#s=0;#n=!1;#r="#fff";#a=new AbortController;#l=function(t){let e;return(...i)=>{clearTimeout(e),e=setTimeout(()=>t(...i),100)}}(()=>{this.#h()});constructor(e,i){if(e.classList.contains("cropt-container"))throw new Error("Cropt appears initialized (element has 'cropt-container' class). Aborting.");i.viewport&&(i.viewport={...this.options.viewport,...i.viewport},(!i.viewport?.width||i.viewport.width<100)&&(i.viewport.width=Math.max(100,e.clientWidth-60)),(!i.viewport?.height||i.viewport.height<100)&&(i.viewport.height=Math.max(100,e.clientHeight-60))),this.options={...this.options,...i},this.options.transparencyColor&&(this.#r=this.options.transparencyColor),this.element=e,this.element.classList.add("cropt-container"),this.elements=t(),this.elements.toolBar.classList.add("cr-toolbar-wrap"),this.elements.boundary.classList.add("cr-boundary"),this.elements.viewport.classList.add("cr-viewport"),this.elements.overlay.classList.add("cr-overlay"),this.elements.controls.classList.add("cr-controls"),this.elements.viewport.setAttribute("tabindex","0"),this.#d(this.elements.preview),this.elements.boundary.appendChild(this.elements.preview),this.elements.boundary.appendChild(this.elements.viewport),this.elements.boundary.appendChild(this.elements.overlay),this.elements.boundary.appendChild(this.elements.controls),this.#m(),this.options.enableRotateBtns&&(this.elements.rotateLeft.type="button",this.elements.rotateLeft.innerHTML="↺",this.elements.rotateLeft.setAttribute("aria-label","rotate left"),this.elements.rotateLeft.classList.add("cr-rotate-btn","cr-rotate-left"),this.elements.rotateRight.type="button",this.elements.rotateRight.innerHTML="↻",this.elements.rotateRight.setAttribute("aria-label","rotate right"),this.elements.rotateRight.classList.add("cr-rotate-btn","cr-rotate-right"),this.elements.toolBar.appendChild(this.elements.rotateLeft),this.elements.toolBar.appendChild(this.elements.rotateRight)),this.elements.zoomer.type="range",this.elements.zoomer.step="0.001",this.elements.zoomer.value="1",this.options.enableZoomSlider&&(this.elements.zoomer.className=this.options.zoomerInputClass,this.elements.zoomer.setAttribute("aria-label","zoom"),this.elements.toolBar.appendChild(this.elements.zoomer)),this.element.appendChild(this.elements.boundary),this.element.appendChild(this.elements.toolBar),this.elements.toolBar.childNodes.length?this.element.style.setProperty("--cropt-toolbar","32px"):this.element.style.setProperty("--cropt-toolbar","0px"),this.#p(),this.#c(),this.#g(),this.#u()}bind(t,e){if(!t)throw new Error("src cannot be empty");return function(t){return new Promise((e,i)=>{const o=new Image;o.crossOrigin="anonymous",o.onload=()=>{e(o)},o.onerror=i,o.src=t})}(t).then(async t=>{if(this.#w(t),"object"==typeof e&&e?.transform)e?.viewport&&this.setOptions({viewport:e.viewport}),setTimeout(async()=>{e.transform?.rotate&&await this.setRotation(e.transform.rotate);const t=e.transform?.scale||null;this.#v(t),this.#b(e.transform),this.#h()},0);else{const t=Number(e)||null;this.#f(t)}})}#y(){const t=t=>Math.round(Math.max(0,t/this.#o)),e=this.elements.preview.getBoundingClientRect(),i=this.elements.viewport.getBoundingClientRect(),o=this.elements.viewport.offsetWidth,s=this.elements.viewport.offsetHeight,n=(i.width-o)/2,r=(i.height-s)/2,a=i.left-e.left,l=i.top-e.top;return{left:t(a),top:t(l),right:t(a+o+n),bottom:t(l+s+r),width:t(o+n),height:t(s+r)}}get(){const t=this.#y();let e={x:t.left,y:t.top,width:t.width,height:t.height};const i=this.elements.preview.naturalWidth,o=this.elements.preview.naturalHeight;return 90===this.#s||270===this.#s?(e.width=t.height,e.height=t.width,90===this.#s?(e.x=t.top,e.y=i-t.left-t.width):(e.x=o-t.top-t.height,e.y=t.left)):180===this.#s&&(e.x=i-t.left-t.width,e.y=o-t.top-t.height),e.x=Math.max(0,e.x),e.y=Math.max(0,e.y),{crop:e,transform:this.#b(),viewport:{width:Math.round(this.options.viewport.width),height:Math.round(this.options.viewport.height),borderRadius:this.options.viewport.borderRadius}}}toCanvas(t=null,e=""){const i=this.#y(),o=t&&t<0;t&&o&&(t=-t);let s=i.width,n=i.height;if(t&&(!o||s>t||n>t)){const e=this.elements.viewport.getBoundingClientRect(),i=e.width/e.height;i>1?(s=t,n=t/i):(n=t,s=t*i)}return Promise.resolve(this.#C(i,s,n,e))}toBlob(t=null,e="image/webp",i=1){return"image/webp"===e&&i<1&&!document.createElement("canvas").toDataURL("image/webp").startsWith("data:image/webp")&&(e="image/jpeg"),new Promise((o,s)=>{this.toCanvas(t,e).then(t=>{t.toBlob(e=>{null===e?s("Canvas blob is null"):(e.width=t.width,e.height=t.height,o(e))},e,i)})})}refresh(){this.#f()}setOptions(t){const e=this.options.viewport.width,i=this.options.viewport.height,o=this.options.viewport;t.viewport&&(t.viewport={...o,...t.viewport}),this.options={...this.options,...t},this.#p(),o.width===e&&o.height===i||this.#v()}setZoom(t){const e=this.elements.zoomer,i=parseFloat(e.min),o=parseFloat(e.max);e.value=Math.max(i,Math.min(o,t)).toFixed(3),this.#x()}async setRotation(t){if(void 0===t)return;const e=(t%360+360)%360,i=e-this.#s;0!==i&&(this.#s=e,await this.#R(i),this.#f())}async#R(t){const e=this.elements.preview,i=await createImageBitmap(e),o=90===Math.abs(t%180),s=document.createElement("canvas");s.width=o?i.height:i.width,s.height=o?i.width:i.height;const n=s.getContext("2d");if(!n)throw new Error("Could not get canvas context");n.translate(s.width/2,s.height/2),n.rotate(t*Math.PI/180),n.drawImage(i,-i.width/2,-i.height/2),i.close();const r=await new Promise((t,e)=>{s.toBlob(i=>i?t(i):e(new Error("Failed to create blob")),"image/webp",1)});this.#n&&URL.revokeObjectURL(e.src),e.src=URL.createObjectURL(r),await e.decode(),this.#n=!0}destroy(){this.#a.abort(),this.#n&&URL.revokeObjectURL(this.elements.preview.src),this.element.removeChild(this.elements.boundary),this.element.classList.remove("cropt-container"),this.element.removeChild(this.elements.toolBar),this.elements=t()}#b(t){const e=this.elements.preview,i=()=>{const[t,i]=(e.style.transformOrigin||"0px 0px").split(" ");return{x:parseFloat(t)||0,y:parseFloat(i)||0}};if(void 0!==t){const{x:o=0,y:s=0,scale:n=1}=t;if(e.style.transform=`translate(${o}px, ${s}px) scale(${n})`,void 0!==t.origin){const i=t.origin.x??0,o=t.origin.y??0;e.style.transformOrigin=`${i}px ${o}px`}return{x:o,y:s,scale:n,rotate:this.#s,origin:i()}}const o=e.style.transform||"";let s=0,n=0,r=1;for(const t of["translate","scale"]){const e=new RegExp(`${t}s*\\(([^)]+)\\)`),i=o.match(e);if(i){const e=i[1].trim();if("translate"===t){const[t,i]=e.split(",").map(t=>t.trim());s=Math.round(parseFloat(t))||0,n=void 0!==i?Math.round(parseFloat(i))||0:s}else"scale"===t&&(r=parseFloat(e)||1)}}return{x:s,y:n,scale:r,rotate:this.#s,origin:i()}}#p(){if(!this.elements?.viewport)return;const t=this.elements.viewport;t.style.borderRadius=this.options.viewport?.borderRadius||"50%",t.style.width=(this.options.viewport?.width||100)+"px",t.style.height=(this.options.viewport?.height||100)+"px",this.#L()}#m(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e}=this.elements;t.classList.add("cr-resize-handle","cr-resize-handle-right");const i=document.createElement("div");i.classList.add("cr-resize-handle-grabber"),t.appendChild(i),e.classList.add("cr-resize-handle","cr-resize-handle-bottom");const o=document.createElement("div");o.classList.add("cr-resize-handle-grabber"),e.appendChild(o),this.elements.controls.appendChild(t),this.elements.controls.appendChild(e),this.#E(),setTimeout(()=>this.#L(),200)}#L(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e,viewport:i,boundary:o}=this.elements,s=this.options.viewport.width,n=this.options.viewport.height,r=i.getBoundingClientRect(),a=o.getBoundingClientRect(),l=r.left-a.left,h=r.top-a.top;t.style.left=l+s-22+"px",t.style.top=h+n/2-22+"px",e.style.left=l+s/2-22+"px",e.style.top=h+n-22+"px"}#E(){let t=0,e=0;const i=i=>{i.preventDefault();const o=i.pageX-t,s=Math.floor(.95*this.elements.boundary.clientWidth),n=Math.min(s,Math.max(this.#e,e+o));this.options.viewport.width=n,this.#p()},o=()=>{document.removeEventListener("pointermove",i),document.removeEventListener("pointerup",o)};this.elements.resizeHandleRight.addEventListener("pointerdown",s=>{0===s.button&&(s.preventDefault(),s.stopPropagation(),t=s.pageX,e=this.options.viewport.width,document.addEventListener("pointermove",i,{signal:this.#a.signal}),document.addEventListener("pointerup",o,{signal:this.#a.signal}))},{signal:this.#a.signal});let s=0,n=0;const r=t=>{t.preventDefault();const e=t.pageY-s,i=Math.floor(.95*this.elements.boundary.clientHeight),o=Math.min(i,Math.max(this.#i,n+e));this.options.viewport.height=o,this.#p()},a=()=>{document.removeEventListener("pointermove",r),document.removeEventListener("pointerup",a)};this.elements.resizeHandleBottom.addEventListener("pointerdown",t=>{0===t.button&&(t.preventDefault(),t.stopPropagation(),s=t.pageY,n=this.options.viewport.height,document.addEventListener("pointermove",r,{signal:this.#a.signal}),document.addEventListener("pointerup",a,{signal:this.#a.signal}))},{signal:this.#a.signal})}#B(t){const e=document.createElement("canvas"),i=e.getContext("2d");if(null===i)throw new Error("Canvas context cannot be null");e.width=t.width,e.height=t.height;const o=this.elements.preview;return i.drawImage(o,t.left,t.top,t.width,t.height,0,0,e.width,e.height),e}#C(t,e,i,o){const s=this.#B(t),n=s.getContext("2d"),r=document.createElement("canvas"),a=r.getContext("2d"),l=document.createElement("canvas"),h=l.getContext("2d");if(l.width=e,l.height=i,null===h||null===n||null===a)throw new Error("Canvas context cannot be null");let d={width:s.width,height:s.height};for(;d.width>2*l.width;){let t=d.width,e=d.height;r.width=t,r.height=e,a.clearRect(0,0,r.width,r.height),a.drawImage(s,0,0),d={width:Math.floor(t/2),height:Math.floor(e/2)},n.clearRect(0,0,t,e),n.drawImage(r,0,0,t,e,0,0,d.width,d.height)}return"image/jpeg"===o&&(h.fillStyle=this.#r,h.fillRect(0,0,l.width,l.height)),h.drawImage(s,0,0,d.width,d.height,0,0,l.width,l.height),s.width=s.height=0,r.width=r.height=0,l}#z(){const t=this.#o,e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.clientWidth/2,o=this.elements.boundary.clientHeight/2,s=this.elements.preview.getBoundingClientRect(),n=e.width/2,r=e.height/2,a=-1*(n/t-i),l=-1*(r/t-o),h=1/t*n,d=1/t*r;return{translate:{maxX:a,minX:a-(s.width*(1/t)-e.width*(1/t)),maxY:l,minY:l-(s.height*(1/t)-e.height*(1/t))},origin:{maxX:s.width*(1/t)-h,minX:h,maxY:s.height*(1/t)-d,minY:d}}}#M(t,i){const o=this.elements.preview.getBoundingClientRect(),s=this.elements.viewport.getBoundingClientRect(),n=this.#b();n.y+=e(s.top-o.top,i,s.bottom-o.bottom),n.x+=e(s.left-o.left,t,s.right-o.right),this.#P(n),this.#l()}#c(){let t=0,e=0,i=[],o=0,s=s=>{s.preventDefault();const n=i.findIndex(t=>t.pointerId===s.pointerId);if(-1!==n){if(i[n]=s,2===i.length){let t=i[0],e=i[1],s=Math.hypot(t.pageX-e.pageX,t.pageY-e.pageY);return 0===o&&(o=s/this.#o),void this.setZoom(s/o)}0===o&&(this.#M(s.pageX-t,s.pageY-e),t=s.pageX,e=s.pageY)}},n=t=>{const e=i.findIndex(e=>e.pointerId===t.pointerId);-1!==e&&i.splice(e,1),0===i.length&&(this.elements.overlay.removeEventListener("pointermove",s),this.elements.overlay.removeEventListener("pointerup",n),this.elements.overlay.removeEventListener("pointerout",n),this.#I(!1,this.elements.preview),o=0)};this.elements.overlay.addEventListener("pointerdown",o=>{o.button||(o.preventDefault(),i.push(o),this.elements.overlay.setPointerCapture(o.pointerId),i.length>1||(t=o.pageX,e=o.pageY,this.#I(!0,this.elements.preview),this.elements.overlay.addEventListener("pointermove",s,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerup",n,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerout",n,{signal:this.#a.signal})))},{signal:this.#a.signal}),this.options.enableKeypress&&document.addEventListener("keydown",t=>{if(document.activeElement&&["INPUT","TEXTAREA","SELECT","BUTTON"].includes(document.activeElement.nodeName))return;const e=function(t){switch(t){case"ArrowLeft":return[2,0];case"ArrowUp":return[0,2];case"ArrowRight":return[-2,0];case"ArrowDown":return[0,-2];default:return null}}(t.key);if(null!==e)if(t.shiftKey&&e[1]){t.preventDefault();const i=parseFloat(this.elements.zoomer.value);this.setZoom(i+.005*e[1])}else t.preventDefault(),this.#M(e[0],e[1])},{signal:this.#a.signal})}#g(){this.options.enableZoomSlider&&this.elements.zoomer.addEventListener("input",()=>this.#x(),{signal:this.#a.signal}),"off"!==this.options.mouseWheelZoom&&this.elements.boundary.addEventListener("wheel",t=>{let e=0;("ctrl"!==this.options.mouseWheelZoom||t.ctrlKey)&&(t.deltaY&&(e=-1*t.deltaY/2e3),t.preventDefault(),this.setZoom(this.#o+e*this.#o))},{signal:this.#a.signal})}#x(){const t=this.#b();this.#o=parseFloat(this.elements.zoomer.value),t.scale=this.#o;const e=this.#z(),i=e.translate,o=e.origin;t.x>=i.maxX&&(t.origin.x=o.minX,t.x=i.maxX),t.x<=i.minX&&(t.origin.x=o.maxX,t.x=i.minX),t.y>=i.maxY&&(t.origin.y=o.minY,t.y=i.maxY),t.y<=i.minY&&(t.origin.y=o.maxY,t.y=i.minY),this.#b(t),this.#l()}#u(){this.options.enableRotateBtns&&(this.elements.rotateLeft.addEventListener("click",()=>this.setRotation(this.#s-90),{signal:this.#a.signal}),this.elements.rotateRight.addEventListener("click",()=>this.setRotation(this.#s+90),{signal:this.#a.signal}))}#w(t){this.#d(t),this.elements.preview.parentNode&&this.elements.preview.parentNode.replaceChild(t,this.elements.preview),this.elements.preview=t}#d(t){t.classList.add("cr-image"),t.style.background=this.#r,t.setAttribute("alt","preview"),this.#I(!1,t)}#I(t,e){e.setAttribute("aria-grabbed",t.toString()),this.elements.boundary.setAttribute("aria-dropeffect",t?"move":"none")}#H(){return null!==this.elements.preview.offsetParent}#h(){const t=this.elements.boundary.getBoundingClientRect(),e=this.elements.preview.getBoundingClientRect(),i=this.elements.overlay;i.style.width=e.width+"px",i.style.height=e.height+"px",i.style.top=e.top-t.top+"px",i.style.left=e.left-t.left+"px"}#f(t=null){if(!this.#H())return;const e={x:0,y:0,scale:1,origin:{x:0,y:0}};this.#b(e),this.#v(t),e.scale=this.#o,this.#b(e),this.#Z(),this.#h()}#P(t){const e=this.elements.viewport.getBoundingClientRect(),i=this.elements.preview.getBoundingClientRect(),{origin:o}=this.#b(),s=e.top-i.top+e.height/2,n=e.left-i.left+e.width/2,r={x:Math.round(n/this.#o),y:Math.round(s/this.#o)};t.x=Math.round(t.x-(r.x-(o.x??0))*(1-this.#o)),t.y=Math.round(t.y-(r.y-(o.y??0))*(1-this.#o)),this.#b({...t,origin:r})}#v(t=null){const e=this.elements.preview,i=this.elements.viewport.getBoundingClientRect(),o=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight);let s=this.#t;if(o>=s&&(s+=o),this.elements.zoomer.min=o.toFixed(3),this.elements.zoomer.max=s.toFixed(3),null===t){const i=this.elements.boundary.getBoundingClientRect();t=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight)}this.setZoom(t)}#Z(){const t=this.elements.preview.getBoundingClientRect(),e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.getBoundingClientRect(),o=e.left-i.left,s=e.top-i.top,n=o-(t.width-e.width)/2,r=s-(t.height-e.height)/2;this.#P({x:n,y:r,scale:this.#o})}}if(typeof window!=='undefined')window.Cropt=Cropt;else if(typeof module!=='undefined'&&module.exports)module.exports=Cropt; else if(typeof define==='function'&&define.amd)define(()=>Cropt);
1
+ function t(){return{boundary:document.createElement("div"),viewport:document.createElement("div"),preview:document.createElement("img"),overlay:document.createElement("div"),controls:document.createElement("div"),resizeHandleRight:document.createElement("div"),resizeHandleBottom:document.createElement("div"),toolBar:document.createElement("div"),zoomer:document.createElement("input"),rotateLeft:document.createElement("button"),rotateRight:document.createElement("button")}}function e(t,e,i){return Math.round(Math.max(Math.min(t,e),i))}class Cropt{element;elements;options={mouseWheelZoom:"on",viewport:{width:0,height:0,borderRadius:"0px"},zoomerInputClass:"cr-slider",enableZoomSlider:!0,enableKeypress:!1,resizeBars:!1,enableRotateBtns:!1};#t=.85;#e=50;#i=50;#o=1;#n=0;#s=!1;#r="#fff";#a=new AbortController;#l=function(t){let e;return(...i)=>{window.clearTimeout(e),e=window.setTimeout(()=>t(...i),100)}}(()=>{this.#h()});constructor(e,i){if(e.classList.contains("cropt-container"))throw new Error("Cropt appears initialized (element has 'cropt-container' class). Aborting.");i.viewport&&(i.viewport={...this.options.viewport,...i.viewport},(!i.viewport?.width||i.viewport.width<100)&&(i.viewport.width=Math.max(100,e.clientWidth-60)),(!i.viewport?.height||i.viewport.height<100)&&(i.viewport.height=Math.max(100,e.clientHeight-60))),this.options={...this.options,...i},this.options.transparencyColor&&(this.#r=this.options.transparencyColor),this.element=e,this.element.classList.add("cropt-container"),this.elements=t(),this.elements.toolBar.classList.add("cr-toolbar-wrap"),this.elements.boundary.classList.add("cr-boundary"),this.elements.viewport.classList.add("cr-viewport"),this.elements.overlay.classList.add("cr-overlay"),this.elements.controls.classList.add("cr-controls"),this.elements.viewport.setAttribute("tabindex","0"),this.#d(this.elements.preview),this.elements.boundary.appendChild(this.elements.preview),this.elements.boundary.appendChild(this.elements.viewport),this.elements.boundary.appendChild(this.elements.overlay),this.elements.boundary.appendChild(this.elements.controls),this.#m(),this.options.enableRotateBtns&&(this.elements.rotateLeft.type="button",this.elements.rotateLeft.innerHTML="↺",this.elements.rotateLeft.setAttribute("aria-label","rotate left"),this.elements.rotateLeft.classList.add("cr-rotate-btn","cr-rotate-left"),this.elements.rotateRight.type="button",this.elements.rotateRight.innerHTML="↻",this.elements.rotateRight.setAttribute("aria-label","rotate right"),this.elements.rotateRight.classList.add("cr-rotate-btn","cr-rotate-right"),this.elements.toolBar.appendChild(this.elements.rotateLeft),this.elements.toolBar.appendChild(this.elements.rotateRight)),this.elements.zoomer.type="range",this.elements.zoomer.step="0.0001",this.elements.zoomer.value="1",this.options.enableZoomSlider&&(this.elements.zoomer.className=this.options.zoomerInputClass,this.elements.zoomer.setAttribute("aria-label","zoom"),this.elements.toolBar.appendChild(this.elements.zoomer)),this.element.appendChild(this.elements.boundary),this.element.appendChild(this.elements.toolBar),this.elements.toolBar.childNodes.length?this.element.style.setProperty("--cropt-toolbar","32px"):this.element.style.setProperty("--cropt-toolbar","0px"),this.#p(),this.#c(),this.#g(),this.#u()}bind(t,e){if(!t)throw new Error("src cannot be empty");return function(t){return new Promise((e,i)=>{const o=new Image;o.crossOrigin="anonymous",o.onload=()=>{e(o)},o.onerror=i,o.src=t})}(t).then(async t=>{if(this.#w(t),"object"==typeof e&&e?.transform)e?.viewport&&this.setOptions({viewport:e.viewport}),setTimeout(async()=>{e.transform?.rotate&&await this.setRotation(e.transform.rotate);const t=e.transform?.scale||null;this.#v(t),this.#b(e.transform),this.#h()},0);else{const t=Number(e)||null;this.#f(t)}})}#y(){const t=t=>Math.round(Math.max(0,t/this.#o)),e=this.elements.preview.getBoundingClientRect(),i=this.elements.viewport.getBoundingClientRect(),o=this.elements.viewport.offsetWidth,n=this.elements.viewport.offsetHeight,s=(i.width-o)/2,r=(i.height-n)/2,a=i.left-e.left,l=i.top-e.top;return{left:t(a),top:t(l),right:t(a+o+s),bottom:t(l+n+r),width:t(o+s),height:t(n+r)}}get(){const t=this.#y();let e={x:t.left,y:t.top,width:t.width,height:t.height};const i=this.elements.preview.naturalWidth,o=this.elements.preview.naturalHeight;return 90===this.#n||270===this.#n?(e.width=t.height,e.height=t.width,90===this.#n?(e.x=t.top,e.y=i-t.left-t.width):(e.x=o-t.top-t.height,e.y=t.left)):180===this.#n&&(e.x=i-t.left-t.width,e.y=o-t.top-t.height),e.x=Math.max(0,e.x),e.y=Math.max(0,e.y),{crop:e,transform:this.#b(),viewport:{width:Math.round(this.options.viewport.width),height:Math.round(this.options.viewport.height),borderRadius:this.options.viewport.borderRadius}}}toCanvas(t=null,e=""){const i=this.#y(),o=t&&t<0;t&&o&&(t=-t);let n=i.width,s=i.height;if(t&&(!o||n>t||s>t)){const e=this.elements.viewport.getBoundingClientRect(),i=e.width/e.height;i>1?(n=t,s=t/i):(s=t,n=t*i)}return Promise.resolve(this.#C(i,n,s,e))}toBlob(t=null,e="image/webp",i=1){return"image/webp"===e&&i<1&&!document.createElement("canvas").toDataURL("image/webp").startsWith("data:image/webp")&&(e="image/jpeg"),new Promise((o,n)=>{this.toCanvas(t,e).then(t=>{t.toBlob(e=>{null===e?n("Canvas blob is null"):(e.width=t.width,e.height=t.height,o(e))},e,i)})})}refresh(){this.#f()}setOptions(t){const e=this.options.viewport.width,i=this.options.viewport.height,o=this.options.viewport;t.viewport&&(t.viewport={...o,...t.viewport}),this.options={...this.options,...t},this.#p(),o.width===e&&o.height===i||this.#v()}setZoom(t){const e=this.elements.zoomer,i=parseFloat(e.min),o=parseFloat(e.max);e.value=Math.max(i,Math.min(o,t)).toFixed(4),this.#x()}async setRotation(t){if(void 0===t)return;const e=(t%360+360)%360,i=e-this.#n;0!==i&&(this.#n=e,await this.#R(i),this.#f())}async#R(t){const e=this.elements.preview,i=await createImageBitmap(e),o=90===Math.abs(t%180),n=document.createElement("canvas");n.width=o?i.height:i.width,n.height=o?i.width:i.height;const s=n.getContext("2d");if(!s)throw new Error("Could not get canvas context");s.translate(n.width/2,n.height/2),s.rotate(t*Math.PI/180),s.drawImage(i,-i.width/2,-i.height/2),i.close();const r=await new Promise((t,e)=>{n.toBlob(i=>i?t(i):e(new Error("Failed to create blob")),"image/webp",1)});this.#s&&URL.revokeObjectURL(e.src),e.src=URL.createObjectURL(r),await e.decode(),this.#s=!0}destroy(){this.#a.abort(),this.#s&&URL.revokeObjectURL(this.elements.preview.src),this.element.removeChild(this.elements.boundary),this.element.classList.remove("cropt-container"),this.element.removeChild(this.elements.toolBar),this.elements=t()}#b(t){const e=this.elements.preview,i=()=>{const[t,i]=(e.style.transformOrigin||"0px 0px").split(" ");return{x:parseFloat(t)||0,y:parseFloat(i)||0}};if(void 0!==t){const{x:o=0,y:n=0,scale:s=1}=t;if(e.style.transform=`translate(${o}px, ${n}px) scale(${s})`,void 0!==t.origin){const i=t.origin.x??0,o=t.origin.y??0;e.style.transformOrigin=`${i}px ${o}px`}return{x:o,y:n,scale:s,rotate:this.#n,origin:i()}}const o=e.style.transform||"";let n=0,s=0,r=1;for(const t of["translate","scale"]){const e=new RegExp(`${t}s*\\(([^)]+)\\)`),i=o.match(e);if(i){const e=i[1].trim();if("translate"===t){const[t,i]=e.split(",").map(t=>t.trim());n=Math.round(parseFloat(t))||0,s=void 0!==i?Math.round(parseFloat(i))||0:n}else"scale"===t&&(r=parseFloat(e)||1)}}return{x:n,y:s,scale:r,rotate:this.#n,origin:i()}}#p(){if(!this.elements?.viewport)return;const t=this.elements.viewport;t.style.borderRadius=this.options.viewport?.borderRadius||"50%",t.style.width=(this.options.viewport?.width||100)+"px",t.style.height=(this.options.viewport?.height||100)+"px",this.#L()}#m(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e}=this.elements;t.classList.add("cr-resize-handle","cr-resize-handle-right");const i=document.createElement("div");i.classList.add("cr-resize-handle-grabber"),t.appendChild(i),e.classList.add("cr-resize-handle","cr-resize-handle-bottom");const o=document.createElement("div");o.classList.add("cr-resize-handle-grabber"),e.appendChild(o),this.elements.controls.appendChild(t),this.elements.controls.appendChild(e),this.#E(),setTimeout(()=>this.#L(),200)}#L(){if(!this.options.resizeBars)return;const{resizeHandleRight:t,resizeHandleBottom:e,viewport:i,boundary:o}=this.elements,n=this.options.viewport.width,s=this.options.viewport.height,r=i.getBoundingClientRect(),a=o.getBoundingClientRect(),l=r.left-a.left,h=r.top-a.top;t.style.left=l+n-22+"px",t.style.top=h+s/2-22+"px",e.style.left=l+n/2-22+"px",e.style.top=h+s-22+"px"}#E(){let t=0,e=0;const i=i=>{i.preventDefault();const o=i.pageX-t,n=Math.floor(.95*this.elements.boundary.clientWidth),s=Math.min(n,Math.max(this.#e,e+o));this.options.viewport.width=s,this.#p()},o=()=>{document.removeEventListener("pointermove",i),document.removeEventListener("pointerup",o)};this.elements.resizeHandleRight.addEventListener("pointerdown",n=>{0===n.button&&(n.preventDefault(),n.stopPropagation(),t=n.pageX,e=this.options.viewport.width,document.addEventListener("pointermove",i,{signal:this.#a.signal}),document.addEventListener("pointerup",o,{signal:this.#a.signal}))},{signal:this.#a.signal});let n=0,s=0;const r=t=>{t.preventDefault();const e=t.pageY-n,i=Math.floor(.95*this.elements.boundary.clientHeight),o=Math.min(i,Math.max(this.#i,s+e));this.options.viewport.height=o,this.#p()},a=()=>{document.removeEventListener("pointermove",r),document.removeEventListener("pointerup",a)};this.elements.resizeHandleBottom.addEventListener("pointerdown",t=>{0===t.button&&(t.preventDefault(),t.stopPropagation(),n=t.pageY,s=this.options.viewport.height,document.addEventListener("pointermove",r,{signal:this.#a.signal}),document.addEventListener("pointerup",a,{signal:this.#a.signal}))},{signal:this.#a.signal})}#B(t){const e=document.createElement("canvas"),i=e.getContext("2d");if(null===i)throw new Error("Canvas context cannot be null");e.width=t.width,e.height=t.height;const o=this.elements.preview;return i.drawImage(o,t.left,t.top,t.width,t.height,0,0,e.width,e.height),e}#C(t,e,i,o){const n=this.#B(t),s=n.getContext("2d"),r=document.createElement("canvas"),a=r.getContext("2d"),l=document.createElement("canvas"),h=l.getContext("2d");if(l.width=e,l.height=i,null===h||null===s||null===a)throw new Error("Canvas context cannot be null");let d={width:n.width,height:n.height};for(;d.width>2*l.width;){let t=d.width,e=d.height;r.width=t,r.height=e,a.clearRect(0,0,r.width,r.height),a.drawImage(n,0,0),d={width:Math.floor(t/2),height:Math.floor(e/2)},s.clearRect(0,0,t,e),s.drawImage(r,0,0,t,e,0,0,d.width,d.height)}return"image/jpeg"===o&&(h.fillStyle=this.#r,h.fillRect(0,0,l.width,l.height)),h.drawImage(n,0,0,d.width,d.height,0,0,l.width,l.height),n.width=n.height=0,r.width=r.height=0,l}#z(){const t=this.#o,e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.clientWidth/2,o=this.elements.boundary.clientHeight/2,n=this.elements.preview.getBoundingClientRect(),s=e.width/2,r=e.height/2,a=-1*(s/t-i),l=-1*(r/t-o),h=1/t*s,d=1/t*r;return{translate:{maxX:a,minX:a-(n.width*(1/t)-e.width*(1/t)),maxY:l,minY:l-(n.height*(1/t)-e.height*(1/t))},origin:{maxX:n.width*(1/t)-h,minX:h,maxY:n.height*(1/t)-d,minY:d}}}#M(t,i){const o=this.elements.preview.getBoundingClientRect(),n=this.elements.viewport.getBoundingClientRect(),s=this.#b();s.y+=e(n.top-o.top,i,n.bottom-o.bottom),s.x+=e(n.left-o.left,t,n.right-o.right),this.#P(s),this.#l()}#c(){let t=0,e=0,i=[],o=0,n=n=>{n.preventDefault();const s=i.findIndex(t=>t.pointerId===n.pointerId);if(-1!==s){if(i[s]=n,2===i.length){let t=i[0],e=i[1],n=Math.hypot(t.pageX-e.pageX,t.pageY-e.pageY);return 0===o&&(o=n/this.#o),void this.setZoom(n/o)}0===o&&(this.#M(n.pageX-t,n.pageY-e),t=n.pageX,e=n.pageY)}},s=t=>{const e=i.findIndex(e=>e.pointerId===t.pointerId);-1!==e&&i.splice(e,1),0===i.length&&(this.elements.overlay.removeEventListener("pointermove",n),this.elements.overlay.removeEventListener("pointerup",s),this.elements.overlay.removeEventListener("pointerout",s),this.#I(!1,this.elements.preview),o=0)};this.elements.overlay.addEventListener("pointerdown",o=>{o.button||(o.preventDefault(),i.push(o),this.elements.overlay.setPointerCapture(o.pointerId),i.length>1||(t=o.pageX,e=o.pageY,this.#I(!0,this.elements.preview),this.elements.overlay.addEventListener("pointermove",n,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerup",s,{signal:this.#a.signal}),this.elements.overlay.addEventListener("pointerout",s,{signal:this.#a.signal})))},{signal:this.#a.signal}),this.options.enableKeypress&&document.addEventListener("keydown",t=>{if(document.activeElement&&["INPUT","TEXTAREA","SELECT","BUTTON"].includes(document.activeElement.nodeName))return;const e=function(t){switch(t){case"ArrowLeft":return[2,0];case"ArrowUp":return[0,2];case"ArrowRight":return[-2,0];case"ArrowDown":return[0,-2];default:return null}}(t.key);if(null!==e)if(t.shiftKey&&e[1]){t.preventDefault();const i=parseFloat(this.elements.zoomer.value);this.setZoom(i+.005*e[1])}else t.preventDefault(),this.#M(e[0],e[1])},{signal:this.#a.signal})}#g(){this.options.enableZoomSlider&&this.elements.zoomer.addEventListener("input",()=>this.#x(),{signal:this.#a.signal}),"off"!==this.options.mouseWheelZoom&&this.elements.boundary.addEventListener("wheel",t=>{let e=0;("ctrl"!==this.options.mouseWheelZoom||t.ctrlKey)&&(t.deltaY&&(e=-1*t.deltaY/2e3),t.preventDefault(),this.setZoom(this.#o+e*this.#o))},{signal:this.#a.signal})}#x(){const t=this.#b();this.#o=parseFloat(this.elements.zoomer.value),t.scale=this.#o;const e=this.#z(),i=e.translate,o=e.origin;t.x>=i.maxX&&(t.origin.x=o.minX,t.x=i.maxX),t.x<=i.minX&&(t.origin.x=o.maxX,t.x=i.minX),t.y>=i.maxY&&(t.origin.y=o.minY,t.y=i.maxY),t.y<=i.minY&&(t.origin.y=o.maxY,t.y=i.minY),this.#b(t),this.#l()}#u(){this.options.enableRotateBtns&&(this.elements.rotateLeft.addEventListener("click",()=>this.setRotation(this.#n-90),{signal:this.#a.signal}),this.elements.rotateRight.addEventListener("click",()=>this.setRotation(this.#n+90),{signal:this.#a.signal}))}#w(t){this.#d(t),this.elements.preview.parentNode&&this.elements.preview.parentNode.replaceChild(t,this.elements.preview),this.elements.preview=t}#d(t){t.classList.add("cr-image"),t.style.background=this.#r,t.setAttribute("alt","preview"),this.#I(!1,t)}#I(t,e){e.setAttribute("aria-grabbed",t.toString()),this.elements.boundary.setAttribute("aria-dropeffect",t?"move":"none")}#H(){return null!==this.elements.preview.offsetParent}#h(){const t=this.elements.boundary.getBoundingClientRect(),e=this.elements.preview.getBoundingClientRect(),i=this.elements.overlay;i.style.width=e.width+"px",i.style.height=e.height+"px",i.style.top=e.top-t.top+"px",i.style.left=e.left-t.left+"px"}#f(t=null){if(!this.#H())return;const e={x:0,y:0,scale:1,origin:{x:0,y:0}};this.#b(e),this.#v(t),e.scale=this.#o,this.#b(e),this.#Z(),this.#h()}#P(t){const e=this.elements.viewport.getBoundingClientRect(),i=this.elements.preview.getBoundingClientRect(),{origin:o}=this.#b(),n=e.top-i.top+e.height/2,s=e.left-i.left+e.width/2,r={x:Math.round(s/this.#o),y:Math.round(n/this.#o)};t.x=Math.round(t.x-(r.x-(o.x??0))*(1-this.#o)),t.y=Math.round(t.y-(r.y-(o.y??0))*(1-this.#o)),this.#b({...t,origin:r})}#v(t=null){const e=this.elements.preview,i=this.elements.viewport.getBoundingClientRect(),o=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight);let n=this.#t;if(o>=n&&(n+=o),this.elements.zoomer.min=o.toFixed(3),this.elements.zoomer.max=n.toFixed(3),null===t){const i=this.elements.boundary.getBoundingClientRect();t=Math.max(i.width/e.naturalWidth,i.height/e.naturalHeight)}this.setZoom(t)}#Z(){const t=this.elements.preview.getBoundingClientRect(),e=this.elements.viewport.getBoundingClientRect(),i=this.elements.boundary.getBoundingClientRect(),o=e.left-i.left,n=e.top-i.top,s=o-(t.width-e.width)/2,r=n-(t.height-e.height)/2;this.#P({x:s,y:r,scale:this.#o})}}if(typeof window!=='undefined')window.Cropt=Cropt;else if(typeof module!=='undefined'&&module.exports)module.exports=Cropt; else if(typeof define==='function'&&define.amd)define(()=>Cropt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cropt2",
3
- "version": "2.0.18",
3
+ "version": "2.0.19",
4
4
  "description": "A lightweight but powerful JavaScript image cropper",
5
5
  "keywords": [
6
6
  "cropping",
@@ -50,8 +50,8 @@
50
50
  "typescript": "^5.9.3"
51
51
  },
52
52
  "scripts": {
53
- "build:js:esm": "tsc --declaration && tsc src/cropt.ts --module ESNext --target ES2022 --outDir dist/esm && terser dist/cropt.js -c drop_console=true,dead_code=true,unused=true,passes=3 --toplevel -m -o dist/cropt.esm.min.js",
54
- "build:js:classic": "tsc src/cropt.ts --target ES2022 --outDir dist/ && terser dist/cropt.js -c drop_console=true,dead_code=true,unused=true,passes=3 --toplevel -m -o dist/cropt.min.js && node package.mjs dist/cropt.min.js",
53
+ "build:js:esm": "tsc --declaration && tsc src/cropt.ts --module ESNext --moduleResolution bundler --target ES2022 --outDir dist/esm && terser dist/cropt.js -c drop_console=true,dead_code=true,unused=true,passes=3 --toplevel -m -o dist/cropt.esm.min.js",
54
+ "build:js:classic": "tsc src/cropt.ts --moduleResolution bundler --target ES2022 --outDir dist/ && terser dist/cropt.js -c drop_console=true,dead_code=true,unused=true,passes=3 --toplevel -m -o dist/cropt.min.js && node package.mjs dist/cropt.min.js",
55
55
  "build:css": "postcss src/cropt.css -u cssnano --no-map -o dist/cropt.min.css",
56
56
  "build": "npm run build:js:esm && npm run build:js:classic && npm run build:css",
57
57
  "format": "prettier . --write",
@@ -66,4 +66,4 @@
66
66
  },
67
67
  "homepage": "https://mindflowgo.github.io/cropt/",
68
68
  "packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
69
- }
69
+ }