From 2fd9c0c5a204ee209a0d5a47fe0cefcb44d85aae Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 11 Feb 2026 01:48:23 +0000
Subject: [PATCH] =added helper to CRUD.js to ignore upload field changes if an upload is in progress
---
assets/js/min/uploader.min.js | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/assets/js/min/uploader.min.js b/assets/js/min/uploader.min.js
index c7dec67..5378e52 100644
--- a/assets/js/min/uploader.min.js
+++ b/assets/js/min/uploader.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.queue=window.jvbQueue,this.error=window.jvbError,this.templates=window.jvbTemplates,this.subscribers=new Set,this.initStores(),this.initWorker(),this.fields=new Map,this.uploads=new Map,this.groups=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.sortables=new Map,this.changes=new Map,this.previewUrls=new Set,this.initElements(),this.initListeners(),this.defineTemplates()}defineTemplates(){const e=this.templates,t=this;e.define("uploadItem",{refs:{select:'[name="select-item"]',featured:'[name="featured"]',img:"img",video:"video",file:"label > span",details:"details",alt:'[name="image-alt-text"]',title:'[name="image-title"]',description:'[name="image-caption"]'},manyRefs:{inputs:"input, select, textarea"},setup({el:e,refs:s,manyRefs:i,data:r}){let a,o,l,d=!1;switch(Object.hasOwn(r,"file")?(e.dataset.uploadId=r.uploadId,a=t.getSubtypeFromMime(r.file.type)||"image",o="document"!==a&&t.createPreviewUrl(r.file),d=o,l=r.file.name||""):(e.dataset.id=r.id,a=t.getSubtypeFromURL(r.medium??r.src),o=r.medium??r.src,l=r["image-alt-text"]??""),e.dataset.subtype=a,s.featured&&(s.featured.value=r.uploadId),a){case"image":s.img&&(s.img.src=o,s.img.alt=l,d&&(s.img.dataset.previewUrl=d)),s.video&&s.video.remove(),s.file&&s.file.remove();break;case"video":s.video&&(s.video.src=o,s.video.alt=l,d&&(s.video.dataset.previewUrl=d)),s.img&&s.img.remove(),s.file&&s.file.remove();break;case"document":if(s.preview){let e=r.file.name.split(".").pop()?.toLowerCase()??"",t={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},i=window.getIcon(t[e]??"file");s.preview.innerText=r.file.name??r.title,s.preview.prepend(i)}s.img&&s.img.remove(),s.video&&s.video.remove()}if(s.details&&(Object.hasOwn(r,"field")&&Object.hasOwn(r.field,"config")&&Object.hasOwn(r.field.config,"showMeta")&&!r.field.config.showMeta?s.details.remove():(Object.hasOwn(r,"id")?s.details.dataset.attachmentId=r.id:Object.hasOwn(r,"uploadId")&&(s.details.dataset.uploadId=r.uploadId),s.details.setAttribute("data-ignore",""),"image"!==a&&s.alt?s.alt.closest(".field")?.remove():Object.hasOwn(r,"image-alt-text")&&s.alt&&(s.alt.value=r["image-alt-text"]),(Object.hasOwn(r,"title")||Object.hasOwn(r,"file"))&&s.title&&(s.title.value=r.title||r.file.name),Object.hasOwn(r,"image-caption")&&s.description&&(s.description.value=r["image-caption"]))),e.draggable="single"!==e.dataset.mode,i.inputs)for(let t of i.inputs){let s=t.closest("[data-field]")??e;window.prefixInput(t,`${r.id??r.uploadId}-`,s)}}}),e.define("imageGroup",{refs:{selectAll:"[data-select-all]",fields:".fields",details:"details",grid:".item-grid"},setup({el:t,refs:s,manyRefs:i,data:r}){if(t.dataset.groupId=r.groupId,s.selectAll){let e=s.selectAll.closest(".field");window.prefixInput(s.selectAll,`select-all-${r.groupId}`,e,!0)}let a=e.create("groupMetadata",{groupId:r.groupId});a?s.fields.append(a):s.details.remove(),s.grid&&(s.grid.dataset.groupId=r.groupId)}}),e.define("groupMetadata",{manyRefs:{inputs:"input,textarea,select"},setup({el:e,refs:t,manyRefs:s,data:i}){t.inputs&&t.inputs.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${i.groupId}-`,t)}))}}),e.define("restoreNotification",{refs:{details:".details",wrap:".wrap"},setup({el:t,refs:s,manyRefs:i,data:r}){if(s.details){let e=r.bySource.size>1?` across ${r.bySource.size} pages`:"",t=r.pendingUploads.length>1?"uploads":"upload";s.details.textContent=`${r.pendingUploads.length} ${t} can be recovered${e}`}if(!s.wrap)return void console.warn("No wrap element in template");let a=1;for(const[t,i]of r.bySource){let r={index:a,isCurrent:t===window.location.href,src:t,uploads:i};s.wrap.append(e.create("restoreField",r)),a++}}}),e.define("restoreField",{refs:{h3:"h3",a:"h3 a",grid:".item-grid"},async setup({el:e,refs:s,manyRefs:i,data:r}){let a=t.registerField(e,!1,!1,`recovery_${r.index}`);r.isCurrent?(e.open=!0,s.a?.remove(),s.h3&&(s.h3.textContent="From this page:")):s.a&&(s.a.href=r.src,s.a.title="Navigate to page and restore",s.a.textContent=r.src);let o=[...new Set(r.uploads.map((e=>e.group??"preview")))];for(let e of o){let i="preview"===e||t.stores.groups.get(e);if(!i)continue;let o=await t.createGroupElement(e,a),l=o.querySelector(".item-grid"),d=r.uploads.filter((t=>t.group===("preview"===e)?null:e));for(const[e,t]of Object.entries(i.fields??{})){let s=o.querySelector(`input[name*="${e}"]`);s&&(s.value=t)}for(let e of d){let s=await t.createUpload(e.id,t.formatFile(e),a);l.append(s)}s.grid.append(o)}}})}initStores(){const{uploads:e,groups:t}=window.jvbStore.register("uploads",[{storeName:"uploads",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"status",keyPath:"status"},{name:"group",keyPath:"group"},{name:"src",keyPath:"src"}]},{storeName:"groups",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"src",keyPath:"src"}]}]);this.stores={uploads:e,groups:t,ready:[]},this.stores.uploads.subscribe(this.handleStores.bind(this,"uploads")),this.stores.groups.subscribe(this.handleStores.bind(this,"groups")),this.queue.subscribe(((e,t)=>{if(("operation-status"===e||"cancel-operation"===e)&&["image_upload","video_upload","document_upload"].includes(t.type)){let s=[];if(t.data)if(t.data instanceof FormData){const e=this.stores.uploads.formDataToObject(t.data);s=e.upload_ids||[]}else s=t.data.upload_ids||[];if(0===s.length&&t.result&&t.result.upload_ids&&(s=t.result.upload_ids),!s||0===s.length)return void console.warn("[UploadManager] No upload_ids found for operation:",{id:t.id,type:t.type,status:t.status,hasData:!!t.data,hasResult:!!t.result});if("cancel-operation"===e)return this.handleOperationCancelled(s);this.setBulkUpload(s,"status",t.status).then((()=>{console.log(`[UploadManager] Updated ${s.length} uploads to status: ${t.status}`)})),"completed"===t.status&&("process_upload_groups"===t.type?(s.forEach((e=>{this.setBulkUpload([e],"serverProcessed",!0).then((()=>{}))})),t.result&&t.result.created_posts&&console.log("[UploadManager] Created posts:",t.result.created_posts),setTimeout((()=>{s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))}),2e3)):s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))),"failed"!==t.status&&"failed_permanent"!==t.status||console.error("[UploadManager] Operation failed:",{id:t.id,type:t.type,uploadIds:s,error:t.error_message})}}))}storesReady(){return 2===this.stores.ready.length}handleStores(e,t){"data-ready"===t&&(this.stores.ready.push(e),this.storesReady()&&this.checkRecovery().then((()=>{})))}initWorker(){this.worker=null,this.workerState={worker:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:3e3,maxConcurrent:3,restartAfterTimeout:!0}}}initElements(){this.selectors={fields:{field:"[data-upload-field]",input:'input[type="file"]',dropZone:".file-upload-wrapper",preview:".preview-wrap",grid:".item-grid.preview",progress:{progress:".file-upload-container .progress",fill:".file-upload-container .progress .fill",details:".file-upload-container .progress .details",icon:".file-upload-container .progress .icon"},selectAll:"[data-select-all]",actions:".selection-actions",count:".selected .info",hidden:'input[type="hidden"]'},groups:{container:".group-display",grid:".item-grid.groups",empty:".empty-group",header:".sidebar .header"},group:{item:".upload-group",actions:".selection-actions",selectAll:'[name="select-all-group"]',count:".group-header .info",fields:"details .fields",grid:".item-grid.group",total:".group-content .group-count"},items:{item:".item.upload",checkbox:'[name="select-item"]',featured:'[name="featured"]',image:"img",details:"details",progress:{progress:".progress",fill:".fill",details:".details",icon:".icon"}}}}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.dragEnterHandler=this.handleDragEnter.bind(this),this.dragLeaveHandler=this.handleDragLeave.bind(this),this.dragOverHandler=this.handleDragOver.bind(this),this.dropHandler=this.handleDrop.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}async setUpload(e,t){const s={...{id:e,attachment:null,group:null,field:null,src:window.location.href,blob:null,status:"local_processing",operationId:null,fields:{}},...t};return Object.preventExtensions(s),await this.stores.uploads.save(s),s}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls.delete(e))}formatFile(e){return e.blob?new File([e.blob],e.fields.originalName||"file",{type:e.fields.type||e.blob.type,lastModified:e.fields.lastModified||Date.now()}):null}handleClick(e){let t=window.targetCheck(e,this.selectors.fields.dropZone);t&&!e.target.matches("input, button, a")&&t.querySelector(this.selectors.fields.input)?.click();const s=window.targetCheck(e,"[data-action]");s&&this.handleAction(s)}handleAction(e){const t=e.dataset.action,s=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(s).then((()=>{}));break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e).then((()=>{}));break;case"upload":this.queueUploads("uploads/groups",s).then((()=>{}));break;case"restore":this.handleRestoreSelected().then((()=>{}));break;case"restore-all":this.handleRestoreAll().then((()=>{}));break;case"clear-cache":this.handleClearCache().then((()=>{}))}}handleChange(e){let t=this.getFieldIdFromElement(e.target);if(t)if(e.target.matches(this.selectors.fields.input)){const s=Array.from(e.target.files);s.length>0&&this.processFiles(t,s).then((()=>{}))}else e.target.matches(this.selectors.items.checkbox)||e.target.matches(this.selectors.items.featured)||e.target.matches('[name*="select-"]')||("post_group"===this.fields.get(t).config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e));else{e.target.closest("[data-upload-id], [data-attachment-id]")&&this.queueUploadMeta(e)}}handleGroupMetaChange(e){const t=e.dataset.groupId;if(!t)return;const s=e.name;if(!s)return;const i=e.value,r=s.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");window.debouncer.schedule(`group-meta-${t}-${r}`,(async()=>{const e=this.stores.groups.get(t);e&&(e.fields||(e.fields={}),e.fields[r]=i,await this.setGroup(t,e))}),300)}handleDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.fields.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleDragLeave(e){const t=e.target.closest(this.selectors.fields.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.fields.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleDrop(e){const t=e.target.closest(this.selectors.fields.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover"),t.classList.add("uploading");const s=Array.from(e.dataTransfer.files);if(0===s.length)return;const i=this.getFieldIdFromElement(t);i&&(this.processFiles(i,s).then((()=>{this.updateHandlerItems(i)})),this.a11y.announce(`${s.length} file(s) dropped for upload`))}async queueUploads(e,t,s=null){let i=new FormData;const r=this.fields.get(t);if(!r)return;let a=this.stores.uploads.filterByIndex({field:t});if(0===a.length)return;const[o,l]=["uploads"===e,"uploads/groups"===e];let d,n,u,p,c;i.append("fieldId",r.id),i.append("content",r.config.content),o&&(i.append("mode",r.config.mode),i.append("field_name",r.config.name),i.append("fieldId",r.id),i.append("field_type",r.config.type),i.append("subtype",r.config.subtype),i.append("item_id",r.config.itemID),i.append("destination",r.config.destination),s&&i.append("depends_on",s)),l?({posts:d,uploadMap:n,files:u}=this.collectGroups(t)):o&&({uploadMap:n,files:u}=this.collectUploads(t)),l&&i.append("posts",JSON.stringify(d)),u.forEach((e=>{i.append("files[]",e)})),i.append("upload_ids",JSON.stringify(n)),o?(p=`Uploading ${a.length} file${a.length>1?"s":""} to server...`,c=`Uploading ${a.length} file${a.length>1?"s":""}...`):l&&(p=`Creating ${d.length} ${r.config.content}${d.length>1?"s":""} from uploads...`,c=`Creating ${d.length} post${d.length>1?"s":""}...`),await this.setBulkUpload(a,"status","queued");let h=this.sendToQueue(e,i,p,c);if("uploads/groups"===e){let e=r.element.closest("details");e&&(e.open=!1),this.notify("groups_uploaded",{fieldId:t,posts:d,content:r.config.content})}return h?(r.operationId=h,await this.setBulkUpload(a,"operationId",h),await this.setBulkUpload(a,"status","uploading"),await this.setBulkGroup(t,"operationId",h),this.fields.set(r.id,r),this.notify("sent-to-queue",{field:r,operation:h})):await this.setBulkUpload(a,"status","failed"),h}async sendToQueue(e,t,s="",i="",r=!1){""===i&&(i=s);const a={endpoint:e,method:"POST",data:t,title:s,popup:i,canMerge:r,sendNow:"uploads/groups"===e,headers:{"X-Action-Nonce":window.auth.getNonce("dash")},append:"_upload"};try{return await this.queue.addToQueue(a)}catch(e){return this.error.log(e,{component:"UploadManager",action:"sentToQueue"}),!1}}collectGroups(e){let t=this.stores.uploads.filterByIndex({field:e}),s=[],i=[],r=[];const a=this.stores.groups.filterByIndex({field:e}).filter((e=>{const t=this.getGroupUploadsInOrder(e);return t.length>0&&t.some((e=>this.formatFile(e)))}));for(const e of a){const t=this.groups.get(e.id)?.element,a=this.collectGroupFieldsFromDOM(t,e.id),o={groupId:e.id,images:[],fields:a},l=this.getGroupUploadsInOrder(e);for(const t of l){const s=this.formatFile(t);if(s){r.push(s);const a={upload_id:t.id,index:i.length},l=this.uploads.get(t.id),d=l?.element?.querySelector(`input[name="${e.id}_featured"]`);d?.checked&&(o.fields.featured=t.id),o.images.push(a),i.push(t.id)}}o.images.length>0&&s.push(o)}const o=t.filter((e=>!e.group));for(const e of o){const t={groupId:window.generateID("group"),images:[],fields:{}},a=this.formatFile(e);if(a){r.push(a);const s={upload_id:e.id,index:i.length};t.images.push(s),i.push(e.id)}t.images.length>0&&s.push(t)}return{posts:s,uploadMap:i,files:r}}getGroupUploadsInOrder(e){return e.uploads&&0!==e.uploads.length?e.uploads.map((e=>this.stores.uploads.get(e))).filter(Boolean):[]}collectGroupFieldsFromDOM(e,t){if(!e)return{};const s={};return e.querySelectorAll("input, textarea, select").forEach((e=>{const i=e.name.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");["featured","select-all"].some((e=>i.includes(e)))||e.value&&(s[i]=e.value)})),s}collectUploads(e){let t=this.stores.uploads.filterByIndex({field:e});if(0===t.length)return;let s=[],i=[];for(const e of t){const t=this.formatFile(e);t&&(i.push(t),s.push(e.id))}return{uploadMap:s,files:i}}queueUploadMeta(e){let t=e.target.closest("[data-attachment-id]")?.dataset.attachmentId,s=!1;if(!t&&(t=e.target.closest("[data-upload-id]")?.dataset.uploadId,s=!0,!t))return;if(!this.changes.has(t)){let e={};s?e.uploadId=t:e.attachmentId=t,this.changes.set(t,e)}let i=e.target.closest("[data-field]").dataset.field;this.changes.get(t)[i]=e.target.value,this.scheduleSave()}scheduleSave(){window.debouncer.schedule("upload-meta",(async()=>{if(this.changes.size>0){let e={};for(let[t,s]of this.changes.entries())console.log(t,s),e[t]=s;let t={user:window.auth.getUser(),items:e};await this.sendToQueue("uploads/meta",t,"Uploading Meta","Uploading Meta",!0),this.changes.clear()}}),2e3)}scanFields(e,t=!0,s=!0){e.querySelectorAll(this.selectors.fields.field).forEach((e=>this.registerField(e,t,s)))}registerField(e,t=!0,s=!0,i=null){const r={element:e,id:i||this.determineFieldId(e),config:this.extractFieldConfig(e,t,s),uploads:new Set,operationId:null,groups:[],ui:window.uiFromSelectors(this.selectors.fields,e),groupUI:window.uiFromSelectors(this.selectors.groups,e)};return this.fields.set(r.id,r),e.dataset.uploader=r.id,this.getSelectionHandler(r.id),"single"!==r.config.type&&this.initSortable(r.id),r.id}extractFieldConfig(e,t,s){return{autoUpload:t,showMeta:s,destination:e.dataset.destination||"meta",content:this.extractFieldContent(e),mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:this.extractFieldItemId(e)??0,maxFiles:parseInt(e.dataset.maxFiles)??25,subType:e.dataset.subtype??"image"}}extractFieldContent(e){return e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||null}extractFieldItemId(e){return e.dataset.itemId||e.closest("dialog")?.dataset.itemId||null}determineFieldId(e){let t=this.extractFieldContent(e);t=null===t?"":t+"_";let s=this.extractFieldItemId(e);s=null===s?"":s+"_";return`${t}${s}${e.dataset.field||""}`}getFieldIdFromElement(e){const t=e.closest(this.selectors.fields.field);return t?.dataset.uploader||null}updateFieldProgress(e,t,s,i){const r=this.fields.get(e);r&&window.showProgress(r.ui.progress,t,s,i)}getWorker(){return this.workerState.worker||"undefined"==typeof OffscreenCanvas||(this.workerState.worker=new Worker("worker.js"),this.workerState.worker.onmessage=e=>this.handleWorkerMessage(e),this.workerState.worker.onerror=e=>this.handleWorkerError(e)),this.workerState.worker}handleWorkerMessage(e){const{id:t,blob:s}=e.data,i=this.workerState.tasks.get(t);i&&(clearTimeout(i.timeoutId),i.resolve(s),this.workerState.tasks.delete(t))}handleWorkerError(e){this.workerState.tasks.forEach((t=>{clearTimeout(t.timeoutId),t.reject(e)})),this.workerState.tasks.clear(),this.restartWorker()}restartWorker(){this.workerState.worker&&(this.workerState.worker.terminate(),this.workerState.worker=null),this.workerState.restart.count++}async processImages(e,t=2200,s=2200){const i=[],r=[...e],a=this.workerState.settings.maxConcurrent,o=async()=>{for(;r.length>0;){const e=r.shift(),a=await this.processImage(e.file,t,s);i.push({uploadId:e.uploadId,blob:a})}};return await Promise.all(Array.from({length:Math.min(a,e.length)},(()=>o()))),i}async processImage(e,t=2200,s=2200,i=3e3){if("undefined"==typeof OffscreenCanvas)return this.resizeImage(e,t,s);try{return await this.withTimeout(this.workerImage(e,t,s),i)}catch(i){return this.resizeImage(e,t,s)}}withTimeout(e,t){return Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new Error("Timeout"))),t)))])}async workerImage(e,t=2200,s=2200){const{settings:i,restart:r}=this.workerState;if(r.count>=r.max)throw new Error("Worker max restarts exceeded");const a=await createImageBitmap(e);let{width:o,height:l}=a;if(o>t||l>s){const e=Math.min(t/o,s/l);o=Math.round(o*e),l=Math.round(l*e)}const d=this.getWorker(),n=crypto.randomUUID();return new Promise(((t,s)=>{const r=setTimeout((()=>{this.workerState.tasks.delete(n),i.restartAfterTimeout&&this.restartWorker(),s(new Error("Timeout"))}),i.timeout);this.workerState.tasks.set(n,{resolve:t,reject:s,timeoutId:r}),d.postMessage({id:n,imageBitmap:a,width:o,height:l,type:e.type,quality:.9},[a])}))}resizeImage(e,t,s){return new Promise((i=>{const r=new Image;r.onload=()=>{URL.revokeObjectURL(r.src);let{width:a,height:o}=r;if(a>t||o>s){const e=Math.min(t/a,s/o);a=Math.round(a*e),o=Math.round(o*e)}const l=document.createElement("canvas");l.width=a,l.height=o,l.getContext("2d").drawImage(r,0,0,a,o),l.toBlob(i,e.type,.9)},r.src=URL.createObjectURL(e)}))}async processFiles(e,t){let s=this.fields.get(e);if(!s)return;s.groupUI.container&&(s.groupUI.container.hidden=!1);const i=t.length;let r=0;this.updateFieldProgress(e,0,i,"Processing files...");const a=await Promise.all(t.map((async t=>{const s=window.generateID("upload"),i=await this.setUpload(s,{id:s,field:e,status:"local_processing",fields:{originalName:t.name,originalSize:t.size,type:t.type,lastModified:t.lastModified}}),r=await this.createUpload(s,t,e);return this.uploads.set(s,{element:r,ui:window.uiFromSelectors(this.selectors.items,r)}),await this.addToGroup(s,null),{uploadId:s,upload:i,file:t}}))),o=a.filter((e=>e.file.type.startsWith("image/"))),l=a.filter((e=>!e.file.type.startsWith("image/"))),d=await this.processImages(o.map((e=>({file:e.file,uploadId:e.uploadId}))));for(const{uploadId:t,blob:s}of d){const a=o.find((e=>e.uploadId===t));a&&(a.upload.blob=s,a.upload.fields.size=s.size,a.upload.status="queued",await this.setUpload(t,a.upload),r++,this.updateFieldProgress(e,r,i,"Processing files..."))}for(const{uploadId:t,upload:s,file:a}of l)s.blob=a,s.status="queued",await this.setUpload(t,s),r++,this.updateFieldProgress(e,r,i,"Processing files...");this.maybeLockUploads(e),s.config.autoUpload&&"post_group"!==s.config.destination&&await this.queueUploads("uploads",e)}async checkRecovery(){const e=this.stores.uploads.filterByIndex({status:["local_processing","queued","uploading"]}),t=Array.from(this.stores.groups.data.values());for(const e of t){this.stores.uploads.filterByIndex({group:e.id}).length>0||await this.stores.groups.delete(e.id)}if(0===e.length)return;const s=new Map;e.forEach((e=>{const t=e.src||"unknown";s.has(t)||s.set(t,[]),s.get(t).push(e)}));let i={bySource:s,pendingUploads:e};document.body.append(this.templates.create("restoreNotification",i));let r=document.querySelector("dialog.restore-uploads");this.restoreModal=new window.jvbModal(r),this.restoreSelection=new window.jvbHandleSelection(r,{wrapper:{wrapper:".restore-field",id:"selection"},items:".item-grid.restore",selectAll:{bulkControls:".selection-actions",checkbox:"#select-all-restore",count:".selection-count"}}),this.restoreModal.handleOpen()}async handleRestoreSelected(){if(!this.restoreSelection)return;let e=Array.from(this.restoreSelection.selectedItems);0!==e.length&&await this.restoreSelectedUploads(e)}async handleRestoreAll(){if(!this.restoreModal)return;const e=Array.from(this.restoreModal.modal.querySelectorAll(".item.upload")).map((e=>e.dataset.uploadId));await this.restoreSelectedUploads(e)}async restoreSelectedUploads(e){let t=window.location.href,s=Array.from(this.stores.uploads.data.values()).filter((s=>e.includes(s.id)&&s.src===t)),i=[...new Set(s.map((e=>e.group)))].filter(Boolean),r=s[0].field;if(!document.querySelector(`[data-uploader="${r}"]`))return void console.log("No field found for "+r);let a=this.fields.get(r);a.groupUI.container&&(a.groupUI.container.hidden=!1);let o=[];for(let e of i){let t=this.stores.groups.get(e);await this.createGroup(r,e);let i=this.groups.get(e),a=s.filter((t=>t.group===e));if(t&&this.groups.has(e)){let e=t.fields;for(const[t,s]of Object.entries(e)){let e=i.element.querySelector(`input[name*="${t}"]`);e&&(e.value=s)}}else e=null;for(let t of a){let s=await this.createUpload(t.id,this.formatFile(t),r);this.uploads.set(t.id,{element:s,ui:window.uiFromSelectors(this.selectors.items,s)}),await this.addToGroup(t.id,e),o.push(t.id)}}let l=s.filter((e=>!o.includes(e.id)));for(let e of l){let t=await this.createUpload(e.id,this.formatFile(e),r);this.uploads.set(e.id,{element:t,ui:window.uiFromSelectors(this.selectors.items,t)}),await this.addToGroup(e.id,null)}this.cleanupRestore()}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}getStatusText(e){return{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"}[e]||e}getStatusProgress(e){return{local_processing:28,queued:50,uploading:66,pending:75,processing:89,completed:100}[e]??0}async createUpload(e,t,s){let i=this.fields.get(s);if(!i)return null;let r={uploadId:e,file:t,field:i};return this.templates.create("uploadItem",r)}getSubtypeFromURL(e){const t=e.split("?")[0].toLowerCase();return[".webp",".jpg",".jpeg",".png",".gif",".svg"].some((e=>t.endsWith(e)))?"image":[".mp4",".ogg",".mov",".webm",".avi"].some((e=>t.endsWith(e)))?"video":"document"}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}async handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const s=t.dataset.uploadId,i=t.dataset.id;if((s||i)&&confirm("Remove this item?")){if(s)await this.removeUpload(s);else{const s=this.getFieldIdFromElement(e);t.remove(),s&&(this.updateHiddenInput(s),this.maybeLockUploads(s))}this.a11y.announce("Item removed")}}updateHiddenInput(e){const t=this.fields.get(e);if(!t?.ui.hidden)return;const s=Array.from(t.ui.grid?.querySelectorAll(this.selectors.items.item)||[]).map((e=>e.dataset.id||e.dataset.uploadId)).filter(Boolean);t.ui.hidden.value=s.join(","),t.ui.hidden.dispatchEvent(new Event("change",{bubbles:!0}))}async setBulkUpload(e,t,s){const i=Array.from(e).map((async e=>{if("string"==typeof e&&(e=await this.stores.uploads.get(e)),e)return"status"===t&&await this.setUploadStatus(e,s),e[t]=s,this.stores.uploads.save(e)}));await Promise.all(i)}async setUploadStatus(e,t){"string"==typeof e&&(e=await this.stores.uploads.get(e)),e&&e.progress&&window.showProgress(e.progress,this.getStatusProgress(t),100,this.getStatusText(t),this.queue.icons[t]??"")}async removeUpload(e){let t=this.stores.uploads.get(e);if(!t)return;const s=t.field;if(t.group){let s=this.stores.groups.get(t.group);s.uploads=s.uploads.filter((t=>t!==e)),0===s.uploads.length?await this.removeGroup(s.id,!1):await this.stores.groups.save(s)}await this.clearUpload(e),this.updateHiddenInput(s),this.maybeLockUploads(s);let i=this.selectionHandlers.get(s);i&&i.deselect(e),this.a11y.announce("Upload removed")}async clearUpload(e){const t=this.uploads.get(e);if(t&&(this.revokePreviewUrl(t.preview),t.element)){const e=t.element.dataset.previewUrl;this.revokePreviewUrl(e),t.element.remove()}this.uploads.delete(e),await this.stores.uploads.delete(e)}async handleAddToGroup(e){const t=this.selected.get(e);if(!t||0===t.size)return;let s=await this.createGroup(e);s&&(await Promise.all(Array.from(t).map((e=>this.addToGroup(e,s)))),this.selectionHandlers.get(e)?.clearSelection(),this.a11y.announce(`Created group with ${t.size} items`))}async createGroup(e,t=null){let s=this.fields.get(e);if(!s)return;t||(t=window.generateID("group"));const i=this.createGroupElement(t,e);if(!i)return null;const r=s.groupUI.empty;r?.nextSibling?s.groupUI.grid.insertBefore(i,r.nextSibling):s.groupUI.grid.append(i);const a=i.querySelector(".item-grid");a&&(a.dataset.groupId=t,this.createSortable(e,a,t));let o=this.stores.groups.data.has(t)?this.stores.groups.data.get(t):{};return await this.setGroup(t,{...o,id:t,field:e}),t}createGroupElement(e,t=null){let s={groupId:e,fieldId:t},i=this.templates.create("imageGroup",s);return this.groups.set(e,{element:i,ui:window.uiFromSelectors(this.selectors.group,i)}),this.getSelectionHandler(t)?.addWrapper(i),i}async setGroup(e,t){const s={...{id:e,src:window.location.href,uploads:[],operationId:null,field:null,fields:{}},...t};Object.preventExtensions(s),await this.stores.groups.save(s)}async setBulkGroup(e,t,s){let i=this.stores.groups.filterByIndex({field:e});if(0===i.length)return;let r=i.map((e=>{e[t]=s,this.stores.groups.save(e)}));await Promise.all(r)}async addToGroup(e,t=null){const s=this.stores.uploads.get(e),i=this.uploads.get(e);if(!s||!i)return;const r=this.fields.get(s.field);if(!r)return;if(null!==i.element?.parentElement&&(!t&&null===s.group||t===s.group))return void this.handleReorder(s.field,t);if(s.group){const t=this.stores.groups.get(s.group);t&&(t.uploads=t.uploads.filter((t=>t!==e)),0===t.uploads.length?await this.removeGroup(t.id,!1):await this.stores.groups.save(t))}i.ui.checkbox&&(i.ui.checkbox.checked=!1);const a=this.selectionHandlers.get(s.field);if(a&&a.isSelected(e)&&a.deselect(e),this.selected.get(s.field)?.has(e)&&this.selected.get(s.field).delete(e),i.ui.featured&&(i.ui.featured.hidden=!t),t){i.ui.featured&&(i.ui.featured.name=`${t}_featured`);let r=this.stores.groups.get(t);r&&(r.uploads.push(e),s.group=t,await this.stores.groups.save(r))}else s.group=null;let o=t?this.groups.get(t)?.ui.grid:r.ui.grid;o&&(o.append(i.element),t&&await this.handleReorder(s.field,t)),await this.stores.uploads.save(s)}handleDeleteGroup(e){const t=e.closest(this.selectors.group.item);if(!t)return;let s=t.dataset.groupId;if(!confirm("Delete this group? Items will be moved back to the upload area."))return;let i=this.stores.uploads.filterByIndex({group:s});Promise.all(i.map((e=>this.addToGroup(e.id,null)))).then((()=>{this.removeGroup(s,!1).then((()=>{})),this.a11y.announce("Group deleted. Items returned to upload area")}))}async removeGroup(e,t=!0){let s=this.groups.get(e),i=this.stores.groups.get(e);if(!i)return;let r=!0;t&&i.uploads.length>0&&(r=window.confirm("Keep uploads in this group?")),await Promise.all(i.uploads.map((e=>r?this.addToGroup(e,null):this.removeUpload(e))));if(this.fields.get(i.field)){const t=this.getGroupKey(i.field,e),r=this.selectionHandlers.get(t);r?.destroy&&r.destroy(),this.selectionHandlers.get(i.field)?.removeWrapper(s.element);const a=this.sortables.get(t);a?.destroy&&a.destroy(),this.sortables.delete(t)}s?.element&&s.element.remove(),this.groups.delete(e),await this.stores.groups.delete(e),this.a11y.announce("Group removed")}maybeLockUploads(e){let t=this.fields.get(e);if(!t||!t.ui.dropZone)return;let s=this.stores.uploads.filterByIndex({field:e}).length,i=t.config.maxFiles??25;t.ui.dropZone.hidden=s>=i}async handleOperationCancelled(e){0!==e.length&&e.forEach((e=>{this.removeUpload(e)}))}getGroupKey(e,t=null){return t?`${e}_${t}`:`${e}`}getSelectionHandler(e){let t=this.getGroupKey(e);if(!this.selectionHandlers.has(t)){let s=this.fields.get(e);if(!s)return;if("post_group"!==s.config.destination)return;let i=new window.jvbHandleSelection(s.element,{selectAll:{checkbox:this.selectors.fields.selectAll,count:this.selectors.fields.count,bulkControls:this.selectors.fields.actions},item:{item:this.selectors.items.item,checkbox:this.selectors.items.checkbox,idAttribute:"uploadId"},wrapper:{wrapper:".preview-wrap, .upload-group",id:"groupId"}});i.subscribe(((t,s)=>{this.selected.set(e,s.selectedItems)})),this.selectionHandlers.set(t,i)}return this.selectionHandlers.get(t)}updateHandlerItems(e){let t=this.getSelectionHandler(e);t&&t.collectItems()}initSortable(e){if(!window.Sortable)return;const t=this.fields.get(e);t&&(!Sortable._multiDragMounted&&Sortable.MultiDrag&&(Sortable.mount(new Sortable.MultiDrag),Sortable._multiDragMounted=!0),this.createSortable(e,t.ui.grid,null),this.initEmptyGroupDropZone(e))}createSortable(e,t,s){if(!t)return null;const i=this.getGroupKey(e,s);if(this.sortables.has(i))return this.sortables.get(i);const r=new Sortable(t,{animation:150,draggable:".item",multiDrag:!0,selectedClass:"selected",avoidImplicitDeselect:!0,group:{name:e,pull:!0,put:!0},dragClass:"dragging",ignore:".empty-group",onStart:t=>{const s=t.item,i=s?.dataset.uploadId,r=this.selected.get(e);if(i&&(!r||!r.has(i))){const t=this.selectionHandlers.get(e);t&&t.select(i)}},onEnd:t=>this.sortableDrop(t,e)});return this.sortables.set(i,r),r}initEmptyGroupDropZone(e){const t=this.fields.get(e),s=t?.groupUI?.empty;s&&(s.addEventListener("dragover",(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="move",s.classList.add("drag-over")})),s.addEventListener("dragleave",(e=>{s.contains(e.relatedTarget)||s.classList.remove("drag-over")})),s.addEventListener("drop",(async t=>{t.preventDefault(),t.stopPropagation(),s.classList.remove("drag-over");const i=this.selected.get(e);if(!i||0===i.size)return;const r=await this.createGroup(e);r&&(await Promise.all(Array.from(i).map((e=>this.addToGroup(e,r)))),this.selectionHandlers.get(e)?.clearSelection())})))}async sortableDrop(e,t){const s=e.to,i=(e.items?.length>0?Array.from(e.items):[e.item]).map((e=>e.dataset.uploadId)).filter(Boolean);if(0===i.length)return;const r=s.dataset.groupId||null;for(const e of i)await this.addToGroup(e,r);await this.handleReorder(t,r),this.selectionHandlers.get(t)?.clearSelection()}handleReorder(e,t=null){let s=t?this.groups.get(t)?.ui.grid:this.fields.get(e)?.ui.grid;if(s){if(t){let e=Array.from(s.children).filter((e=>e.matches(this.selectors.items.item)&&!e.classList.contains("ghost"))).map((e=>e.dataset.uploadId)).filter((e=>e)),i=this.stores.groups.get(t);i&&(i.uploads=e,this.stores.groups.save(i).then((()=>{})))}else this.updateHiddenInput(e);this.a11y.announce("Items reordered")}else console.log("Couldn't Reorder items...")}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((s=>{try{s(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){this.subscribers.clear(),this.previewUrls.forEach((e=>{this.revokePreviewUrl(e)})),this.previewUrls.clear()}cleanupAllPreviewUrls(){this.previewUrls.forEach((e=>this.revokePreviewUrl(e))),this.previewUrls.clear()}async handleClearCache(){const e=window.location.href,t=this.stores.uploads.filterByIndex({src:e}),s=this.stores.groups.filterByIndex({src:e});await Promise.all([...t.map((e=>this.clearUpload(e.id))),...s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id))))]),this.restoreModal&&this.cleanupRestore(),this.a11y.announce("Cache cleared for this page")}async getFilesForForm(e){const t=e.querySelectorAll(this.selectors.fields.field),s=[];for(const e of t){const t=this.determineFieldId(e),i=e.dataset.field,r=this.stores.uploads.filterByIndex({field:t});for(const e of r){const t=this.formatFile(e);t&&s.push({file:t,fieldName:i,uploadId:e.id,meta:e.fields||{}})}}return s}async clearFieldFromStores(e){const t=this.stores.uploads.filterByIndex({field:e}),s=this.stores.groups.filterByIndex({field:e});await Promise.all(t.map((e=>this.clearUpload(e.id)))),await Promise.all(s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id)))))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbUploads=new e)}))}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.queue=window.jvbQueue,this.error=window.jvbError,this.templates=window.jvbTemplates,this.subscribers=new Set,this.initStores(),this.initWorker(),this.fields=new Map,this.uploads=new Map,this.groups=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.sortables=new Map,this.changes=new Map,this.previewUrls=new Set,this.initElements(),this.initListeners(),this.defineTemplates()}defineTemplates(){const e=this.templates,t=this;e.define("uploadItem",{refs:{select:'[name="select-item"]',featured:'[name="featured"]',img:"img",video:"video",file:"label > span",details:"details",alt:'[name="image-alt-text"]',title:'[name="image-title"]',description:'[name="image-caption"]'},manyRefs:{inputs:"input, select, textarea"},setup({el:e,refs:s,manyRefs:i,data:r}){let a,o,l,d=!1;switch(Object.hasOwn(r,"file")?(e.dataset.uploadId=r.uploadId,a=t.getSubtypeFromMime(r.file.type)||"image",o="document"!==a&&t.createPreviewUrl(r.file),d=o,l=r.file.name||""):(e.dataset.id=r.id,a=t.getSubtypeFromURL(r.medium??r.src),o=r.medium??r.src,l=r["image-alt-text"]??""),e.dataset.subtype=a,s.featured&&(s.featured.value=r.uploadId),a){case"image":s.img&&(s.img.src=o,s.img.alt=l,d&&(s.img.dataset.previewUrl=d)),s.video&&s.video.remove(),s.file&&s.file.remove();break;case"video":s.video&&(s.video.src=o,s.video.alt=l,d&&(s.video.dataset.previewUrl=d)),s.img&&s.img.remove(),s.file&&s.file.remove();break;case"document":if(s.preview){let e=r.file.name.split(".").pop()?.toLowerCase()??"",t={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},i=window.getIcon(t[e]??"file");s.preview.innerText=r.file.name??r.title,s.preview.prepend(i)}s.img&&s.img.remove(),s.video&&s.video.remove()}if(s.details&&(Object.hasOwn(r,"field")&&Object.hasOwn(r.field,"config")&&Object.hasOwn(r.field.config,"showMeta")&&!r.field.config.showMeta?s.details.remove():(Object.hasOwn(r,"id")?s.details.dataset.attachmentId=r.id:Object.hasOwn(r,"uploadId")&&(s.details.dataset.uploadId=r.uploadId),s.details.setAttribute("data-ignore",""),"image"!==a&&s.alt?s.alt.closest(".field")?.remove():Object.hasOwn(r,"image-alt-text")&&s.alt&&(s.alt.value=r["image-alt-text"]),(Object.hasOwn(r,"title")||Object.hasOwn(r,"file"))&&s.title&&(s.title.value=r.title||r.file.name),Object.hasOwn(r,"image-caption")&&s.description&&(s.description.value=r["image-caption"]))),e.draggable="single"!==e.dataset.mode,i.inputs)for(let t of i.inputs){let s=t.closest("[data-field]")??e;window.prefixInput(t,`${r.id??r.uploadId}-`,s)}}}),e.define("imageGroup",{refs:{selectAll:"[data-select-all]",fields:".fields",details:"details",grid:".item-grid"},setup({el:t,refs:s,manyRefs:i,data:r}){if(t.dataset.groupId=r.groupId,s.selectAll){let e=s.selectAll.closest(".field");window.prefixInput(s.selectAll,`select-all-${r.groupId}`,e,!0)}let a=e.create("groupMetadata",{groupId:r.groupId});a?s.fields.append(a):s.details.remove(),s.grid&&(s.grid.dataset.groupId=r.groupId)}}),e.define("groupMetadata",{manyRefs:{inputs:"input,textarea,select"},setup({el:e,refs:t,manyRefs:s,data:i}){t.inputs&&t.inputs.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${i.groupId}-`,t)}))}}),e.define("restoreNotification",{refs:{details:".details",wrap:".wrap"},setup({el:t,refs:s,manyRefs:i,data:r}){if(s.details){let e=r.bySource.size>1?` across ${r.bySource.size} pages`:"",t=r.pendingUploads.length>1?"uploads":"upload";s.details.textContent=`${r.pendingUploads.length} ${t} can be recovered${e}`}if(!s.wrap)return void console.warn("No wrap element in template");let a=1;for(const[t,i]of r.bySource){let r={index:a,isCurrent:t===window.location.href,src:t,uploads:i};s.wrap.append(e.create("restoreField",r)),a++}}}),e.define("restoreField",{refs:{h3:"h3",a:"h3 a",grid:".item-grid"},async setup({el:e,refs:s,manyRefs:i,data:r}){let a=t.registerField(e,!1,!1,`recovery_${r.index}`);r.isCurrent?(e.open=!0,s.a?.remove(),s.h3&&(s.h3.textContent="From this page:")):s.a&&(s.a.href=r.src,s.a.title="Navigate to page and restore",s.a.textContent=r.src);let o=[...new Set(r.uploads.map((e=>e.group??"preview")))];for(let e of o){let i="preview"===e||t.stores.groups.get(e);if(!i)continue;let o=await t.createGroupElement(e,a),l=o.querySelector(".item-grid"),d=r.uploads.filter((t=>t.group===("preview"===e)?null:e));for(const[e,t]of Object.entries(i.fields??{})){let s=o.querySelector(`input[name*="${e}"]`);s&&(s.value=t)}for(let e of d){let s=await t.createUpload(e.id,t.formatFile(e),a);l.append(s)}s.grid.append(o)}}})}initStores(){const{uploads:e,groups:t}=window.jvbStore.register("uploads",[{storeName:"uploads",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"status",keyPath:"status"},{name:"group",keyPath:"group"},{name:"src",keyPath:"src"}]},{storeName:"groups",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"src",keyPath:"src"}]}]);this.stores={uploads:e,groups:t,ready:[]},this.stores.uploads.subscribe(this.handleStores.bind(this,"uploads")),this.stores.groups.subscribe(this.handleStores.bind(this,"groups")),this.queue.subscribe(((e,t)=>{if(("operation-status"===e||"cancel-operation"===e)&&["image_upload","video_upload","document_upload"].includes(t.type)){let s=[];if(console.log("upload operation complete",t),t.data)if(t.data instanceof FormData){const e=this.stores.uploads.formDataToObject(t.data);s=e.upload_ids||[]}else s=t.data.upload_ids||[];if(0===s.length&&t.result&&t.result.upload_ids&&(s=t.result.upload_ids),!s||0===s.length)return void console.warn("[UploadManager] No upload_ids found for operation:",{id:t.id,type:t.type,status:t.status,hasData:!!t.data,hasResult:!!t.result});if("cancel-operation"===e)return this.handleOperationCancelled(s);this.setBulkUpload(s,"status",t.status).then((()=>{console.log(`[UploadManager] Updated ${s.length} uploads to status: ${t.status}`)})),"completed"===t.status&&("process_upload_groups"===t.type?(s.forEach((e=>{this.setBulkUpload([e],"serverProcessed",!0).then((()=>{}))})),t.result&&t.result.created_posts&&console.log("[UploadManager] Created posts:",t.result.created_posts),setTimeout((()=>{s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))}),2e3)):s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))),"failed"!==t.status&&"failed_permanent"!==t.status||console.error("[UploadManager] Operation failed:",{id:t.id,type:t.type,uploadIds:s,error:t.error_message})}}))}storesReady(){return 2===this.stores.ready.length}handleStores(e,t){"data-ready"===t&&(this.stores.ready.push(e),this.storesReady()&&this.checkRecovery().then((()=>{})))}initWorker(){this.worker=null,this.workerState={worker:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:3e3,maxConcurrent:3,restartAfterTimeout:!0}}}initElements(){this.selectors={fields:{field:"[data-upload-field]",input:'input[type="file"]',dropZone:".file-upload-wrapper",preview:".preview-wrap",grid:".item-grid.preview",progress:{progress:".file-upload-container .progress",fill:".file-upload-container .progress .fill",details:".file-upload-container .progress .details",icon:".file-upload-container .progress .icon"},selectAll:"[data-select-all]",actions:".selection-actions",count:".selected .info",hidden:'input[type="hidden"]'},groups:{container:".group-display",grid:".item-grid.groups",empty:".empty-group",header:".sidebar .header"},group:{item:".upload-group",actions:".selection-actions",selectAll:'[name="select-all-group"]',count:".group-header .info",fields:"details .fields",grid:".item-grid.group",total:".group-content .group-count"},items:{item:".item.upload",checkbox:'[name="select-item"]',featured:'[name="featured"]',image:"img",details:"details",progress:{progress:".progress",fill:".fill",details:".details",icon:".icon"}}}}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.dragEnterHandler=this.handleDragEnter.bind(this),this.dragLeaveHandler=this.handleDragLeave.bind(this),this.dragOverHandler=this.handleDragOver.bind(this),this.dropHandler=this.handleDrop.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}async setUpload(e,t){const s={...{id:e,attachment:null,group:null,field:null,src:window.location.href,blob:null,status:"local_processing",operationId:null,fields:{}},...t};return Object.preventExtensions(s),await this.stores.uploads.save(s),s}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls.delete(e))}formatFile(e){return e.blob?new File([e.blob],e.fields.originalName||"file",{type:e.fields.type||e.blob.type,lastModified:e.fields.lastModified||Date.now()}):null}handleClick(e){let t=window.targetCheck(e,this.selectors.fields.dropZone);t&&!e.target.matches("input, button, a")&&t.querySelector(this.selectors.fields.input)?.click();const s=window.targetCheck(e,"[data-action]");s&&this.handleAction(s)}handleAction(e){const t=e.dataset.action,s=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(s).then((()=>{}));break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e).then((()=>{}));break;case"upload":this.queueUploads("uploads/groups",s).then((()=>{}));break;case"restore":this.handleRestoreSelected().then((()=>{}));break;case"restore-all":this.handleRestoreAll().then((()=>{}));break;case"clear-cache":this.handleClearCache().then((()=>{}))}}handleChange(e){let t=this.getFieldIdFromElement(e.target);if(t)if(e.target.matches(this.selectors.fields.input)){const s=Array.from(e.target.files);s.length>0&&this.processFiles(t,s).then((()=>{}))}else e.target.matches(this.selectors.items.checkbox)||e.target.matches(this.selectors.items.featured)||e.target.matches('[name*="select-"]')||("post_group"===this.fields.get(t).config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e));else{e.target.closest("[data-upload-id], [data-attachment-id]")&&this.queueUploadMeta(e)}}handleGroupMetaChange(e){const t=e.dataset.groupId;if(!t)return;const s=e.name;if(!s)return;const i=e.value,r=s.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");window.debouncer.schedule(`group-meta-${t}-${r}`,(async()=>{const e=this.stores.groups.get(t);e&&(e.fields||(e.fields={}),e.fields[r]=i,await this.setGroup(t,e))}),300)}handleDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.fields.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleDragLeave(e){const t=e.target.closest(this.selectors.fields.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.fields.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleDrop(e){const t=e.target.closest(this.selectors.fields.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover"),t.classList.add("uploading");const s=Array.from(e.dataTransfer.files);if(0===s.length)return;const i=this.getFieldIdFromElement(t);i&&(this.processFiles(i,s).then((()=>{this.updateHandlerItems(i)})),this.a11y.announce(`${s.length} file(s) dropped for upload`))}async queueUploads(e,t,s=null){let i=new FormData;const r=this.fields.get(t);if(!r)return;let a=this.stores.uploads.filterByIndex({field:t});if(0===a.length)return;const[o,l]=["uploads"===e,"uploads/groups"===e];let d,n,u,p,c;i.append("fieldId",r.id),i.append("content",r.config.content),o&&(i.append("mode",r.config.mode),i.append("field_name",r.config.name),i.append("fieldId",r.id),i.append("field_type",r.config.type),i.append("subtype",r.config.subtype),i.append("item_id",r.config.itemID),i.append("destination",r.config.destination),s&&i.append("depends_on",s)),l?({posts:d,uploadMap:n,files:u}=this.collectGroups(t)):o&&({uploadMap:n,files:u}=this.collectUploads(t)),l&&i.append("posts",JSON.stringify(d)),u.forEach((e=>{i.append("files[]",e)})),i.append("upload_ids",JSON.stringify(n)),o?(p=`Uploading ${a.length} file${a.length>1?"s":""} to server...`,c=`Uploading ${a.length} file${a.length>1?"s":""}...`):l&&(p=`Creating ${d.length} ${r.config.content}${d.length>1?"s":""} from uploads...`,c=`Creating ${d.length} post${d.length>1?"s":""}...`),await this.setBulkUpload(a,"status","queued");let h=this.sendToQueue(e,i,p,c);if("uploads/groups"===e){let e=r.element.closest("details");e&&(e.open=!1),this.notify("groups_uploaded",{fieldId:t,posts:d,content:r.config.content})}return h?(r.operationId=h,await this.setBulkUpload(a,"operationId",h),await this.setBulkUpload(a,"status","uploading"),await this.setBulkGroup(t,"operationId",h),this.fields.set(r.id,r),this.notify("sent-to-queue",{field:r,operation:h})):await this.setBulkUpload(a,"status","failed"),h}async sendToQueue(e,t,s="",i="",r=!1){""===i&&(i=s);const a={endpoint:e,method:"POST",data:t,title:s,popup:i,canMerge:r,sendNow:"uploads/groups"===e,headers:{"X-Action-Nonce":window.auth.getNonce("dash")},append:"_upload"};try{return await this.queue.addToQueue(a)}catch(e){return this.error.log(e,{component:"UploadManager",action:"sentToQueue"}),!1}}collectGroups(e){let t=this.stores.uploads.filterByIndex({field:e}),s=[],i=[],r=[];const a=this.stores.groups.filterByIndex({field:e}).filter((e=>{const t=this.getGroupUploadsInOrder(e);return t.length>0&&t.some((e=>this.formatFile(e)))}));for(const e of a){const t=this.groups.get(e.id)?.element,a=this.collectGroupFieldsFromDOM(t,e.id),o={groupId:e.id,images:[],fields:a},l=this.getGroupUploadsInOrder(e);for(const t of l){const s=this.formatFile(t);if(s){r.push(s);const a={upload_id:t.id,index:i.length},l=this.uploads.get(t.id),d=l?.element?.querySelector(`input[name="${e.id}_featured"]`);d?.checked&&(o.fields.featured=t.id),o.images.push(a),i.push(t.id)}}o.images.length>0&&s.push(o)}const o=t.filter((e=>!e.group));for(const e of o){const t={groupId:window.generateID("group"),images:[],fields:{}},a=this.formatFile(e);if(a){r.push(a);const s={upload_id:e.id,index:i.length};t.images.push(s),i.push(e.id)}t.images.length>0&&s.push(t)}return{posts:s,uploadMap:i,files:r}}getGroupUploadsInOrder(e){return e.uploads&&0!==e.uploads.length?e.uploads.map((e=>this.stores.uploads.get(e))).filter(Boolean):[]}collectGroupFieldsFromDOM(e,t){if(!e)return{};const s={};return e.querySelectorAll("input, textarea, select").forEach((e=>{const i=e.name.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");["featured","select-all"].some((e=>i.includes(e)))||e.value&&(s[i]=e.value)})),s}collectUploads(e){let t=this.stores.uploads.filterByIndex({field:e});if(0===t.length)return;let s=[],i=[];for(const e of t){const t=this.formatFile(e);t&&(i.push(t),s.push(e.id))}return{uploadMap:s,files:i}}queueUploadMeta(e){let t=e.target.closest("[data-attachment-id]")?.dataset.attachmentId,s=!1;if(!t&&(t=e.target.closest("[data-upload-id]")?.dataset.uploadId,s=!0,!t))return;if(!this.changes.has(t)){let e={};s?e.uploadId=t:e.attachmentId=t,this.changes.set(t,e)}let i=e.target.closest("[data-field]").dataset.field;this.changes.get(t)[i]=e.target.value,this.scheduleSave()}scheduleSave(){window.debouncer.schedule("upload-meta",(async()=>{if(this.changes.size>0){let e={};for(let[t,s]of this.changes.entries())console.log(t,s),e[t]=s;let t={user:window.auth.getUser(),items:e};await this.sendToQueue("uploads/meta",t,"Uploading Meta","Uploading Meta",!0),this.changes.clear()}}),2e3)}scanFields(e,t=!0,s=!0){e.querySelectorAll(this.selectors.fields.field).forEach((e=>this.registerField(e,t,s)))}registerField(e,t=!0,s=!0,i=null){const r={element:e,id:i||this.determineFieldId(e),config:this.extractFieldConfig(e,t,s),uploads:new Set,operationId:null,groups:[],ui:window.uiFromSelectors(this.selectors.fields,e),groupUI:window.uiFromSelectors(this.selectors.groups,e)};return this.fields.set(r.id,r),e.dataset.uploader=r.id,this.getSelectionHandler(r.id),"single"!==r.config.type&&this.initSortable(r.id),r.id}extractFieldConfig(e,t,s){return{autoUpload:t,showMeta:s,destination:e.dataset.destination||"meta",content:this.extractFieldContent(e),mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:this.extractFieldItemId(e)??0,maxFiles:parseInt(e.dataset.maxFiles)??25,subType:e.dataset.subtype??"image"}}extractFieldContent(e){return e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||null}extractFieldItemId(e){return e.dataset.itemId||e.closest("dialog")?.dataset.itemId||null}determineFieldId(e){let t=this.extractFieldContent(e);t=null===t?"":t+"_";let s=this.extractFieldItemId(e);s=null===s?"":s+"_";return`${t}${s}${e.dataset.field||""}`}getFieldIdFromElement(e){const t=e.closest(this.selectors.fields.field);return t?.dataset.uploader||null}updateFieldProgress(e,t,s,i){const r=this.fields.get(e);r&&window.showProgress(r.ui.progress,t,s,i)}getWorker(){return this.workerState.worker||"undefined"==typeof OffscreenCanvas||(this.workerState.worker=new Worker("worker.js"),this.workerState.worker.onmessage=e=>this.handleWorkerMessage(e),this.workerState.worker.onerror=e=>this.handleWorkerError(e)),this.workerState.worker}handleWorkerMessage(e){const{id:t,blob:s}=e.data,i=this.workerState.tasks.get(t);i&&(clearTimeout(i.timeoutId),i.resolve(s),this.workerState.tasks.delete(t))}handleWorkerError(e){this.workerState.tasks.forEach((t=>{clearTimeout(t.timeoutId),t.reject(e)})),this.workerState.tasks.clear(),this.restartWorker()}restartWorker(){this.workerState.worker&&(this.workerState.worker.terminate(),this.workerState.worker=null),this.workerState.restart.count++}async processImages(e,t=2200,s=2200){const i=[],r=[...e],a=this.workerState.settings.maxConcurrent,o=async()=>{for(;r.length>0;){const e=r.shift(),a=await this.processImage(e.file,t,s);i.push({uploadId:e.uploadId,blob:a})}};return await Promise.all(Array.from({length:Math.min(a,e.length)},(()=>o()))),i}async processImage(e,t=2200,s=2200,i=3e3){if("undefined"==typeof OffscreenCanvas)return this.resizeImage(e,t,s);try{return await this.withTimeout(this.workerImage(e,t,s),i)}catch(i){return this.resizeImage(e,t,s)}}withTimeout(e,t){return Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new Error("Timeout"))),t)))])}async workerImage(e,t=2200,s=2200){const{settings:i,restart:r}=this.workerState;if(r.count>=r.max)throw new Error("Worker max restarts exceeded");const a=await createImageBitmap(e);let{width:o,height:l}=a;if(o>t||l>s){const e=Math.min(t/o,s/l);o=Math.round(o*e),l=Math.round(l*e)}const d=this.getWorker(),n=crypto.randomUUID();return new Promise(((t,s)=>{const r=setTimeout((()=>{this.workerState.tasks.delete(n),i.restartAfterTimeout&&this.restartWorker(),s(new Error("Timeout"))}),i.timeout);this.workerState.tasks.set(n,{resolve:t,reject:s,timeoutId:r}),d.postMessage({id:n,imageBitmap:a,width:o,height:l,type:e.type,quality:.9},[a])}))}resizeImage(e,t,s){return new Promise((i=>{const r=new Image;r.onload=()=>{URL.revokeObjectURL(r.src);let{width:a,height:o}=r;if(a>t||o>s){const e=Math.min(t/a,s/o);a=Math.round(a*e),o=Math.round(o*e)}const l=document.createElement("canvas");l.width=a,l.height=o,l.getContext("2d").drawImage(r,0,0,a,o),l.toBlob(i,e.type,.9)},r.src=URL.createObjectURL(e)}))}async processFiles(e,t){let s=this.fields.get(e);if(!s)return;s.groupUI.container&&(s.groupUI.container.hidden=!1);const i=t.length;let r=0;this.updateFieldProgress(e,0,i,"Processing files...");const a=await Promise.all(t.map((async t=>{const s=window.generateID("upload"),i=await this.setUpload(s,{id:s,field:e,status:"local_processing",fields:{originalName:t.name,originalSize:t.size,type:t.type,lastModified:t.lastModified}}),r=await this.createUpload(s,t,e);return this.uploads.set(s,{element:r,ui:window.uiFromSelectors(this.selectors.items,r)}),await this.addToGroup(s,null),{uploadId:s,upload:i,file:t}}))),o=a.filter((e=>e.file.type.startsWith("image/"))),l=a.filter((e=>!e.file.type.startsWith("image/"))),d=await this.processImages(o.map((e=>({file:e.file,uploadId:e.uploadId}))));for(const{uploadId:t,blob:s}of d){const a=o.find((e=>e.uploadId===t));a&&(a.upload.blob=s,a.upload.fields.size=s.size,a.upload.status="queued",await this.setUpload(t,a.upload),r++,this.updateFieldProgress(e,r,i,"Processing files..."))}for(const{uploadId:t,upload:s,file:a}of l)s.blob=a,s.status="queued",await this.setUpload(t,s),r++,this.updateFieldProgress(e,r,i,"Processing files...");this.maybeLockUploads(e),s.config.autoUpload&&"post_group"!==s.config.destination&&await this.queueUploads("uploads",e)}async checkRecovery(){const e=this.stores.uploads.filterByIndex({status:["local_processing","queued","uploading"]}),t=Array.from(this.stores.groups.data.values());for(const e of t){this.stores.uploads.filterByIndex({group:e.id}).length>0||await this.stores.groups.delete(e.id)}if(0===e.length)return;const s=new Map;e.forEach((e=>{const t=e.src||"unknown";s.has(t)||s.set(t,[]),s.get(t).push(e)}));let i={bySource:s,pendingUploads:e};document.body.append(this.templates.create("restoreNotification",i));let r=document.querySelector("dialog.restore-uploads");this.restoreModal=new window.jvbModal(r),this.restoreSelection=new window.jvbHandleSelection(r,{wrapper:{wrapper:".restore-field",id:"selection"},items:".item-grid.restore",selectAll:{bulkControls:".selection-actions",checkbox:"#select-all-restore",count:".selection-count"}}),this.restoreModal.handleOpen()}async handleRestoreSelected(){if(!this.restoreSelection)return;let e=Array.from(this.restoreSelection.selectedItems);0!==e.length&&await this.restoreSelectedUploads(e)}async handleRestoreAll(){if(!this.restoreModal)return;const e=Array.from(this.restoreModal.modal.querySelectorAll(".item.upload")).map((e=>e.dataset.uploadId));await this.restoreSelectedUploads(e)}async restoreSelectedUploads(e){let t=window.location.href,s=Array.from(this.stores.uploads.data.values()).filter((s=>e.includes(s.id)&&s.src===t)),i=[...new Set(s.map((e=>e.group)))].filter(Boolean),r=s[0].field;if(!document.querySelector(`[data-uploader="${r}"]`))return void console.log("No field found for "+r);let a=this.fields.get(r);a.groupUI.container&&(a.groupUI.container.hidden=!1);let o=[];for(let e of i){let t=this.stores.groups.get(e);await this.createGroup(r,e);let i=this.groups.get(e),a=s.filter((t=>t.group===e));if(t&&this.groups.has(e)){let e=t.fields;for(const[t,s]of Object.entries(e)){let e=i.element.querySelector(`input[name*="${t}"]`);e&&(e.value=s)}}else e=null;for(let t of a){let s=await this.createUpload(t.id,this.formatFile(t),r);this.uploads.set(t.id,{element:s,ui:window.uiFromSelectors(this.selectors.items,s)}),await this.addToGroup(t.id,e),o.push(t.id)}}let l=s.filter((e=>!o.includes(e.id)));for(let e of l){let t=await this.createUpload(e.id,this.formatFile(e),r);this.uploads.set(e.id,{element:t,ui:window.uiFromSelectors(this.selectors.items,t)}),await this.addToGroup(e.id,null)}this.cleanupRestore()}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}getStatusText(e){return{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"}[e]||e}getStatusProgress(e){return{local_processing:28,queued:50,uploading:66,pending:75,processing:89,completed:100}[e]??0}async createUpload(e,t,s){let i=this.fields.get(s);if(!i)return null;let r={uploadId:e,file:t,field:i};return this.templates.create("uploadItem",r)}getSubtypeFromURL(e){const t=e.split("?")[0].toLowerCase();return[".webp",".jpg",".jpeg",".png",".gif",".svg"].some((e=>t.endsWith(e)))?"image":[".mp4",".ogg",".mov",".webm",".avi"].some((e=>t.endsWith(e)))?"video":"document"}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}async handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const s=t.dataset.uploadId,i=t.dataset.id;if((s||i)&&confirm("Remove this item?")){if(s)await this.removeUpload(s);else{const s=this.getFieldIdFromElement(e);t.remove(),s&&(this.updateHiddenInput(s),this.maybeLockUploads(s))}this.a11y.announce("Item removed")}}updateHiddenInput(e){const t=this.fields.get(e);if(!t?.ui.hidden)return;const s=Array.from(t.ui.grid?.querySelectorAll(this.selectors.items.item)||[]).map((e=>e.dataset.id||e.dataset.uploadId)).filter(Boolean);t.ui.hidden.value=s.join(","),t.ui.hidden.dispatchEvent(new Event("change",{bubbles:!0}))}async setBulkUpload(e,t,s){const i=Array.from(e).map((async e=>{if("string"==typeof e&&(e=await this.stores.uploads.get(e)),e)return"status"===t&&await this.setUploadStatus(e,s),e[t]=s,this.stores.uploads.save(e)}));await Promise.all(i)}async setUploadStatus(e,t){"string"==typeof e&&(e=await this.stores.uploads.get(e)),e&&e.progress&&window.showProgress(e.progress,this.getStatusProgress(t),100,this.getStatusText(t),this.queue.icons[t]??"")}async removeUpload(e){let t=this.stores.uploads.get(e);if(!t)return;const s=t.field;if(t.group){let s=this.stores.groups.get(t.group);s.uploads=s.uploads.filter((t=>t!==e)),0===s.uploads.length?await this.removeGroup(s.id,!1):await this.stores.groups.save(s)}await this.clearUpload(e),this.updateHiddenInput(s),this.maybeLockUploads(s);let i=this.selectionHandlers.get(s);i&&i.deselect(e),this.a11y.announce("Upload removed")}async clearUpload(e){const t=this.uploads.get(e);if(t&&(this.revokePreviewUrl(t.preview),t.element)){const e=t.element.dataset.previewUrl;this.revokePreviewUrl(e),t.element.remove()}this.uploads.delete(e),await this.stores.uploads.delete(e)}async handleAddToGroup(e){const t=this.selected.get(e);if(!t||0===t.size)return;let s=await this.createGroup(e);s&&(await Promise.all(Array.from(t).map((e=>this.addToGroup(e,s)))),this.selectionHandlers.get(e)?.clearSelection(),this.a11y.announce(`Created group with ${t.size} items`))}async createGroup(e,t=null){let s=this.fields.get(e);if(!s)return;t||(t=window.generateID("group"));const i=this.createGroupElement(t,e);if(!i)return null;const r=s.groupUI.empty;r?.nextSibling?s.groupUI.grid.insertBefore(i,r.nextSibling):s.groupUI.grid.append(i);const a=i.querySelector(".item-grid");a&&(a.dataset.groupId=t,this.createSortable(e,a,t));let o=this.stores.groups.data.has(t)?this.stores.groups.data.get(t):{};return await this.setGroup(t,{...o,id:t,field:e}),t}createGroupElement(e,t=null){let s={groupId:e,fieldId:t},i=this.templates.create("imageGroup",s);return this.groups.set(e,{element:i,ui:window.uiFromSelectors(this.selectors.group,i)}),this.getSelectionHandler(t)?.addWrapper(i),i}async setGroup(e,t){const s={...{id:e,src:window.location.href,uploads:[],operationId:null,field:null,fields:{}},...t};Object.preventExtensions(s),await this.stores.groups.save(s)}async setBulkGroup(e,t,s){let i=this.stores.groups.filterByIndex({field:e});if(0===i.length)return;let r=i.map((e=>{e[t]=s,this.stores.groups.save(e)}));await Promise.all(r)}async addToGroup(e,t=null){const s=this.stores.uploads.get(e),i=this.uploads.get(e);if(!s||!i)return;const r=this.fields.get(s.field);if(!r)return;if(null!==i.element?.parentElement&&(!t&&null===s.group||t===s.group))return void this.handleReorder(s.field,t);if(s.group){const t=this.stores.groups.get(s.group);t&&(t.uploads=t.uploads.filter((t=>t!==e)),0===t.uploads.length?await this.removeGroup(t.id,!1):await this.stores.groups.save(t))}i.ui.checkbox&&(i.ui.checkbox.checked=!1);const a=this.selectionHandlers.get(s.field);if(a&&a.isSelected(e)&&a.deselect(e),this.selected.get(s.field)?.has(e)&&this.selected.get(s.field).delete(e),i.ui.featured&&(i.ui.featured.hidden=!t),t){i.ui.featured&&(i.ui.featured.name=`${t}_featured`);let r=this.stores.groups.get(t);r&&(r.uploads.push(e),s.group=t,await this.stores.groups.save(r))}else s.group=null;let o=t?this.groups.get(t)?.ui.grid:r.ui.grid;o&&(o.append(i.element),t&&await this.handleReorder(s.field,t)),await this.stores.uploads.save(s)}handleDeleteGroup(e){const t=e.closest(this.selectors.group.item);if(!t)return;let s=t.dataset.groupId;if(!confirm("Delete this group? Items will be moved back to the upload area."))return;let i=this.stores.uploads.filterByIndex({group:s});Promise.all(i.map((e=>this.addToGroup(e.id,null)))).then((()=>{this.removeGroup(s,!1).then((()=>{})),this.a11y.announce("Group deleted. Items returned to upload area")}))}async removeGroup(e,t=!0){let s=this.groups.get(e),i=this.stores.groups.get(e);if(!i)return;let r=!0;t&&i.uploads.length>0&&(r=window.confirm("Keep uploads in this group?")),await Promise.all(i.uploads.map((e=>r?this.addToGroup(e,null):this.removeUpload(e))));if(this.fields.get(i.field)){const t=this.getGroupKey(i.field,e),r=this.selectionHandlers.get(t);r?.destroy&&r.destroy(),this.selectionHandlers.get(i.field)?.removeWrapper(s.element);const a=this.sortables.get(t);a?.destroy&&a.destroy(),this.sortables.delete(t)}s?.element&&s.element.remove(),this.groups.delete(e),await this.stores.groups.delete(e),this.a11y.announce("Group removed")}maybeLockUploads(e){let t=this.fields.get(e);if(!t||!t.ui.dropZone)return;let s=this.stores.uploads.filterByIndex({field:e}).length,i=t.config.maxFiles??25;t.ui.dropZone.hidden=s>=i}async handleOperationCancelled(e){0!==e.length&&e.forEach((e=>{this.removeUpload(e)}))}getGroupKey(e,t=null){return t?`${e}_${t}`:`${e}`}getSelectionHandler(e){let t=this.getGroupKey(e);if(!this.selectionHandlers.has(t)){let s=this.fields.get(e);if(!s)return;if("post_group"!==s.config.destination)return;let i=new window.jvbHandleSelection(s.element,{selectAll:{checkbox:this.selectors.fields.selectAll,count:this.selectors.fields.count,bulkControls:this.selectors.fields.actions},item:{item:this.selectors.items.item,checkbox:this.selectors.items.checkbox,idAttribute:"uploadId"},wrapper:{wrapper:".preview-wrap, .upload-group",id:"groupId"}});i.subscribe(((t,s)=>{this.selected.set(e,s.selectedItems)})),this.selectionHandlers.set(t,i)}return this.selectionHandlers.get(t)}updateHandlerItems(e){let t=this.getSelectionHandler(e);t&&t.collectItems()}initSortable(e){if(!window.Sortable)return;const t=this.fields.get(e);t&&(!Sortable._multiDragMounted&&Sortable.MultiDrag&&(Sortable.mount(new Sortable.MultiDrag),Sortable._multiDragMounted=!0),this.createSortable(e,t.ui.grid,null),this.initEmptyGroupDropZone(e))}createSortable(e,t,s){if(!t)return null;const i=this.getGroupKey(e,s);if(this.sortables.has(i))return this.sortables.get(i);const r=new Sortable(t,{animation:150,draggable:".item",multiDrag:!0,selectedClass:"selected",avoidImplicitDeselect:!0,group:{name:e,pull:!0,put:!0},dragClass:"dragging",ignore:".empty-group",onStart:t=>{const s=t.item,i=s?.dataset.uploadId,r=this.selected.get(e);if(i&&(!r||!r.has(i))){const t=this.selectionHandlers.get(e);t&&t.select(i)}},onEnd:t=>this.sortableDrop(t,e)});return this.sortables.set(i,r),r}initEmptyGroupDropZone(e){const t=this.fields.get(e),s=t?.groupUI?.empty;s&&(s.addEventListener("dragover",(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="move",s.classList.add("drag-over")})),s.addEventListener("dragleave",(e=>{s.contains(e.relatedTarget)||s.classList.remove("drag-over")})),s.addEventListener("drop",(async t=>{t.preventDefault(),t.stopPropagation(),s.classList.remove("drag-over");const i=this.selected.get(e);if(!i||0===i.size)return;const r=await this.createGroup(e);r&&(await Promise.all(Array.from(i).map((e=>this.addToGroup(e,r)))),this.selectionHandlers.get(e)?.clearSelection())})))}async sortableDrop(e,t){const s=e.to,i=(e.items?.length>0?Array.from(e.items):[e.item]).map((e=>e.dataset.uploadId)).filter(Boolean);if(0===i.length)return;const r=s.dataset.groupId||null;for(const e of i)await this.addToGroup(e,r);await this.handleReorder(t,r),this.selectionHandlers.get(t)?.clearSelection()}handleReorder(e,t=null){let s=t?this.groups.get(t)?.ui.grid:this.fields.get(e)?.ui.grid;if(s){if(t){let e=Array.from(s.children).filter((e=>e.matches(this.selectors.items.item)&&!e.classList.contains("ghost"))).map((e=>e.dataset.uploadId)).filter((e=>e)),i=this.stores.groups.get(t);i&&(i.uploads=e,this.stores.groups.save(i).then((()=>{})))}else this.updateHiddenInput(e);this.a11y.announce("Items reordered")}else console.log("Couldn't Reorder items...")}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((s=>{try{s(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){this.subscribers.clear(),this.previewUrls.forEach((e=>{this.revokePreviewUrl(e)})),this.previewUrls.clear()}cleanupAllPreviewUrls(){this.previewUrls.forEach((e=>this.revokePreviewUrl(e))),this.previewUrls.clear()}async handleClearCache(){const e=window.location.href,t=this.stores.uploads.filterByIndex({src:e}),s=this.stores.groups.filterByIndex({src:e});await Promise.all([...t.map((e=>this.clearUpload(e.id))),...s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id))))]),this.restoreModal&&this.cleanupRestore(),this.a11y.announce("Cache cleared for this page")}async getFilesForForm(e){const t=e.querySelectorAll(this.selectors.fields.field),s=[];for(const e of t){const t=this.determineFieldId(e),i=e.dataset.field,r=this.stores.uploads.filterByIndex({field:t});for(const e of r){const t=this.formatFile(e);t&&s.push({file:t,fieldName:i,uploadId:e.id,meta:e.fields||{}})}}return s}async clearFieldFromStores(e){const t=this.stores.uploads.filterByIndex({field:e}),s=this.stores.groups.filterByIndex({field:e});await Promise.all(t.map((e=>this.clearUpload(e.id)))),await Promise.all(s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id)))))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbUploads=new e)}))}))})();
\ No newline at end of file
--
Gitblit v1.10.0