web-mojo 2.4.4 → 2.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/admin-models.cjs.js +1 -1
  3. package/dist/admin-models.cjs.js.map +1 -1
  4. package/dist/admin-models.es.js +1 -1
  5. package/dist/admin-models.es.js.map +1 -1
  6. package/dist/admin.cjs.js +1 -1
  7. package/dist/admin.css +258 -6
  8. package/dist/admin.es.js +1 -1
  9. package/dist/auth.cjs.js +1 -1
  10. package/dist/auth.es.js +1 -1
  11. package/dist/charts.cjs.js +1 -1
  12. package/dist/charts.es.js +1 -1
  13. package/dist/chat.css +5 -0
  14. package/dist/chunks/AssistantPanelView-CkQEcaFk.js +2 -0
  15. package/dist/chunks/AssistantPanelView-CkQEcaFk.js.map +1 -0
  16. package/dist/chunks/AssistantPanelView-D1wEbgtM.js +2 -0
  17. package/dist/chunks/AssistantPanelView-D1wEbgtM.js.map +1 -0
  18. package/dist/chunks/ChatView-D2WOSxPu.js +2 -0
  19. package/dist/chunks/ChatView-D2WOSxPu.js.map +1 -0
  20. package/dist/chunks/ChatView-kWguc444.js +2 -0
  21. package/dist/chunks/ChatView-kWguc444.js.map +1 -0
  22. package/dist/chunks/{Collection-7F3lsq4z.js → Collection-C0pHSKDH.js} +2 -2
  23. package/dist/chunks/{Collection-7F3lsq4z.js.map → Collection-C0pHSKDH.js.map} +1 -1
  24. package/dist/chunks/{Collection-Cgxbmj8G.js → Collection-DNmr743A.js} +2 -2
  25. package/dist/chunks/{Collection-Cgxbmj8G.js.map → Collection-DNmr743A.js.map} +1 -1
  26. package/dist/chunks/{ContextMenu-afYD0lFk.js → ContextMenu-BeveGkJr.js} +2 -2
  27. package/dist/chunks/{ContextMenu-afYD0lFk.js.map → ContextMenu-BeveGkJr.js.map} +1 -1
  28. package/dist/chunks/{ContextMenu-Ba4fjHxg.js → ContextMenu-T3yDdsIe.js} +2 -2
  29. package/dist/chunks/{ContextMenu-Ba4fjHxg.js.map → ContextMenu-T3yDdsIe.js.map} +1 -1
  30. package/dist/chunks/{DataView-DFGIE3wK.js → DataView-VZIXSsZa.js} +2 -2
  31. package/dist/chunks/{DataView-DFGIE3wK.js.map → DataView-VZIXSsZa.js.map} +1 -1
  32. package/dist/chunks/{DataView-fA6qQbvN.js → DataView-z2rxXk4L.js} +2 -2
  33. package/dist/chunks/{DataView-fA6qQbvN.js.map → DataView-z2rxXk4L.js.map} +1 -1
  34. package/dist/chunks/{FormView-CgnaTPkQ.js → FormView-COIPtbrd.js} +2 -2
  35. package/dist/chunks/{FormView-CgnaTPkQ.js.map → FormView-COIPtbrd.js.map} +1 -1
  36. package/dist/chunks/{FormView-LRb8scDI.js → FormView-DUXQruUZ.js} +2 -2
  37. package/dist/chunks/{FormView-LRb8scDI.js.map → FormView-DUXQruUZ.js.map} +1 -1
  38. package/dist/chunks/{ListView-Dsezts8J.js → ListView-BjsNHuZ1.js} +2 -2
  39. package/dist/chunks/{ListView-Dsezts8J.js.map → ListView-BjsNHuZ1.js.map} +1 -1
  40. package/dist/chunks/{ListView-Cu6iQ0aG.js → ListView-BxcxIwC3.js} +2 -2
  41. package/dist/chunks/{ListView-Cu6iQ0aG.js.map → ListView-BxcxIwC3.js.map} +1 -1
  42. package/dist/chunks/{MetricsCountryMapView-BsJoEsUE.js → MetricsCountryMapView-Bp3qoVHp.js} +2 -2
  43. package/dist/chunks/{MetricsCountryMapView-BsJoEsUE.js.map → MetricsCountryMapView-Bp3qoVHp.js.map} +1 -1
  44. package/dist/chunks/{MetricsCountryMapView-BkLDonK4.js → MetricsCountryMapView-CWjIEBJB.js} +2 -2
  45. package/dist/chunks/{MetricsCountryMapView-BkLDonK4.js.map → MetricsCountryMapView-CWjIEBJB.js.map} +1 -1
  46. package/dist/chunks/Modal-Bm1OQ8Ou.js +3 -0
  47. package/dist/chunks/{Modal-BRKy85bz.js.map → Modal-Bm1OQ8Ou.js.map} +1 -1
  48. package/dist/chunks/Modal-KnJhNZ1E.js +3 -0
  49. package/dist/chunks/{Modal-DKjxtaZI.js.map → Modal-KnJhNZ1E.js.map} +1 -1
  50. package/dist/chunks/Passkeys-B1-Z4-16.js +2 -0
  51. package/dist/chunks/Passkeys-B1-Z4-16.js.map +1 -0
  52. package/dist/chunks/Passkeys-BlHx11-5.js +2 -0
  53. package/dist/chunks/Passkeys-BlHx11-5.js.map +1 -0
  54. package/dist/chunks/TicketPanelView-DVePzWyJ.js +2 -0
  55. package/dist/chunks/TicketPanelView-DVePzWyJ.js.map +1 -0
  56. package/dist/chunks/TicketPanelView-TYh5iZiR.js +2 -0
  57. package/dist/chunks/TicketPanelView-TYh5iZiR.js.map +1 -0
  58. package/dist/chunks/{TokenManager-CWRL33UM.js → TokenManager-6atX9uKB.js} +2 -2
  59. package/dist/chunks/TokenManager-6atX9uKB.js.map +1 -0
  60. package/dist/chunks/{TokenManager-CVR3ENIS.js → TokenManager-CiNtJclo.js} +2 -2
  61. package/dist/chunks/TokenManager-CiNtJclo.js.map +1 -0
  62. package/dist/chunks/User-9qvKV7G6.js +2 -0
  63. package/dist/chunks/User-9qvKV7G6.js.map +1 -0
  64. package/dist/chunks/{User-BmS8zImI.js → User-BM76Ughk.js} +2 -2
  65. package/dist/chunks/User-BM76Ughk.js.map +1 -0
  66. package/dist/chunks/{UserProfileView-CrmACQjJ.js → UserProfileView-DDflzpTa.js} +2 -2
  67. package/dist/chunks/{UserProfileView-CrmACQjJ.js.map → UserProfileView-DDflzpTa.js.map} +1 -1
  68. package/dist/chunks/{UserProfileView-B56WeGL1.js → UserProfileView-cUF8ED9n.js} +2 -2
  69. package/dist/chunks/{UserProfileView-B56WeGL1.js.map → UserProfileView-cUF8ED9n.js.map} +1 -1
  70. package/dist/chunks/{View-gAghvPMN.js → View-BxlKR1IW.js} +2 -2
  71. package/dist/chunks/{View-gAghvPMN.js.map → View-BxlKR1IW.js.map} +1 -1
  72. package/dist/chunks/{View-IgBQlwd0.js → View-CPWwS19u.js} +2 -2
  73. package/dist/chunks/{View-IgBQlwd0.js.map → View-CPWwS19u.js.map} +1 -1
  74. package/dist/chunks/{WebApp-B6wrmIaj.js → WebApp-BdxhRnnT.js} +2 -2
  75. package/dist/chunks/WebApp-BdxhRnnT.js.map +1 -0
  76. package/dist/chunks/WebApp-Bo_egO0c.js +2 -0
  77. package/dist/chunks/WebApp-Bo_egO0c.js.map +1 -0
  78. package/dist/chunks/admin-CHPo4iDn.js +2 -0
  79. package/dist/chunks/admin-CHPo4iDn.js.map +1 -0
  80. package/dist/chunks/admin-CucHFXfD.js +2 -0
  81. package/dist/chunks/admin-CucHFXfD.js.map +1 -0
  82. package/dist/chunks/admin-models-CkHjtMHf.js +2 -0
  83. package/dist/chunks/admin-models-CkHjtMHf.js.map +1 -0
  84. package/dist/chunks/admin-models-DLtFboxy.js +2 -0
  85. package/dist/chunks/admin-models-DLtFboxy.js.map +1 -0
  86. package/dist/chunks/{exportChart-CmfLeCi8.js → exportChart-BTrEOM9j.js} +2 -2
  87. package/dist/chunks/{exportChart-CmfLeCi8.js.map → exportChart-BTrEOM9j.js.map} +1 -1
  88. package/dist/chunks/{exportChart-Ue-zU0_c.js → exportChart-BfzZUb1j.js} +2 -2
  89. package/dist/chunks/{exportChart-Ue-zU0_c.js.map → exportChart-BfzZUb1j.js.map} +1 -1
  90. package/dist/chunks/{index-DTXotoXw.js → index-Cxffar1o.js} +2 -2
  91. package/dist/chunks/{index-DTXotoXw.js.map → index-Cxffar1o.js.map} +1 -1
  92. package/dist/chunks/{index-NXnA6T-5.js → index-DsID1QpB.js} +2 -2
  93. package/dist/chunks/{index-NXnA6T-5.js.map → index-DsID1QpB.js.map} +1 -1
  94. package/dist/chunks/{version-CpR8sTVV.js → version-BYy2UAT0.js} +2 -2
  95. package/dist/chunks/{version-CpR8sTVV.js.map → version-BYy2UAT0.js.map} +1 -1
  96. package/dist/chunks/{version-CsLH6aiB.js → version-BxLEknar.js} +2 -2
  97. package/dist/chunks/{version-CsLH6aiB.js.map → version-BxLEknar.js.map} +1 -1
  98. package/dist/css/web-mojo.css +1 -1
  99. package/dist/docit.cjs.js +1 -1
  100. package/dist/docit.es.js +1 -1
  101. package/dist/index.cjs.js +1 -1
  102. package/dist/index.cjs.js.map +1 -1
  103. package/dist/index.es.js +1 -1
  104. package/dist/index.es.js.map +1 -1
  105. package/dist/lightbox.cjs.js +1 -1
  106. package/dist/lightbox.es.js +1 -1
  107. package/dist/map.cjs.js +1 -1
  108. package/dist/map.es.js +1 -1
  109. package/dist/timeline.cjs.js +1 -1
  110. package/dist/timeline.es.js +1 -1
  111. package/dist/user-profile.cjs.js +1 -1
  112. package/dist/user-profile.es.js +1 -1
  113. package/dist/web-mojo.lite.iife.js +33 -4
  114. package/dist/web-mojo.lite.iife.js.map +1 -1
  115. package/dist/web-mojo.lite.iife.min.js +45 -45
  116. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  117. package/package.json +1 -1
  118. package/dist/chunks/AssistantPanelView-B3orPoag.js +0 -2
  119. package/dist/chunks/AssistantPanelView-B3orPoag.js.map +0 -1
  120. package/dist/chunks/AssistantPanelView-GeIHDvyN.js +0 -2
  121. package/dist/chunks/AssistantPanelView-GeIHDvyN.js.map +0 -1
  122. package/dist/chunks/ChatView-B0xHv7Nq.js +0 -2
  123. package/dist/chunks/ChatView-B0xHv7Nq.js.map +0 -1
  124. package/dist/chunks/ChatView-CUmRTKna.js +0 -2
  125. package/dist/chunks/ChatView-CUmRTKna.js.map +0 -1
  126. package/dist/chunks/Modal-BRKy85bz.js +0 -3
  127. package/dist/chunks/Modal-DKjxtaZI.js +0 -3
  128. package/dist/chunks/Passkeys-CMh9iSax.js +0 -2
  129. package/dist/chunks/Passkeys-CMh9iSax.js.map +0 -1
  130. package/dist/chunks/Passkeys-DF7mRGYj.js +0 -2
  131. package/dist/chunks/Passkeys-DF7mRGYj.js.map +0 -1
  132. package/dist/chunks/TokenManager-CVR3ENIS.js.map +0 -1
  133. package/dist/chunks/TokenManager-CWRL33UM.js.map +0 -1
  134. package/dist/chunks/User-BmS8zImI.js.map +0 -1
  135. package/dist/chunks/User-CayBjzMG.js +0 -2
  136. package/dist/chunks/User-CayBjzMG.js.map +0 -1
  137. package/dist/chunks/WebApp-B6wrmIaj.js.map +0 -1
  138. package/dist/chunks/WebApp-DeHPnmbD.js +0 -2
  139. package/dist/chunks/WebApp-DeHPnmbD.js.map +0 -1
  140. package/dist/chunks/admin-FvzwR-f7.js +0 -2
  141. package/dist/chunks/admin-FvzwR-f7.js.map +0 -1
  142. package/dist/chunks/admin-yjCkhxvU.js +0 -2
  143. package/dist/chunks/admin-yjCkhxvU.js.map +0 -1
