(()=>{class e{constructor(){this.queue=window.jvbQueue,this.a11y=window.jvbA11y,this.error=window.jvbError,this.fieldStoreReady=!1,this.uploadStoreReady=!1,this.hasCheckedForUploads=!1;const{fields:e,uploads:t}=window.jvbStore.register("uploads",[{storeName:"fields",keyPath:"id",indexes:[{name:"fieldId",keyPath:"fieldId"},{name:"timestamp",keyPath:"timestamp"},{name:"content",keyPath:"content"},{name:"itemId",keyPath:"itemId"},{name:"status",keyPath:"status"}],TTL:6048e5,delayFetch:!0},{storeName:"uploads",keyPath:"id",storeBlobs:!0,indexes:[{name:"fieldId",keyPath:"fieldId"},{name:"status",keyPath:"status"},{name:"groupId",keyPath:"groupId"},{name:"attachmentId",keyPath:"attachmentId"}],delayFetch:!0}]);this.fieldStore=e,this.uploadStore=t,window.jvbUploadBlobs=this.uploadStore,this.fieldStore.subscribe(this.handleFieldStoreEvent.bind(this)),this.uploadStore.subscribe(this.handleUploadStoreEvent.bind(this)),this.uploadElements=new Map,this.fieldElements=new Map,this.groupElements=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.previewUrls=new Set,this.sortableInstances=new Map,this.initWorker(),this.subscribers=new Set,this.selectors={field:{field:"[data-upload-field]",input:'input[type="file"]',dropZone:".file-upload-container",preview:".item-grid.preview",progress:".image-progress"},groups:{container:".upload-group",grid:".item-grid.group",header:".group-header",selectAll:'[name="select-all-group"]',actions:".group-actions",count:".selection-controls .info"},items:{item:"[data-upload-id]",checkbox:'[name*="select-item"]',featured:'[name="featured"]',details:"details"}},this.statusMapping={received:"Image Received",local_processing:"Processing Image...",queued:"Waiting to upload...",uploading:"Uploading to Server",pending:"Successfully sent to server. In line for further processing.",processing:"Processing on server...",completed:"Upload complete!",failed:"Upload failed (will retry)",failed_permanent:"Upload failed permanently"},this.init()}async init(){this.initListeners(),this.queue.subscribe(((e,t)=>{if(!["uploads","uploads/meta","uploads/groups"].includes(t.endpoint))return;const o=t.data instanceof FormData?t.data.get("fieldId"):t.data?.fieldId;switch(e){case"cancel-operation":o&&this.handleOperationCancelled(o);break;case"operation-status":o&&this.updateFieldStatus(o,t.status);break;case"operation-complete":this.handleOperationComplete(t,o);break;case"operation-failed":case"operation-failed-permanent":this.handleOperationFailed(t,o)}})),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}initWorker(){this.worker={worker:null,timeout:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:1e4,batchSize:1,maxConcurrent:3,restartAfterTimeout:!0}}}scanFields(e,t){console.log(t,"autoUpload");e.querySelectorAll(this.selectors.field.field).forEach((e=>this.registerUploader(e,t)))}registerUploader(e,t){const o=this.determineFieldId(e),s=this.extractFieldConfig(e,t),a=this.buildFieldUI(e);console.log(s,"registering with config");const r={id:o,config:s,uploads:new Set,groups:[],state:"ready",timestamp:Date.now()};return this.fieldStore.save(r),this.fieldElements.set(o,{element:e,ui:a,config:s}),e.dataset.uploader=o,this.addFieldSelectionHandler(o),"single"!==s.type&&this.initSortable(o),o}extractFieldConfig(e,t){return{autoUpload:t,destination:e.dataset.destination||"meta",content:e.dataset.content||null,mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:e.dataset.itemId||0,maxFiles:parseInt(e.dataset.maxFiles)||999,subtype:e.dataset.subtype||"image"}}buildFieldUI(e){let t={field:e,input:e.querySelector(this.selectors.field.input),dropZone:e.querySelector(this.selectors.field.dropZone),preview:e.querySelector(this.selectors.field.preview),progress:{progress:e.querySelector(this.selectors.field.progress),bar:e.querySelector(".bar"),fill:e.querySelector(".fill"),details:e.querySelector(".details"),text:e.querySelector(".details .text"),count:e.querySelector(".details .count")}},o=e.querySelector(".group-display");return o&&(t.groups={display:o,container:e.querySelector(".item-grid.groups"),empty:e.querySelector(".empty-group"),groups:new Map}),t}initSortable(e){if(!window.Sortable)return;!Sortable._multiDragMounted&&Sortable.MultiDrag&&(Sortable.mount(new Sortable.MultiDrag),Sortable._multiDragMounted=!0);const t=this.fieldElements.get(e);if(!t)return;t.element.querySelectorAll(".item-grid.preview, .item-grid.group").forEach((t=>{const o=t.classList.contains("group")?t.closest(".upload-group")?.dataset.groupId:null;this.createSortableForGrid(t,e,o)}));const o=t.element.querySelector(".empty-group");o&&!o.sortableInstance&&(o.sortableInstance=new Sortable(o,{animation:150,draggable:".item",multiDrag:!0,selectedClass:"selected-for-drag",avoidImplicitDeselect:!0,group:{name:e,pull:!1,put:!0},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",onEnd:t=>this.handleDrop(t,e)}))}syncSortableSelection(e,t){this.sortableInstances.forEach(((o,s)=>{if(s.startsWith(e)){o.el.querySelectorAll(".item").forEach((e=>{const o=e.dataset.uploadId;t.has(o)?Sortable.utils.select(e):Sortable.utils.deselect(e)}))}}))}handleDrop(e,t){const o=e.to,s=e.from,a=e.items?.length>0?e.items:[e.item],r=a.map((e=>e.dataset.uploadId));switch(this.getDropTargetType(o)){case"empty-group":this.handleDropToEmptyGroup(a,r,t);break;case"preview":default:this.handleDropToPreview(a,r,t);break;case"group":this.handleDropToGroup(a,r,o,s,t)}this.updateSortableState(o),s!==o&&this.updateSortableState(s)}getDropTargetType(e){return e.classList.contains("empty-group")?"empty-group":e.classList.contains("preview")?"preview":e.classList.contains("group")?"group":"unknown"}handleDropToGroup(e,t,o,s,a){try{if(o===s)return void this.handleReorder({to:o,items:e});t.forEach((e=>{this.addToGroup(e,o,!1)})),this.schedulePersistance(a);const r=e.length>1?`Moved ${e.length} items to group`:"Moved item to group";this.a11y.announce(r);const i=this.selectionHandlers.get(a);i?.clearSelection()}catch(t){this.handleDropError(e,a,t)}}handleDropToPreview(e,t,o){try{t.forEach((e=>{this.removeFromGroup(e)})),this.schedulePersistance(o);const s=e.length>1?`Moved ${e.length} items to preview`:"Moved item to preview";this.a11y.announce(s);const a=this.selectionHandlers.get(o);a?.clearSelection()}catch(t){this.handleDropError(e,o,t)}}handleDropError(e,t,o,s="An error occurred"){console.error("Drop error:",o);const a=this.fieldElements.get(t);a?.ui?.preview&&e.forEach((e=>a.ui.preview.appendChild(e))),this.a11y.announce(`${s}. Items returned to preview.`)}handleDropToEmptyGroup(e,t,o){try{const s=this.createGroup(o);if(!s)return void this.handleDropError(e,o,new Error("Group creation failed"),"Failed to create group");e.forEach(((e,o)=>{s.grid.appendChild(e),this.addToGroup(t[o],s.grid,!1)})),this.schedulePersistance(o);const a=e.length>1?`Created group with ${e.length} items`:"Created group with item";this.a11y.announce(a);const r=this.selectionHandlers.get(o);r?.clearSelection()}catch(t){this.handleDropError(e,o,t)}}updateSortableState(e){const t=e?.sortableInstance;t&&t.option("disabled",!1)}refreshSortable(e){const t=this.fieldElements.get(e);if(!t)return;t.element.querySelectorAll(".item-grid.preview, .item-grid.group").forEach((e=>this.updateSortableState(e)))}handleReorder(e){const t=e.to,o=t.closest(".field, .upload");if(!o)return;let s=Array.from(t.querySelectorAll(".item:not(.sortable-ghost):not(.sortable-clone)")).map((e=>e.dataset.uploadId)).filter((e=>e)),a=o.querySelector('input[type="hidden"]');a&&s.length>0&&(a.value=s.join(","));const r=this.getFieldIdFromElement(t);if(r){const e=this.getFieldData(r);if(t.classList.contains("group")){const o=t.dataset.groupId,a=e?.groups?.find((e=>e.id===o));a&&(a.uploads=s)}this.schedulePersistance(r)}this.a11y.announce("Item reordered"),o.dispatchEvent(new CustomEvent("jvb-items-reordered",{detail:{from:e.from,to:e.to,oldIndex:e.oldIndex,newIndex:e.newIndex,items:s},bubbles:!0}))}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),this.dragEnterHandler=this.handleExternalDragEnter.bind(this),this.dragLeaveHandler=this.handleExternalDragLeave.bind(this),this.dragOverHandler=this.handleExternalDragOver.bind(this),this.dropHandler=this.handleExternalDrop.bind(this),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler)}handleExternalDragLeave(e){const t=e.target.closest(this.selectors.field.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleExternalDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.field.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleExternalDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.field.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleExternalDrop(e){const t=e.target.closest(this.selectors.field.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover");const o=Array.from(e.dataTransfer.files);if(0===o.length)return;const s=this.getFieldIdFromElement(t);s&&(this.processFiles(s,o),this.a11y.announce(`${o.length} file(s) dropped for upload`))}handleClick(e){if(e.target.matches(this.selectors.field.dropZone)||e.target.closest(this.selectors.field.dropZone)){const t=e.target.closest(this.selectors.field.dropZone);if(t&&!e.target.matches("input, button, a")){const e=t.querySelector(this.selectors.field.input);e?.click()}}const t=e.target.closest("[data-action]");t&&this.handleAction(t)}handleChange(e){const t=this.getFieldIdFromElement(e.target);if(e.target.matches(this.selectors.field.input)){const o=Array.from(e.target.files);o.length>0&&t&&this.processFiles(t,o)}if(t){const o=this.getFieldData(t);if(!o.config.autoUpload)return;"post_group"===o?.config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e)}}async processFiles(e,t){const o=this.getFieldData(e),s=this.fieldElements.get(e);if(!o||!s)return;s.ui.dropZone&&(s.ui.dropZone.hidden=!0),s.ui.groups?.display&&(s.ui.groups.display.hidden=!1);const a=t.length;let r=0;this.updateUploadProgress(e,0,a,"Processing files...");const i=Array.from(t).map((async t=>{try{const i=`upload_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,n={id:i,attachmentId:null,fieldId:e,status:"local_processing",groupId:null,meta:{originalName:t.name,size:t.size,type:t.type}};await this.uploadStore.save(n);const l=this.createPreviewUrl(t),d=t.type.startsWith("image/")?await this.processImage(t,o.config.subtype):t;this.showUploadProgress(i,!0),this.updateUploadItemProgress(i,50,"local_processing"),await this.saveBlobData(i,d||t);const c=this.getSubtypeFromMime(t.type),u=this.createUploadElement({id:i,preview:l,meta:n.meta,subtype:c},"post_group"===o.config.destination);s.ui.preview&&(s.ui.preview.appendChild(u),this.uploadElements.set(i,{element:u,preview:l,location:s.ui.preview}));const p=this.uploadStore.get(i);return p&&(p.status="processed",await this.uploadStore.save(p)),o.uploads.add(i),await this.saveFieldData(o),r++,this.updateUploadProgress(e,r,a,"Processing files..."),this.updateUploadItemProgress(i,100,"processed"),setTimeout((()=>this.showUploadProgress(i,!1)),1e3),i}catch(o){return console.error("Error processing file:",t.name,o),r++,this.updateUploadProgress(e,r,a,"Processing files..."),null}}));await Promise.all(i),this.updateFieldState(e),this.refreshSortable(e),o.config.autoUpload&&"post_group"!==o.config.destination&&(await this.queueUpload(e),this.maybeLockUploads(e))}async processImage(e,t){const o=this.worker.settings.timeout;return new Promise(((s,a)=>{let r,i=!1;r=setTimeout((()=>{i||(i=!0,this.worker.tasks.delete(t),this.worker.settings.restartAfterTimeout&&this.restartCompressionWorker(),a(new Error(`Processing timeout for ${e.name}`)))}),o),this.worker.tasks.set(t,{file:e,timeoutId:r}),this.handleProcess(e,t).then((e=>{i||(i=!0,clearTimeout(r),this.worker.tasks.delete(t),s(e))})).catch((e=>{i||(i=!0,clearTimeout(r),this.worker.tasks.delete(t),a(e))}))}))}async handleProcess(e,t){if(!e.type.startsWith("image/"))return e;const o=this.getMaxDimension();if(this.shouldUseWorker(e))try{if(this.worker.worker||this.initCompressionWorker(),this.worker.worker)return await this.processWithWorker(e,t,o,.85)}catch(e){console.warn("Worker processing failed, falling back to main thread:",e)}return await this.processOnMainThread(e,o,.85)}async processOnMainThread(e,t,o){return new Promise(((s,a)=>{const r=new Image,i=document.createElement("canvas"),n=i.getContext("2d");let l=null;const d=()=>{r.onload=null,r.onerror=null,l&&(URL.revokeObjectURL(l),l=null),i.width=1,i.height=1,n.clearRect(0,0,1,1)};r.onload=()=>{try{const{width:l,height:c}=this.calculateOptimalDimensions(r,t);i.width=l,i.height=c,n.imageSmoothingEnabled=!0,n.imageSmoothingQuality="high",n.drawImage(r,0,0,l,c);const u=this.getOptimalFormat(e),p=this.getOptimalQuality(e,o);i.toBlob((t=>{if(d(),t){const o=new File([t],this.getProcessedFileName(e,u),{type:u,lastModified:Date.now()});s(o)}else a(new Error("Canvas toBlob failed"))}),u,p)}catch(e){d(),a(new Error(`Canvas processing failed: ${e.message}`))}},r.onerror=()=>{d(),a(new Error(`Failed to load image: ${e.name}`))};try{l=this.createPreviewUrl(e),r.src=l}catch(e){d(),a(new Error(`Failed to create object URL: ${e.message}`))}}))}getOptimalFormat(e){return"image/gif"===e.type||"image/svg+xml"===e.type?e.type:this.supportsWebP()?"image/webp":"image/jpeg"}getOptimalQuality(e,t){return e.size<512e3?Math.max(t,.9):e.size<2097152?t:Math.min(t,.8)}getProcessedFileName(e,t){return e.name.replace(/\.[^/.]+$/,"")+({"image/webp":".webp","image/jpeg":".jpg","image/png":".png","image/gif":".gif"}[t]||".jpg")}getMaxDimension(){const e=window.screen.width,t=window.devicePixelRatio||1;return e*t>2560?2400:e*t>1920?1920:1200}shouldUseWorker(e){return this.worker.worker&&e.size>1048576&&"undefined"!=typeof OffscreenCanvas}async processWithWorker(e,t,o,s){return new Promise(((a,r)=>{if(!this.worker.worker)return void r(new Error("Worker not available"));const i=`${t}_${Date.now()}`,n=t=>{if(t.data.messageId===i)if(this.worker.worker.removeEventListener("message",n),this.worker.worker.removeEventListener("error",l),t.data.success){const o=new File([t.data.blob],this.getProcessedFileName(e,t.data.format||"image/webp"),{type:t.data.format||"image/webp",lastModified:Date.now()});a(o)}else r(new Error(t.data.error||"Worker processing failed"))},l=e=>{this.worker.worker.removeEventListener("message",n),this.worker.worker.removeEventListener("error",l),r(new Error(`Worker error: ${e.message}`))};this.worker.worker.addEventListener("message",n),this.worker.worker.addEventListener("error",l),this.worker.worker.postMessage({messageId:i,file:e,maxDimension:o,quality:s,outputFormat:this.getOptimalFormat(e)})}))}restartCompressionWorker(){this.worker.worker&&(this.worker.worker.terminate(),this.worker.worker=null),this.worker.tasks.clear(),this.worker.restart.count>=this.worker.restart.max?console.error("Max worker restarts reached, disabling worker"):(this.worker.restart.count++,this.initCompressionWorker())}initCompressionWorker(){if(!this.worker.worker&&"undefined"!=typeof Worker)try{const e=new Blob(["\n\t\t\t\tself.onmessage = async function(e) {\n\t\t\t\t\tconst { messageId, file, maxDimension, quality, outputFormat } = e.data;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst bitmap = await createImageBitmap(file);\n\t\t\t\t\t\tconst scale = Math.min(maxDimension / bitmap.width, maxDimension / bitmap.height, 1);\n\t\t\t\t\t\tconst width = Math.round(bitmap.width * scale);\n\t\t\t\t\t\tconst height = Math.round(bitmap.height * scale);\n\t\t\t\t\t\tconst canvas = new OffscreenCanvas(width, height);\n\t\t\t\t\t\tconst ctx = canvas.getContext('2d');\n\t\t\t\t\t\tctx.imageSmoothingEnabled = true;\n\t\t\t\t\t\tctx.imageSmoothingQuality = 'high';\n\t\t\t\t\t\tctx.drawImage(bitmap, 0, 0, width, height);\n\t\t\t\t\t\tbitmap.close();\n\t\t\t\t\t\tconst blob = await canvas.convertToBlob({ type: outputFormat, quality: quality });\n\t\t\t\t\t\tself.postMessage({ messageId, success: true, blob: blob, format: outputFormat });\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tself.postMessage({ messageId, success: false, error: error.message });\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t"],{type:"application/javascript"});this.worker.worker=new Worker(this.createPreviewUrl(e))}catch(e){console.warn("Failed to initialize compression worker:",e),this.worker.worker=null}}calculateOptimalDimensions(e,t){let{width:o,height:s}=e;if(o<=t&&s<=t)return{width:o,height:s};const a=Math.min(t/o,t/s);return{width:Math.round(o*a),height:Math.round(s*a)}}supportsWebP(){return 0===document.createElement("canvas").toDataURL("image/webp").indexOf("data:image/webp")}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls||(this.previewUrls=new Set),this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls?.delete(e))}async submitUploads(e){const t=this.getFieldData(e);this.fieldElements.get(e);if(!t?.uploads||0===t.uploads.size)return;let o=Array.from(t.uploads);if(0===o.length)return void this.error.log("No uploads to upload",{component:"UploadManager",action:"submitGroupedUploads",fieldId:e});const s=this.getFieldGroups(e);if(0===s.length)return void this.error.log("No groups created for post_group upload",{component:"UploadManager",action:"submitGroupedUploads",fieldId:e});const a=[],r=new FormData;let i=[];for(const e of s){const t={images:[],fields:{}};for(let[o,s]of Object.entries(e.changes))t.fields[o]=s;const s=o.filter((t=>{const o=this.uploadStore.get(t);return o?.groupId===e.id}));for(const e of s){const o=await this.getBlobData(e);if(o){r.append("files[]",o);const s={upload_id:e,index:i.length},a=this.uploadElements.get(e),n=a?.element?.querySelector('[name="featured"]');n?.checked&&(t.fields.featured=e),t.images.push(s),i.push(e)}}a.push(t)}const n=o.filter((e=>{const t=this.uploadStore.get(e);return!t?.groupId}));for(const e of n){const t={images:[],fields:{}},o=await this.getBlobData(e);if(o){r.append("files[]",o);const s={upload_id:e,index:i.length};t.images.push(s),i.push(e)}a.push(t)}r.append("content",t.config.content),r.append("user",t.config.itemID),r.append("posts",JSON.stringify(a)),r.append("upload_ids",JSON.stringify(i));const l={endpoint:"uploads/groups",method:"POST",data:r,title:`Creating ${a.length} ${t.config.content}${a.length>1?"s":""} from uploads...`,popup:`Creating ${a.length} post${a.length>1?"s":""}...`,canMerge:!1,headers:{action_nonce:window.auth.getNonce("dash")},append:"_upload"};try{const e=await this.queue.addToQueue(l);return o.forEach((t=>{const o=this.uploadStore.get(t);o&&(o.operationId=e,o.status="queued",this.uploadStore.save(o),this.updateUploadStatus(t,"queued"))})),t.operationId=e,await this.saveFieldData(t),this.a11y.announce(`Creating ${a.length} post${a.length>1?"s":""} from your uploads`),e}catch(t){throw this.error.log(t,{component:"UploadManager",action:"submitGroupedUploads",fieldId:e}),t}}async queueUpload(e){const t=this.getFieldData(e);if(!t?.uploads||0===t.uploads.size)return;const o=Array.from(t.uploads),s=this.prepareUploadData(t,o);this.a11y.announce("Queuing for upload");const a={endpoint:"uploads",method:"POST",data:s,title:`Uploading ${o.length} file${o.length>1?"s":""} to server...`,popup:`Uploading ${o.length} file${o.length>1?"s":""}...`,canMerge:!1,headers:{action_nonce:window.auth.getNonce("dash")},append:"_upload"};try{const e=await this.queue.addToQueue(a);return o.forEach((t=>{const o=this.uploadStore.get(t);o&&(o.operationId=e,o.status="queued",this.uploadStore.save(o),this.updateUploadStatus(t,"queued"))})),t.operationId=e,await this.saveFieldData(t),e}catch(e){throw e}}async prepareUploadData(e,t){const o=new FormData;o.append("content",e.config.content),o.append("mode",e.config.mode),o.append("field_name",e.config.name),o.append("fieldId",e.id),o.append("field_type",e.config.type),o.append("subtype",e.config.subtype),o.append("item_id",e.config.itemID),o.append("destination",e.config.destination||"meta");let s=[];const a=t.map((async e=>{const t=this.uploadStore.get(e);if(!t)return;const a=await this.getBlobData(e);a&&(o.append("files[]",a),s.push(t.id))}));return await Promise.all(a),o.append("upload_ids",JSON.stringify(s)),o}async queueUploadMeta(e){const t=this.getUploadIdFromElement(e.target),o=this.uploadStore.get(t);if(!o)return;if(!this.getFieldData(o.fieldId))return;let s={};s[e.target.name]=e.target.value,o.meta={...o.meta,...s},await this.uploadStore.save(o);let a={};a[o.attachmentId??o.id]=o.meta;const r={endpoint:"uploads/meta",method:"POST",data:a,title:"Updating meta",canMerge:!0,headers:{action_nonce:window.auth.getNonce("dash")}};try{await this.queue.addToQueue(r)}catch(e){this.error.log(e,{component:"UploadManager",action:"sendMetaUpdate",uploadId:o.id})}}async handleOperationComplete(e,t){if((e.result?.data||e.serverData?.data||[]).forEach((e=>{const t=this.uploadStore.get(e.upload_id);t&&(t.attachmentId=e.attachment_id,t.status="completed",this.uploadStore.save(t),this.updateUploadStatus(e.upload_id,"completed"))})),!t)return;const o=this.getFieldData(t);if(!o)return;const s=Array.from(o.uploads).filter((e=>{const t=this.uploadStore.get(e);return"completed"===t?.status}));for(const e of s)await this.clearUpload(e,!1),o.uploads.delete(e);0===o.uploads.size?(await this.clearFieldFromStores(t),this.a11y.announce("All uploads completed successfully")):await this.saveFieldData(o),this.updateFieldState(t)}handleOperationFailed(e,t){(e.data instanceof FormData?JSON.parse(e.data.get("upload_ids")||"[]"):e.data.upload_ids||[]).forEach((t=>{const o=this.uploadStore.get(t);o&&(o.status="operation-failed-permanent"===e.status?"failed_permanent":"failed",this.uploadStore.save(o),this.updateUploadStatus(t,o.status))})),t&&this.updateFieldState(t)}async handleOperationCancelled(e){const t=this.getFieldData(e);if(!t)return;const o=t.uploads instanceof Set?Array.from(t.uploads):t.uploads;for(const e of o)await this.clearUpload(e,!1);await this.clearFieldFromStores(e),this.updateFieldState(e),this.a11y.announce("Upload cancelled")}getFieldGroups(e){const t=this.getFieldData(e);return t?.groups?t.groups.map((e=>({id:e.id,uploads:e.uploads||[],changes:e.changes||{}}))):[]}getSelectedRestorationUploads(e){let t=[];return e.querySelectorAll("[type=checkbox]:checked").forEach((e=>{const o=e.closest(".item");o&&t.push({uploadId:o.dataset.uploadId,fieldId:o.dataset.fieldId})})),t}async restoreSelectedUploads(e){const t=new Map;e.forEach((e=>{t.has(e.fieldId)||t.set(e.fieldId,[]),t.get(e.fieldId).push(e.uploadId)}));for(const[e,o]of t.entries()){const t=this.fieldStore.get(e);t&&(t.uploads=o,await this.restoreField(t))}}async restoreField(e){const{config:t,context:o,uploads:s,groups:a,id:r}=e;o?.modalType&&await this.openModalForRestore(o);let i=document.querySelector(`.field.upload[data-field="${t.name}"]`);if(!i){const e=`${t.content}_${t.itemID}_${t.name}`;i=document.querySelector(`.field.upload[data-uploader="${e}"]`)}if(!i)return void console.warn(`Field ${t.name} not found for restoration`,t);let n=i.dataset.uploader;n&&this.fieldElements.has(n)||(n=this.registerUploader(i));const l=this.fieldElements.get(n),d=this.getFieldData(n);if(!l||!d)return void console.error("Failed to register field for restoration");d.state=e.state||"ready",l.ui||(l.ui=this.buildFieldUI(i)),l.ui.groups?.display&&(l.ui.groups.display.hidden=!1),l.ui.dropZone&&(l.ui.dropZone.hidden=!0),a&&a.length>0&&await this.restoreGroups(n,a);const c=s instanceof Set?Array.from(s):Array.isArray(s)?s:[];for(const e of c){const t=this.uploadStore.get(e);t&&await this.restoreUpload(n,t)}await this.saveFieldData(d),this.updateFieldState(n),this.maybeLockUploads(n),this.refreshSortable(n),console.log(t),t.autoUpload&&"direct"===t.mode&&"post_group"!==t.destination&&await this.queueUpload(n)}async restoreUpload(e,t){const o=this.fieldElements.get(e),s=this.getFieldData(e);if(!o||!s)return void console.error("Field not found for upload restoration:",e);const a=await this.getBlobData(t.id);if(!a)return void console.warn("Blob data not found for upload:",t.id);const r=this.createPreviewUrl(a),i=this.getSubtypeFromMime(a.type),n=this.createUploadElement({id:t.id,preview:r,meta:t.meta||{originalName:a.name,size:a.size,type:a.type},subtype:i},"post_group"===s.config.destination);let l;if(t.groupId){const e=this.groupElements.get(t.groupId);if(e?.grid){l=e.grid;const o=s.groups?.find((e=>e.id===t.groupId));o&&(o.uploads||(o.uploads=[]),o.uploads.includes(t.id)||o.uploads.push(t.id))}else l=o.ui.preview,t.groupId=null}else l=o.ui.preview;l?l.appendChild(n):o.ui.preview&&(o.ui.preview.appendChild(n),l=o.ui.preview),this.uploadElements.set(t.id,{element:n,preview:r,location:l}),s.uploads||(s.uploads=new Set),s.uploads.add(t.id),t.status="processed",await this.uploadStore.save(t),l&&this.updateSortableState(l)}async restoreGroups(e,t){const o=this.fieldElements.get(e),s=this.getFieldData(e);if(o&&s){for(const o of t){const t=this.createGroup(e,o.id);if(!t){console.warn("Failed to create group:",o.id);continue}const a=s.groups?.find((e=>e.id===o.id));if(a&&(o.changes&&(a.changes={...o.changes}),o.uploads&&(a.uploads=[...o.uploads]),o.changes)){const e=t.element.querySelector('[name*="post_title"]'),s=t.element.querySelector('[name*="post_excerpt"]');e&&o.changes.post_title&&(e.value=o.changes.post_title),s&&o.changes.post_excerpt&&(s.value=o.changes.post_excerpt)}}await this.saveFieldData(s)}else console.error("Field not found for group restoration:",e)}async openModalForRestore(e){if(!e)return;const{modalType:t,itemId:o}=e;let s=null;switch(t){case"create":s=document.querySelector('[data-action="create"]');break;case"edit":o&&(s=document.querySelector(`[data-action="edit"][data-id="${o}"]`));break;case"bulkEdit":s=document.querySelector('[data-action="bulk-edit"]')}s?(s.click(),await new Promise((e=>setTimeout(e,300)))):console.warn("Modal trigger not found for restoration:",e)}formatBytes(e,t=2){if(0===e)return"0 Bytes";const o=t<0?0:t,s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(o))+" "+["Bytes","KB","MB","GB"][s]}async clearUpload(e,t=!0){const o=this.uploadElements.get(e);if(o&&(this.revokePreviewUrl(o.preview),o.element)){const e=o.element.dataset.previewUrl;this.revokePreviewUrl(e),delete o.element.dataset.previewUrl}if(this.uploadElements.delete(e),await this.uploadStore.delete(e),t){const t=this.uploadStore.get(e);t?.fieldId&&await this.schedulePersistance(t.fieldId)}}async clearFieldFromStores(e){const t=this.getFieldData(e);if(t?.uploads){const e=t.uploads instanceof Set?Array.from(t.uploads):t.uploads;for(const t of e)await this.uploadStore.delete(t)}await this.fieldStore.delete(e)}cleanupAllPreviewUrls(){this.previewUrls&&(this.previewUrls.forEach((e=>{try{URL.revokeObjectURL(e)}catch(e){}})),this.previewUrls.clear())}updateFieldState(e){const t=this.fieldElements.get(e),o=this.getFieldData(e);if(!t||!o)return;const s=t.element,a=o.uploads?.size||0,r=t.ui.groups?.container?.querySelectorAll(".upload-group").length>0;s.dataset.hasUploads=a>0?"true":"false",s.dataset.uploadCount=a.toString(),s.dataset.hasGroups=r?"true":"false",t.ui.preview&&t.ui.preview.setAttribute("aria-label",`Upload preview area with ${a} item${1!==a?"s":""}`)}updateUploadProgress(e,t,o,s){const a=this.fieldElements.get(e);if(!a?.ui?.progress?.progress)return;const r=a.ui.progress,i=o>0?t/o*100:0;r.fill&&(r.fill.style.width=`${i}%`),r.text&&(r.text.textContent=s),r.count&&(r.count.textContent=`${t}/${o}`),r.progress.hidden=t===o}updateFieldStatus(e,t){const o=this.getFieldData(e);o&&(o.state=t,this.saveFieldData(o))}updateUploadStatus(e,t){const o=this.uploadStore.get(e);o&&(o.status=t,this.uploadStore.save(o),this.updateUploadUI(e))}updateUploadUI(e){const t=this.uploadElements.get(e),o=this.uploadStore.get(e);if(!o||!t?.element)return;t.element.className=t.element.className.replace(/status-[\w-]+/g,""),t.element.classList.add(`status-${o.status}`);t.element.querySelector(".progress")&&this.updateUploadItemProgress(e,this.getStatusProgress(o.status),o.status)}showUploadProgress(e,t=!0){const o=this.uploadElements.get(e);if(!o?.element)return;const s=o.element.querySelector(".progress");s&&(t?(s.style.removeProperty("animation"),s.hidden=!1):(s.style.animation="fadeOut var(--transition-base)",setTimeout((()=>{s.hidden=!0}),300)))}updateUploadItemProgress(e,t,o=null){const s=this.uploadElements.get(e);if(!s?.element)return;const a=s.element.querySelector(".progress");if(!a)return;const r=a.querySelector(".fill"),i=a.querySelector(".details"),n=a.querySelector(".icon");r&&(r.style.width=`${t}%`),o&&i&&(i.textContent=this.getStatusText(o)),o&&n&&(n.innerHTML=this.getStatusIcon(o).outerHTML)}maybeLockUploads(e){const t=this.fieldElements.get(e),o=this.getFieldData(e);if(!t?.ui?.dropZone||!o)return;const s=o.uploads?.size||0,a="post_group"===o.config.destination?20:o.config?.maxFiles||999;t.ui.dropZone.hidden=s>=a,t.element.classList.toggle("at-max-uploads",s>=a),"post_group"===o.config.destination&&s>=a&&this.a11y.announce("Maximum of 20 uploads reached. Please submit current uploads before adding more.")}createSortableForGrid(e,t,o=null){if(!e||e.sortableInstance)return;const s=new Sortable(e,{animation:150,draggable:".item",multiDrag:!0,selectedClass:"selected-for-drag",avoidImplicitDeselect:!0,group:{name:t,pull:!0,put:!0},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",onEnd:e=>this.handleDrop(e,t),onSelect:e=>{const t=e.item.querySelector('[name*="select-item"]');t&&!t.checked&&(t.checked=!0,t.dispatchEvent(new Event("change",{bubbles:!0})))},onDeselect:e=>{const t=e.item.querySelector('[name*="select-item"]');t&&t.checked&&(t.checked=!1,t.dispatchEvent(new Event("change",{bubbles:!0})))},onAdd:e=>this.updateSortableState(e.to),onRemove:e=>this.updateSortableState(e.from)});e.sortableInstance=s;const a=o?`${t}-group-${o}`:`${t}-preview`;return this.sortableInstances.set(a,s),s}createGroup(e,t=null){const o=this.getFieldData(e),s=this.fieldElements.get(e);if(!o||!s)return null;t||(t=`group_${Date.now()}_${Math.random().toString(36).substr(2,9)}`);const a=this.createGroupElement(t,e);if(!a)return null;s.ui.groups||(s.ui.groups={groups:new Map,container:null,empty:null,display:null}),s.ui.groups.groups.set(t,a),s.ui.groups.container&&s.ui.groups.empty?s.ui.groups.container.insertBefore(a,s.ui.groups.empty):s.ui.groups.container&&s.ui.groups.container.appendChild(a);const r=a.querySelector(".item-grid.group");this.groupElements.set(t,{element:a,grid:r,fieldId:e}),o.groups||(o.groups=[]);return o.groups.find((e=>e.id===t))||(o.groups.push({id:t,uploads:[],changes:{}}),this.saveFieldData(o)),this.addGroupSelectionHandler(e,t),r&&this.createSortableForGrid(r,e,t),{id:t,element:a,grid:r}}createGroupElement(e,t){let o=window.getTemplate("imageGroup");if(!o)return;o.dataset.groupId=e,o.dataset.fieldId=t;let s=window.getTemplate("groupMetadata");const a=o.querySelector(".fields");if(a&&s){a.append(s);const r=a.querySelector('[name="post_title"]'),i=a.querySelector('[name="post_excerpt"]');r&&(r.id=`${e}_title`,r.name=`${e}[post_title]`),i&&(i.id=`${e}_excerpt`,i.name=`${e}[post_excerpt]`);const n=this.getFieldData(t);if(n&&""!==n.config.content){let e=o.querySelector("summary");e&&(e.textContent=n.config.content+" Fields")}}else o.querySelector("details")?.remove();const r=o.querySelector(".item-grid.group");return r&&(r.dataset.groupId=e),o}deleteGroup(e,t=!0){const o=this.groupElements.get(e);if(!o)return;const s=this.getFieldData(o.fieldId);if(!s)return;const a=s.groups?.find((t=>t.id===e));let r=!0;t&&a?.uploads?.length>0&&(r=!window.confirm("Delete uploads in group?")),t&&r&&a?.uploads&&a.uploads.forEach((e=>{this.removeFromGroup(e)})),s.groups&&(s.groups=s.groups.filter((t=>t.id!==e)),this.saveFieldData(s)),o.element&&(o.element.remove(),this.a11y.announce("Group removed")),this.groupElements.delete(e);const i=`${o.fieldId}-group-${e}`,n=this.sortableInstances.get(i);n?.destroy&&n.destroy(),this.sortableInstances.delete(i),this.schedulePersistance(o.fieldId)}addToGroup(e,t=null,o=!0){const s=this.uploadStore.get(e),a=this.uploadElements.get(e);if(!s||!a)return;const r=this.getFieldData(s.fieldId),i=this.fieldElements.get(s.fieldId);if(!r||!i)return;if(!t&&a.location===i.ui.preview||t===a.location)return;if(s.groupId){const t=r.groups?.find((e=>e.id===s.groupId));t&&(t.uploads=t.uploads.filter((t=>t!==e)),0===t.uploads.length&&this.deleteGroup(s.groupId))}const n=a.element.querySelector('[name*="select-item"]');n&&(n.checked=!1);let l=a.element.querySelector('[name="featured"]');if(l&&(l.hidden=!t),!t||t.classList.contains("preview"))t=i.ui.preview,s.groupId=null;else{const o=t.dataset.groupId;l&&(l.name=o+"_"+l.name);const a=r.groups?.find((e=>e.id===o));a&&(a.uploads||(a.uploads=[]),a.uploads.push(e),s.groupId=o)}a.location=t,t.append(a.element),this.uploadStore.save(s),o&&this.saveFieldData(r),this.updateSortableState(t),a.location&&a.location!==t&&this.updateSortableState(a.location)}removeFromGroup(e){const t=this.uploadStore.get(e),o=this.uploadElements.get(e);if(!t||!o)return;const s=this.getFieldData(t.fieldId),a=this.fieldElements.get(t.fieldId);if(!s||!a)return;if(t.groupId){const o=s.groups?.find((e=>e.id===t.groupId));o&&(o.uploads=o.uploads.filter((t=>t!==e)),0===o.uploads.length&&this.deleteGroup(t.groupId,!1)),t.groupId=null}a.ui?.preview&&(a.ui.preview.appendChild(o.element),o.location=a.ui.preview);const r=o.element.querySelector('[name="featured"]');r&&(r.hidden=!0,r.checked=!1),this.uploadStore.save(t),this.updateSortableState(a.ui.preview)}removeUpload(e,t){const o=this.getFieldData(e),s=this.uploadStore.get(t),a=this.uploadElements.get(t);if(!o||!s)return;if(o.uploads?.delete(t),s.groupId){const e=o.groups?.find((e=>e.id===s.groupId));e&&(e.uploads=e.uploads.filter((e=>e!==t)),0===e.uploads.length&&this.deleteGroup(s.groupId))}a?.element?.remove(),this.clearUpload(t),this.saveFieldData(o),this.updateFieldState(e),this.maybeLockUploads(e);const r=this.selectionHandlers.get(e);r&&r.deselect(t),this.a11y.announce("Upload removed")}handleGroupMetaChange(e){const t=this.getGroupFromElement(e);if(!t)return;const o=this.getFieldData(t.fieldId),s=o?.groups?.find((e=>e.id===t.element.dataset.groupId));if(!s)return;s.changes||(s.changes={});let a=e.name;a.includes("group")&&(a=a.replace(`${s.id}_`,"").replace(`${s.id}[`,"").replace("]","")),s.changes[a]=e.value,this.saveFieldData(o),this.schedulePersistance(t.fieldId)}handleAction(e){const t=e.dataset.action,o=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(e);break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e);break;case"upload":const t=this.fieldElements.get(o);t&&(t.element.closest("details").open=!1,document.body.classList.add("uploading"),this.submitUploads(o));break;case"restore":this.handleRestoreUploads().then((()=>{}));break;case"restore-all":this.handleRestoreAll().then((()=>{}));break;case"clear-cache":confirm("Save these uploads for later?")||this.cleanupStoredUploads(),this.cleanupRestore()}}handleAddToGroup(e){const t=e.closest(this.selectors.field.field),o=t?.dataset.uploader;if(!o)return;const s=this.selected.get(o);if(s&&0!==s.size){const e=this.createGroup(o);if(!e)return;s.forEach((t=>{this.addToGroup(t,e.grid)}));const t=this.selectionHandlers.get(o);t?.clearSelection(),this.a11y.announce(`Created group with ${s.size} items`)}else this.createGroup(o);this.schedulePersistance(o)}handleDeleteGroup(e){const t=e.closest(this.selectors.groups.container);if(!t)return;const o=t.dataset.groupId,s=this.getFieldIdFromElement(t);if(!confirm("Delete this group? Items will be moved back to the upload area."))return;t.querySelectorAll(this.selectors.items.item).forEach((e=>{const t=e.dataset.uploadId;this.removeFromGroup(t)})),this.deleteGroup(o),this.a11y.announce("Group deleted, items returned to upload area"),this.schedulePersistance(s)}handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const o=t.dataset.uploadId,s=this.getFieldIdFromElement(t);confirm("Remove this item?")&&(this.removeUpload(s,o),this.a11y.announce("Item removed"),this.schedulePersistance(s))}addFieldSelectionHandler(e){if(this.selectionHandlers.has(e))return this.selectionHandlers.get(e);const t=this.fieldElements.get(e);if(!t?.element)return;const o=new window.jvbHandleSelection({container:t.element,ui:{selectAll:t.element.querySelector('[name="select-all-uploads"]'),bulkControls:t.element.querySelector(".selection-actions"),count:t.element.querySelector(".selection-count")},itemSelector:"[data-upload-id]",checkboxSelector:'[name*="select-item"]'});return o.subscribe(((t,o)=>{switch(t){case"item-selected":case"item-deselected":case"range-selected":this.syncSortableSelection(e,o.selectedItems),this.selected.set(e,o.selectedItems);break;case"select-all":this.handleSelectAll(o.container,o.selected)}})),this.selectionHandlers.set(e,o),o}addGroupSelectionHandler(e,t){const o=`${e}_${t}`;if(this.selectionHandlers.has(o))return this.selectionHandlers.get(o);const s=this.groupElements.get(t);if(!s?.element)return;const a=new window.jvbHandleSelection({container:s.element,ui:{selectAll:s.element.querySelector(this.selectors.groups.selectAll),bulkControls:s.element.querySelector(this.selectors.groups.actions),count:s.element.querySelector(this.selectors.groups.count)},itemSelector:"[data-upload-id]",checkboxSelector:'[name*="select-item"]'});return a.subscribe(((t,o)=>{switch(t){case"item-selected":case"item-deselected":case"range-selected":this.selected.set(e,o.selectedItems);break;case"select-all":this.handleSelectAll(o.container,o.selected)}})),this.selectionHandlers.set(o,a),a}handleSelectAll(e,t){}getFieldData(e){const t=this.fieldStore.get(e);return t?(Array.isArray(t.uploads)?t.uploads=new Set(t.uploads):t.uploads||(t.uploads=new Set),Array.isArray(t.groups)||(t.groups=[]),t):null}async saveFieldData(e){await this.fieldStore.save({...e,timestamp:Date.now()})}determineFieldId(e){return`${e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||""}_${e.dataset.itemId||e.closest("dialog")?.dataset.itemId||""}_${e.dataset.field||""}`}getFromElement(e,t){const o={field:{selector:this.selectors.field.field,key:"uploader",getRuntimeData:e=>this.fieldElements.get(e),getStoreData:e=>this.getFieldData(e)},upload:{selector:this.selectors.items.item,key:"uploadId",getRuntimeData:e=>this.uploadElements.get(e),getStoreData:e=>this.uploadStore.get(e)},group:{selector:this.selectors.groups.container,key:"groupId",getRuntimeData:e=>this.groupElements.get(e),getStoreData:e=>{const t=this.groupElements.get(e);if(!t)return null;const o=this.getFieldData(t.fieldId);return o?.groups?.find((t=>t.id===e))}}},s=o[t];if(!s)return null;const a=e.closest(s.selector);if(!a)return null;const r=a.dataset[s.key];return{...s.getRuntimeData(r),...s.getStoreData(r)}}getFieldFromElement(e){return this.getFromElement(e,"field")}getUploadFromElement(e){return this.getFromElement(e,"upload")}getGroupFromElement(e){return this.getFromElement(e,"group")}getFieldIdFromElement(e){const t=this.getFromElement(e,"field");return t?.id??null}getUploadIdFromElement(e){const t=this.getFromElement(e,"upload");return t?.id??null}getGroupIdFromElement(e){const t=this.getFromElement(e,"group");return t?.id??null}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}getStatusText(e){return this.statusMapping[e]||e}getStatusIcon(e){return window.getIcon(this.queue.icons[e])}getStatusProgress(e){return{local_processing:28,queued:50,uploading:66,pending:75,processing:89,completed:100}[e]||0}createUploadElement(e,t=!1){let o=window.getTemplate("uploadItem");if(!o)return;o.dataset.uploadId=e.id,o.dataset.subtype=e.subtype||"image";let[s,a,r,i,n]=[o.querySelector('[name="featured"]'),o.querySelector("img"),o.querySelector("video"),o.querySelector("label > span"),o.querySelector("details")];switch(s&&(s.value=e.id),e.subtype){case"image":a&&(a.src=e.preview,a.alt=e.meta?.originalName||""),r?.remove(),i?.remove();break;case"video":r&&(r.src=e.preview),a?.remove(),i?.remove();break;case"document":const t=e.meta?.originalName||"",o=t.split(".").pop()?.toLowerCase()||"",s={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},n=window.getIcon(s[o]||"file");i&&(i.innerText=t,i.prepend(n)),a?.remove(),r?.remove()}if(n){let e=window.getTemplate("uploadMeta");e&&n.append(e)}return o.draggable=t,o.querySelectorAll("input").forEach((t=>{let o=t.id;if(o){let s=o+e.id,a=t.parentNode.querySelector(`label[for="${o}"]`);t.id=s,a&&(a.htmlFor=s)}})),o}schedulePersistance(e){const t=`persist_${e}`;window.debouncer.schedule(t,(()=>this.persistFieldState(e)),250)}async persistFieldState(e){const t=this.getFieldData(e);t&&await this.saveFieldData(t)}async saveBlobData(e,t){const o=await t.arrayBuffer(),s=this.uploadStore.get(e)||{id:e};s.blobData={buffer:o,name:t.name,type:t.type,size:t.size,lastModified:t.lastModified||Date.now()},await this.uploadStore.save(s)}async getBlobData(e){const t=this.uploadStore.get(e);if(!t?.blobData)return null;const o=new Blob([t.blobData.buffer],{type:t.blobData.type});return new File([o],t.blobData.name,{type:t.blobData.type,lastModified:t.blobData.lastModified})}async getFilesForForm(e){const t=e.querySelectorAll("[data-upload-field]"),o=[];for(const e of t){const t=this.determineFieldId(e),s=await this.getFilesForField(t);o.push(...s)}return o}async getFilesForField(e){const t=this.getFieldData(e);if(!t?.uploads)return[];const o=[],s=t.uploads instanceof Set?Array.from(t.uploads):t.uploads;for(const e of s){const s=this.uploadStore.get(e);if(!s)continue;const a=await this.getBlobData(e);a&&o.push({file:a,uploadId:e,fieldName:t.config.name,meta:s.meta||{}})}return o}handleFieldStoreEvent(e,t){if("data-loaded"===e)this.fieldStoreReady=!0,this.checkIfBothStoresReady()}handleUploadStoreEvent(e,t){switch(e){case"data-loaded":this.uploadStoreReady=!0,this.checkIfBothStoresReady();break;case"item-saved":this.showSaveIndicator(t.key)}}checkIfBothStoresReady(){this.fieldStoreReady&&this.uploadStoreReady&&!this.hasCheckedForUploads&&(this.hasCheckedForUploads=!0,this.checkForStoredUploads())}async checkForStoredUploads(){const e=this.fieldStore.getAll().filter((e=>{if(!e.uploads)return!1;return(e.uploads instanceof Set?Array.from(e.uploads):Array.isArray(e.uploads)?e.uploads:[]).some((e=>{const t=this.uploadStore.get(e);return t&&!t.operationId&&["completed","processed","local_processing","processed-original"].includes(t.status)}))}));0!==e.length&&await this.showRecoveryNotification(e)}async showRecoveryNotification(e){const t=e.reduce(((e,t)=>e+t.uploads.length),0),o=e.reduce(((e,t)=>e+(t.groups?.length||0)),0);let s,a=window.getTemplate("restoreNotification");if(!a)return void console.error("Restore notification template not found");if(o>0){s=`${o} ${o>1?"groups":"group"} with ${t} ${t>1?"uploads":"upload"} can be restored.`}else s=`${t} upload(s) from ${e.length} field(s) can be recovered.`;const r=a.querySelector(".restore-details");r&&(r.textContent=s);for(const t of e){let e=window.getTemplate("restoreField");if(!e)continue;const o=e.querySelector("h3");o&&(o.textContent=t.config.name||"Unnamed Field");const s=e.querySelector(".item-grid.restore");for(let e of t.uploads){const o=this.uploadStore.get(e);let a=window.getTemplate("uploadItem");if(!a)continue;const r=await this.getBlobData(o.id);if(r)try{const e=this.createPreviewUrl(r);let[s,i,n,l,d]=[a.querySelector('[name="featured"]'),a.querySelector("img"),a.querySelector("video"),a.querySelector("label > span"),a.querySelector("details")];a.dataset.uploadId=o.id,a.dataset.fieldId=t.id;let c=this.getSubtypeFromMime(r.type);switch(a.dataset.subtype=c,c){case"image":[i.src,i.alt]=[e,r.name??o.meta?.originalName??""],n.remove(),l.remove();break;case"video":n.src=e,i.remove(),l.remove();break;case"document":let t;switch(""){case"pdf":t=window.getIcon("file-pdf");break;case"csv":t=window.getIcon("file-csv");break;case"doc":t=window.getIcon("file-doc");break;case"txt":t=window.getIcon("file-txt");break;case"xls":t=window.getIcon("file-xls");break;default:t=window.getIcon("file")}l.innerText=o.originalFile.name,l.prepend(t),i.remove(),n.remove()}a.dataset.previewUrl=e}catch(e){console.warn("Failed to create preview for upload:",o.id,e)}const i=a.querySelector("summary span");i&&(i.textContent=o.meta?.originalName||"Unknown file");const n=a.querySelector("details");n&&o.meta&&(n.textContent=`${this.formatBytes(o.meta.size)} • ${o.meta.type}`),a.querySelectorAll("input").forEach((e=>{let t=e.id;if(t){let s=t+o.id,a=e.parentNode.querySelector(`label[for="${t}"]`);e.id=s,a&&(a.htmlFor=s)}})),s&&s.appendChild(a)}a.querySelector(".wrap").appendChild(s)}document.querySelector(".field.upload").appendChild(a),a=document.querySelector("dialog.restore-uploads"),this.restoreModal=new window.jvbModal(a),this.restoreSelection=new window.jvbHandleSelection({container:a,ui:{selectAll:a.querySelector("#select-all-restore"),count:a.querySelector(".selection-count")}}),this.restoreModal.handleOpen()}async handleRestoreUploads(){let e=document.querySelector("dialog.restore-uploads");if(!e)return;const t=this.getSelectedRestorationUploads(e);0!==t.length&&(await this.restoreSelectedUploads(t),this.cleanupRestore())}async handleRestoreAll(){let e=document.querySelector("dialog.restore-uploads");if(!e)return;const t=[];e.querySelectorAll(".item.upload").forEach((e=>{let o=e.dataset.uploadId,s=e.dataset.fieldId;t.push({uploadId:o,fieldId:s})})),await this.restoreSelectedUploads(t),this.cleanupRestore()}showSaveIndicator(e){}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}async cleanupStoredUploads(){await this.fieldStore.clear(),await this.uploadStore.clear()}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((o=>{try{o(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){document.removeEventListener("click",this.clickHandler),document.removeEventListener("change",this.changeHandler),document.removeEventListener("dragenter",this.dragEnterHandler),document.removeEventListener("dragleave",this.dragLeaveHandler),document.removeEventListener("dragover",this.dragOverHandler),document.removeEventListener("drop",this.dropHandler),this.dragController&&this.dragController.destroy(),this.selectionHandlers.forEach((e=>e.destroy())),this.selectionHandlers.clear(),this.cleanupAllPreviewUrls(),this.sortableInstances.forEach((e=>{e?.destroy&&e.destroy()})),this.sortableInstances.clear(),this.uploadElements.clear(),this.fieldElements.clear(),this.groupElements.clear(),this.selected.clear(),this.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbUploads=new e)}))}))})();
|