/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */
import $ from"jquery";import{html}from"lit";import{unsafeHTML}from"lit/directives/unsafe-html.js";import"jquery-ui/draggable.js";import"jquery-ui/resizable.js";import FormEngineValidation from"@typo3/backend/form-engine-validation.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Cropper from"cropperjs";import{default as Modal}from"@typo3/backend/modal.js";import"@typo3/backend/element/spinner-element.js";class ImageManipulation{constructor(){this.initialized=!1,this.cropImageContainerSelector="#t3js-crop-image-container",this.cropImageSelector="#t3js-crop-image",this.coverAreaSelector=".t3js-cropper-cover-area",this.cropInfoSelector=".t3js-cropper-info-crop",this.focusAreaSelector="#t3js-cropper-focus-area",this.defaultFocusArea={height:1/3,width:1/3,x:0,y:0},this.defaultOpts={autoCrop:!0,autoCropArea:.7,dragMode:"crop",guides:!0,responsive:!0,viewMode:1,zoomable:!1,checkCrossOrigin:!1},this.cropBuiltHandler=()=>{this.initialized=!0;const t=this.cropper.getImageData(),e=this.currentModal.querySelector(this.cropImageSelector);$(this.currentModal).find(".cropper-canvas img").removeClass("cropper-hide"),this.imageOriginalSizeFactor=parseInt(e.dataset.originalWidth,10)/t.naturalWidth,this.cropVariantTriggers.each(((e,r)=>{const a=$(r).attr("data-crop-variant-id"),i=this.convertRelativeToAbsoluteCropArea(this.data[a].cropArea,t),o=$.extend(!0,{},this.data[a],{cropArea:i});this.updatePreviewThumbnail(o,$(r))})),this.currentCropVariant.cropArea=this.convertRelativeToAbsoluteCropArea(this.currentCropVariant.cropArea,t),this.cropBox=$(this.currentModal).find(".cropper-crop-box"),this.setCropArea(this.currentCropVariant.cropArea),this.currentCropVariant.coverAreas&&this.initCoverAreas(this.cropBox,this.currentCropVariant.coverAreas),this.currentCropVariant.focusArea&&(ImageManipulation.isEmptyArea(this.currentCropVariant.focusArea)&&(this.currentCropVariant.focusArea=$.extend(!0,{},this.defaultFocusArea)),this.initFocusArea(this.cropBox),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)),this.currentCropVariant.selectedRatio&&$(this.currentModal).find(`[data-bs-option='${this.currentCropVariant.selectedRatio}']`).addClass("active")},this.cropMoveHandler=t=>{if(!this.initialized)return;this.currentCropVariant.cropArea=$.extend(!0,this.currentCropVariant.cropArea,{height:Math.floor(t.detail.height),width:Math.floor(t.detail.width),x:Math.floor(t.detail.x),y:Math.floor(t.detail.y)}),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.updateCropVariantData(this.currentCropVariant);const e=Math.round(this.currentCropVariant.cropArea.width*this.imageOriginalSizeFactor),r=Math.round(this.currentCropVariant.cropArea.height*this.imageOriginalSizeFactor);this.cropInfo.text(`${e}×${r} px`)},this.cropStartHandler=()=>{this.currentCropVariant.focusArea&&(this.focusArea.draggable("option","disabled",!0),this.focusArea.resizable("option","disabled",!0))},this.cropEndHandler=()=>{this.currentCropVariant.focusArea&&(this.focusArea.draggable("option","disabled",!1),this.focusArea.resizable("option","disabled",!1))}}static isEmptyArea(t){return $.isEmptyObject(t)}static wait(t,e){window.setTimeout(t,e)}static toCssPercent(t){return 100*t+"%"}static serializeCropVariants(t){return JSON.stringify(t,((t,e)=>"id"===t||"title"===t||"allowedAspectRatios"===t||"coverAreas"===t?void 0:e))}initializeTrigger(){$(".t3js-image-manipulation-trigger").off("click").on("click",(t=>{t.preventDefault(),this.trigger=$(t.currentTarget),this.show()}))}async initializeCropperModal(){const t=this.currentModal.querySelector(this.cropImageSelector);await new Promise((e=>{t.complete?e():t.addEventListener("load",(()=>e()))})),this.init()}show(){const t=this.trigger.data("modalTitle"),e=this.trigger.data("buttonPreviewText"),r=this.trigger.data("buttonDismissText"),a=this.trigger.data("buttonSaveText"),i=this.trigger.data("url"),o=this.trigger.data("payload");this.currentModal=Modal.advanced({additionalCssClasses:["modal-image-manipulation","cropper"],buttons:[{btnClass:"btn-default float-start",name:"preview",icon:"actions-view",text:e},{btnClass:"btn-default",name:"dismiss",icon:"actions-close",text:r},{btnClass:"btn-primary",name:"save",icon:"actions-document-save",text:a}],content:html`<div class="modal-loading"><typo3-backend-spinner size="default"></typo3-backend-spinner></div>`,size:Modal.sizes.full,style:Modal.styles.dark,title:t,staticBackdrop:!0}),this.currentModal.addEventListener("typo3-modal-shown",(()=>{new AjaxRequest(i).post(o).then((async t=>{const e=await t.resolve();this.currentModal.templateResultContent=html`${unsafeHTML(e)}`,this.currentModal.updateComplete.then((()=>this.initializeCropperModal()))}))})),this.currentModal.addEventListener("typo3-modal-hide",(()=>{this.destroy()}))}init(){const t=this.currentModal.querySelector(this.cropImageSelector),e=this.trigger.attr("data-crop-variants");if(!e)throw new TypeError("ImageManipulation: No cropVariants data found for image");this.data=$.isEmptyObject(this.data)?JSON.parse(e):this.data,this.cropVariantTriggers=$(this.currentModal).find(".t3js-crop-variant-trigger"),this.activeCropVariantTrigger=$(this.currentModal).find(".t3js-crop-variant-trigger.is-active"),this.cropInfo=$(this.currentModal).find(this.cropInfoSelector),this.saveButton=$(this.currentModal).find("button[name=save]"),this.previewButton=$(this.currentModal).find("button[name=preview]"),this.dismissButton=$(this.currentModal).find("button[name=dismiss]"),this.resetButton=$(this.currentModal).find("button[name=reset]"),this.aspectRatioTrigger=$(this.currentModal).find("label[data-method=setAspectRatio]"),this.currentCropVariant=this.data[this.activeCropVariantTrigger.attr("data-crop-variant-id")],this.cropVariantTriggers.off("click").on("click",(t=>{if($(t.currentTarget).hasClass("is-active"))return t.stopPropagation(),void t.preventDefault();this.activeCropVariantTrigger.removeClass("is-active"),$(t.currentTarget).addClass("is-active"),this.activeCropVariantTrigger=$(t.currentTarget);const e=this.data[this.activeCropVariantTrigger.attr("data-crop-variant-id")],r=this.cropper.getImageData();e.cropArea=this.convertRelativeToAbsoluteCropArea(e.cropArea,r),this.currentCropVariant=$.extend(!0,{},e),this.update(e)})),this.aspectRatioTrigger.off("click").on("click",(t=>{const e=$(t.currentTarget).attr("data-bs-option"),r=$.extend(!0,{},this.currentCropVariant),a=r.allowedAspectRatios[e];this.setAspectRatio(a),this.setCropArea(r.cropArea),this.currentCropVariant=$.extend(!0,{},r,{selectedRatio:e}),this.update(this.currentCropVariant)})),this.saveButton.off("click").on("click",(()=>{this.save(this.data)})),this.trigger.attr("data-preview-url")?this.previewButton.off("click").on("click",(()=>{this.openPreview(this.data)})):this.previewButton.hide(),this.dismissButton.off("click").on("click",(()=>{this.currentModal.hideModal()})),this.resetButton.off("click").on("click",(t=>{const e=this.cropper.getImageData(),r=$(t.currentTarget).attr("data-crop-variant");if(t.preventDefault(),t.stopPropagation(),!r)throw new TypeError("TYPO3 Cropper: No cropVariant data attribute found on reset element.");const a=JSON.parse(r),i=this.convertRelativeToAbsoluteCropArea(a.cropArea,e);this.currentCropVariant=$.extend(!0,{},a,{cropArea:i}),this.update(this.currentCropVariant)})),ImageManipulation.isEmptyArea(this.currentCropVariant.cropArea)&&(this.defaultOpts=$.extend({autoCropArea:1},this.defaultOpts)),this.cropper=new Cropper(t,$.extend(this.defaultOpts,{ready:this.cropBuiltHandler,crop:this.cropMoveHandler,cropend:this.cropEndHandler,cropstart:this.cropStartHandler,data:this.currentCropVariant.cropArea})),this.update(this.currentCropVariant)}update(t){const e=$.extend(!0,{},t),r=t.allowedAspectRatios[t.selectedRatio];$(this.currentModal).find("[data-bs-option]").removeClass("active"),$(this.currentModal).find(`[data-bs-option="${t.selectedRatio}"]`).addClass("active"),this.setAspectRatio(r),this.setCropArea(e.cropArea),this.currentCropVariant=$.extend(!0,{},e,t),this.cropBox.find(this.coverAreaSelector).remove(),this.cropBox.has(this.focusAreaSelector).length&&(this.focusArea.resizable("destroy").draggable("destroy"),this.focusArea.remove()),t.focusArea&&(ImageManipulation.isEmptyArea(t.focusArea)&&(this.currentCropVariant.focusArea=$.extend(!0,{},this.defaultFocusArea)),this.initFocusArea(this.cropBox),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)),t.coverAreas&&this.initCoverAreas(this.cropBox,this.currentCropVariant.coverAreas),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger)}initFocusArea(t){this.focusArea=$('<div id="t3js-cropper-focus-area" class="cropper-focus-area"></div>'),t.append(this.focusArea),this.focusArea.draggable({containment:t,create:()=>{const t="undefined"!=typeof window&&void 0!==window.document,e=!(!t||!window.document.documentElement)&&"ontouchstart"in window.document.documentElement,r=!!t&&"PointerEvent"in window,a=r?"pointerdown":e?"touchstart":"mousedown",i=r?"pointerup pointercancel":e?"touchend touchcancel":"mouseup";this.focusArea.on(a,(()=>{this.cropper.disable()})),this.focusArea.on(i,(()=>{this.cropper.enable()})),this.scaleAndMoveFocusArea(this.currentCropVariant.focusArea)},drag:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.checkFocusAndCoverAreasCollision(o,s)?this.focusArea.addClass("has-nodrop"):this.focusArea.removeClass("has-nodrop")},revert:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;return!!this.checkFocusAndCoverAreasCollision(o,s)&&(this.focusArea.removeClass("has-nodrop"),ImageManipulation.wait((()=>{o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updateCropVariantData(this.currentCropVariant)}),250),!0)},revertDuration:200,stop:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o}=this.currentCropVariant;o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.scaleAndMoveFocusArea(o)}}).resizable({containment:t,handles:"all",resize:()=>{const{left:e,top:r}=t.offset(),{left:a,top:i}=this.focusArea.offset(),{focusArea:o,coverAreas:s}=this.currentCropVariant;o.height=this.focusArea.height()/t.height(),o.width=this.focusArea.width()/t.width(),o.x=(a-e)/t.width(),o.y=(i-r)/t.height(),this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.checkFocusAndCoverAreasCollision(o,s)?this.focusArea.addClass("has-nodrop"):this.focusArea.removeClass("has-nodrop")},stop:(e,r)=>{const{left:a,top:i}=t.offset(),{left:o,top:s}=this.focusArea.offset(),{focusArea:n,coverAreas:c}=this.currentCropVariant;this.checkFocusAndCoverAreasCollision(n,c)?r.element.animate($.extend(r.originalPosition,r.originalSize),250,(()=>{n.height=this.focusArea.height()/t.height(),n.width=this.focusArea.width()/t.width(),n.x=(o-a)/t.width(),n.y=(s-i)/t.height(),this.scaleAndMoveFocusArea(n),this.focusArea.removeClass("has-nodrop")})):this.scaleAndMoveFocusArea(n)}})}initCoverAreas(t,e){e.forEach((e=>{const r=$('<div class="cropper-cover-area t3js-cropper-cover-area"></div>');t.append(r),r.css({height:ImageManipulation.toCssPercent(e.height),left:ImageManipulation.toCssPercent(e.x),top:ImageManipulation.toCssPercent(e.y),width:ImageManipulation.toCssPercent(e.width)})}))}updatePreviewThumbnail(t,e){let r;const a=e.find(".t3js-cropper-preview-thumbnail-crop-area"),i=e.find(".t3js-cropper-preview-thumbnail-crop-image"),o=e.find(".t3js-cropper-preview-thumbnail-focus-area"),s=this.cropper.getImageData();a.css({height:ImageManipulation.toCssPercent(t.cropArea.height/s.naturalHeight),left:ImageManipulation.toCssPercent(t.cropArea.x/s.naturalWidth),top:ImageManipulation.toCssPercent(t.cropArea.y/s.naturalHeight),width:ImageManipulation.toCssPercent(t.cropArea.width/s.naturalWidth)}),t.focusArea&&o.css({height:ImageManipulation.toCssPercent(t.focusArea.height),left:ImageManipulation.toCssPercent(t.focusArea.x),top:ImageManipulation.toCssPercent(t.focusArea.y),width:ImageManipulation.toCssPercent(t.focusArea.width)}),r=a.css(["width","height","left","top"]),i.css({height:parseFloat(r.height)*(1/(t.cropArea.height/s.naturalHeight))+"px",margin:-1*parseFloat(r.left)+"px",marginTop:-1*parseFloat(r.top)+"px",width:parseFloat(r.width)*(1/(t.cropArea.width/s.naturalWidth))+"px"})}scaleAndMoveFocusArea(t){this.focusArea.css({height:ImageManipulation.toCssPercent(t.height),left:ImageManipulation.toCssPercent(t.x),top:ImageManipulation.toCssPercent(t.y),width:ImageManipulation.toCssPercent(t.width)}),this.currentCropVariant.focusArea=t,this.updatePreviewThumbnail(this.currentCropVariant,this.activeCropVariantTrigger),this.updateCropVariantData(this.currentCropVariant)}updateCropVariantData(t){const e=this.cropper.getImageData(),r=this.convertAbsoluteToRelativeCropArea(t.cropArea,e);this.data[t.id]=$.extend(!0,{},t,{cropArea:r})}setAspectRatio(t){this.cropper.setAspectRatio(t.value)}setCropArea(t){const e=this.currentCropVariant.allowedAspectRatios[this.currentCropVariant.selectedRatio];0===e.value?this.cropper.setData({height:t.height,width:t.width,x:t.x,y:t.y}):this.cropper.setData({height:t.height,width:t.height*e.value,x:t.x,y:t.y})}checkFocusAndCoverAreasCollision(t,e){return!!e&&e.some((e=>t.x<e.x+e.width&&t.x+t.width>e.x&&t.y<e.y+e.height&&t.height+t.y>e.y))}convertAbsoluteToRelativeCropArea(t,e){const{height:r,width:a,x:i,y:o}=t;return{height:r/e.naturalHeight,width:a/e.naturalWidth,x:i/e.naturalWidth,y:o/e.naturalHeight}}convertRelativeToAbsoluteCropArea(t,e){const{height:r,width:a,x:i,y:o}=t;return{height:r*e.naturalHeight,width:a*e.naturalWidth,x:i*e.naturalWidth,y:o*e.naturalHeight}}setPreviewImages(t){const e=this.cropper.image,r=this.cropper.getImageData();Object.keys(t).forEach((a=>{const i=t[a],o=this.convertRelativeToAbsoluteCropArea(i.cropArea,r),s=this.trigger.closest(".form-group").find(`.t3js-image-manipulation-preview[data-crop-variant-id="${a}"]`),n=this.trigger.closest(".form-group").find(`.t3js-image-manipulation-selected-ratio[data-crop-variant-id="${a}"]`);if(0===s.length)return;let c=s.width(),h=s.data("preview-height");const p=o.width/o.height,d=c/p;d>h?c=h*p:h=d,c>o.width&&(c=o.width,h=o.height);const l=c/o.width,u=$("<div />").html('<img src="'+e.src+'">'),g=$(this.currentModal).find(`.t3-js-ratio-title[data-ratio-id="${i.id}${i.selectedRatio}"]`);n.text(g.text()),u.addClass("cropper-preview-container"),s.empty().append(u),u.wrap('<span class="thumbnail thumbnail-status"></span>'),u.width(c).height(h).find("img").css({height:r.naturalHeight*l,left:-o.x*l,top:-o.y*l,width:r.naturalWidth*l})}))}openPreview(t){const e=ImageManipulation.serializeCropVariants(t);let r=this.trigger.attr("data-preview-url");r=r+(r.includes("?")?"&":"?")+"cropVariants="+encodeURIComponent(e),window.open(r,"TYPO3ImageManipulationPreview")}save(t){const e=ImageManipulation.serializeCropVariants(t),r=$(`#${this.trigger.attr("data-field")}`);this.trigger.attr("data-crop-variants",JSON.stringify(t)),this.setPreviewImages(t),r.val(e),FormEngineValidation.markFieldAsChanged(r),this.currentModal.hideModal()}destroy(){this.currentModal&&(this.cropper instanceof Cropper&&this.cropper.destroy(),this.initialized=!1,this.cropper=null,this.currentModal=null,this.data=null)}}export default new ImageManipulation;