@@ -1,3 +0,0 @@
1
- "use strict";const e=require("./View-IgBQlwd0.js"),t=require("./Collection-7F3lsq4z.js"),i=require("./User-BmS8zImI.js");class ToastService{constructor(e={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:3e3,maxToasts:5,...e},this.toasts=/* @__PURE__ */new Map,this.toastCounter=0,this.init()}init(){this.createContainer()}createContainer(){let e=document.getElementById(this.options.containerId);e||(e=document.createElement("div"),e.id=this.options.containerId,e.className=`toast-container position-fixed ${this.getPositionClasses()}`,e.style.zIndex="1070",e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),document.body.appendChild(e)),this.container=e}getPositionClasses(){const e={"top-start":"top-0 start-0 p-3","top-center":"top-0 start-50 translate-middle-x p-3","top-end":"top-0 end-0 p-3","middle-start":"top-50 start-0 translate-middle-y p-3","middle-center":"top-50 start-50 translate-middle p-3","middle-end":"top-50 end-0 translate-middle-y p-3","bottom-start":"bottom-0 start-0 p-3","bottom-center":"bottom-0 start-50 translate-middle-x p-3","bottom-end":"bottom-0 end-0 p-3"};return e[this.options.position]||e["top-end"]}success(e,t={}){return this.show(e,"success",{icon:"bi-check-circle-fill",...t})}error(e,t={}){return this.show(e,"error",{icon:"bi-exclamation-triangle-fill",autohide:!0,...t})}info(e,t={}){return this.show(e,"info",{icon:"bi-info-circle-fill",...t})}warning(e,t={}){return this.show(e,"warning",{icon:"bi-exclamation-triangle-fill",...t})}plain(e,t={}){return this.show(e,"plain",{...t})}show(e,t="info",i={}){this.enforceMaxToasts();const o="toast-"+ ++this.toastCounter,s={title:this.getDefaultTitle(t),icon:this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...i},n=this.createToastElement(o,e,t,s);if(this.container.appendChild(n),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const a=new bootstrap.Toast(n,{autohide:s.autohide,delay:s.delay});return this.toasts.set(o,{element:n,bootstrap:a,type:t,message:e}),n.addEventListener("hidden.bs.toast",()=>{this.cleanup(o)}),a.show(),{id:o,hide:()=>{try{a.hide()}catch(e){console.warn("Error hiding toast:",e)}},dispose:()=>this.cleanup(o),updateProgress:i.updateProgress||null}}showView(e,t="info",i={}){this.enforceMaxToasts();const o="toast-"+ ++this.toastCounter,s={title:i.title||this.getDefaultTitle(t),icon:i.icon||this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,size:"md",...i},n=this.createViewToastElement(o,e,t,s);if(this.container.appendChild(n),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const a=new bootstrap.Toast(n,{autohide:s.autohide,delay:s.delay});this.toasts.set(o,{element:n,bootstrap:a,type:t,view:e,message:"View toast"}),n.addEventListener("hidden.bs.toast",()=>{this.cleanupView(o)});const r=n.querySelector(".toast-view-body");return r&&e&&e.render(!0,r),a.show(),{id:o,view:e,hide:()=>{try{a.hide()}catch(e){console.warn("Error hiding view toast:",e)}},dispose:()=>this.cleanupView(o),updateProgress:t=>{e&&"function"==typeof e.updateProgress&&e.updateProgress(t)}}}createToastElement(e,t,i,o){const s=document.createElement("div");s.id=e,s.className=`toast toast-service-${i}${o.size?` toast-${o.size}`:""}`,s.setAttribute("role","alert"),s.setAttribute("aria-live","assertive"),s.setAttribute("aria-atomic","true");const n=o.title||o.icon?this.createToastHeader(o,i):"",a=this.createToastBody(t,o.icon&&!o.title);return s.innerHTML=`\n ${n}\n ${a}\n `,s}createViewToastElement(e,t,i,o){const s=document.createElement("div");s.id=e,s.className=`toast toast-service-${i}${o.size?` toast-${o.size}`:""}`,s.setAttribute("role","alert"),s.setAttribute("aria-live","assertive"),s.setAttribute("aria-atomic","true");const n=o.title||o.icon?this.createToastHeader(o,i):"",a=this.createViewToastBody();return s.innerHTML=`\n ${n}\n ${a}\n `,s}createViewToastBody(){return'\n <div class="toast-body p-0">\n <div class="toast-view-body p-3"></div>\n </div>\n '}createToastHeader(e,t){const i=e.icon?`<i class="${e.icon} toast-service-icon me-2"></i>`:"",o=e.title?`<strong class="me-auto">${i}${this.escapeHtml(e.title)}</strong>`:"",s=e.showTime?`<small class="text-muted">${this.getTimeString()}</small>`:"",n=e.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return o||s||n?`\n <div class="toast-header">\n ${o}\n ${s}\n ${n}\n </div>\n `:""}createToastBody(e,t=!1){return`\n <div class="toast-body d-flex align-items-center">\n ${t?`<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>`:""}\n <span>${this.escapeHtml(e)}</span>\n </div>\n `}getDefaultTitle(e){return{success:"Success",error:"Error",warning:"Warning",info:"Information",plain:""}[e]||"Notification"}getDefaultIcon(e){return{success:"bi-check-circle-fill",error:"bi-exclamation-triangle-fill",warning:"bi-exclamation-triangle-fill",info:"bi-info-circle-fill",plain:""}[e]||"bi-info-circle-fill"}enforceMaxToasts(){if(Array.from(this.toasts.values()).length>=this.options.maxToasts){const e=this.toasts.keys().next().value,t=this.toasts.get(e);t&&t.bootstrap.hide()}}cleanup(e){const t=this.toasts.get(e);if(t){try{t.bootstrap.dispose()}catch(i){console.warn("Error disposing toast:",i)}t.element&&t.element.parentNode&&t.element.parentNode.removeChild(t.element),this.toasts.delete(e)}}cleanupView(e){const t=this.toasts.get(e);if(t){if(t.view&&"function"==typeof t.view.dispose)try{t.view.dispose()}catch(i){console.warn("Error disposing view in toast:",i)}try{t.bootstrap.dispose()}catch(i){console.warn("Error disposing toast:",i)}t.element&&t.element.parentNode&&t.element.parentNode.removeChild(t.element),this.toasts.delete(e)}}hideAll(){this.toasts.forEach((e,t)=>{e.bootstrap.hide()})}clearAll(){this.toasts.forEach((e,t)=>{this.cleanup(t)})}getTimeString(){/* @__PURE__ */
2
- return(new Date).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}dispose(){this.clearAll(),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}getStats(){const e={total:this.toasts.size,byType:{}};return this.toasts.forEach(t=>{e.byType[t.type]=(e.byType[t.type]||0)+1}),e}setOptions(e){this.options={...this.options,...e},e.position&&this.container&&(this.container.className=`toast-container position-fixed ${this.getPositionClasses()}`)}}class ProgressView extends e.View{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=t.dataFormatter.pipe(this.filesize,"filesize"),this.progress=0,this.percentage=0,this.loaded=0,this.total=this.filesize,this.loadedFormatted="0 B",this.totalFormatted=this.filesizeFormatted,this.status="Starting upload...",this.showCancel=!1!==e.showCancel,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return'\n <div class="progress-view">\n <div class="d-flex justify-content-between align-items-start mb-2">\n <div class="flex-grow-1 min-width-0">\n <div class="fw-medium text-truncate" title="{{filename}}">\n <i class="bi bi-file-earmark me-1"></i>\n {{filename}}\n </div>\n <small class="text-muted">{{status}}</small>\n </div>\n {{#showCancel}}\n <button type="button" \n class="btn btn-sm btn-outline-secondary ms-2" \n data-action="cancel"\n {{#cancelled}}disabled{{/cancelled}}>\n <i class="bi bi-x"></i>\n </button>\n {{/showCancel}}\n </div>\n \n <div class="progress mb-2" style="height: 8px;">\n <div class="progress-bar" \n role="progressbar" \n style="width: {{percentage}}%"\n aria-valuenow="{{percentage}}" \n aria-valuemin="0" \n aria-valuemax="100">\n </div>\n </div>\n \n <div class="d-flex justify-content-between">\n <small class="text-muted">\n {{loadedFormatted}} / {{totalFormatted}}\n </small>\n <small class="text-muted">\n {{percentage}}%\n </small>\n </div>\n </div>\n '}updateProgress(e){this.cancelled||this.completed||(this.progress=e.progress,this.percentage=e.percentage,this.loaded=e.loaded,this.total=e.total||this.filesize,this.loadedFormatted=t.dataFormatter.pipe(this.loaded,"filesize"),this.totalFormatted=t.dataFormatter.pipe(this.total,"filesize"),this.percentage<100?this.status=`Uploading... ${this.percentage}%`:this.status="Finalizing upload...",this.render())}markCompleted(e="Upload completed!"){this.completed=!0,this.progress=1,this.percentage=100,this.status=e,this.render()}markFailed(e="Upload failed"){this.status=e,this.render()}markCancelled(){this.cancelled=!0,this.status="Upload cancelled",this.render()}async onActionCancel(e,t,i){if(!this.cancelled&&!this.completed&&(i.disabled=!0,this.markCancelled(),this.emit("cancel"),"function"==typeof this.onCancel))try{await this.onCancel()}catch(o){console.error("Error in cancel callback:",o)}}setFilename(e){this.filename=e,this.render()}setFilesize(e){this.filesize=e,this.filesizeFormatted=t.dataFormatter.pipe(e,"filesize"),this.total=e,this.totalFormatted=this.filesizeFormatted,this.render()}getPercentage(){return this.percentage}isCompleted(){return this.completed}isCancelled(){return this.cancelled}getStats(){return{filename:this.filename,filesize:this.filesize,progress:this.progress,percentage:this.percentage,loaded:this.loaded,total:this.total,cancelled:this.cancelled,completed:this.completed,status:this.status}}}class FileUpload{constructor(e,t={}){if(this.fileModel=e,this.options={file:null,name:null,group:null,description:null,onProgress:null,onComplete:null,onError:null,showToast:!0,...t},!(this.options.file&&this.options.file instanceof File))throw new Error("FileUpload requires a valid File object");this.cancelled=!1,this.uploadRequest=null,this.progressToast=null,this.progressView=null,this.toastService=null,this.options.showToast&&(this.toastService=new ToastService),this.promise=this._startUpload()}async _startUpload(){try{let t,i,o;this.options.showToast&&this._showProgressToast();try{t=await this._initiateUpload()}catch(e){throw new Error(`Failed to initiate upload: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");if(!t||!t.upload_url)throw new Error("Invalid upload response: missing upload URL");if("string"==typeof t.upload_url)i={url:t.upload_url,method:"PUT",fields:null,headers:{}};else{if(!t.upload_url||"object"!=typeof t.upload_url||!t.upload_url.upload_url)throw new Error(`Invalid upload response: unrecognised upload_url format. Server returned: ${JSON.stringify(t.upload_url)}`);i={url:t.upload_url.upload_url,method:t.upload_url.method||"POST",fields:t.upload_url.fields||null,headers:t.upload_url.headers||{}}}try{o=await this._performUpload(i)}catch(e){throw new Error(`File upload failed: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");try{await this._completeUpload()}catch(e){console.warn("Failed to mark upload as completed:",e)}return this._onComplete(this.fileModel),this.fileModel}catch(e){throw"Upload cancelled"!==e.message&&this._onError(e),e}}async _initiateUpload(){try{const e={filename:this.options.name||this.options.file.name,file_size:this.options.file.size,content_type:this.options.file.type};this.options.group&&(e.group=this.options.group),this.options.description&&(e.description=this.options.description);const t=await this.fileModel.rest.POST("/api/fileman/upload/initiate",e);if(!t)throw new Error("No response from upload initiation API");if(!t.data)throw new Error("Upload initiation response missing data");if(!t.data.status){const e=t.data.error||"Upload initiation failed";throw new Error(e)}if(!t.data.data)throw new Error("Upload initiation response missing data payload");return t.data.data.id&&this.fileModel.set("id",t.data.data.id),t.data.data}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload initiation. Please check your connection.");throw e}}async _performUpload(e){return new Promise((t,i)=>{if(!(this.options.file instanceof File))return void i(new Error("Only single File objects are supported"));const{url:o,method:s,fields:n,headers:a}=e,r="POST"===s&&null!==n,l=new XMLHttpRequest;this.uploadRequest=l,l.upload.onprogress=e=>{this.cancelled||this._onProgress({progress:e.loaded/e.total,loaded:e.loaded,total:e.total,percentage:Math.round(e.loaded/e.total*100)})},l.onload=()=>{l.status>=200&&l.status<300?t({data:l.response,status:l.status,statusText:l.statusText,xhr:l}):i(new Error(`Upload failed: ${l.status} ${l.statusText}`))},l.onerror=()=>i(new Error("Upload failed: Network error")),l.ontimeout=()=>i(new Error("Upload timed out — file may be too large or connection too slow")),l.onabort=()=>i(new Error("Upload cancelled"));let d=o;o.startsWith("/")&&!o.startsWith("/api/")&&(d="/api"+o);const c=this.fileModel.rest.buildUrl(d);if(l.open(s,c),l.timeout=3e4,r){for(const[t,i]of Object.entries(a||{}))"content-type"!==t.toLowerCase()&&l.setRequestHeader(t,i);const e=new FormData;for(const[t,i]of Object.entries(n))e.append(t,i);e.append("file",this.options.file),l.send(e)}else{l.setRequestHeader("Content-Type",this.options.file.type);for(const[e,t]of Object.entries(a||{}))"content-type"!==e.toLowerCase()&&l.setRequestHeader(e,t);l.send(this.options.file)}})}async _completeUpload(){try{const e=await this.fileModel.save({action:"mark_as_completed"});if(!e)throw new Error("No response from upload completion API");if(e.data&&!e.data.status){const t=e.data.error||"Failed to mark upload as completed";throw new Error(t)}return e}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload completion. The file may have uploaded successfully.");throw e}}_onProgress(e){this.progressToast&&this.progressToast.updateProgress&&this.progressToast.updateProgress(e),"function"==typeof this.options.onProgress&&this.options.onProgress(e)}_onComplete(e){this.progressView&&this.progressView.markCompleted("Upload completed successfully!"),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast:",e)}},2e3),"function"==typeof this.options.onComplete&&this.options.onComplete(e)}_onError(e){if(this.progressToast)try{this.progressToast.hide()}catch(t){console.warn("Error hiding progress toast on error:",t)}this.toastService&&this.toastService.error(`Upload failed: ${e.message}`),"function"==typeof this.options.onError&&this.options.onError(e)}_showProgressToast(){this.progressView=new ProgressView({filename:this.options.name||this.options.file.name,filesize:this.options.file.size,showCancel:!0,onCancel:()=>this.cancel()}),this.progressToast=this.toastService.showView(this.progressView,"info",{title:"File Upload",autohide:!1,dismissible:!1})}cancel(){return!this.cancelled&&(this.cancelled=!0,this.uploadRequest&&"function"==typeof this.uploadRequest.abort&&this.uploadRequest.abort(),this.progressView&&this.progressView.markCancelled(),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast on cancel:",e)}},1500),!0)}isCancelled(){return this.cancelled}then(e,t){return this.promise.then(e,t)}catch(e){return this.promise.catch(e)}finally(e){return this.promise.finally(e)}getStats(){return{filename:this.options.file.name,size:this.options.file.size,type:this.options.file.type,cancelled:this.cancelled,group:this.options.group,description:this.options.description}}}class FileManager extends t.Model{constructor(e={}){super(e,{endpoint:"/api/fileman/manager"})}}class FileManagerList extends t.Collection{constructor(e={}){super({ModelClass:FileManager,endpoint:"/api/fileman/manager",size:10,...e})}}const o={create:{title:"Add Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"use",type:"text",label:"Use",placeholder:"Enter User or Leave Blank",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,value:"s3://BUCKET_NAME/OPTION_FOLDER",placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6}]},edit:{title:"Edit Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"use",type:"text",label:"Use",placeholder:"Enter User or Leave Blank",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"allowed_origins",type:"text",label:"Domains Who Can Upload",cols:12},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6},{name:"is_public",type:"switch",label:"Is Public",default:!0,cols:6}]},owners:{fields:[{type:"collection",name:"group",label:"Group (Owner)",Collection:i.GroupList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300},{type:"collection",name:"user",label:"User (Owner)",Collection:i.UserList,labelField:"display_name",valueField:"id",maxItems:10,placeholder:"Search users...",emptyFetch:!1,debounceMs:300}]},credentials:{fields:[{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"}]}};let s=class extends t.Model{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return"image"===this.get("category")}getCategory(){return this.get("category")||this._inferCategoryFromContentType()}_inferCategoryFromContentType(){const e=(this.get("content_type")||"").toLowerCase();return e?e.startsWith("image/")?"image":e.startsWith("video/")?"video":e.startsWith("audio/")?"audio":"application/pdf"===e?"pdf":e.startsWith("text/")||"application/msword"===e||e.startsWith("application/vnd.openxmlformats-officedocument.wordprocessingml")||"application/vnd.oasis.opendocument.text"===e?"document":"application/vnd.ms-excel"===e||e.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml")||"application/vnd.oasis.opendocument.spreadsheet"===e?"spreadsheet":"application/vnd.ms-powerpoint"===e||e.startsWith("application/vnd.openxmlformats-officedocument.presentationml")||"application/vnd.oasis.opendocument.presentation"===e?"presentation":"application/zip"===e||"application/x-rar-compressed"===e||"application/x-7z-compressed"===e||"application/x-tar"===e||"application/gzip"===e?"archive":"other":"other"}hasRenditions(){const e=this.get("renditions");return!(!e||!Object.keys(e).length)}isUploadPending(){const e=this.get("upload_status");return!(!e||"completed"===e||"failed"===e)}regenerateRenditions(e){const t=this.id||this.get("id");if(!t)return Promise.reject(new Error("Cannot regenerate renditions on an unsaved file"));const i=Array.isArray(e)&&e.length?{regenerate_renditions:e}:{regenerate_renditions:!0};return this.rest.POST(`${this.endpoint}/${t}`,i)}share(e=!0){const t=this.id||this.get("id");return t?this.rest.POST(`${this.endpoint}/${t}`,{share:e}):Promise.reject(new Error("Cannot share an unsaved file"))}getRenditions(){const e=this.get("renditions");return e?Object.values(e):[]}getBestImageRendition(){const e=this.getRenditions().filter(e=>e&&"string"==typeof e.content_type&&e.content_type.startsWith("image/"));return e.length?e.reduce((e,t)=>{const i=(parseInt(e.width)||0)*(parseInt(e.height)||0);return(parseInt(t.width)||0)*(parseInt(t.height)||0)>i?t:e}):null}getThumbnailUrl(){const e=this.get("renditions")||{};if(e.thumbnail&&e.thumbnail.url)return e.thumbnail.url;const t=this.getBestImageRendition();return t?t.url:null}upload(e={}){return new FileUpload(this,e)}};class FileList extends t.Collection{constructor(e={}){super({ModelClass:s,endpoint:"/api/fileman/file",size:10,...e})}}class ModalView extends e.View{static _openDialogs=[];static _baseZIndex={backdrop:1050,modal:1055};static getFullscreenAwareZIndex(){return document.querySelector(".table-fullscreen")?{backdrop:10040,modal:10050}:ModalView._baseZIndex}static fixAllBackdropStacking(){const e=document.querySelectorAll(".modal-backdrop"),t=ModalView._openDialogs;if(0===e.length||0===t.length)return;const i=[...t].sort((e,t)=>(e._dialogZIndex||0)-(t._dialogZIndex||0)),o=document.querySelector(".table-fullscreen")||document.body;e.forEach((e,t)=>{if(t>=i.length)return;const s=i[t];e.style.zIndex=s._dialogZIndex-5,e.parentNode!==o&&o.appendChild(e)})}static updateAllBackdropStacking(){ModalView.fixAllBackdropStacking()}static getMountTarget(){return document.querySelector(".table-fullscreen")||document.body}constructor(e={}){const t=e.id||`modal-${Date.now()}-${Math.random().toString(36).slice(2,7)}`;super({...e,id:t,tagName:"div",className:`modal ${!1!==e.fade?"fade":""} ${e.className||""}`.trim().replace(/\s+/g," "),attributes:{tabindex:"-1","aria-hidden":"true","aria-labelledby":e.labelledBy||`${t}-label`,"aria-describedby":e.describedBy||null,...e.attributes}}),this.modalId=t,this.title=e.title||"",this.titleId=`${this.modalId}-label`,this.size=e.size||"",this.centered=void 0!==e.centered&&e.centered,this.scrollable=void 0!==e.scrollable&&e.scrollable,this.autoSize=e.autoSize||"auto"===e.size,this.backdrop=void 0===e.backdrop||e.backdrop,this.keyboard=void 0===e.keyboard||e.keyboard,this.focus=void 0===e.focus||e.focus,this.header=void 0===e.header||e.header,this.headerContent=e.headerContent||null,this.headerView=null,this.closeButton=void 0===e.closeButton||e.closeButton,this.contextMenu=e.contextMenu||null,this._processHeaderContent(this.headerContent),this.body=e.body??e.view??e.message??e.content??"",this.bodyView=null,this.bodyClass=e.bodyClass||"",this.noBodyPadding=e.noBodyPadding||!1,this.minWidth=e.minWidth||300,this.minHeight=e.minHeight||200,e.maxHeight&&(this.maxHeight=e.maxHeight),this.maxWidthPercent=e.maxWidthPercent||.9,this.maxHeightPercent=e.maxHeightPercent||.8,this._processBodyContent(this.body),this.footer=e.footer||null,this.footerView=null,this.footerClass=e.footerClass||"",this._processFooterContent(this.footer),this.buttons=e.buttons||null,this.onShow=e.onShow||null,this.onShown=e.onShown||null,this.onHide=e.onHide||null,this.onHidden=e.onHidden||null,this.onHidePrevented=e.onHidePrevented||null,this.autoShow=void 0!==e.autoShow&&e.autoShow,this.modal=null,this.relatedTarget=e.relatedTarget||null}_processBodyContent(t){if(t instanceof e.View||t&&"object"==typeof t&&"function"==typeof t.render)this.bodyView=t,this.body="",this.addChild(this.bodyView);else if("function"==typeof t)try{const i=t();i instanceof e.View?(this.bodyView=i,this.body="",this.addChild(this.bodyView)):i instanceof Promise?(this.bodyPromise=i,this.body='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.body=i}catch(i){console.error("ModalView: error processing body function:",i),this.body=t}else this.body=t}_processHeaderContent(t){if(t instanceof e.View)this.headerView=t,this.headerContent=null,this.addChild(this.headerView);else if("function"==typeof t)try{const i=t();i instanceof e.View?(this.headerView=i,this.headerContent=null,this.addChild(this.headerView)):i instanceof Promise?(this.headerPromise=i,this.headerContent='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.headerContent=i}catch(i){console.error("ModalView: error processing headerContent function:",i),this.headerContent=t}else this.headerContent=t}_processFooterContent(t){if(t instanceof e.View)this.footerView=t,this.footer=null,this.addChild(this.footerView);else if("function"==typeof t)try{const i=t();i instanceof e.View?(this.footerView=i,this.footer=null,this.addChild(this.footerView)):i instanceof Promise?(this.footerPromise=i,this.footer='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.footer=i}catch(i){console.error("ModalView: error processing footer function:",i),this.footer=t}else this.footer=t}async getTemplate(){const e=["modal-dialog"];return this.size&&"auto"!==this.size&&(this.size.startsWith("fullscreen")?e.push(`modal-${this.size}`):["sm","lg","xl","xxl"].includes(this.size)&&(e.push(`modal-${this.size}`),["lg","xl","xxl"].includes(this.size)&&e.push("modal-fullscreen-sm-down"))),this.centered&&e.push("modal-dialog-centered"),this.scrollable&&(this.maxHeight?e.push("overflow-hidden"):e.push("modal-dialog-scrollable")),`\n <div class="${e.join(" ")}">\n <div class="modal-content">\n ${await this.buildHeader()}\n ${await this.buildBody()}\n ${await this.buildFooter()}\n </div>\n </div>\n `}async buildHeader(){if(!this.header)return"";if(this.headerView)return this.headerView.replaceById=!0,`<div class="modal-header" data-view-container="header">\n <div id="${this.headerView.id}"></div>\n </div>`;if(this.headerContent)return`<div class="modal-header">${this.headerContent}</div>`;let e="";return this.contextMenu&&this.contextMenu.items&&this.contextMenu.items.length>0?e=await this.buildContextMenu():this.closeButton&&(e='<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>'),`\n <div class="modal-header">\n ${this.title?`<h5 class="modal-title" id="${this.titleId}">${this.title}</h5>`:""}\n ${e}\n </div>\n `}async buildContextMenu(){const e=await this.filterContextMenuItems();if(0===e.length)return this.closeButton?'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>':"";const t=this.contextMenu.icon||"bi-three-dots-vertical";return`\n <div class="dropdown">\n <button class="${this.contextMenu.buttonClass||"btn btn-link p-1 mojo-modal-context-menu-btn"}" type="button" data-bs-toggle="dropdown" aria-expanded="false">\n <i class="${t}"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n ${e.map(e=>{if("divider"===e.type)return'<li><hr class="dropdown-divider"></li>';const t=e.icon?`<i class="${e.icon} me-2"></i>`:"",i=e.label||"";if(e.href)return`<li><a class="dropdown-item" href="${e.href}"${e.target?` target="${e.target}"`:""}>${t}${i}</a></li>`;if(e.action){const o=Object.keys(e).filter(e=>e.startsWith("data-")).map(t=>`${t}="${e[t]}"`).join(" ");return`<li><a class="dropdown-item" data-action="${e.action}" ${o}>${t}${i}</a></li>`}return""}).join("")}\n </ul>\n </div>\n `}async filterContextMenuItems(){if(!this.contextMenu||!this.contextMenu.items)return[];const e=[];for(const i of this.contextMenu.items)if("divider"!==i.type){if(i.permissions)try{const e=this.getApp?.();let t=e?.activeUser||e?.getState?.("activeUser")||null;if(!t&&"undefined"!=typeof window&&window.getApp)try{t=window.getApp()?.activeUser}catch{}if(!t?.hasPermission)continue;if(!t.hasPermission(i.permissions))continue}catch(t){console.warn("ModalView: error checking permissions for context menu item:",t);continue}e.push(i)}else e.push(i);return e}async buildBody(){const e=`modal-body ${this.noBodyPadding?"modal-body-flush":""} ${this.bodyClass}`.replace(/\s+/g," ").trim();return this.bodyView?(this.bodyView.replaceById=!0,`<div class="${e}" data-view-container="body">\n <div id="${this.bodyView.id}"></div>\n </div>`):this.body||""===this.body?`<div class="${e}">${this.body}</div>`:""}async buildFooter(){if(this.footerView)return`<div class="modal-footer ${this.footerClass}" data-view-container="footer"></div>`;if(null!==this.footer&&"string"==typeof this.footer)return`<div class="modal-footer ${this.footerClass}">${this.footer}</div>`;if(this.buttons&&this.buttons.length>0){const e=this.buttons.map(e=>{const t=e.dismiss?'data-bs-dismiss="modal"':"",i=e.action?`data-action="${e.action}"`:"",o=e.id?`id="${e.id}"`:"",s=e.disabled?"disabled":"";return`\n <button type="${e.type||"button"}"\n class="btn ${e.class||"btn-secondary"}"\n ${o} ${t} ${i} ${s}>\n ${e.icon?`<i class="bi ${e.icon} me-1"></i>`:""}\n ${e.text||"Button"}\n </button>\n `}).join("");return`<div class="modal-footer ${this.footerClass}">${e}</div>`}return""}async mount(e=null){if(!this.mounted&&!this.destroyed){if(!this.element)throw new Error("Cannot mount modal without element");return await this.onBeforeMount(),ModalView.getMountTarget().appendChild(this.element),this.bindEvents(),this.mounted=!0,await this.onAfterMount(),this.emit("mounted",{view:this}),this}}async onAfterRender(){if(await super.onAfterRender(),window.Prism&&this.element&&this.element.querySelectorAll("pre code").length>0&&window.Prism.highlightAllUnder(this.element),this.autoSize)this.setupAutoSizing();else if(this.maxHeight){const e=this.element.querySelector(".modal-body");e&&(e.style.maxHeight=`${this.maxHeight}px`)}}async onAfterMount(){await super.onAfterMount(),"undefined"!=typeof window&&window.bootstrap?.Modal&&("static"===this.backdrop&&this.element.setAttribute("data-bs-backdrop","static"),this.keyboard||this.element.setAttribute("data-bs-keyboard","false"),this.modal=new window.bootstrap.Modal(this.element,{backdrop:this.backdrop,keyboard:this.keyboard,focus:this.focus}),this.bindBootstrapEvents(),this.autoShow&&this.show(this.relatedTarget))}setupAutoSizing(){this.element&&(this.element.addEventListener("shown.bs.modal",()=>{this.applyAutoSizing()},{once:!0}),setTimeout(()=>{this.isShown()&&this.applyAutoSizing()},100))}applyAutoSizing(){if(this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),i=this.element.querySelector(".modal-body");if(!e||!t||!i)return void console.warn("ModalView auto-sizing: required elements not found");if(this.bodyView&&!this.bodyView.element)return void setTimeout(()=>this.applyAutoSizing(),50);const o={dialogMaxWidth:e.style.maxWidth,dialogWidth:e.style.width,contentWidth:t.style.width,contentMaxHeight:t.style.maxHeight,hadScrollableClass:e.classList.contains("modal-dialog-scrollable")};e.style.maxWidth="none",e.style.width="auto",t.style.width="auto",t.style.maxHeight="none",t.offsetHeight;const s=t.getBoundingClientRect(),n=40,a=Math.min(window.innerWidth*this.maxWidthPercent,window.innerWidth-n);let r=Math.min(window.innerHeight*this.maxHeightPercent,window.innerHeight-n),l=Math.max(this.minWidth,Math.ceil(s.width+20)),d=Math.max(this.minHeight,Math.ceil(s.height));this.maxHeight&&(r=Math.min(this.maxHeight,r),e.style.maxHeight=`${r}px`),l=Math.min(l,a);const c=s.height>r;e.style.maxWidth=`${l}px`,e.style.width=`${l}px`,c&&(e.classList.contains("modal-dialog-scrollable")||e.classList.add("modal-dialog-scrollable"),t.style.maxHeight=`${r}px`,d=r),this.autoSizedWidth=l,this.autoSizedHeight=d,this._originalStyles=o}catch(e){console.error("ModalView: error in auto-sizing:",e);const t=this.element?.querySelector(".modal-dialog");t&&(t.style.maxWidth="")}}resetAutoSizing(){if(this.autoSize&&this._originalStyles&&this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),i=this.element.querySelector(".modal-body");e&&t&&i&&(e.style.maxWidth=this._originalStyles.dialogMaxWidth||"",e.style.width=this._originalStyles.dialogWidth||"",t.style.width=this._originalStyles.contentWidth||"",t.style.maxHeight=this._originalStyles.contentMaxHeight||"",!this._originalStyles.hadScrollableClass&&e.classList.contains("modal-dialog-scrollable")&&e.classList.remove("modal-dialog-scrollable"),delete this.autoSizedWidth,delete this.autoSizedHeight,delete this._originalStyles)}catch(e){console.error("ModalView: error resetting auto-sizing:",e)}}bindBootstrapEvents(){this.element.addEventListener("show.bs.modal",e=>{const t=ModalView._openDialogs.length,i=ModalView.getFullscreenAwareZIndex().modal+20*t;this.element.style.zIndex=i,this._dialogZIndex=i,this._backdropZIndex=i-10,ModalView._openDialogs.push(this),this.onShow&&this.onShow(e),this.emit("show",{dialog:this,relatedTarget:e.relatedTarget})}),this.element.addEventListener("shown.bs.modal",e=>{if(setTimeout(()=>ModalView.fixAllBackdropStacking(),50),this.onShown&&this.onShown(e),this.emit("shown",{dialog:this,relatedTarget:e.relatedTarget}),this.focus){const e=this.element.querySelector('input:not([type="hidden"]), textarea, select');e&&e.focus()}}),this.element.addEventListener("hide.bs.modal",e=>{const t=this.element.querySelector(":focus");t&&t.blur(),this.onHide&&!1===this.onHide(e)?e.preventDefault():this.emit("hide",{dialog:this})}),this.element.addEventListener("hidden.bs.modal",e=>{const t=ModalView._openDialogs.indexOf(this);t>-1&&ModalView._openDialogs.splice(t,1),ModalView._openDialogs.length>0&&(document.body.classList.add("modal-open"),setTimeout(()=>ModalView.fixAllBackdropStacking(),50)),this.previousFocus&&document.body.contains(this.previousFocus)&&this.previousFocus.focus(),this.onHidden&&this.onHidden(e),this.emit("hidden",{dialog:this})}),this.element.addEventListener("hidePrevented.bs.modal",e=>{this.onHidePrevented&&this.onHidePrevented(e),this.emit("hidePrevented",{dialog:this})})}show(e=null){this.previousFocus=document.activeElement,window.lastDialog=this,this.modal&&this.modal.show(e)}hide(){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal&&this.modal.hide()}toggle(e=null){this.modal&&this.modal.toggle(e)}isShown(){return this.element?.classList.contains("show")||!1}getModal(){return this.modal}handleUpdate(){this.modal&&this.modal.handleUpdate()}async setContent(t){if(t instanceof e.View){this.bodyView&&(await this.bodyView.destroy(),this.removeChild(this.bodyView)),this.bodyView=t,this.body="",this.addChild(this.bodyView);const e=this.element?.querySelector(".modal-body");e&&(e.innerHTML="",await this.bodyView.render(e))}else{this.body=t;const e=this.element?.querySelector(".modal-body");e&&(e.innerHTML=t)}this.handleUpdate()}setTitle(e){this.title=e;const t=this.element?.querySelector(".modal-title");t&&(t.textContent=e)}setLoading(e=!0,t="Loading..."){const i=this.element?.querySelector(".modal-body");i&&(e?i.innerHTML=`\n <div class="text-center py-4">\n <div class="spinner-border text-primary mb-3" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <p>${t}</p>\n </div>\n `:this.bodyView&&i.replaceChildren(this.bodyView.element))}async destroy(){if(this.modal){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal.dispose(),this.modal=null}this.previousFocus&&document.body.contains(this.previousFocus)&&(this.previousFocus.focus(),this.previousFocus=null),this.autoSize&&this.resetAutoSizing(),await super.destroy()}async onBeforeDestroy(){this.headerView&&await this.headerView.destroy(),this.bodyView&&await this.bodyView.destroy(),this.footerView&&await this.footerView.destroy(),await super.onBeforeDestroy(),this.modal&&(this.modal.dispose(),this.modal=null)}}let n=null,a=0,r=null;const l={show(e){"string"==typeof e&&(e={message:e});const{message:t="Loading...",timeout:i=3e4}=e||{};if(a++,1===a){r&&clearTimeout(r);const e=ModalView.getFullscreenAwareZIndex().modal+1e3;n||(n=document.createElement("div"),n.className="mojo-loading-overlay",n.innerHTML=`\n <div class="mojo-loading-card">\n <div class="mojo-loading-spinner"></div>\n <div class="mojo-loading-message">${t}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin { to { transform: rotate(360deg); } }\n</style>\n `,document.body.appendChild(n)),n.style.zIndex=String(e);const o=n.querySelector(".mojo-loading-message");o&&(o.textContent=t),requestAnimationFrame(()=>{n&&n.classList.add("show")}),i>0&&(r=setTimeout(()=>{console.error("BusyIndicator timed out."),l.hide(!0)},i))}else if(n){const e=n.querySelector(".mojo-loading-message");e&&(e.textContent=t)}},hide(e=!1){e?a=0:a--,a>0||(a=0,r&&(clearTimeout(r),r=null),n&&(n.classList.remove("show"),setTimeout(()=>{n&&0===a&&(n.remove(),n=null)},200)))},isShown:()=>null!==n&&a>0},d="\n max-height: 60vh;\n overflow-y: auto;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 1.25rem;\n border-radius: 0.5rem;\n margin: 0;\n font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', 'Monaco', monospace;\n font-size: 0.9rem;\n line-height: 1.6;\n border: 1px solid #2d2d30;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);\n".replace(/\s+/g," ").trim();class CodeViewer extends e.View{constructor(e={}){super({tagName:"div",className:"mojo-code-viewer",...e}),this.code=e.code||"",this.language=e.language||"javascript"}async getTemplate(){return CodeViewer.formatCode(this.code,this.language)}static formatCode(e,t="javascript"){let i;i=window.Prism&&window.Prism.languages[t]?window.Prism.highlight(e,window.Prism.languages[t],t):String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;");const o=window.Prism?`language-${t}`:"";return`\n <style>\n .dialog-code-block .token.comment { color: #6a9955; }\n .dialog-code-block .token.string { color: #ce9178; }\n .dialog-code-block .token.keyword { color: #569cd6; }\n .dialog-code-block .token.function { color: #dcdcaa; }\n .dialog-code-block .token.number { color: #b5cea8; }\n .dialog-code-block .token.operator { color: #d4d4d4; }\n .dialog-code-block .token.class-name { color: #4ec9b0; }\n .dialog-code-block .token.punctuation { color: #d4d4d4; }\n .dialog-code-block .token.boolean { color: #569cd6; }\n .dialog-code-block .token.property { color: #9cdcfe; }\n .dialog-code-block .token.tag { color: #569cd6; }\n .dialog-code-block .token.attr-name { color: #9cdcfe; }\n .dialog-code-block .token.attr-value { color: #ce9178; }\n .dialog-code-block ::selection { background: #264f78; }\n</style>\n <pre class="${o} dialog-code-block" style="${d}">\n <code class="${o}" style="color: inherit; background: transparent; text-shadow: none;">${i}</code>\n </pre>\n `}static highlightCodeBlocks(e=document){window.Prism&&window.Prism.highlightAllUnder&&window.Prism.highlightAllUnder(e)}}class HtmlPreview extends e.View{constructor(e={}){super({tagName:"div",className:"mojo-html-preview",...e}),this.html=e.html||e.content||"",this.height=e.height||500}async getTemplate(){return`\n <div class="html-preview-container">\n <div class="d-flex justify-content-between align-items-center mb-2">\n <small class="text-muted">Preview (sandboxed)</small>\n <button type="button" class="btn btn-sm btn-outline-secondary" data-action="refresh">\n <i class="bi bi-arrow-clockwise"></i> Refresh\n </button>\n </div>\n <iframe\n class="border rounded w-100 mojo-html-preview-frame"\n style="height: ${this.height}px; background: white;"\n sandbox="allow-same-origin"\n frameborder="0"\n ></iframe>\n </div>\n `}async onAfterMount(){await super.onAfterMount(),this._writeIframe()}onActionRefresh(){this._writeIframe()}_writeIframe(){const e=this.element?.querySelector(".mojo-html-preview-frame");if(!e)return;const t=e.contentDocument||e.contentWindow?.document;t&&(t.open(),t.write(this.html),t.close())}setHtml(e){this.html=e,this._writeIframe()}}class Modal{static _renderAndAwait(e,{buttons:t=null,rejectOnDismiss:i=!1,onAction:o=null,cleanup:s=null}={}){const n=ModalView.getMountTarget();return new Promise((a,r)=>{let l=!1;const d=e=>{l||(l=!0,a(e))},c=e=>{l||(l=!0,r(e))};(async()=>{try{await e.render(!0,n)}catch(a){return void c(a)}t&&t.length>0&&e.element&&e.element.querySelectorAll(".modal-footer button").forEach((i,s)=>{const n=t[s];n&&i.addEventListener("click",async t=>{if(l)return;const i=void 0!==n.value?n.value:n.action??s;if("function"!=typeof n.handler)if("function"==typeof o&&n.action)try{const a=await o(n.action,{dialog:e,button:n,index:s,event:t});if(null===a||!1===a)return;const r=!0===a||void 0===a?i:a;n.dismiss||e.hide(),d(r)}catch(a){console.error("Modal onAction error:",a)}else n.dismiss||e.hide(),d(i);else try{const o=await n.handler({dialog:e,button:n,index:s,event:t});if(null===o||!1===o)return;const a=!0===o||void 0===o?i:o;n.dismiss||e.hide(),d(a)}catch(a){console.error("Modal button handler error:",a)}})}),e.on("hidden",()=>{l||(i?c(new Error("Dialog dismissed")):d(null)),setTimeout(async()=>{try{"function"==typeof s&&await s(e)}catch(a){console.error("Modal cleanup error:",a)}try{await e.destroy()}catch(a){console.error("Modal destroy error:",a)}e.element?.parentNode&&e.element.parentNode.removeChild(e.element)},100)}),e.show()})()})}static async dialog(e={}){"string"==typeof e&&(e={...arguments[2]||{},body:arguments[0],title:arguments[1]||"Alert"});const{title:t="Dialog",content:i,body:o,view:s,message:n,size:a="md",centered:r=!0,buttons:l=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:d=!1,...c}=e,h=new ModalView({title:t,body:o??s??n??i??"",size:a,centered:r,buttons:l,...c});return Modal._renderAndAwait(h,{buttons:l,rejectOnDismiss:d})}static async drawer(t={}){const{eyebrow:i,title:o,meta:s=[],view:n,body:a,size:r="lg",...l}=t,d=s.length?`\n <div class="modal-drawer-meta">\n ${s.map(e=>"string"==typeof e?`<span>${Modal._esc(e)}</span>`:`<span>${e.icon?`<i class="${Modal._esc(e.icon)} me-1"></i>`:""}${Modal._esc(e.text||"")}</span>`).join("")}\n </div>`:"",c=`\n <div class="modal-drawer-head">\n ${i?`<span class="modal-drawer-eyebrow">${Modal._esc(i)}</span>`:""}\n <h2 class="modal-drawer-title">${Modal._esc(o||"")}</h2>\n ${d}\n </div>\n `;let h;return h=n&&"object"==typeof n&&"function"==typeof n.render?new class extends e.View{async getTemplate(){return`${c}<div class="modal-drawer-body" data-container="drawer-body"></div>`}async onInit(){n.containerId="drawer-body",this.addChild(n)}}:`${c}<div class="modal-drawer-body">${a||""}</div>`,Modal.dialog({header:!1,body:h,size:r,centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...l})}static _esc(e){const t="undefined"!=typeof document?document.createElement("div"):null;return t?(t.textContent=String(e??""),t.innerHTML):String(e??"")}static async show(e,t={}){return Modal.dialog({header:void 0!==t.title&&!!t.title,title:t.title||void 0,body:e,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...t})}static async showModel(e,t={}){const i=e.constructor,o=i?.VIEW_CLASS;if(!o)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${i?.name||"model"}. Set ${i?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const s=new o({model:e});return Modal.show(s,t)}static async showModelById(e,t,i={}){const o=new e({id:t});return await o.fetch(),o.id?Modal.showModel(o,i):(Modal.alert({message:`Could not find ${e.name||"record"} with ID: ${t}`,type:"warning"}),null)}static async showModelView(e,t={}){const i=e.constructor,o=i?.VIEW_CLASS;if(!o)throw new Error(`Modal.showModelView: No VIEW_CLASS defined on ${i?.name||"model"}.`);const s=new o({model:e});return Modal.dialog({header:!1,body:s,size:"lg",centered:!1,...t})}static async alert(e={},t,i){let o;o="string"==typeof e?{message:e,...void 0!==t?{title:t}:{},...i||{}}:{...e};const{message:s="",title:n="Alert",type:a="info",icon:r,className:l,...d}=o,c="danger"===a?"error":a,h=[`modal-alert modal-alert-${c}`,l].filter(Boolean).join(" "),u=void 0!==r?r:{info:"bi-info-circle",success:"bi-check-circle",warning:"bi-exclamation-triangle",error:"bi-x-circle"}[c],p=u?`<i class="bi ${u} modal-alert-icon"></i>`:"",m=`<span class="modal-alert-headline">${n}</span>`;return Modal.dialog({title:`${p}${m}`,body:`<p class="modal-alert-message">${s}</p>`,size:"sm",centered:!0,className:h,buttons:[{text:"OK",class:"btn-primary",value:!0}],...d})}static async confirm(e,t="Confirm",i={}){let o;"object"==typeof e&&null!==e?(o=(i=e).message,t=i.title||t):o=e;const s=[{text:i.cancelText||"Cancel",class:"btn-secondary",dismiss:!0,action:"cancel"},{text:i.confirmText||"Confirm",class:i.confirmClass||"btn-primary",action:"confirm"}],n=new ModalView({title:t,body:`<p>${o}</p>`,size:i.size||"sm",centered:!0,backdrop:"static",buttons:s,...i});return"confirm"===await Modal._renderAndAwait(n,{buttons:s})}static async prompt(e,t="Input",i={}){const o=`prompt-input-${Date.now()}`,s=i.defaultValue||"",n=i.inputType||"text",a=i.placeholder||"",r=[{text:"Cancel",class:"btn-secondary",dismiss:!0},{text:"OK",class:"btn-primary",action:"ok"}],l=new ModalView({title:t,body:`\n <p>${e}</p>\n <input type="${n}"\n class="form-control"\n id="${o}"\n value="${s}"\n placeholder="${a}">\n `,size:i.size||"sm",centered:!0,backdrop:"static",buttons:r,...i});return l.on("shown",()=>{const e=l.element.querySelector(`#${o}`);e&&(e.focus(),e.select())}),Modal._renderAndAwait(l,{buttons:r,onAction:async e=>{if("ok"!==e)return null;const t=l.element.querySelector(`#${o}`);return t?t.value:null}})}static showError(e){return Modal.alert(e,"Error",{type:"error"})}static async form(e={}){const{title:t="Form",formConfig:i={},size:o="md",centered:s=!0,submitText:n="Submit",cancelText:a="Cancel",...r}=e,l=new(0,(await Promise.resolve().then(()=>require("./FormView-LRb8scDI.js")).then(e=>e.FormView$1)).default)({fileHandling:e.fileHandling||"base64",data:e.data,defaults:e.defaults,model:e.model,formConfig:{fields:i.fields||e.fields,...i,submitButton:!1,resetButton:!1}}),d=[{text:a,class:"btn-secondary",action:"cancel"},{text:n,class:"btn-primary",action:"submit"}],c=new ModalView({title:t,body:l,size:o,centered:s,buttons:d,...r});return Modal._renderAndAwait(c,{buttons:d,onAction:async t=>{if("cancel"===t)return c.hide(),null;if("submit"!==t)return null;if(!l.validate())return l.focusFirstError(),!1;if(e.autoSave&&e.model){c.setLoading(!0);const e=await l.saveModel();return e.success?e:(c.setLoading(!1),await c.render(),c.getApp()?.toast?.error(e.message),!1)}try{return await l.getFormData()}catch(i){return console.error("Modal.form: error collecting form data:",i),l.showError("Error collecting form data"),!1}},cleanup:async()=>{try{await l.destroy()}catch{}}})}static async modelForm(e={}){const{title:t="Edit",formConfig:i={},size:o="md",centered:s=!0,submitText:n="Save",cancelText:a="Cancel",model:r,fields:l,...d}=e;if(!r)throw new Error("Modal.modelForm requires a model");const c=new(0,(await Promise.resolve().then(()=>require("./FormView-LRb8scDI.js")).then(e=>e.FormView$1)).default)({fileHandling:e.fileHandling||"base64",model:r,data:e.data,defaults:e.defaults,formConfig:{fields:l||i.fields||[],...i,submitButton:!1,resetButton:!1}}),h=[{text:a,class:"btn-secondary",action:"cancel"},{text:n,class:"btn-primary",action:"submit"}],u=new ModalView({title:t,body:c,size:o,centered:s,buttons:h,...d});return Modal._renderAndAwait(u,{buttons:h,onAction:async e=>{if("cancel"===e)return u.hide(),null;if("submit"!==e)return null;u.setLoading(!0,"Saving...");try{const e=await c.handleSubmit();if(e.success)return e;u.setLoading(!1);let t=e.error;return e.data?.error&&(t=e.data.error),u.getApp()?.toast?.error(t),!1}catch(t){return console.error("Modal.modelForm: error saving:",t),await u.setContent(c),c.showError(t.message||"An error occurred while saving"),!1}},cleanup:async()=>{try{await c.destroy()}catch{}}})}static async data(e={}){const{title:t="Data View",data:i={},model:o=null,fields:s=[],columns:n=2,responsive:a=!0,showEmptyValues:r=!1,emptyValueText:l="—",size:d="lg",centered:c=!0,closeText:h="Close",...u}=e,p=new(0,(await Promise.resolve().then(()=>require("./DataView-DFGIE3wK.js"))).default)({data:i,model:o,fields:s,columns:n,responsive:a,showEmptyValues:r,emptyValueText:l}),m=[{text:h,class:"btn-secondary",value:"close"}],w=new ModalView({title:t,body:p,size:d,centered:c,buttons:m,...u});return p.on("field:click",e=>w.emit("dataview:field:click",e)),p.on("error",e=>w.emit("dataview:error",e)),Modal._renderAndAwait(w,{buttons:m,cleanup:async()=>{try{await p.destroy()}catch{}}})}static async code(e={}){const{code:t="",language:i="javascript",title:o="Source Code",size:s="lg",...n}=e,a=new CodeViewer({code:t,language:i}),r=[{text:"Copy to Clipboard",class:"btn-primary",icon:"bi-clipboard",action:"copy"},{text:"Close",class:"btn-secondary",dismiss:!0}],l=new ModalView({title:o,body:a,size:s,scrollable:!0,buttons:r,...n});return Modal._renderAndAwait(l,{buttons:r,onAction:async e=>{if("copy"!==e)return null;if(!navigator.clipboard)return!1;try{await navigator.clipboard.writeText(t),Modal._showCopySuccess(l)}catch(i){console.error("Modal.code: clipboard write failed:",i)}return!1}})}static _showCopySuccess(e){const t=e.element?.querySelector('[data-action="copy"]');if(!t)return;const i=t.innerHTML;t.innerHTML='<i class="bi bi-check me-1"></i>Copied!',t.classList.remove("btn-primary"),t.classList.add("btn-success"),t.disabled=!0,setTimeout(()=>{t.innerHTML=i,t.classList.remove("btn-success"),t.classList.add("btn-primary"),t.disabled=!1},2e3)}static async htmlPreview(e={}){const{html:t=e.content||"",title:i="HTML Preview",size:o="lg",height:s=500,...n}=e,a=new HtmlPreview({html:t,height:s}),r=[{text:"Close",class:"btn-secondary",dismiss:!0}],l=new ModalView({title:i,body:a,size:o,scrollable:!1,buttons:r,...n});return Modal._renderAndAwait(l,{buttons:r})}static async updateModelImage(e={},t={}){const i=e.upload||!1,o=t.name||e.field||"image",n={title:"Upload Your Avatar",model:null,autoSave:!i,size:"sm",fields:[{type:"image",name:o,size:"lg",imageSize:{width:200,height:200},placeholder:"Upload your image",...t}],...e},a=await Modal.form(n);if(!i||!a||!e.model)return a;const r=a[o];if(!r||!r.startsWith("data:"))return a;const l=r.split(","),d=l[0]?.match(/:(.*?);/),c=d?.[1]||"image/png",h=atob(l[1]);let u=h.length;const p=new Uint8Array(u);for(;u--;)p[u]=h.charCodeAt(u);const m=c.split("/")[1]||"png",w="undefined"!=typeof window&&window.File||globalThis.File;if(!w)throw new Error("File API is not available in this environment");const g=new w([p],`${o}.${m}`,{type:c}),f=new s;return await f.upload({file:g,name:`${o}.${m}`,description:e.uploadDescription||`${o} upload`,showToast:!0}),e.model.save({[o]:f.id})}static loading(e){l.show(e)}static hideLoading(e){l.hide(e)}static showBusy(e){return Modal.loading(e)}static hideBusy(e){return Modal.hideLoading(e)}}const c=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,default:Modal},Symbol.toStringTag,{value:"Module"}));exports.CodeViewer=CodeViewer,exports.File=s,exports.FileForms={create:{title:"Add File",fields:[]},edit:{title:"Edit File Backend",fields:[]}},exports.FileList=FileList,exports.FileManager=FileManager,exports.FileManagerForms=o,exports.FileManagerList=FileManagerList,exports.FileUpload=FileUpload,exports.Modal=Modal,exports.Modal$1=c,exports.ModalView=ModalView,exports.ProgressView=ProgressView,exports.ToastService=ToastService;
3
- //# sourceMappingURL=Modal-BRKy85bz.js.map
@@ -1,3 +0,0 @@
1
- import{V as e}from"./View-gAghvPMN.js";import{d as t,M as i,C as o}from"./Collection-Cgxbmj8G.js";import{G as s,a as n}from"./User-CayBjzMG.js";class ToastService{constructor(e={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:3e3,maxToasts:5,...e},this.toasts=/* @__PURE__ */new Map,this.toastCounter=0,this.init()}init(){this.createContainer()}createContainer(){let e=document.getElementById(this.options.containerId);e||(e=document.createElement("div"),e.id=this.options.containerId,e.className=`toast-container position-fixed ${this.getPositionClasses()}`,e.style.zIndex="1070",e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),document.body.appendChild(e)),this.container=e}getPositionClasses(){const e={"top-start":"top-0 start-0 p-3","top-center":"top-0 start-50 translate-middle-x p-3","top-end":"top-0 end-0 p-3","middle-start":"top-50 start-0 translate-middle-y p-3","middle-center":"top-50 start-50 translate-middle p-3","middle-end":"top-50 end-0 translate-middle-y p-3","bottom-start":"bottom-0 start-0 p-3","bottom-center":"bottom-0 start-50 translate-middle-x p-3","bottom-end":"bottom-0 end-0 p-3"};return e[this.options.position]||e["top-end"]}success(e,t={}){return this.show(e,"success",{icon:"bi-check-circle-fill",...t})}error(e,t={}){return this.show(e,"error",{icon:"bi-exclamation-triangle-fill",autohide:!0,...t})}info(e,t={}){return this.show(e,"info",{icon:"bi-info-circle-fill",...t})}warning(e,t={}){return this.show(e,"warning",{icon:"bi-exclamation-triangle-fill",...t})}plain(e,t={}){return this.show(e,"plain",{...t})}show(e,t="info",i={}){this.enforceMaxToasts();const o="toast-"+ ++this.toastCounter,s={title:this.getDefaultTitle(t),icon:this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...i},n=this.createToastElement(o,e,t,s);if(this.container.appendChild(n),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const a=new bootstrap.Toast(n,{autohide:s.autohide,delay:s.delay});return this.toasts.set(o,{element:n,bootstrap:a,type:t,message:e}),n.addEventListener("hidden.bs.toast",()=>{this.cleanup(o)}),a.show(),{id:o,hide:()=>{try{a.hide()}catch(e){console.warn("Error hiding toast:",e)}},dispose:()=>this.cleanup(o),updateProgress:i.updateProgress||null}}showView(e,t="info",i={}){this.enforceMaxToasts();const o="toast-"+ ++this.toastCounter,s={title:i.title||this.getDefaultTitle(t),icon:i.icon||this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,size:"md",...i},n=this.createViewToastElement(o,e,t,s);if(this.container.appendChild(n),"undefined"==typeof bootstrap)throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const a=new bootstrap.Toast(n,{autohide:s.autohide,delay:s.delay});this.toasts.set(o,{element:n,bootstrap:a,type:t,view:e,message:"View toast"}),n.addEventListener("hidden.bs.toast",()=>{this.cleanupView(o)});const r=n.querySelector(".toast-view-body");return r&&e&&e.render(!0,r),a.show(),{id:o,view:e,hide:()=>{try{a.hide()}catch(e){console.warn("Error hiding view toast:",e)}},dispose:()=>this.cleanupView(o),updateProgress:t=>{e&&"function"==typeof e.updateProgress&&e.updateProgress(t)}}}createToastElement(e,t,i,o){const s=document.createElement("div");s.id=e,s.className=`toast toast-service-${i}${o.size?` toast-${o.size}`:""}`,s.setAttribute("role","alert"),s.setAttribute("aria-live","assertive"),s.setAttribute("aria-atomic","true");const n=o.title||o.icon?this.createToastHeader(o,i):"",a=this.createToastBody(t,o.icon&&!o.title);return s.innerHTML=`\n ${n}\n ${a}\n `,s}createViewToastElement(e,t,i,o){const s=document.createElement("div");s.id=e,s.className=`toast toast-service-${i}${o.size?` toast-${o.size}`:""}`,s.setAttribute("role","alert"),s.setAttribute("aria-live","assertive"),s.setAttribute("aria-atomic","true");const n=o.title||o.icon?this.createToastHeader(o,i):"",a=this.createViewToastBody();return s.innerHTML=`\n ${n}\n ${a}\n `,s}createViewToastBody(){return'\n <div class="toast-body p-0">\n <div class="toast-view-body p-3"></div>\n </div>\n '}createToastHeader(e,t){const i=e.icon?`<i class="${e.icon} toast-service-icon me-2"></i>`:"",o=e.title?`<strong class="me-auto">${i}${this.escapeHtml(e.title)}</strong>`:"",s=e.showTime?`<small class="text-muted">${this.getTimeString()}</small>`:"",n=e.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return o||s||n?`\n <div class="toast-header">\n ${o}\n ${s}\n ${n}\n </div>\n `:""}createToastBody(e,t=!1){return`\n <div class="toast-body d-flex align-items-center">\n ${t?`<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>`:""}\n <span>${this.escapeHtml(e)}</span>\n </div>\n `}getDefaultTitle(e){return{success:"Success",error:"Error",warning:"Warning",info:"Information",plain:""}[e]||"Notification"}getDefaultIcon(e){return{success:"bi-check-circle-fill",error:"bi-exclamation-triangle-fill",warning:"bi-exclamation-triangle-fill",info:"bi-info-circle-fill",plain:""}[e]||"bi-info-circle-fill"}enforceMaxToasts(){if(Array.from(this.toasts.values()).length>=this.options.maxToasts){const e=this.toasts.keys().next().value,t=this.toasts.get(e);t&&t.bootstrap.hide()}}cleanup(e){const t=this.toasts.get(e);if(t){try{t.bootstrap.dispose()}catch(i){console.warn("Error disposing toast:",i)}t.element&&t.element.parentNode&&t.element.parentNode.removeChild(t.element),this.toasts.delete(e)}}cleanupView(e){const t=this.toasts.get(e);if(t){if(t.view&&"function"==typeof t.view.dispose)try{t.view.dispose()}catch(i){console.warn("Error disposing view in toast:",i)}try{t.bootstrap.dispose()}catch(i){console.warn("Error disposing toast:",i)}t.element&&t.element.parentNode&&t.element.parentNode.removeChild(t.element),this.toasts.delete(e)}}hideAll(){this.toasts.forEach((e,t)=>{e.bootstrap.hide()})}clearAll(){this.toasts.forEach((e,t)=>{this.cleanup(t)})}getTimeString(){/* @__PURE__ */
2
- return(new Date).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}dispose(){this.clearAll(),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container)}getStats(){const e={total:this.toasts.size,byType:{}};return this.toasts.forEach(t=>{e.byType[t.type]=(e.byType[t.type]||0)+1}),e}setOptions(e){this.options={...this.options,...e},e.position&&this.container&&(this.container.className=`toast-container position-fixed ${this.getPositionClasses()}`)}}class ProgressView extends e{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=t.pipe(this.filesize,"filesize"),this.progress=0,this.percentage=0,this.loaded=0,this.total=this.filesize,this.loadedFormatted="0 B",this.totalFormatted=this.filesizeFormatted,this.status="Starting upload...",this.showCancel=!1!==e.showCancel,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return'\n <div class="progress-view">\n <div class="d-flex justify-content-between align-items-start mb-2">\n <div class="flex-grow-1 min-width-0">\n <div class="fw-medium text-truncate" title="{{filename}}">\n <i class="bi bi-file-earmark me-1"></i>\n {{filename}}\n </div>\n <small class="text-muted">{{status}}</small>\n </div>\n {{#showCancel}}\n <button type="button" \n class="btn btn-sm btn-outline-secondary ms-2" \n data-action="cancel"\n {{#cancelled}}disabled{{/cancelled}}>\n <i class="bi bi-x"></i>\n </button>\n {{/showCancel}}\n </div>\n \n <div class="progress mb-2" style="height: 8px;">\n <div class="progress-bar" \n role="progressbar" \n style="width: {{percentage}}%"\n aria-valuenow="{{percentage}}" \n aria-valuemin="0" \n aria-valuemax="100">\n </div>\n </div>\n \n <div class="d-flex justify-content-between">\n <small class="text-muted">\n {{loadedFormatted}} / {{totalFormatted}}\n </small>\n <small class="text-muted">\n {{percentage}}%\n </small>\n </div>\n </div>\n '}updateProgress(e){this.cancelled||this.completed||(this.progress=e.progress,this.percentage=e.percentage,this.loaded=e.loaded,this.total=e.total||this.filesize,this.loadedFormatted=t.pipe(this.loaded,"filesize"),this.totalFormatted=t.pipe(this.total,"filesize"),this.percentage<100?this.status=`Uploading... ${this.percentage}%`:this.status="Finalizing upload...",this.render())}markCompleted(e="Upload completed!"){this.completed=!0,this.progress=1,this.percentage=100,this.status=e,this.render()}markFailed(e="Upload failed"){this.status=e,this.render()}markCancelled(){this.cancelled=!0,this.status="Upload cancelled",this.render()}async onActionCancel(e,t,i){if(!this.cancelled&&!this.completed&&(i.disabled=!0,this.markCancelled(),this.emit("cancel"),"function"==typeof this.onCancel))try{await this.onCancel()}catch(o){console.error("Error in cancel callback:",o)}}setFilename(e){this.filename=e,this.render()}setFilesize(e){this.filesize=e,this.filesizeFormatted=t.pipe(e,"filesize"),this.total=e,this.totalFormatted=this.filesizeFormatted,this.render()}getPercentage(){return this.percentage}isCompleted(){return this.completed}isCancelled(){return this.cancelled}getStats(){return{filename:this.filename,filesize:this.filesize,progress:this.progress,percentage:this.percentage,loaded:this.loaded,total:this.total,cancelled:this.cancelled,completed:this.completed,status:this.status}}}class FileUpload{constructor(e,t={}){if(this.fileModel=e,this.options={file:null,name:null,group:null,description:null,onProgress:null,onComplete:null,onError:null,showToast:!0,...t},!(this.options.file&&this.options.file instanceof File))throw new Error("FileUpload requires a valid File object");this.cancelled=!1,this.uploadRequest=null,this.progressToast=null,this.progressView=null,this.toastService=null,this.options.showToast&&(this.toastService=new ToastService),this.promise=this._startUpload()}async _startUpload(){try{let t,i,o;this.options.showToast&&this._showProgressToast();try{t=await this._initiateUpload()}catch(e){throw new Error(`Failed to initiate upload: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");if(!t||!t.upload_url)throw new Error("Invalid upload response: missing upload URL");if("string"==typeof t.upload_url)i={url:t.upload_url,method:"PUT",fields:null,headers:{}};else{if(!t.upload_url||"object"!=typeof t.upload_url||!t.upload_url.upload_url)throw new Error(`Invalid upload response: unrecognised upload_url format. Server returned: ${JSON.stringify(t.upload_url)}`);i={url:t.upload_url.upload_url,method:t.upload_url.method||"POST",fields:t.upload_url.fields||null,headers:t.upload_url.headers||{}}}try{o=await this._performUpload(i)}catch(e){throw new Error(`File upload failed: ${e.message}`)}if(this.cancelled)throw new Error("Upload cancelled");try{await this._completeUpload()}catch(e){console.warn("Failed to mark upload as completed:",e)}return this._onComplete(this.fileModel),this.fileModel}catch(e){throw"Upload cancelled"!==e.message&&this._onError(e),e}}async _initiateUpload(){try{const e={filename:this.options.name||this.options.file.name,file_size:this.options.file.size,content_type:this.options.file.type};this.options.group&&(e.group=this.options.group),this.options.description&&(e.description=this.options.description);const t=await this.fileModel.rest.POST("/api/fileman/upload/initiate",e);if(!t)throw new Error("No response from upload initiation API");if(!t.data)throw new Error("Upload initiation response missing data");if(!t.data.status){const e=t.data.error||"Upload initiation failed";throw new Error(e)}if(!t.data.data)throw new Error("Upload initiation response missing data payload");return t.data.data.id&&this.fileModel.set("id",t.data.data.id),t.data.data}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload initiation. Please check your connection.");throw e}}async _performUpload(e){return new Promise((t,i)=>{if(!(this.options.file instanceof File))return void i(new Error("Only single File objects are supported"));const{url:o,method:s,fields:n,headers:a}=e,r="POST"===s&&null!==n,l=new XMLHttpRequest;this.uploadRequest=l,l.upload.onprogress=e=>{this.cancelled||this._onProgress({progress:e.loaded/e.total,loaded:e.loaded,total:e.total,percentage:Math.round(e.loaded/e.total*100)})},l.onload=()=>{l.status>=200&&l.status<300?t({data:l.response,status:l.status,statusText:l.statusText,xhr:l}):i(new Error(`Upload failed: ${l.status} ${l.statusText}`))},l.onerror=()=>i(new Error("Upload failed: Network error")),l.ontimeout=()=>i(new Error("Upload timed out — file may be too large or connection too slow")),l.onabort=()=>i(new Error("Upload cancelled"));let d=o;o.startsWith("/")&&!o.startsWith("/api/")&&(d="/api"+o);const c=this.fileModel.rest.buildUrl(d);if(l.open(s,c),l.timeout=3e4,r){for(const[t,i]of Object.entries(a||{}))"content-type"!==t.toLowerCase()&&l.setRequestHeader(t,i);const e=new FormData;for(const[t,i]of Object.entries(n))e.append(t,i);e.append("file",this.options.file),l.send(e)}else{l.setRequestHeader("Content-Type",this.options.file.type);for(const[e,t]of Object.entries(a||{}))"content-type"!==e.toLowerCase()&&l.setRequestHeader(e,t);l.send(this.options.file)}})}async _completeUpload(){try{const e=await this.fileModel.save({action:"mark_as_completed"});if(!e)throw new Error("No response from upload completion API");if(e.data&&!e.data.status){const t=e.data.error||"Failed to mark upload as completed";throw new Error(t)}return e}catch(e){if("Network Error"===e.message||"TypeError"===e.name)throw new Error("Network error during upload completion. The file may have uploaded successfully.");throw e}}_onProgress(e){this.progressToast&&this.progressToast.updateProgress&&this.progressToast.updateProgress(e),"function"==typeof this.options.onProgress&&this.options.onProgress(e)}_onComplete(e){this.progressView&&this.progressView.markCompleted("Upload completed successfully!"),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast:",e)}},2e3),"function"==typeof this.options.onComplete&&this.options.onComplete(e)}_onError(e){if(this.progressToast)try{this.progressToast.hide()}catch(t){console.warn("Error hiding progress toast on error:",t)}this.toastService&&this.toastService.error(`Upload failed: ${e.message}`),"function"==typeof this.options.onError&&this.options.onError(e)}_showProgressToast(){this.progressView=new ProgressView({filename:this.options.name||this.options.file.name,filesize:this.options.file.size,showCancel:!0,onCancel:()=>this.cancel()}),this.progressToast=this.toastService.showView(this.progressView,"info",{title:"File Upload",autohide:!1,dismissible:!1})}cancel(){return!this.cancelled&&(this.cancelled=!0,this.uploadRequest&&"function"==typeof this.uploadRequest.abort&&this.uploadRequest.abort(),this.progressView&&this.progressView.markCancelled(),this.progressToast&&setTimeout(()=>{try{this.progressToast&&"function"==typeof this.progressToast.hide&&this.progressToast.hide()}catch(e){console.warn("Error hiding progress toast on cancel:",e)}},1500),!0)}isCancelled(){return this.cancelled}then(e,t){return this.promise.then(e,t)}catch(e){return this.promise.catch(e)}finally(e){return this.promise.finally(e)}getStats(){return{filename:this.options.file.name,size:this.options.file.size,type:this.options.file.type,cancelled:this.cancelled,group:this.options.group,description:this.options.description}}}class FileManager extends i{constructor(e={}){super(e,{endpoint:"/api/fileman/manager"})}}class FileManagerList extends o{constructor(e={}){super({ModelClass:FileManager,endpoint:"/api/fileman/manager",size:10,...e})}}const a={create:{title:"Add Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"use",type:"text",label:"Use",placeholder:"Enter User or Leave Blank",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,value:"s3://BUCKET_NAME/OPTION_FOLDER",placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6}]},edit:{title:"Edit Storage Backend",fields:[{name:"name",type:"text",label:"Display Name",placeholder:"Enter Display Name",cols:12},{name:"use",type:"text",label:"Use",placeholder:"Enter User or Leave Blank",cols:12},{name:"backend_url",type:"text",label:"Backend URL",required:!0,placeholder:"s3://bucket_name/optional folder",help:"Format: service://path. Valid services: s3",cols:12},{name:"allowed_origins",type:"text",label:"Domains Who Can Upload",cols:12},{name:"is_default",type:"switch",label:"Is Default",cols:6},{name:"is_active",type:"switch",label:"Is Active",default:!0,cols:6},{name:"is_public",type:"switch",label:"Is Public",default:!0,cols:6}]},owners:{fields:[{type:"collection",name:"group",label:"Group (Owner)",Collection:s,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300},{type:"collection",name:"user",label:"User (Owner)",Collection:n,labelField:"display_name",valueField:"id",maxItems:10,placeholder:"Search users...",emptyFetch:!1,debounceMs:300}]},credentials:{fields:[{name:"aws_region",type:"select",label:"AWS Region (optional)",value:"us-east-1",options:[{value:"",text:"System Default"},{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with S3 permissions",columns:12,help:"Optional, AWS Key with S3 permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with S3 permissions",columns:12,help:"Optional, AWS Secret with S3 permissions"}]}};let r=class extends i{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return"image"===this.get("category")}getCategory(){return this.get("category")||this._inferCategoryFromContentType()}_inferCategoryFromContentType(){const e=(this.get("content_type")||"").toLowerCase();return e?e.startsWith("image/")?"image":e.startsWith("video/")?"video":e.startsWith("audio/")?"audio":"application/pdf"===e?"pdf":e.startsWith("text/")||"application/msword"===e||e.startsWith("application/vnd.openxmlformats-officedocument.wordprocessingml")||"application/vnd.oasis.opendocument.text"===e?"document":"application/vnd.ms-excel"===e||e.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml")||"application/vnd.oasis.opendocument.spreadsheet"===e?"spreadsheet":"application/vnd.ms-powerpoint"===e||e.startsWith("application/vnd.openxmlformats-officedocument.presentationml")||"application/vnd.oasis.opendocument.presentation"===e?"presentation":"application/zip"===e||"application/x-rar-compressed"===e||"application/x-7z-compressed"===e||"application/x-tar"===e||"application/gzip"===e?"archive":"other":"other"}hasRenditions(){const e=this.get("renditions");return!(!e||!Object.keys(e).length)}isUploadPending(){const e=this.get("upload_status");return!(!e||"completed"===e||"failed"===e)}regenerateRenditions(e){const t=this.id||this.get("id");if(!t)return Promise.reject(new Error("Cannot regenerate renditions on an unsaved file"));const i=Array.isArray(e)&&e.length?{regenerate_renditions:e}:{regenerate_renditions:!0};return this.rest.POST(`${this.endpoint}/${t}`,i)}share(e=!0){const t=this.id||this.get("id");return t?this.rest.POST(`${this.endpoint}/${t}`,{share:e}):Promise.reject(new Error("Cannot share an unsaved file"))}getRenditions(){const e=this.get("renditions");return e?Object.values(e):[]}getBestImageRendition(){const e=this.getRenditions().filter(e=>e&&"string"==typeof e.content_type&&e.content_type.startsWith("image/"));return e.length?e.reduce((e,t)=>{const i=(parseInt(e.width)||0)*(parseInt(e.height)||0);return(parseInt(t.width)||0)*(parseInt(t.height)||0)>i?t:e}):null}getThumbnailUrl(){const e=this.get("renditions")||{};if(e.thumbnail&&e.thumbnail.url)return e.thumbnail.url;const t=this.getBestImageRendition();return t?t.url:null}upload(e={}){return new FileUpload(this,e)}};class FileList extends o{constructor(e={}){super({ModelClass:r,endpoint:"/api/fileman/file",size:10,...e})}}const l={create:{title:"Add File",fields:[]},edit:{title:"Edit File Backend",fields:[]}};class ModalView extends e{static _openDialogs=[];static _baseZIndex={backdrop:1050,modal:1055};static getFullscreenAwareZIndex(){return document.querySelector(".table-fullscreen")?{backdrop:10040,modal:10050}:ModalView._baseZIndex}static fixAllBackdropStacking(){const e=document.querySelectorAll(".modal-backdrop"),t=ModalView._openDialogs;if(0===e.length||0===t.length)return;const i=[...t].sort((e,t)=>(e._dialogZIndex||0)-(t._dialogZIndex||0)),o=document.querySelector(".table-fullscreen")||document.body;e.forEach((e,t)=>{if(t>=i.length)return;const s=i[t];e.style.zIndex=s._dialogZIndex-5,e.parentNode!==o&&o.appendChild(e)})}static updateAllBackdropStacking(){ModalView.fixAllBackdropStacking()}static getMountTarget(){return document.querySelector(".table-fullscreen")||document.body}constructor(e={}){const t=e.id||`modal-${Date.now()}-${Math.random().toString(36).slice(2,7)}`;super({...e,id:t,tagName:"div",className:`modal ${!1!==e.fade?"fade":""} ${e.className||""}`.trim().replace(/\s+/g," "),attributes:{tabindex:"-1","aria-hidden":"true","aria-labelledby":e.labelledBy||`${t}-label`,"aria-describedby":e.describedBy||null,...e.attributes}}),this.modalId=t,this.title=e.title||"",this.titleId=`${this.modalId}-label`,this.size=e.size||"",this.centered=void 0!==e.centered&&e.centered,this.scrollable=void 0!==e.scrollable&&e.scrollable,this.autoSize=e.autoSize||"auto"===e.size,this.backdrop=void 0===e.backdrop||e.backdrop,this.keyboard=void 0===e.keyboard||e.keyboard,this.focus=void 0===e.focus||e.focus,this.header=void 0===e.header||e.header,this.headerContent=e.headerContent||null,this.headerView=null,this.closeButton=void 0===e.closeButton||e.closeButton,this.contextMenu=e.contextMenu||null,this._processHeaderContent(this.headerContent),this.body=e.body??e.view??e.message??e.content??"",this.bodyView=null,this.bodyClass=e.bodyClass||"",this.noBodyPadding=e.noBodyPadding||!1,this.minWidth=e.minWidth||300,this.minHeight=e.minHeight||200,e.maxHeight&&(this.maxHeight=e.maxHeight),this.maxWidthPercent=e.maxWidthPercent||.9,this.maxHeightPercent=e.maxHeightPercent||.8,this._processBodyContent(this.body),this.footer=e.footer||null,this.footerView=null,this.footerClass=e.footerClass||"",this._processFooterContent(this.footer),this.buttons=e.buttons||null,this.onShow=e.onShow||null,this.onShown=e.onShown||null,this.onHide=e.onHide||null,this.onHidden=e.onHidden||null,this.onHidePrevented=e.onHidePrevented||null,this.autoShow=void 0!==e.autoShow&&e.autoShow,this.modal=null,this.relatedTarget=e.relatedTarget||null}_processBodyContent(t){if(t instanceof e||t&&"object"==typeof t&&"function"==typeof t.render)this.bodyView=t,this.body="",this.addChild(this.bodyView);else if("function"==typeof t)try{const i=t();i instanceof e?(this.bodyView=i,this.body="",this.addChild(this.bodyView)):i instanceof Promise?(this.bodyPromise=i,this.body='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.body=i}catch(i){console.error("ModalView: error processing body function:",i),this.body=t}else this.body=t}_processHeaderContent(t){if(t instanceof e)this.headerView=t,this.headerContent=null,this.addChild(this.headerView);else if("function"==typeof t)try{const i=t();i instanceof e?(this.headerView=i,this.headerContent=null,this.addChild(this.headerView)):i instanceof Promise?(this.headerPromise=i,this.headerContent='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.headerContent=i}catch(i){console.error("ModalView: error processing headerContent function:",i),this.headerContent=t}else this.headerContent=t}_processFooterContent(t){if(t instanceof e)this.footerView=t,this.footer=null,this.addChild(this.footerView);else if("function"==typeof t)try{const i=t();i instanceof e?(this.footerView=i,this.footer=null,this.addChild(this.footerView)):i instanceof Promise?(this.footerPromise=i,this.footer='<div class="text-center"><div class="spinner-border spinner-border-sm"></div></div>'):this.footer=i}catch(i){console.error("ModalView: error processing footer function:",i),this.footer=t}else this.footer=t}async getTemplate(){const e=["modal-dialog"];return this.size&&"auto"!==this.size&&(this.size.startsWith("fullscreen")?e.push(`modal-${this.size}`):["sm","lg","xl","xxl"].includes(this.size)&&(e.push(`modal-${this.size}`),["lg","xl","xxl"].includes(this.size)&&e.push("modal-fullscreen-sm-down"))),this.centered&&e.push("modal-dialog-centered"),this.scrollable&&(this.maxHeight?e.push("overflow-hidden"):e.push("modal-dialog-scrollable")),`\n <div class="${e.join(" ")}">\n <div class="modal-content">\n ${await this.buildHeader()}\n ${await this.buildBody()}\n ${await this.buildFooter()}\n </div>\n </div>\n `}async buildHeader(){if(!this.header)return"";if(this.headerView)return this.headerView.replaceById=!0,`<div class="modal-header" data-view-container="header">\n <div id="${this.headerView.id}"></div>\n </div>`;if(this.headerContent)return`<div class="modal-header">${this.headerContent}</div>`;let e="";return this.contextMenu&&this.contextMenu.items&&this.contextMenu.items.length>0?e=await this.buildContextMenu():this.closeButton&&(e='<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>'),`\n <div class="modal-header">\n ${this.title?`<h5 class="modal-title" id="${this.titleId}">${this.title}</h5>`:""}\n ${e}\n </div>\n `}async buildContextMenu(){const e=await this.filterContextMenuItems();if(0===e.length)return this.closeButton?'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>':"";const t=this.contextMenu.icon||"bi-three-dots-vertical";return`\n <div class="dropdown">\n <button class="${this.contextMenu.buttonClass||"btn btn-link p-1 mojo-modal-context-menu-btn"}" type="button" data-bs-toggle="dropdown" aria-expanded="false">\n <i class="${t}"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n ${e.map(e=>{if("divider"===e.type)return'<li><hr class="dropdown-divider"></li>';const t=e.icon?`<i class="${e.icon} me-2"></i>`:"",i=e.label||"";if(e.href)return`<li><a class="dropdown-item" href="${e.href}"${e.target?` target="${e.target}"`:""}>${t}${i}</a></li>`;if(e.action){const o=Object.keys(e).filter(e=>e.startsWith("data-")).map(t=>`${t}="${e[t]}"`).join(" ");return`<li><a class="dropdown-item" data-action="${e.action}" ${o}>${t}${i}</a></li>`}return""}).join("")}\n </ul>\n </div>\n `}async filterContextMenuItems(){if(!this.contextMenu||!this.contextMenu.items)return[];const e=[];for(const i of this.contextMenu.items)if("divider"!==i.type){if(i.permissions)try{const e=this.getApp?.();let t=e?.activeUser||e?.getState?.("activeUser")||null;if(!t&&"undefined"!=typeof window&&window.getApp)try{t=window.getApp()?.activeUser}catch{}if(!t?.hasPermission)continue;if(!t.hasPermission(i.permissions))continue}catch(t){console.warn("ModalView: error checking permissions for context menu item:",t);continue}e.push(i)}else e.push(i);return e}async buildBody(){const e=`modal-body ${this.noBodyPadding?"modal-body-flush":""} ${this.bodyClass}`.replace(/\s+/g," ").trim();return this.bodyView?(this.bodyView.replaceById=!0,`<div class="${e}" data-view-container="body">\n <div id="${this.bodyView.id}"></div>\n </div>`):this.body||""===this.body?`<div class="${e}">${this.body}</div>`:""}async buildFooter(){if(this.footerView)return`<div class="modal-footer ${this.footerClass}" data-view-container="footer"></div>`;if(null!==this.footer&&"string"==typeof this.footer)return`<div class="modal-footer ${this.footerClass}">${this.footer}</div>`;if(this.buttons&&this.buttons.length>0){const e=this.buttons.map(e=>{const t=e.dismiss?'data-bs-dismiss="modal"':"",i=e.action?`data-action="${e.action}"`:"",o=e.id?`id="${e.id}"`:"",s=e.disabled?"disabled":"";return`\n <button type="${e.type||"button"}"\n class="btn ${e.class||"btn-secondary"}"\n ${o} ${t} ${i} ${s}>\n ${e.icon?`<i class="bi ${e.icon} me-1"></i>`:""}\n ${e.text||"Button"}\n </button>\n `}).join("");return`<div class="modal-footer ${this.footerClass}">${e}</div>`}return""}async mount(e=null){if(!this.mounted&&!this.destroyed){if(!this.element)throw new Error("Cannot mount modal without element");return await this.onBeforeMount(),ModalView.getMountTarget().appendChild(this.element),this.bindEvents(),this.mounted=!0,await this.onAfterMount(),this.emit("mounted",{view:this}),this}}async onAfterRender(){if(await super.onAfterRender(),window.Prism&&this.element&&this.element.querySelectorAll("pre code").length>0&&window.Prism.highlightAllUnder(this.element),this.autoSize)this.setupAutoSizing();else if(this.maxHeight){const e=this.element.querySelector(".modal-body");e&&(e.style.maxHeight=`${this.maxHeight}px`)}}async onAfterMount(){await super.onAfterMount(),"undefined"!=typeof window&&window.bootstrap?.Modal&&("static"===this.backdrop&&this.element.setAttribute("data-bs-backdrop","static"),this.keyboard||this.element.setAttribute("data-bs-keyboard","false"),this.modal=new window.bootstrap.Modal(this.element,{backdrop:this.backdrop,keyboard:this.keyboard,focus:this.focus}),this.bindBootstrapEvents(),this.autoShow&&this.show(this.relatedTarget))}setupAutoSizing(){this.element&&(this.element.addEventListener("shown.bs.modal",()=>{this.applyAutoSizing()},{once:!0}),setTimeout(()=>{this.isShown()&&this.applyAutoSizing()},100))}applyAutoSizing(){if(this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),i=this.element.querySelector(".modal-body");if(!e||!t||!i)return void console.warn("ModalView auto-sizing: required elements not found");if(this.bodyView&&!this.bodyView.element)return void setTimeout(()=>this.applyAutoSizing(),50);const o={dialogMaxWidth:e.style.maxWidth,dialogWidth:e.style.width,contentWidth:t.style.width,contentMaxHeight:t.style.maxHeight,hadScrollableClass:e.classList.contains("modal-dialog-scrollable")};e.style.maxWidth="none",e.style.width="auto",t.style.width="auto",t.style.maxHeight="none",t.offsetHeight;const s=t.getBoundingClientRect(),n=40,a=Math.min(window.innerWidth*this.maxWidthPercent,window.innerWidth-n);let r=Math.min(window.innerHeight*this.maxHeightPercent,window.innerHeight-n),l=Math.max(this.minWidth,Math.ceil(s.width+20)),d=Math.max(this.minHeight,Math.ceil(s.height));this.maxHeight&&(r=Math.min(this.maxHeight,r),e.style.maxHeight=`${r}px`),l=Math.min(l,a);const c=s.height>r;e.style.maxWidth=`${l}px`,e.style.width=`${l}px`,c&&(e.classList.contains("modal-dialog-scrollable")||e.classList.add("modal-dialog-scrollable"),t.style.maxHeight=`${r}px`,d=r),this.autoSizedWidth=l,this.autoSizedHeight=d,this._originalStyles=o}catch(e){console.error("ModalView: error in auto-sizing:",e);const t=this.element?.querySelector(".modal-dialog");t&&(t.style.maxWidth="")}}resetAutoSizing(){if(this.autoSize&&this._originalStyles&&this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),i=this.element.querySelector(".modal-body");e&&t&&i&&(e.style.maxWidth=this._originalStyles.dialogMaxWidth||"",e.style.width=this._originalStyles.dialogWidth||"",t.style.width=this._originalStyles.contentWidth||"",t.style.maxHeight=this._originalStyles.contentMaxHeight||"",!this._originalStyles.hadScrollableClass&&e.classList.contains("modal-dialog-scrollable")&&e.classList.remove("modal-dialog-scrollable"),delete this.autoSizedWidth,delete this.autoSizedHeight,delete this._originalStyles)}catch(e){console.error("ModalView: error resetting auto-sizing:",e)}}bindBootstrapEvents(){this.element.addEventListener("show.bs.modal",e=>{const t=ModalView._openDialogs.length,i=ModalView.getFullscreenAwareZIndex().modal+20*t;this.element.style.zIndex=i,this._dialogZIndex=i,this._backdropZIndex=i-10,ModalView._openDialogs.push(this),this.onShow&&this.onShow(e),this.emit("show",{dialog:this,relatedTarget:e.relatedTarget})}),this.element.addEventListener("shown.bs.modal",e=>{if(setTimeout(()=>ModalView.fixAllBackdropStacking(),50),this.onShown&&this.onShown(e),this.emit("shown",{dialog:this,relatedTarget:e.relatedTarget}),this.focus){const e=this.element.querySelector('input:not([type="hidden"]), textarea, select');e&&e.focus()}}),this.element.addEventListener("hide.bs.modal",e=>{const t=this.element.querySelector(":focus");t&&t.blur(),this.onHide&&!1===this.onHide(e)?e.preventDefault():this.emit("hide",{dialog:this})}),this.element.addEventListener("hidden.bs.modal",e=>{const t=ModalView._openDialogs.indexOf(this);t>-1&&ModalView._openDialogs.splice(t,1),ModalView._openDialogs.length>0&&(document.body.classList.add("modal-open"),setTimeout(()=>ModalView.fixAllBackdropStacking(),50)),this.previousFocus&&document.body.contains(this.previousFocus)&&this.previousFocus.focus(),this.onHidden&&this.onHidden(e),this.emit("hidden",{dialog:this})}),this.element.addEventListener("hidePrevented.bs.modal",e=>{this.onHidePrevented&&this.onHidePrevented(e),this.emit("hidePrevented",{dialog:this})})}show(e=null){this.previousFocus=document.activeElement,window.lastDialog=this,this.modal&&this.modal.show(e)}hide(){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal&&this.modal.hide()}toggle(e=null){this.modal&&this.modal.toggle(e)}isShown(){return this.element?.classList.contains("show")||!1}getModal(){return this.modal}handleUpdate(){this.modal&&this.modal.handleUpdate()}async setContent(t){if(t instanceof e){this.bodyView&&(await this.bodyView.destroy(),this.removeChild(this.bodyView)),this.bodyView=t,this.body="",this.addChild(this.bodyView);const e=this.element?.querySelector(".modal-body");e&&(e.innerHTML="",await this.bodyView.render(e))}else{this.body=t;const e=this.element?.querySelector(".modal-body");e&&(e.innerHTML=t)}this.handleUpdate()}setTitle(e){this.title=e;const t=this.element?.querySelector(".modal-title");t&&(t.textContent=e)}setLoading(e=!0,t="Loading..."){const i=this.element?.querySelector(".modal-body");i&&(e?i.innerHTML=`\n <div class="text-center py-4">\n <div class="spinner-border text-primary mb-3" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n <p>${t}</p>\n </div>\n `:this.bodyView&&i.replaceChildren(this.bodyView.element))}async destroy(){if(this.modal){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal.dispose(),this.modal=null}this.previousFocus&&document.body.contains(this.previousFocus)&&(this.previousFocus.focus(),this.previousFocus=null),this.autoSize&&this.resetAutoSizing(),await super.destroy()}async onBeforeDestroy(){this.headerView&&await this.headerView.destroy(),this.bodyView&&await this.bodyView.destroy(),this.footerView&&await this.footerView.destroy(),await super.onBeforeDestroy(),this.modal&&(this.modal.dispose(),this.modal=null)}}let d=null,c=0,h=null;const u={show(e){"string"==typeof e&&(e={message:e});const{message:t="Loading...",timeout:i=3e4}=e||{};if(c++,1===c){h&&clearTimeout(h);const e=ModalView.getFullscreenAwareZIndex().modal+1e3;d||(d=document.createElement("div"),d.className="mojo-loading-overlay",d.innerHTML=`\n <div class="mojo-loading-card">\n <div class="mojo-loading-spinner"></div>\n <div class="mojo-loading-message">${t}</div>\n </div>\n <style>\n .mojo-loading-overlay {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background: rgba(255, 255, 255, 0.4);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.2s ease;\n }\n .mojo-loading-overlay.show { opacity: 1; }\n .mojo-loading-card {\n display: flex; align-items: center; gap: 0.85rem;\n background: #fff;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n padding: 1rem 1.5rem;\n box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 4px rgba(0,0,0,0.04);\n }\n .mojo-loading-spinner {\n width: 22px; height: 22px;\n border: 2.5px solid #e9ecef;\n border-top-color: #0d6efd;\n border-radius: 50%;\n animation: mojo-spin 0.7s linear infinite;\n flex-shrink: 0;\n }\n .mojo-loading-message {\n font-size: 0.88rem;\n font-weight: 500;\n color: #495057;\n white-space: nowrap;\n }\n @keyframes mojo-spin { to { transform: rotate(360deg); } }\n</style>\n `,document.body.appendChild(d)),d.style.zIndex=String(e);const o=d.querySelector(".mojo-loading-message");o&&(o.textContent=t),requestAnimationFrame(()=>{d&&d.classList.add("show")}),i>0&&(h=setTimeout(()=>{console.error("BusyIndicator timed out."),u.hide(!0)},i))}else if(d){const e=d.querySelector(".mojo-loading-message");e&&(e.textContent=t)}},hide(e=!1){e?c=0:c--,c>0||(c=0,h&&(clearTimeout(h),h=null),d&&(d.classList.remove("show"),setTimeout(()=>{d&&0===c&&(d.remove(),d=null)},200)))},isShown:()=>null!==d&&c>0},p="\n max-height: 60vh;\n overflow-y: auto;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 1.25rem;\n border-radius: 0.5rem;\n margin: 0;\n font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', 'Monaco', monospace;\n font-size: 0.9rem;\n line-height: 1.6;\n border: 1px solid #2d2d30;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);\n".replace(/\s+/g," ").trim();class CodeViewer extends e{constructor(e={}){super({tagName:"div",className:"mojo-code-viewer",...e}),this.code=e.code||"",this.language=e.language||"javascript"}async getTemplate(){return CodeViewer.formatCode(this.code,this.language)}static formatCode(e,t="javascript"){let i;i=window.Prism&&window.Prism.languages[t]?window.Prism.highlight(e,window.Prism.languages[t],t):String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;");const o=window.Prism?`language-${t}`:"";return`\n <style>\n .dialog-code-block .token.comment { color: #6a9955; }\n .dialog-code-block .token.string { color: #ce9178; }\n .dialog-code-block .token.keyword { color: #569cd6; }\n .dialog-code-block .token.function { color: #dcdcaa; }\n .dialog-code-block .token.number { color: #b5cea8; }\n .dialog-code-block .token.operator { color: #d4d4d4; }\n .dialog-code-block .token.class-name { color: #4ec9b0; }\n .dialog-code-block .token.punctuation { color: #d4d4d4; }\n .dialog-code-block .token.boolean { color: #569cd6; }\n .dialog-code-block .token.property { color: #9cdcfe; }\n .dialog-code-block .token.tag { color: #569cd6; }\n .dialog-code-block .token.attr-name { color: #9cdcfe; }\n .dialog-code-block .token.attr-value { color: #ce9178; }\n .dialog-code-block ::selection { background: #264f78; }\n</style>\n <pre class="${o} dialog-code-block" style="${p}">\n <code class="${o}" style="color: inherit; background: transparent; text-shadow: none;">${i}</code>\n </pre>\n `}static highlightCodeBlocks(e=document){window.Prism&&window.Prism.highlightAllUnder&&window.Prism.highlightAllUnder(e)}}class HtmlPreview extends e{constructor(e={}){super({tagName:"div",className:"mojo-html-preview",...e}),this.html=e.html||e.content||"",this.height=e.height||500}async getTemplate(){return`\n <div class="html-preview-container">\n <div class="d-flex justify-content-between align-items-center mb-2">\n <small class="text-muted">Preview (sandboxed)</small>\n <button type="button" class="btn btn-sm btn-outline-secondary" data-action="refresh">\n <i class="bi bi-arrow-clockwise"></i> Refresh\n </button>\n </div>\n <iframe\n class="border rounded w-100 mojo-html-preview-frame"\n style="height: ${this.height}px; background: white;"\n sandbox="allow-same-origin"\n frameborder="0"\n ></iframe>\n </div>\n `}async onAfterMount(){await super.onAfterMount(),this._writeIframe()}onActionRefresh(){this._writeIframe()}_writeIframe(){const e=this.element?.querySelector(".mojo-html-preview-frame");if(!e)return;const t=e.contentDocument||e.contentWindow?.document;t&&(t.open(),t.write(this.html),t.close())}setHtml(e){this.html=e,this._writeIframe()}}class Modal{static _renderAndAwait(e,{buttons:t=null,rejectOnDismiss:i=!1,onAction:o=null,cleanup:s=null}={}){const n=ModalView.getMountTarget();return new Promise((a,r)=>{let l=!1;const d=e=>{l||(l=!0,a(e))},c=e=>{l||(l=!0,r(e))};(async()=>{try{await e.render(!0,n)}catch(a){return void c(a)}t&&t.length>0&&e.element&&e.element.querySelectorAll(".modal-footer button").forEach((i,s)=>{const n=t[s];n&&i.addEventListener("click",async t=>{if(l)return;const i=void 0!==n.value?n.value:n.action??s;if("function"!=typeof n.handler)if("function"==typeof o&&n.action)try{const a=await o(n.action,{dialog:e,button:n,index:s,event:t});if(null===a||!1===a)return;const r=!0===a||void 0===a?i:a;n.dismiss||e.hide(),d(r)}catch(a){console.error("Modal onAction error:",a)}else n.dismiss||e.hide(),d(i);else try{const o=await n.handler({dialog:e,button:n,index:s,event:t});if(null===o||!1===o)return;const a=!0===o||void 0===o?i:o;n.dismiss||e.hide(),d(a)}catch(a){console.error("Modal button handler error:",a)}})}),e.on("hidden",()=>{l||(i?c(new Error("Dialog dismissed")):d(null)),setTimeout(async()=>{try{"function"==typeof s&&await s(e)}catch(a){console.error("Modal cleanup error:",a)}try{await e.destroy()}catch(a){console.error("Modal destroy error:",a)}e.element?.parentNode&&e.element.parentNode.removeChild(e.element)},100)}),e.show()})()})}static async dialog(e={}){"string"==typeof e&&(e={...arguments[2]||{},body:arguments[0],title:arguments[1]||"Alert"});const{title:t="Dialog",content:i,body:o,view:s,message:n,size:a="md",centered:r=!0,buttons:l=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:d=!1,...c}=e,h=new ModalView({title:t,body:o??s??n??i??"",size:a,centered:r,buttons:l,...c});return Modal._renderAndAwait(h,{buttons:l,rejectOnDismiss:d})}static async drawer(t={}){const{eyebrow:i,title:o,meta:s=[],view:n,body:a,size:r="lg",...l}=t,d=s.length?`\n <div class="modal-drawer-meta">\n ${s.map(e=>"string"==typeof e?`<span>${Modal._esc(e)}</span>`:`<span>${e.icon?`<i class="${Modal._esc(e.icon)} me-1"></i>`:""}${Modal._esc(e.text||"")}</span>`).join("")}\n </div>`:"",c=`\n <div class="modal-drawer-head">\n ${i?`<span class="modal-drawer-eyebrow">${Modal._esc(i)}</span>`:""}\n <h2 class="modal-drawer-title">${Modal._esc(o||"")}</h2>\n ${d}\n </div>\n `;let h;return h=n&&"object"==typeof n&&"function"==typeof n.render?new class extends e{async getTemplate(){return`${c}<div class="modal-drawer-body" data-container="drawer-body"></div>`}async onInit(){n.containerId="drawer-body",this.addChild(n)}}:`${c}<div class="modal-drawer-body">${a||""}</div>`,Modal.dialog({header:!1,body:h,size:r,centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...l})}static _esc(e){const t="undefined"!=typeof document?document.createElement("div"):null;return t?(t.textContent=String(e??""),t.innerHTML):String(e??"")}static async show(e,t={}){return Modal.dialog({header:void 0!==t.title&&!!t.title,title:t.title||void 0,body:e,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...t})}static async showModel(e,t={}){const i=e.constructor,o=i?.VIEW_CLASS;if(!o)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${i?.name||"model"}. Set ${i?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const s=new o({model:e});return Modal.show(s,t)}static async showModelById(e,t,i={}){const o=new e({id:t});return await o.fetch(),o.id?Modal.showModel(o,i):(Modal.alert({message:`Could not find ${e.name||"record"} with ID: ${t}`,type:"warning"}),null)}static async showModelView(e,t={}){const i=e.constructor,o=i?.VIEW_CLASS;if(!o)throw new Error(`Modal.showModelView: No VIEW_CLASS defined on ${i?.name||"model"}.`);const s=new o({model:e});return Modal.dialog({header:!1,body:s,size:"lg",centered:!1,...t})}static async alert(e={},t,i){let o;o="string"==typeof e?{message:e,...void 0!==t?{title:t}:{},...i||{}}:{...e};const{message:s="",title:n="Alert",type:a="info",icon:r,className:l,...d}=o,c="danger"===a?"error":a,h=[`modal-alert modal-alert-${c}`,l].filter(Boolean).join(" "),u=void 0!==r?r:{info:"bi-info-circle",success:"bi-check-circle",warning:"bi-exclamation-triangle",error:"bi-x-circle"}[c],p=u?`<i class="bi ${u} modal-alert-icon"></i>`:"",m=`<span class="modal-alert-headline">${n}</span>`;return Modal.dialog({title:`${p}${m}`,body:`<p class="modal-alert-message">${s}</p>`,size:"sm",centered:!0,className:h,buttons:[{text:"OK",class:"btn-primary",value:!0}],...d})}static async confirm(e,t="Confirm",i={}){let o;"object"==typeof e&&null!==e?(o=(i=e).message,t=i.title||t):o=e;const s=[{text:i.cancelText||"Cancel",class:"btn-secondary",dismiss:!0,action:"cancel"},{text:i.confirmText||"Confirm",class:i.confirmClass||"btn-primary",action:"confirm"}],n=new ModalView({title:t,body:`<p>${o}</p>`,size:i.size||"sm",centered:!0,backdrop:"static",buttons:s,...i});return"confirm"===await Modal._renderAndAwait(n,{buttons:s})}static async prompt(e,t="Input",i={}){const o=`prompt-input-${Date.now()}`,s=i.defaultValue||"",n=i.inputType||"text",a=i.placeholder||"",r=[{text:"Cancel",class:"btn-secondary",dismiss:!0},{text:"OK",class:"btn-primary",action:"ok"}],l=new ModalView({title:t,body:`\n <p>${e}</p>\n <input type="${n}"\n class="form-control"\n id="${o}"\n value="${s}"\n placeholder="${a}">\n `,size:i.size||"sm",centered:!0,backdrop:"static",buttons:r,...i});return l.on("shown",()=>{const e=l.element.querySelector(`#${o}`);e&&(e.focus(),e.select())}),Modal._renderAndAwait(l,{buttons:r,onAction:async e=>{if("ok"!==e)return null;const t=l.element.querySelector(`#${o}`);return t?t.value:null}})}static showError(e){return Modal.alert(e,"Error",{type:"error"})}static async form(e={}){const{title:t="Form",formConfig:i={},size:o="md",centered:s=!0,submitText:n="Submit",cancelText:a="Cancel",...r}=e,l=new(0,(await import("./FormView-CgnaTPkQ.js").then(e=>e.c)).default)({fileHandling:e.fileHandling||"base64",data:e.data,defaults:e.defaults,model:e.model,formConfig:{fields:i.fields||e.fields,...i,submitButton:!1,resetButton:!1}}),d=[{text:a,class:"btn-secondary",action:"cancel"},{text:n,class:"btn-primary",action:"submit"}],c=new ModalView({title:t,body:l,size:o,centered:s,buttons:d,...r});return Modal._renderAndAwait(c,{buttons:d,onAction:async t=>{if("cancel"===t)return c.hide(),null;if("submit"!==t)return null;if(!l.validate())return l.focusFirstError(),!1;if(e.autoSave&&e.model){c.setLoading(!0);const e=await l.saveModel();return e.success?e:(c.setLoading(!1),await c.render(),c.getApp()?.toast?.error(e.message),!1)}try{return await l.getFormData()}catch(i){return console.error("Modal.form: error collecting form data:",i),l.showError("Error collecting form data"),!1}},cleanup:async()=>{try{await l.destroy()}catch{}}})}static async modelForm(e={}){const{title:t="Edit",formConfig:i={},size:o="md",centered:s=!0,submitText:n="Save",cancelText:a="Cancel",model:r,fields:l,...d}=e;if(!r)throw new Error("Modal.modelForm requires a model");const c=new(0,(await import("./FormView-CgnaTPkQ.js").then(e=>e.c)).default)({fileHandling:e.fileHandling||"base64",model:r,data:e.data,defaults:e.defaults,formConfig:{fields:l||i.fields||[],...i,submitButton:!1,resetButton:!1}}),h=[{text:a,class:"btn-secondary",action:"cancel"},{text:n,class:"btn-primary",action:"submit"}],u=new ModalView({title:t,body:c,size:o,centered:s,buttons:h,...d});return Modal._renderAndAwait(u,{buttons:h,onAction:async e=>{if("cancel"===e)return u.hide(),null;if("submit"!==e)return null;u.setLoading(!0,"Saving...");try{const e=await c.handleSubmit();if(e.success)return e;u.setLoading(!1);let t=e.error;return e.data?.error&&(t=e.data.error),u.getApp()?.toast?.error(t),!1}catch(t){return console.error("Modal.modelForm: error saving:",t),await u.setContent(c),c.showError(t.message||"An error occurred while saving"),!1}},cleanup:async()=>{try{await c.destroy()}catch{}}})}static async data(e={}){const{title:t="Data View",data:i={},model:o=null,fields:s=[],columns:n=2,responsive:a=!0,showEmptyValues:r=!1,emptyValueText:l="—",size:d="lg",centered:c=!0,closeText:h="Close",...u}=e,p=new(0,(await import("./DataView-fA6qQbvN.js")).default)({data:i,model:o,fields:s,columns:n,responsive:a,showEmptyValues:r,emptyValueText:l}),m=[{text:h,class:"btn-secondary",value:"close"}],g=new ModalView({title:t,body:p,size:d,centered:c,buttons:m,...u});return p.on("field:click",e=>g.emit("dataview:field:click",e)),p.on("error",e=>g.emit("dataview:error",e)),Modal._renderAndAwait(g,{buttons:m,cleanup:async()=>{try{await p.destroy()}catch{}}})}static async code(e={}){const{code:t="",language:i="javascript",title:o="Source Code",size:s="lg",...n}=e,a=new CodeViewer({code:t,language:i}),r=[{text:"Copy to Clipboard",class:"btn-primary",icon:"bi-clipboard",action:"copy"},{text:"Close",class:"btn-secondary",dismiss:!0}],l=new ModalView({title:o,body:a,size:s,scrollable:!0,buttons:r,...n});return Modal._renderAndAwait(l,{buttons:r,onAction:async e=>{if("copy"!==e)return null;if(!navigator.clipboard)return!1;try{await navigator.clipboard.writeText(t),Modal._showCopySuccess(l)}catch(i){console.error("Modal.code: clipboard write failed:",i)}return!1}})}static _showCopySuccess(e){const t=e.element?.querySelector('[data-action="copy"]');if(!t)return;const i=t.innerHTML;t.innerHTML='<i class="bi bi-check me-1"></i>Copied!',t.classList.remove("btn-primary"),t.classList.add("btn-success"),t.disabled=!0,setTimeout(()=>{t.innerHTML=i,t.classList.remove("btn-success"),t.classList.add("btn-primary"),t.disabled=!1},2e3)}static async htmlPreview(e={}){const{html:t=e.content||"",title:i="HTML Preview",size:o="lg",height:s=500,...n}=e,a=new HtmlPreview({html:t,height:s}),r=[{text:"Close",class:"btn-secondary",dismiss:!0}],l=new ModalView({title:i,body:a,size:o,scrollable:!1,buttons:r,...n});return Modal._renderAndAwait(l,{buttons:r})}static async updateModelImage(e={},t={}){const i=e.upload||!1,o=t.name||e.field||"image",s={title:"Upload Your Avatar",model:null,autoSave:!i,size:"sm",fields:[{type:"image",name:o,size:"lg",imageSize:{width:200,height:200},placeholder:"Upload your image",...t}],...e},n=await Modal.form(s);if(!i||!n||!e.model)return n;const a=n[o];if(!a||!a.startsWith("data:"))return n;const l=a.split(","),d=l[0]?.match(/:(.*?);/),c=d?.[1]||"image/png",h=atob(l[1]);let u=h.length;const p=new Uint8Array(u);for(;u--;)p[u]=h.charCodeAt(u);const m=c.split("/")[1]||"png",g="undefined"!=typeof window&&window.File||globalThis.File;if(!g)throw new Error("File API is not available in this environment");const f=new g([p],`${o}.${m}`,{type:c}),w=new r;return await w.upload({file:f,name:`${o}.${m}`,description:e.uploadDescription||`${o} upload`,showToast:!0}),e.model.save({[o]:w.id})}static loading(e){u.show(e)}static hideLoading(e){u.hide(e)}static showBusy(e){return Modal.loading(e)}static hideBusy(e){return Modal.hideLoading(e)}}const m=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,default:Modal},Symbol.toStringTag,{value:"Module"}));export{CodeViewer as C,r as F,ModalView as M,ProgressView as P,ToastService as T,Modal as a,l as b,FileList as c,FileManager as d,a as e,FileManagerList as f,FileUpload as g,m as h};
3
- //# sourceMappingURL=Modal-DKjxtaZI.js.map
@@ -1,2 +0,0 @@
1
- import{C as t,M as e,d as i,r as s}from"./Collection-Cgxbmj8G.js";import{L as n,a as l}from"./ListView-Cu6iQ0aG.js";import{M as a}from"./View-gAghvPMN.js";import{a as o}from"./Modal-DKjxtaZI.js";import{F as r}from"./FormView-CgnaTPkQ.js";class Log extends e{constructor(t={}){super(t,{endpoint:"/api/logs"})}}class LogList extends t{constructor(t={}){super({ModelClass:Log,endpoint:"/api/logs",size:10,...t})}}class Member extends e{constructor(t={}){super(t,{endpoint:"/api/group/member"})}hasPermission(t){if(Array.isArray(t))return t.some(t=>this.hasPermission(t));const e=this.get("permissions");return!!e&&1==e[t]}async fetchForGroup(t){return await this.fetch({url:`/api/group/${t}/member`})}}class MemberList extends t{constructor(t={}){super({ModelClass:Member,endpoint:"/api/group/member",size:10,...t})}}const c={edit:{title:"Edit Membership",fields:[{name:"user.display_name",type:"text",label:"Display Name",placeholder:"Enter Display Name"},{name:"metadata.role",type:"text",label:"Role",placeholder:"Enter role"},{name:"is_active",type:"switch",label:"Is Enabled",columns:12}]}};Member.BASE_PERMISSIONS=[{name:"manage_group",label:"Group Admin"},{name:"view_metrics",label:"View Metrics"},{name:"view_logs",label:"View Logs"},{name:"view_tickets",label:"View Tickets"},{name:"view_members",label:"View Members"},{name:"manage_members",label:"Manage Members"},{name:"view_billing",label:"View Billing"}],Member.APP_PERMISSIONS=[],Member.PERMISSIONS=[],Member.PERMISSION_FIELDS=[];const d=t=>({name:`permissions.${t.name}`,type:"switch",label:t.label,columns:6,...t.tooltip?{tooltip:t.tooltip}:{}});Member.rebuildPermissions=function(){Member.PERMISSIONS.length=0,Member.PERMISSIONS.push(...Member.BASE_PERMISSIONS,...Member.APP_PERMISSIONS),Member.PERMISSION_FIELDS.length=0,Member.PERMISSION_FIELDS.push(...Member.PERMISSIONS.map(d))},Member.rebuildPermissions(),Member.EDIT_FORM=c.edit,Member.ADD_FORM=c.create;class TableRow extends n{constructor(t={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...t}),this.columns=t.columns||[],this.actions=t.actions||null,this.contextMenu=t.contextMenu||null,this.batchActions=t.batchActions||null,this.tableView=t.tableView||t.listView||null,this.editingCells=/* @__PURE__ */new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(t){if(!t)return"";const e=["sm","md","lg","xl","xxl"];if("string"==typeof t)return e.includes(t)?`d-none d-${t}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${t}. Valid options are: ${e.join(", ")}`),"");if("object"==typeof t){const i=[];if(t.hide){if(!e.includes(t.hide))return console.warn(`Invalid hide breakpoint: ${t.hide}. Valid options are: ${e.join(", ")}`),"";i.push(`d-table-cell d-${t.hide}-none`)}if(t.show){if(!e.includes(t.show))return console.warn(`Invalid show breakpoint: ${t.show}. Valid options are: ${e.join(", ")}`),"";t.hide?i.push(`d-${t.show}-table-cell`):i.push(`d-none d-${t.show}-table-cell`)}return i.join(" ")}return""}buildRowTemplate(){let t="";return this.tableView&&this.tableView.isSelectable()&&(t+='\n <td style="padding: 0;">\n <div class="mojo-select-cell {{#selected}}selected{{/selected}}"\n data-action="select">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </td>\n '),this.columns.forEach((e,i)=>{const s=[e.class||e.className||"",this.getResponsiveClasses(e.visibility),e.editable?"editable-cell":"",this.tableView&&this.tableView.getAlignClass?this.tableView.getAlignClass(e.align):""].filter(t=>t).join(" "),n=this.buildCellTemplate(e,i);let l=e.action;!l&&e.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),t+=l?`<td class="${s}" data-action="${l}" data-column="${e.key}">${n}</td>`:`<td class="${s}" data-column="${e.key}">${n}</td>`}),this.actions?t+=this.buildActionsTemplate():this.contextMenu&&(t+=this.buildContextMenuTemplate()),t}buildCellTemplate(t,e=0){const i=`model.${t.key}`,s=t.formatter||t.format;if(s){if("string"==typeof s)return`{{{${i}|${s}}}}`;if("function"==typeof s)return`<span data-formatter="${t.key}" data-formatter-id="${e}">{{${i}}}</span>`}return t.template?t.template:t.editable?`<span class="cell-content" data-field="${t.key}">{{{${i}}}}</span>`:`{{{${i}}}}`}buildActionsTemplate(){return this.actions&&0!==this.actions.length?`<td><div class="btn-group btn-group-sm">${this.actions.map(t=>{if("string"==typeof t)switch(t){case"view":return'\n <button class="btn btn-sm btn-outline-primary"\n data-action="view"\n title="View">\n <i class="bi bi-eye"></i>\n </button>\n ';case"edit":return'\n <button class="btn btn-sm btn-outline-secondary"\n data-action="edit"\n title="Edit">\n <i class="bi bi-pencil"></i>\n </button>\n ';case"delete":return'\n <button class="btn btn-sm btn-outline-danger"\n data-action="delete"\n title="Delete">\n <i class="bi bi-trash"></i>\n </button>\n ';default:return""}else if("object"==typeof t)return`\n <button class="btn btn-sm ${t.class||"btn-outline-primary"}"\n data-id="${this.model.id}"\n data-action="${t.action}"\n title="${t.label||""}">\n ${t.icon?`<i class="${t.icon}"></i>`:""}\n ${t.label&&!t.icon?t.label:""}\n </button>\n `;return""}).join("")}</div></td>`:""}buildContextMenuTemplate(){return this.contextMenu&&0!==this.contextMenu.length?`\n <td class="text-end" style="width: 1px;">\n <div class="dropdown">\n <button class="btn btn-sm btn-link border-0"\n type="button"\n data-bs-toggle="dropdown"\n aria-expanded="false"\n style="color: #6c757d;">\n <i class="bi bi-three-dots-vertical"></i>\n </button>\n <ul class="dropdown-menu dropdown-menu-end shadow-sm">\n ${this.buildContextMenuItems()}\n </ul>\n </div>\n </td>\n `:""}buildContextMenuItems(){return this.contextMenu.map(t=>{if(t.separator||t.divider)return'<li><hr class="dropdown-divider"></li>';let e="dropdown-item";return("delete"===t.action||t.danger)&&(e+=" text-danger"),t.disabled&&(e+=" disabled"),`\n <li>\n <a class="${e}" href="#"\n data-id="{{model.id}}"\n data-action="${t.action}"\n ${t.disabled?'aria-disabled="true" tabindex="-1"':""}>\n ${t.icon?`<i class="${t.icon} me-2"></i>`:""}\n ${t.label}\n </a>\n </li>\n `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((t,e)=>{if(t.formatter&&"function"==typeof t.formatter){let s=this.element.querySelector(`[data-formatter-id="${e}"]`);if(s||(s=this.element.querySelector(`[data-formatter="${t.key}"]`)),s){const e=this.model.get?this.model.get(t.key):this.model[t.key],n={value:e,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{s.innerHTML=t.formatter(e,n)}catch(i){console.error(`Error formatting cell for column ${t.key}:`,i)}}}}),this.selected&&this.element.classList.add("selected");const t=this.model.get?this.model.get("id"):this.model.id;t&&this.element.setAttribute("data-id",t)}async onActionEditCell(t,e){t.stopPropagation();const i=e.getAttribute("data-column"),s=this.columns.find(t=>t.key===i);s&&s.editable&&(this.editingCells.has(i)||await this.enterEditMode(i,s,e))}async onActionRowClick(t,e){t.target.closest(".btn-group")||t.target.closest(".dropdown")||t.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:e.getAttribute("data-column"),event:t}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:e.getAttribute("data-column"),event:t}))}async onActionView(t,e){t.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:t})}async onActionEdit(t,e){return t.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:t}),!0}async onActionDelete(t,e){t.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:t}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:t})}async enterEditMode(t,e,i){const s=i.querySelector(".cell-content");if(!s)return;this.editingCells.add(t);const n=this.model.get?this.model.get(t):this.model[t],l=this.createCellEditor(e,n),a=s.innerHTML;s.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=l,i.appendChild(o);const r=o.querySelector("input, select, .form-check-input");r&&(r.focus(),"text"!==r.type&&"textarea"!==r.type||r.select()),o.dataset.originalContent=a,o.dataset.columnKey=t,this.setupEditorEvents(o,t,e),this.emit("cell:edit",{row:this,model:this.model,column:t,originalValue:n})}createCellEditor(t,e){const i=t.editableOptions||{};switch(i.type){case"select":return this.createSelectEditor(i,e);case"switch":case"checkbox":return this.createSwitchEditor(i,e);case"textarea":return this.createTextareaEditor(i,e);default:return this.createTextEditor(i,e)}}createTextEditor(t,e){const i=t.placeholder||"";return`\n <div class="d-flex gap-1 align-items-center">\n <input type="${t.inputType||"text"}"\n class="form-control form-control-sm cell-input"\n value="${this.escapeHtml(e||"")}"\n placeholder="${i}">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createTextareaEditor(t,e){const i=t.placeholder||"";return`\n <div class="d-flex gap-1">\n <textarea class="form-control form-control-sm cell-input"\n rows="${t.rows||2}"\n placeholder="${i}">${this.escapeHtml(e||"")}</textarea>\n <div class="d-flex flex-column gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}createSelectEditor(t,e){const i=t.options||[];let s="";return i.forEach(t=>{if("string"==typeof t)s+=`<option value="${t}" ${t===e?"selected":""}>${t}</option>`;else if("object"==typeof t&&void 0!==t.value){const i=t.value===e?"selected":"";s+=`<option value="${t.value}" ${i}>${t.label||t.value}</option>`}}),`\n <div class="d-flex gap-1 align-items-center">\n <select class="form-select form-select-sm cell-input">\n ${s}\n </select>\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n `}createSwitchEditor(t,e){const i=e?"checked":"";return`\n <div class="d-flex gap-2 align-items-center">\n <div class="form-check ${"switch"===t.type?"form-switch":""}">\n <input class="form-check-input cell-input" type="checkbox" ${i}>\n </div>\n <div class="d-flex gap-1">\n <button type="button" class="btn btn-sm btn-success cell-save" title="Save">\n <i class="bi bi-check"></i>\n </button>\n <button type="button" class="btn btn-sm btn-outline-secondary cell-cancel" title="Cancel">\n <i class="bi bi-x"></i>\n </button>\n </div>\n </div>\n `}setupEditorEvents(t,e,i){const s=t.querySelector(".cell-input"),n=t.querySelector(".cell-save"),l=t.querySelector(".cell-cancel");!s||"text"!==s.type&&"email"!==s.type&&"number"!==s.type||s.addEventListener("keydown",s=>{"Enter"===s.key?(s.preventDefault(),this.saveCellEdit(t,e,i)):"Escape"===s.key&&(s.preventDefault(),this.cancelCellEdit(t,e))}),!s||"checkbox"!==s.type&&"SELECT"!==s.tagName||!1===i.autoSave||s.addEventListener("change",()=>{this.saveCellEdit(t,e,i)}),n?.addEventListener("click",()=>{this.saveCellEdit(t,e,i)}),l?.addEventListener("click",()=>{this.cancelCellEdit(t,e)})}async saveCellEdit(t,e,i){const s=t.querySelector(".cell-input");if(!s)return;let n;n="checkbox"===s.type?s.checked:(s.tagName,s.value);const l=this.model.get?this.model.get(e):this.model[e];try{this.model.save?await this.model.save({[e]:n}):this.model[e]=n,this.exitEditMode(t,e,n),this.emit("cell:save",{row:this,model:this.model,column:e,oldValue:l,newValue:n})}catch(a){console.error("Failed to save cell edit:",a),this.emit("cell:save:error",{row:this,model:this.model,column:e,oldValue:l,newValue:n,error:a}),t.classList.add("saving-error"),setTimeout(()=>t.classList.remove("saving-error"),3e3)}}cancelCellEdit(t,e){const i=t.dataset.originalContent;this.exitEditMode(t,e,null,i),this.emit("cell:cancel",{row:this,model:this.model,column:e})}exitEditMode(t,e,s=null,n=null){const l=t.closest("td").querySelector(".cell-content");if(l){if(null!==s){const t=this.columns.find(t=>t.key===e);let n=s;t&&t.formatter&&"string"==typeof t.formatter&&(n=i.pipe(s,t.formatter)),l.innerHTML=this.escapeHtml(n)}else n&&(l.innerHTML=n);l.style.display=""}t.remove(),this.editingCells.delete(e)}escapeHtml(t){if(null==t)return"";const e=document.createElement("div");return e.textContent=t,e.innerHTML}select(){super.select(),this.addClass("selected");const t=this.element?.querySelector(".mojo-select-cell");t&&t.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const t=this.element?.querySelector(".mojo-select-cell");t&&t.classList.remove("selected")}}const h={exact:{display:"is",description:"Exact match"},in:{display:"in",description:"Match any of the values (comma-separated)"},not:{display:"is not",description:"Does not match"},not_in:{display:"not in",description:"Does not match any of the values"},gt:{display:">",description:"Greater than"},gte:{display:">=",description:"Greater than or equal to"},lt:{display:"<",description:"Less than"},lte:{display:"<=",description:"Less than or equal to"},contains:{display:"contains",description:"Contains substring (case-sensitive)"},icontains:{display:"contains",description:"Contains substring (case-insensitive)"},startswith:{display:"starts with",description:"Starts with substring (case-sensitive)"},istartswith:{display:"starts with",description:"Starts with substring (case-insensitive)"},endswith:{display:"ends with",description:"Ends with substring (case-sensitive)"},iendswith:{display:"ends with",description:"Ends with substring (case-insensitive)"},isnull:{display:t=>"true"===t||!0===t?"is null":"is not null",description:"Check if value is null or not"},range:{display:"between",description:"Between two values (comma-separated)"}};function u(t){if(!t||"string"!=typeof t)return{field:t,lookup:null};const e=t.split("__");if(1===e.length)return{field:t,lookup:null};const i=e[e.length-1];return h[i]?{field:e.slice(0,-1).join("__"),lookup:i}:{field:t,lookup:null}}function m(t,e,i){if(!t||null==e)return"";const{field:s,lookup:n}=u(t),l=h[n];if(e&&"object"==typeof e&&!Array.isArray(e)){const t=void 0!==e.start&&null!==e.start&&""!==e.start,s=void 0!==e.end&&null!==e.end&&""!==e.end;return t||s?t&&s?`${i} between '${e.start}' and '${e.end}'`:t?`${i} from '${e.start}'`:`${i} until '${e.end}'`:`${i} is '${JSON.stringify(e)}'`}const a=Array.isArray(e)?e.join(","):String(e);if(!n||"exact"===n)return`${i} is '${a}'`;if("in"===n||"not_in"===n){const t=a.split(",").map(t=>t.trim()).filter(t=>t);if(0===t.length)return`${i} ${l.display}`;const e=t.map(t=>`'${t}'`).join(", ");return`${i} ${l.display} ${e}`}if("range"===n){const t=a.split(",").map(t=>t.trim()).filter(t=>t);return 2===t.length?`${i} between '${t[0]}' and '${t[1]}'`:`${i} ${l.display} '${a}'`}return"isnull"===n?`${i} ${"function"==typeof l.display?l.display(a):l.display}`:l?`${i} ${l.display} '${a}'`:`${i} is '${a}'`}const p={LOOKUPS:h,parseFilterKey:u,formatFilterDisplay:m,getLookupDescription:function(t){const e=h[t];return e?e.description:"Exact match"},isValidLookup:function(t){return t&&h.hasOwnProperty(t)},getAvailableLookups:function(){return Object.keys(h)},buildFilterKey:function(t,e=null){return t?e?`${t}__${e}`:t:""}};class TableView extends l{constructor(t={}){super({className:"table-view-component",itemClass:t.itemClass||TableRow,selectionMode:t.selectable?"multiple":"none",emptyMessage:t.emptyMessage||"No data available",addButtonIcon:t.addButtonIcon||"bi bi-plus-circle",...t}),this.isFullscreen=!1,this.columns=t.columns||[],this.actions=t.actions||null,this.contextMenu=t.contextMenu||null,this.batchActions=t.batchActions||null,this.searchable=!1!==t.searchable,this.sortable=!1!==t.sortable,this.filterable=!1!==t.filterable,this.paginated=!1!==t.paginated,this.clickAction=t.clickAction||"view",this.itemView=t.itemView,this.addForm=t.addForm,this.editForm=t.editForm,this.deleteTemplate=t.deleteTemplate,this.formDialogConfig=t.formDialogConfig||{},this.viewDialogOptions=t.viewDialogOptions||{},this.exportOptions=t.exportOptions||null,this.options.showExport&&!this.exportOptions&&(this.exportOptions=[{format:"csv",label:"Export as CSV",icon:"bi bi-file-earmark-spreadsheet"},{format:"json",label:"Export as JSON",icon:"bi bi-file-earmark-code"}]),this.exportSource=t.exportSource||"remote",this.filters={},this.additionalFilters=t.filters||[],this.hideActivePills=!0===t.hideActivePills,this.hideActivePillNames=t.hideActivePillNames||[],this.rowAction=t.rowAction||"row-click",this.batchBarLocation=t.batchBarLocation||"bottom",this.options.addButtonLabel=t.addButtonLabel||"Add",this.toolbarButtons=t.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...t.tableOptions},this.searchPlacement=t.searchPlacement||"toolbar",this.searchPlaceholder=t.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(t=>!0===t.footer_total),this.hasFooterTotals=this.footerTotalColumns.length>0,this.template=this.buildTableTemplate(),this.setupCollectionListeners()}setupCollectionListeners(){this.hasFooterTotals&&this.collection&&this.collection.on("reset add remove change",()=>{this.updateFooterTotals()})}initializeColumns(){this.columns.forEach(t=>{!t.key&&t.name&&(t.key=t.name),t.label||t.title||(t.label=t.key.charAt(0).toUpperCase()+t.key.slice(1))})}getResponsiveClasses(t){if(!t)return"";const e=["sm","md","lg","xl","xxl"];if("string"==typeof t)return e.includes(t)?`d-none d-${t}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${t}. Valid options are: ${e.join(", ")}`),"");if("object"==typeof t){const i=[];if(t.hide){if(!e.includes(t.hide))return console.warn(`Invalid hide breakpoint: ${t.hide}. Valid options are: ${e.join(", ")}`),"";i.push(`d-table-cell d-${t.hide}-none`)}if(t.show){if(!e.includes(t.show))return console.warn(`Invalid show breakpoint: ${t.show}. Valid options are: ${e.join(", ")}`),"";t.hide?i.push(`d-${t.show}-table-cell`):i.push(`d-none d-${t.show}-table-cell`)}return i.join(" ")}return""}getAlignClass(t){if(!t)return"";return{left:"text-start",start:"text-start",center:"text-center",right:"text-end",end:"text-end"}[String(t).toLowerCase()]||(console.warn(`Invalid column align: ${t}. Valid options are: left, center, right`),"")}parseColumnKey(t){const e=t.split("|");return{fieldKey:e[0],formatter:e[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const t=this.calculateFooterTotals();let e=0;this.columns.forEach(i=>{if(i.footer_total){const s=`col_${e}`,n=this.element.querySelector(`[data-total-column="${s}"]`);if(n&&t[s]){const e=this.parseColumnKey(i.key).formatter||i.formatter;let l;l=e&&"string"==typeof e?this.formatValue(t[s].value,e):t[s].value,n.textContent=l}e++}})}formatValue(t,e){try{return i.pipe(t,e)}catch(s){return console.warn("Error formatting value:",s),t}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||0===this.collection.length)return{};const t={};return this.footerTotalColumns.forEach((e,i)=>{const{fieldKey:s,formatter:n}=this.parseColumnKey(e.key);let l=0;this.collection.forEach(t=>{const e=t.get?t.get(s):t[s],i=parseFloat(e)||0;l+=i}),e.key,this.collection.length,t[`col_${i}`]={value:l,formatter:n||e.formatter,fieldKey:s,originalKey:e.key}}),t}extractColumnFilters(){this.filters={},this.columns.forEach(t=>{if(t.filter){const{fieldKey:e}=this.parseColumnKey(t.key);this.filters[e]=t.filter}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&"multiple"==this.selectionMode}buildTableTemplate(){const t="top"===this.batchBarLocation?this.buildBatchActionsPanel():"",e="bottom"===this.batchBarLocation?this.buildBatchActionsPanel():"";return`\n <div class="mojo-table-wrapper">\n ${this.buildToolbarTemplate()}\n ${t}\n <div class="table-container"${(()=>{const t=this.tableOptions&&null!=this.tableOptions.fontSize?this.tableOptions.fontSize:this.options&&this.options.fontSize,e="sm"===t?"0.9rem":"xs"===t?"0.8rem":t?String(t):null;return e?` style="font-size: ${e};"`:""})()}>\n {{#loading}}\n <div class="mojo-table-loading d-flex justify-content-center align-items-center py-5">\n <div class="spinner-border" role="status">\n <span class="visually-hidden">Loading...</span>\n </div>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class="table-empty text-center py-5">\n <i class="bi bi-inbox fa-2x mb-2 text-muted"></i>\n <p class="text-muted">{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <table class="${this.buildTableClasses()}">\n ${this.buildTableHeaderTemplate()}\n <tbody data-container="items"></tbody>\n ${this.hasFooterTotals?this.buildTableFooterTemplate():""}\n </table>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${e}\n ${this.buildPaginationTemplate()}\n </div>\n `}buildTableClasses(){let t=["table"];return this.tableOptions.striped&&t.push("table-striped"),this.tableOptions.bordered&&t.push("table-bordered"),this.tableOptions.hover&&t.push("table-hover"),this.tableOptions.responsive&&t.push("table-responsive"),this.tableOptions.background&&t.push(`table-${this.tableOptions.background}`),"sm"===this.tableOptions.size&&t.push("table-sm"),"lg"===this.tableOptions.size&&t.push("table-lg"),t.join(" ")}buildToolbarTemplate(){return this.searchable||this.filterable?`\n <div class="table-action-buttons mb-3">\n <div class="d-flex align-items-center gap-2">\n ${this.buildActionButtonsTemplate()}\n ${this.filterable?this.buildFilterDropdownTemplate():""}\n ${this.searchable&&"toolbar"===this.searchPlacement?this.buildSearchTemplate():""}\n\n </div>\n <div data-container="filter-pills"></div>\n </div>\n `:""}buildActionButtonsTemplate(){let t=[];if(t.push('\n <button class="btn btn-sm btn-outline-secondary btn-refresh"\n data-action="refresh"\n title="Refresh">\n <i class="bi bi-arrow-clockwise"></i>\n </button>\n '),this.isFullscreenSupported()&&t.push('\n <button class="btn btn-sm btn-outline-secondary btn-fullscreen"\n data-action="toggle-fullscreen"\n title="Toggle Fullscreen">\n <i class="bi bi-fullscreen"></i>\n </button>\n '),this.options.showAdd&&t.push(`\n <button class="btn btn-sm btn-success btn-add"\n data-action="add"\n title="${this.options.addButtonLabel}">\n <i class="${this.options.addButtonIcon} me-1"></i>\n <span class="d-none d-lg-inline">${this.options.addButtonLabel}</span>\n </button>\n `),this.options.showExport)if(this.exportOptions&&this.exportOptions.length>1){const e=this.exportOptions.map(t=>`\n <li>\n <a class="dropdown-item" href="#" data-action="export" data-format="${t.format}">\n <i class="${t.icon||"bi bi-file-earmark-arrow-down"} me-2"></i>${t.label}\n </a>\n </li>\n `).join("");t.push(`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false" title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n <ul class="dropdown-menu">\n ${e}\n </ul>\n </div>\n `)}else{const e=this.exportOptions&&1===this.exportOptions.length?this.exportOptions[0].format:"json";t.push(`\n <button class="btn btn-sm btn-outline-secondary btn-export"\n data-action="export"\n data-format="${e}"\n title="Export">\n <i class="bi bi-download me-1"></i>\n <span class="d-none d-lg-inline">Export</span>\n </button>\n `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((e,i)=>{const{label:s="Button",icon:n="",action:l="",handler:a=null,variant:o="outline-secondary",title:r=s,className:c="",permissions:d=null}=e;if(d&&!this.checkPermissions(d))return;const h=n?`<i class="${n} me-1"></i>`:"",u=`<span class="d-none d-lg-inline">${s}</span>`;let m="";a?m=`data-action="custom-toolbar-button" data-button-index="${i}"`:l&&(m=`data-action="${l}"`);const p=`btn btn-sm btn-${o} ${c}`.trim();t.push(`\n <button class="${p}"\n ${m}\n title="${r}">\n ${h}${u}\n </button>\n `)}),t.join("")}buildSearchTemplate(){return'\n <div class="flex-grow-1" style="max-width: 400px;">\n <div class="input-group input-group-sm">\n <span class="input-group-text">\n <i class="bi bi-search"></i>\n </span>\n <input type="search"\n class="form-control"\n placeholder="{{searchPlaceholder}}"\n data-filter="search"\n data-change-action="apply-search"\n value="{{collection.params.search}}"\n aria-label="Search">\n {{#searchValue}}\n <button class="btn btn-outline-secondary" type="button"\n data-action="clear-search"\n title="Clear search">\n <i class="bi bi-x"></i>\n </button>\n {{/searchValue}}\n </div>\n </div>\n '}buildFilterDropdownTemplate(){return this.filters&&Object.keys(this.filters).length>0||this.additionalFilters&&this.additionalFilters.length>0?`\n <div class="dropdown">\n <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button"\n data-bs-toggle="dropdown" aria-expanded="false">\n <i class="bi bi-filter me-1"></i>\n <span class="d-none d-lg-inline">Add Filter</span>\n </button>\n <div class="dropdown-menu" style="min-width: 250px;">\n ${this.buildFilterList()}\n </div>\n </div>\n `:""}buildFilterList(){const t=this.getAllAvailableFilters(),e=this.getActiveFilters();return 0===t.length?'<div class="dropdown-item-text text-muted">No filters available</div>':`\n ${t.map(t=>{const i=e.hasOwnProperty(t.key),s=i?"active":"",n=this.getFilterIcon(t.type||t.config?.type);return`\n <button class="dropdown-item ${s}"\n data-action="add-filter"\n data-filter-key="${t.key}">\n <i class="bi bi-${n} me-2"></i>\n ${t.label}\n ${i?'<i class="bi bi-check-circle ms-auto"></i>':""}\n </button>\n `}).join("")}\n ${Object.keys(e).length>0?'\n <div class="dropdown-divider"></div>\n <button class="dropdown-item text-danger" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-2"></i>Clear All Filters\n </button>\n ':""}\n `}updateFilterPills(){const t=this.element?.querySelector('[data-container="filter-pills"]');if(!t)return;this.getActiveFilters();const e=this.buildActivePills();t.innerHTML=e}updateSearchInputs(t){const e=this.element?.querySelectorAll('[data-filter="search"]');e&&e.forEach(e=>{e.value=t||""})}buildActivePills(){if(this.hideActivePills)return"";const t=this.getActiveFilters(),e=t.search&&""!==t.search.toString().trim();let i=Object.entries(t).filter(([t,e])=>e&&""!==e.toString().trim()&&"search"!==t);return this.hideActivePillNames&&this.hideActivePillNames.length>0&&(i=i.filter(([t])=>!this.hideActivePillNames.includes(t))),0!==i.length||e?`\n <div class="row mt-2">\n <div class="col-12">\n <div class="d-flex flex-wrap align-items-center">\n ${i.map(([t,e])=>{const{field:i}=u(t);return`\n <span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">\n <i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>\n\n <button type="button" class="btn btn-link text-white p-0 ms-1"\n style="font-size: 0.65rem; line-height: 1;"\n data-action="edit-filter"\n data-filter="${t}"\n title="Edit filter">\n ${m(t,e,this.getFilterLabel(i))}\n </button>\n\n <button type="button" class="btn-close btn-close-white ms-1"\n style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"\n data-action="remove-filter"\n data-filter="${t}"\n title="Remove filter">\n </button>\n </span>\n `}).join("")}\n ${i.length>1||i.length>0&&e||0===i.length&&e?'\n <button class="btn btn-sm btn-outline-secondary mb-1 py-0 px-2" style="font-size: 0.75rem;" data-action="clear-all-filters">\n <i class="bi bi-x-circle me-1" style="font-size: 0.7rem;"></i>\n <small>Clear All</small>\n </button>\n ':""}\n </div>\n </div>\n </div>\n `:""}buildTableHeaderTemplate(){let t="";return this.isSelectable()&&(t+='\n <th style="width: 40px; padding: 0;">\n <div class="mojo-select-all-cell" data-action="select-all">\n <div class="mojo-checkbox">\n <i class="bi bi-check"></i>\n </div>\n </div>\n </th>\n '),this.columns.forEach(e=>{const{fieldKey:i}=this.parseColumnKey(e.key),s=this.sortable&&!1!==e.sortable,n=this.getSortBy()===i?this.getSortDirection():null,l=this.getSortIcon(n),a=e.label||e.title||i,o=this.getResponsiveClasses(e.visibility),r=s?`\n <div class="dropdown d-inline-block ms-2">\n <button class="btn btn-sm btn-link p-0 text-decoration-none" type="button"\n data-bs-toggle="dropdown" aria-expanded="false"\n data-column="${i}">\n ${l}\n </button>\n <ul class="dropdown-menu dropdown-menu-end">\n <li><a class="dropdown-item ${"asc"===n?"active":""}"\n data-action="sort" data-field="${i}" data-direction="asc">\n <i class="bi bi-sort-alpha-down me-2"></i>Sort A-Z\n </a></li>\n <li><a class="dropdown-item ${"desc"===n?"active":""}"\n data-action="sort" data-field="${i}" data-direction="desc">\n <i class="bi bi-sort-alpha-down-alt me-2"></i>Sort Z-A\n </a></li>\n <li><a class="dropdown-item ${null===n?"active":""}"\n data-action="sort" data-field="${i}" data-direction="none">\n <i class="bi bi-x-circle me-2"></i>No Sort\n </a></li>\n </ul>\n </div>\n `:"",c=this.getAlignClass(e.align);t+=`\n <th class="${s?"sortable":""} ${o} ${c}">\n <div class="d-flex align-items-center ${"text-center"===c?"justify-content-center":"text-end"===c?"justify-content-end":""}">\n <span>${a}</span>\n ${r}\n </div>\n </th>\n `}),this.actions?t+="<th>Actions</th>":this.contextMenu&&(t+='<th style="width: 1px;"></th>'),`\n <thead>\n <tr>\n ${t}\n </tr>\n </thead>\n `}buildTableFooterTemplate(){let t="";this.isSelectable()&&(t+="<td></td>");let e=0;return this.columns.forEach((i,s)=>{const n=this.getResponsiveClasses(i.visibility),l=this.getAlignClass(i.align);if(i.footer_total){const s=`col_${e}`,a=this.parseColumnKey(i.key).formatter||i.formatter;let o;o=a&&"string"==typeof a?`{{{footerTotals.${s}.value|${a}}}}`:`{{footerTotals.${s}.value}}`,t+=`<td class="table-footer-total ${n} ${l}" data-total-column="${s}">${o}</td>`,e++}else t+=0===s?`<td class="table-footer-label ${n} ${l}"><strong>Totals</strong></td>`:`<td class="${n} ${l}"></td>`}),(this.actions||this.contextMenu)&&(t+="<td></td>"),`\n <tfoot>\n <tr class="table-totals-row">\n ${t}\n </tr>\n </tfoot>\n `}buildBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return"";if("top"===this.batchBarLocation){let t="";return this.batchActions.forEach(e=>{t+=`\n <button class="btn btn-sm btn-outline-secondary" data-action="batch-${e.action}" title="${e.label}">\n <i class="${e.icon} me-1"></i>\n <span class="d-none d-lg-inline">${e.label}</span>\n </button>\n `}),`\n <div class="batch-actions-panel-top alert alert-info d-none mb-3" role="alert">\n <div class="d-flex justify-content-between align-items-center">\n <div class="d-flex align-items-center">\n <strong class="me-2">\n <span class="batch-select-count">0</span> ${this.options.batchPanelTitle||"items"} selected\n </strong>\n </div>\n <div class="d-flex gap-2 align-items-center">\n ${t}\n <button class="btn btn-sm btn-outline-secondary" data-action="clear-selection" title="Clear Selection">\n <i class="bi bi-x-circle me-1"></i>\n <span class="d-none d-lg-inline">Clear</span>\n </button>\n </div>\n </div>\n </div>\n `}{let t="";return this.batchActions.forEach(e=>{t+=`\n <div class="batch-select-action text-center px-2" data-action="batch-${e.action}">\n <div class="batch-action-icon fs-3">\n <i class="${e.icon}"></i>\n </div>\n <div class="batch-action-title small">${e.label}</div>\n </div>\n `}),`\n <div class="batch-actions-panel rounded-start rounded-end" style="display: none;">\n <div class="batch-select-panel rounded-start rounded-end">\n <div class="row g-0">\n <div class="col-auto">\n <div class="batch-select-count rounded-start">0</div>\n </div>\n <div class="col">\n <div class="ps-2 batch-select-title">${this.options.batchPanelTitle||"Rows"}</div>\n </div>\n <div class="col">\n <div class="batch-select-actions d-flex justify-content-end">\n ${t}\n </div>\n </div>\n <div class="col-auto">\n <div class="batch-select-end rounded-end"></div>\n </div>\n </div>\n </div>\n </div>\n `}}buildPaginationTemplate(){return this.paginated?'\n <div class="table-status-bar mt-3">\n <div class="d-flex flex-column flex-lg-row justify-content-center justify-content-lg-between align-items-center gap-3">\n <div class="d-flex flex-column flex-sm-row align-items-center gap-2 gap-sm-3 text-center text-lg-start">\n <span class="text-muted">\n Showing <span data-value="start">0</span> to <span data-value="end">0</span>\n of <span data-value="total">0</span> entries\n </span>\n <div class="d-flex align-items-center">\n <label class="form-label me-2 mb-0">Show:</label>\n <select class="form-select form-select-sm" style="width: auto;" data-change-action="page-size">\n <option value="5">5</option>\n <option value="10">10</option>\n <option value="25">25</option>\n <option value="50">50</option>\n <option value="100">100</option>\n </select>\n </div>\n </div>\n <nav aria-label="Table pagination">\n <ul class="pagination pagination-sm mb-0 justify-content-center" data-container="pagination">\n \x3c!-- Pagination will be rendered here --\x3e\n </ul>\n </nav>\n </div>\n </div>\n ':""}_createItemView(t,e){const i=new this.itemClass({model:t,index:e,listView:this,tableView:this,template:this.itemTemplate,columns:this.columns,actions:this.actions,contextMenu:this.contextMenu,batchActions:this.batchActions,containerId:"items"});return this.itemViews.set(t.id,i),i.on("item:select",t=>{this._onItemSelect(t),this.updateBatchActionsPanel()}),i.on("item:deselect",t=>{this._onItemDeselect(t),this.updateBatchActionsPanel()}),i.on("row:click",this._onRowClick.bind(this)),i.on("row:view",this._onRowView.bind(this)),i.on("row:edit",this._onRowEdit.bind(this)),i.on("row:delete",this._onRowDelete.bind(this)),i.on("cell:edit",this._onCellEdit.bind(this)),i.on("cell:save",this._onCellSave.bind(this)),i.on("cell:cancel",this._onCellCancel.bind(this)),i}async onMounted(){await super.onMounted();const t=this.getActiveFilters();this.collection&&Object.keys(t).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){this.element&&this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",t=>{""===t.target.value&&this.getActiveFilters().search&&this.onActionClearSearch(t,t.target)})})}_onRowClick(t){if(this.emit("row:click",t),this.options.onRowClick)return this.options.onRowClick(t.model,t.event);"view"===this.clickAction?this._onRowView(t):"edit"===this.clickAction&&this._onRowEdit(t)}getModelClass(t){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:t?.constructor?t.constructor:null}getModelName(t){const e=this.getModelClass(t);return e&&(e.MODEL_NAME||e.name.replace(/Model$/,""))||"Item"}getItemViewClass(t){if(this.itemView)return this.itemView;const e=this.getModelClass(t);return e?.VIEW_CLASS?e.VIEW_CLASS:null}getAddFormConfig(t){return this.addForm||t?.ADD_FORM||this.editForm||t?.EDIT_FORM}getEditFormConfig(t){return this.editForm||t?.EDIT_FORM||this.addForm||t?.ADD_FORM}getFormDialogConfig(t){return{...t?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(t,e){return t?a.render(t,e):""}async _onRowView(t){if(this.emit("row:view",t),this.options.onItemView)return void(await this.options.onItemView(t.model,t.event));const e=this.getItemViewClass(t.model);if(e){const i=new e({model:t.model,collection:this.collection});await o.dialog({header:!1,body:i,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(t.model)),...this.viewDialogOptions})}else await o.data({title:`View ${this.getModelName(t.model)} #${t.model.id}`,model:t.model})}async _onRowEdit(t){if(this.emit("row:edit",t),this.options.onItemEdit)return void(await this.options.onItemEdit(t.model,t.event));const e=this.getModelClass(t.model);let i=this.getEditFormConfig(e);if(i){i.fields||(i={title:`Edit ${this.getModelName(t.model)}`,fields:i});const s=await o.modelForm({model:t.model,...i,...this.getFormDialogConfig(e)});if(!s)return;if(!s.success||!s?.result?.data.status)return void o.showError(s?.result?.data?.error||s?.result?.message||"An error occurred")}else{const e=await o.dialog({title:`Edit ${this.getModelName(t.model)} #${t.model.id}`,body:new r({model:t.model,fields:this.options.formFields||[]})});if(e){const i=await t.model.save(e);if(!i.data?.status)return void o.showError(i.data.error||"An error occurred");await this.refresh()}}}async _onRowDelete(t){if(this.emit("row:delete",t),this.options.onItemDelete)return void(await this.options.onItemDelete(t.model,t.event));const e=this.getModelClass(t.model),i=this.deleteTemplate||e?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',s=this.renderTemplateString(i,t.model);await o.confirm({message:s||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await t.model.destroy(),this.collection.fetch())}_onCellEdit(t){this.emit("cell:edit",t)}async _onCellSave(t){this.emit("cell:save",t)}_onCellCancel(t){this.emit("cell:cancel",t)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(t,e){this.isFullscreen?await this.exitFullscreen():await this.enterFullscreen()}async enterFullscreen(){try{this.element.requestFullscreen?await this.element.requestFullscreen():this.element.mozRequestFullScreen?await this.element.mozRequestFullScreen():this.element.webkitRequestFullscreen?await this.element.webkitRequestFullscreen():this.element.msRequestFullscreen&&await this.element.msRequestFullscreen(),this.isFullscreen=!0,this.element.classList.add("table-fullscreen"),this.updateFullscreenButton(),this.setupFullscreenListeners(),this.emit("table:fullscreen:enter")}catch(t){console.warn("Could not enter fullscreen:",t)}}async exitFullscreen(){try{document.exitFullscreen?await document.exitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.msExitFullscreen&&await document.msExitFullscreen(),this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit")}catch(t){console.warn("Could not exit fullscreen:",t)}}updateFullscreenButton(){const t=this.element?.querySelector(".btn-fullscreen"),e=t?.querySelector("i");t&&e&&(this.isFullscreen?(e.className="bi bi-fullscreen-exit",t.title="Exit Fullscreen"):(e.className="bi bi-fullscreen",t.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const t=()=>{!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)&&this.isFullscreen&&(this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit"))};document.addEventListener("fullscreenchange",t),document.addEventListener("mozfullscreenchange",t),document.addEventListener("webkitfullscreenchange",t),document.addEventListener("msfullscreenchange",t),this._fullscreenHandler=t}cleanupFullscreenListeners(){this._fullscreenHandler&&(document.removeEventListener("fullscreenchange",this._fullscreenHandler),document.removeEventListener("mozfullscreenchange",this._fullscreenHandler),document.removeEventListener("webkitfullscreenchange",this._fullscreenHandler),document.removeEventListener("msfullscreenchange",this._fullscreenHandler),this._fullscreenHandler=null)}destroy(){this.cleanupFullscreenListeners(),super.destroy()}async onActionRefresh(t,e){await this.refresh()}async onActionAdd(t,e){if(this.options.onAdd)return this.emit("table:add",{event:t}),void(await this.options.onAdd(t));this.emit("table:add",{event:t});const i=this.getModelClass();if(!i)return void console.warn("Cannot determine Model class for add operation");let s=this.getAddFormConfig(i);if(s){const t=new i;s.fields||(s={title:`Add ${this.getModelName()}`,fields:s});const e=await o.form({model:t,...s,...this.getFormDialogConfig(i)});if(e){this.options.addRequiresActiveGroup&&(e.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(e.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(e,this.options.addFormDefaults);const i=await t.save(e);if(!i?.data.status)return void o.showError(i?.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}else{const t=new i,e=await o.dialog({title:`Add ${this.getModelName()}`,body:new r({model:t,fields:this.options.formFields||[]})});if(e){const i=await t.save(e);if(!i?.data.status)return void o.showError(i.data.error||"An error occurred");this.collection&&this.collection.add(t),await this.refresh()}}}async onActionExport(t,e){const i=e.getAttribute("data-format")||"json";this.emit("table:export",{format:i,source:this.exportSource,event:t}),"remote"===this.exportSource?this.collection?await this.collection.download(i):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],i):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(t,e){const i=e.value.trim();this.collection&&(this.setFilter("search",i),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():await this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:i,event:t}),this.emit("params-changed")}async onActionClearSearch(t,e){this.setFilter("search",null),this.collection&&(this.collection.params.start=0,this.collection.restEnabled&&await this.collection.fetch()),await this.render(),this.updateFilterPills(),this.emit("table:search",{searchTerm:"",event:t}),this.emit("params-changed")}getSortBy(){const t=this.collection?.params?.sort;return t?t.startsWith("-")?t.slice(1):t:null}getSortDirection(){const t=this.collection?.params?.sort;return t&&t.startsWith("-")?"desc":"asc"}getSortIcon(t){return"asc"===t?'<i class="bi bi-sort-alpha-down text-primary"></i>':"desc"===t?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(t,e){t.preventDefault();const i=e.getAttribute("data-field"),s=e.getAttribute("data-direction");if(this.collection){let t;if(t="none"===s?void 0:"desc"===s?`-${i}`:i,this.collection.setParams({...this.collection.params,sort:t,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(t){const e=t.startsWith("-"),i=e?t.slice(1):t;this.collection.sort((t,s)=>{const n=t.get(i),l=s.get(i);return n<l?e?1:-1:n>l?e?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:i,event:t}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const t=this.getSortBy(),e=this.getSortDirection();this.columns.forEach(i=>{if(this.sortable&&!1!==i.sortable){const{fieldKey:s}=this.parseColumnKey(i.key),n=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${s}"]`);if(n){const i=t===s,l=this.getSortIcon(i?e:null);n.innerHTML=l;const a=n.nextElementSibling;if(a){const n=a.querySelector(`[data-field="${s}"][data-direction="asc"]`),l=a.querySelector(`[data-field="${s}"][data-direction="desc"]`),o=a.querySelector(`[data-field="${s}"][data-direction="none"]`);n&&n.classList.toggle("active",i&&"asc"===e),l&&l.classList.toggle("active",i&&"desc"===e),o&&o.classList.toggle("active",!i||t!==s)}}}})}async onActionSelectAll(t,e){t.stopPropagation();const i=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(t=>t.selected);i?this.clearSelection():this.forEachItem(t=>{t.selected||t.select()});const s=this.element?.querySelector(".mojo-select-all-cell");s&&s.classList.toggle("selected",!i),this.updateBatchActionsPanel()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){if(await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.paginated&&this.collection){const t=this.collection.meta?.count||this.collection.length(),e=this.collection.params?.start||0,i=this.collection.params?.size||10,s=Math.min(e+i,t),n=this.element.querySelector('[data-value="start"]'),l=this.element.querySelector('[data-value="end"]'),a=this.element.querySelector('[data-value="total"]');n&&(n.textContent=e+1),l&&(l.textContent=s),a&&(a.textContent=t);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=i),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const t=this.element.querySelector('[data-container="pagination"]');if(!t||!this.collection)return;const e=this.collection.meta?.count||this.collection.length(),i=this.collection.params?.size||10,s=this.collection.params?.start||0,n=Math.floor(s/i)+1,l=Math.ceil(e/i);if(l<=1)return void(t.innerHTML="");const a=n>1?n-1:l,o=n<l?n+1:1,r=[];r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${a}">\n <i class="bi bi-chevron-left"></i>\n </a>\n </li>\n `);const c=/* @__PURE__ */new Set([1,l]);for(let u=n-1;u<=n+1;u++)u>=1&&u<=l&&c.add(u);const d=Array.from(c).sort((t,e)=>t-e);let h=0;for(const u of d)h&&u-h>1&&r.push('\n <li class="page-item disabled"><span class="page-link">…</span></li>\n '),r.push(`\n <li class="page-item ${u===n?"active":""}">\n <a class="page-link" href="#" data-action="page" data-page="${u}">${u}</a>\n </li>\n `),h=u;r.push(`\n <li class="page-item">\n <a class="page-link" href="#" data-action="page" data-page="${o}">\n <i class="bi bi-chevron-right"></i>\n </a>\n </li>\n `),t.innerHTML=r.join("")}async onActionPage(t,e){t.preventDefault();const i=parseInt(e.getAttribute("data-page"),10),s=this.collection.params?.size||10,n=this.collection.meta?.count||this.collection.length(),l=Math.max(1,Math.ceil(n/s));let a=isNaN(i)?1:i;a<1&&(a=l),a>l&&(a=1),this.collection.setParams({...this.collection.params,start:(a-1)*s}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:a,event:t}),this.emit("params-changed")}async onChangePageSize(t,e){const i=parseInt(e.value);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:i}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("table:pagesize",{size:i,event:t}),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:t,size:e,sort:i,...s}=this.collection.params,n={},l=/* @__PURE__ */new Set;return this.getAllAvailableFilters().forEach(t=>{if("daterange"===t.config.type){const e=t.key,i=t.config.startName||"dr_start",a=t.config.endName||"dr_end",o=t.config.fieldName||"dr_field";s[o]===e&&(s[i]||s[a])&&(n[e]={start:s[i]||"",end:s[a]||""},l.add(i),l.add(a),l.add(o))}}),Object.keys(s).forEach(t=>{l.has(t)||(n[t]=s[t])}),Object.keys(n).forEach(t=>{if(n.hasOwnProperty(t)){const e=`${t}__in`;n.hasOwnProperty(e)&&(delete n[t],n[e]=n[e])}}),n}setFilter(t,e){if(!this.collection)return;const i=this.getFilterConfig(t);if(i&&"daterange"===i.type){const s=i.startName||"dr_start",n=i.endName||"dr_end",l=i.fieldName||"dr_field";delete this.collection.params[s],delete this.collection.params[n],delete this.collection.params[l],e&&"object"==typeof e&&(e.start||e.end)&&(e.start&&(this.collection.params[s]=e.start),e.end&&(this.collection.params[n]=e.end),this.collection.params[l]=t)}else{const{field:i,lookup:s}=u(t);if(delete this.collection.params[t],delete this.collection.params[i],delete this.collection.params[`${i}__in`],!e||Array.isArray(e)&&0===e.length)return;Array.isArray(e)?1===e.length?this.collection.params[i]=e[0]:this.collection.params[`${i}__in`]=e.join(","):this.collection.params[t]=e}}getAllAvailableFilters(){const t=[];return this.columns.forEach(e=>{if(e.filter){const{fieldKey:i}=this.parseColumnKey(e.key);t.push({key:i,label:e.filter.label||e.label||i,type:e.filter.type,config:e.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(e=>{t.push({key:e.name||e.key,label:e.label,type:e.type,config:e})}),t}getFilterConfig(t){const e=this.columns.find(e=>{const{fieldKey:i}=this.parseColumnKey(e.key);return i===t});if(e&&e.filter)return e.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const e=this.additionalFilters.find(e=>(e.name||e.key)===t);if(e)return e}return null}getFilterLabel(t){if("search"===t)return"Search";const e=this.filters[t];if(e&&e.label)return e.label;const i=this.additionalFilters.find(e=>(e.name||e.key)===t);return i&&i.label?i.label:t.charAt(0).toUpperCase()+t.slice(1)}getFilterDisplayValue(t,e){if("search"===t)return`"${e}"`;const i=this.filters[t]||this.additionalFilters.find(e=>(e.name||e.key)===t);if(i&&"daterange"===i.type&&"object"==typeof e)return`${e.start||""} to ${e.end||""}`;if(i&&"select"===i.type&&i.options){if("object"==typeof i.options[0]){const t=i.options.find(t=>t.value===e);return t?t.label:e}return e}return e}getFilterIcon(t){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[t]||"filter"}async onActionAddFilter(t,e){const i=e.getAttribute("data-filter-key"),s=this.getFilterConfig(i),n=this.getActiveFilters()[i];if(!s)return void console.warn("No filter config found for key:",i);const l=await o.form({title:`${void 0!==n&&""!==n?"Edit":"Add"} ${this.getFilterLabel(i)} Filter`,size:"md",fields:[this.buildFilterDialogField(s,n,i)]});if(l){const t=this.extractFilterValue(s,l);this.setFilter(i,t),await this.applyFilters()}}buildFilterDialogField(t,e,i){const{name:s,value:n,...l}=t,a={...l,name:"filter_value",label:l.label,value:e,placeholder:l.placeholder||l.placeHolder};if("daterange"===t.type){if(a.startName=a.startName||"dr_start",a.endName=a.endName||"dr_end",a.fieldName=a.fieldName||"dr_field",a.format=a.format||"YYYY-MM-DD",a.displayFormat=a.displayFormat||"MMM DD, YYYY",a.separator=a.separator||" to ",a.label=a.label||"Date Range",e&&"object"==typeof e){const t=t=>{if(!t&&0!==t)return"";if(t instanceof Date&&!isNaN(t))return t.toISOString().slice(0,10);const e=String(t).trim();if(!e)return"";if(/^-?\d+$/.test(e)){const t=Number(e),i=e.length<=10?1e3*t:t,s=new Date(i);if(!isNaN(s))return s.toISOString().slice(0,10)}const i=new Date(e);return isNaN(i)?e:i.toISOString().slice(0,10)};a.startDate=t(e.start||e.from||e.begin||""),a.endDate=t(e.end||e.to||e.finish||"")}}else if("multiselect"===t.type){let i=[];e&&(Array.isArray(e)?i=e:"string"==typeof e&&(i=e.split(",").map(t=>t.trim()).filter(t=>t))),a.value=i,a.placeholder||a.placeHolder||(t.placeholder||t.placeHolder?a.placeholder=t.placeholder||t.placeHolder:t.label&&(a.placeholder=`Select ${t.label}...`))}return a}extractFilterValue(t,e){if("daterange"===t.type){const i=t.startName||"dr_start",s=t.endName||"dr_end";return{start:e[i],end:e[s]}}return t.type,e.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),await this.render()}catch(t){console.error("Failed to fetch filtered data:",t),await this.render()}else await this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(t,e){const i=e.getAttribute("data-filter"),{field:s}=u(i);let n=this.getFilterConfig(s)||this.getFilterConfig(i);const l=this.getActiveFilters(),a=l[i]||l[s];if(!n)return void console.warn("No filter config found for key:",i,"or field:",s);const r={filter_value:a};if("daterange"===n.type&&a&&"object"==typeof a){const t=n.startName||"dr_start",e=n.endName||"dr_end";r[t]=a.start||"",r[e]=a.end||""}const c=await o.form({title:`Edit ${this.getFilterLabel(s)} Filter`,size:"md",data:r,fields:[this.buildFilterDialogField(n,a,s)]});if(c){const t=this.extractFilterValue(n,c);this.setFilter(i,t),await this.applyFilters()}}async onActionRemoveFilter(t,e){const i=e.getAttribute("data-filter"),{field:s}=u(i);this.setFilter(i,null),"search"===i&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:i,field:s}),this.emit("params-changed")}async onActionClearAllFilters(t,e){if(!this.collection)return;const{start:i,size:s,sort:n}=this.collection.params,l={start:i,size:s};n&&(l.sort=n),Array.isArray(this.hideActivePillNames)&&this.hideActivePillNames.length>0&&this.hideActivePillNames.forEach(t=>{void 0!==this.collection.params[t]&&(l[t]=this.collection.params[t]);const e=this.getFilterConfig(t);if(e&&"daterange"===e.type){const t=e.startName||"dr_start",i=e.endName||"dr_end",s=e.fieldName||"dr_field";void 0!==this.collection.params[t]&&(l[t]=this.collection.params[t]),void 0!==this.collection.params[i]&&(l[i]=this.collection.params[i]),void 0!==this.collection.params[s]&&(l[s]=this.collection.params[s])}}),this.collection.params=l,this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}updateBatchActionsPanel(){if(!this.batchActions||0===this.batchActions.length)return;const t=this.getSelectedItems().length;if("top"===this.batchBarLocation){const e=this.element?.querySelector(".batch-actions-panel-top"),i=this.element?.querySelector(".batch-select-count");e&&i&&(i.textContent=t,t>0?e.classList.remove("d-none"):e.classList.add("d-none"))}else{const e=this.element?.querySelector(".batch-actions-panel"),i=this.element?.querySelector(".batch-select-count");e&&i&&(i.textContent=t,e.style.display=t>0?"block":"none")}const e=this.element?.querySelector(".mojo-select-all-cell");if(e){const t=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(t=>t.selected),i=Array.from(this.itemViews.values()).some(t=>t.selected);e.classList.toggle("selected",t),e.classList.toggle("indeterminate",!t&&i);const s=e.querySelector("i");s&&(s.className=!t&&i?"bi bi-dash":"bi bi-check")}}async onActionBatch(t,e){const i=e.getAttribute("data-action").replace("batch-",""),s=this.getSelectedItems();this.emit("batch:action",{action:i,items:s,event:t})}async onActionClearSelection(t,e){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(t,e){const i=parseInt(e.getAttribute("data-button-index"),10),s=this.toolbarButtons[i];s&&"function"==typeof s.handler&&await s.handler.call(this,t,e)}}const b=/* @__PURE__ */Object.freeze(/* @__PURE__ */Object.defineProperty({__proto__:null,default:TableView},Symbol.toStringTag,{value:"Module"}));function f(t){const e=t.replace(/-/g,"+").replace(/_/g,"/"),i=e+"=".repeat((4-e.length%4)%4);return Uint8Array.from(atob(i),t=>t.charCodeAt(0))}function g(t){return btoa(String.fromCharCode(...new Uint8Array(t))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}class Passkey extends e{constructor(t={},e={}){super(t,{endpoint:"/api/account/passkeys",...e})}static suggestName(){const t=navigator.userAgent;let e="Device";/iPad/.test(t)?e="iPad":/iPhone/.test(t)?e="iPhone":/Macintosh|MacIntel/.test(t)?e="Mac":/Android/.test(t)?e="Android":/Windows/.test(t)?e="Windows PC":/Linux/.test(t)&&(e="Linux");let i="";return/Edg\//.test(t)?i="Edge":/Chrome\//.test(t)&&!/Chromium/.test(t)?i="Chrome":/Safari\//.test(t)&&!/Chrome/.test(t)?i="Safari":/Firefox\//.test(t)&&(i="Firefox"),i?`${e} — ${i}`:e}static async register(t){const e=await Passkey.registerBegin();if(!e?.data?.challenge_id||!e?.data?.publicKey)return{success:!1,error:e?.error||"Could not start registration."};const{challenge_id:i,publicKey:s}=e.data;"string"==typeof s.challenge&&(s.challenge=f(s.challenge)),"string"==typeof s.user?.id&&(s.user.id=f(s.user.id)),s.excludeCredentials&&(s.excludeCredentials=s.excludeCredentials.map(t=>({...t,id:"string"==typeof t.id?f(t.id):t.id})));const n=await navigator.credentials.create({publicKey:s});if(!n)return{success:!1,error:"Passkey creation was cancelled."};const l={id:n.id,rawId:g(n.rawId),type:n.type,response:{clientDataJSON:g(n.response.clientDataJSON),attestationObject:g(n.response.attestationObject)}};n.response.getTransports&&(l.transports=n.response.getTransports());const a=await Passkey.registerComplete({challenge_id:i,credential:l,friendly_name:t||"My Passkey"});return a?.data?.id?{success:!0,passkey:a.data}:{success:!1,error:a?.error||"Registration could not be completed."}}static async registerBegin(t={}){try{return await s.POST("/api/account/passkeys/register/begin",{},t.params,{dataOnly:!0})}catch(e){return{success:!1,error:e?.message||"Failed to begin passkey registration"}}}static async registerComplete(t={},e={}){if(!t.challenge_id||!t.credential)return{success:!1,error:"Missing challenge_id or credential data"};try{return await s.POST("/api/account/passkeys/register/complete",t,e.params,{dataOnly:!0})}catch(i){return{success:!1,error:i?.message||"Failed to complete passkey registration"}}}}class PasskeyList extends t{constructor(t={}){super({ModelClass:Passkey,endpoint:"/api/account/passkeys",size:10,...t})}}const y={edit:{fields:[{name:"friendly_name",type:"text",label:"Name",placeholder:"My iPhone",required:!0,columns:12,help:"A friendly name to identify this passkey"},{name:"is_enabled",type:"switch",label:"Enabled",columns:12,help:"Disable to prevent this passkey from being used for authentication"}]}};export{p as D,h as L,MemberList as M,Passkey as P,TableView as T,PasskeyList as a,y as b,TableRow as c,Member as d,Log as e,LogList as f,c as g,m as h,b as i,u as p};
2
- //# sourceMappingURL=Passkeys-CMh9iSax.js.map