web-mojo 2.2.65 → 2.2.66

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 (100) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/admin.cjs.js +1 -1
  3. package/dist/admin.cjs.js.map +1 -1
  4. package/dist/admin.es.js +1 -1
  5. package/dist/admin.es.js.map +1 -1
  6. package/dist/auth.cjs.js +1 -1
  7. package/dist/auth.es.js +1 -1
  8. package/dist/charts.cjs.js +1 -1
  9. package/dist/charts.es.js +1 -1
  10. package/dist/chunks/ChatView-CYIOOvHM.js +2 -0
  11. package/dist/chunks/ChatView-CYIOOvHM.js.map +1 -0
  12. package/dist/chunks/ChatView-DTcs3Xh7.js +2 -0
  13. package/dist/chunks/ChatView-DTcs3Xh7.js.map +1 -0
  14. package/dist/chunks/{Collection-1sPoIFvQ.js → Collection-BgfCzEE5.js} +2 -2
  15. package/dist/chunks/{Collection-1sPoIFvQ.js.map → Collection-BgfCzEE5.js.map} +1 -1
  16. package/dist/chunks/ContextMenu-C7QJDUar.js +2 -0
  17. package/dist/chunks/ContextMenu-C7QJDUar.js.map +1 -0
  18. package/dist/chunks/ContextMenu-C9RiB1kl.js +2 -0
  19. package/dist/chunks/ContextMenu-C9RiB1kl.js.map +1 -0
  20. package/dist/chunks/{DataView--nUWtq6r.js → DataView-Dl2jl_0h.js} +2 -2
  21. package/dist/chunks/{DataView--nUWtq6r.js.map → DataView-Dl2jl_0h.js.map} +1 -1
  22. package/dist/chunks/{Dialog-VoLlToMl.js → Dialog-9FzcToYV.js} +2 -2
  23. package/dist/chunks/{Dialog-wBhTkeWg.js.map → Dialog-9FzcToYV.js.map} +1 -1
  24. package/dist/chunks/{Dialog-wBhTkeWg.js → Dialog-DZG_XYbk.js} +2 -2
  25. package/dist/chunks/{Dialog-VoLlToMl.js.map → Dialog-DZG_XYbk.js.map} +1 -1
  26. package/dist/chunks/Files-BDqCTw9A.js +2 -0
  27. package/dist/chunks/Files-BDqCTw9A.js.map +1 -0
  28. package/dist/chunks/Files-B_NE3QwI.js +2 -0
  29. package/dist/chunks/Files-B_NE3QwI.js.map +1 -0
  30. package/dist/chunks/FormView-CQ0eFCJu.js +3 -0
  31. package/dist/chunks/FormView-CQ0eFCJu.js.map +1 -0
  32. package/dist/chunks/FormView-D2Ia0Idj.js +3 -0
  33. package/dist/chunks/FormView-D2Ia0Idj.js.map +1 -0
  34. package/dist/chunks/{ListView-6JQ6tRXs.js → ListView-BunTmkAU.js} +2 -2
  35. package/dist/chunks/{ListView-6JQ6tRXs.js.map → ListView-BunTmkAU.js.map} +1 -1
  36. package/dist/chunks/{MetricsCountryMapView-J067qrrt.js → MetricsCountryMapView-C_p6hcly.js} +2 -2
  37. package/dist/chunks/{MetricsCountryMapView-J067qrrt.js.map → MetricsCountryMapView-C_p6hcly.js.map} +1 -1
  38. package/dist/chunks/{MetricsMiniChartWidget-DtWq2_YJ.js → MetricsMiniChartWidget-C1RCd-Xw.js} +2 -2
  39. package/dist/chunks/{MetricsMiniChartWidget-DtWq2_YJ.js.map → MetricsMiniChartWidget-C1RCd-Xw.js.map} +1 -1
  40. package/dist/chunks/{MetricsMiniChartWidget-CtCN1rtt.js → MetricsMiniChartWidget-C9QdxxBn.js} +2 -2
  41. package/dist/chunks/{MetricsMiniChartWidget-CtCN1rtt.js.map → MetricsMiniChartWidget-C9QdxxBn.js.map} +1 -1
  42. package/dist/chunks/{PDFViewer-BNCyDElg.js → PDFViewer-B_LSyM7g.js} +2 -2
  43. package/dist/chunks/{PDFViewer-BNCyDElg.js.map → PDFViewer-B_LSyM7g.js.map} +1 -1
  44. package/dist/chunks/{PDFViewer-Di4OHAkv.js → PDFViewer-DI9v3Vkg.js} +2 -2
  45. package/dist/chunks/{PDFViewer-Di4OHAkv.js.map → PDFViewer-DI9v3Vkg.js.map} +1 -1
  46. package/dist/chunks/{Rest-DHbszkuP.js → Rest-DFnCoMvk.js} +2 -2
  47. package/dist/chunks/{Rest-DHbszkuP.js.map → Rest-DFnCoMvk.js.map} +1 -1
  48. package/dist/chunks/TokenManager-Dk5oiCh1.js +2 -0
  49. package/dist/chunks/{TokenManager-8JM2qj_1.js.map → TokenManager-Dk5oiCh1.js.map} +1 -1
  50. package/dist/chunks/TokenManager-DzLjXShS.js +2 -0
  51. package/dist/chunks/{TokenManager-D-9tqubS.js.map → TokenManager-DzLjXShS.js.map} +1 -1
  52. package/dist/chunks/User-BwDOeY-q.js +3 -0
  53. package/dist/chunks/User-BwDOeY-q.js.map +1 -0
  54. package/dist/chunks/User-CbqYlTBK.js +3 -0
  55. package/dist/chunks/User-CbqYlTBK.js.map +1 -0
  56. package/dist/chunks/{WebApp-JJAK0eNM.js → WebApp-BCfA2Dlw.js} +2 -2
  57. package/dist/chunks/{WebApp-JJAK0eNM.js.map → WebApp-BCfA2Dlw.js.map} +1 -1
  58. package/dist/chunks/{WebApp-BdJA4Uup.js → WebApp-kKz5FV57.js} +2 -2
  59. package/dist/chunks/{WebApp-BdJA4Uup.js.map → WebApp-kKz5FV57.js.map} +1 -1
  60. package/dist/chunks/{WebSocketClient-BoF7TV7i.js → WebSocketClient-fEGJDFXJ.js} +2 -2
  61. package/dist/chunks/{WebSocketClient-BoF7TV7i.js.map → WebSocketClient-fEGJDFXJ.js.map} +1 -1
  62. package/dist/chunks/{version-kTtMlfsq.js → version-Bykv8fhW.js} +2 -2
  63. package/dist/chunks/{version-kTtMlfsq.js.map → version-Bykv8fhW.js.map} +1 -1
  64. package/dist/chunks/{version-Cn26llLs.js → version-D0hwRoO4.js} +2 -2
  65. package/dist/chunks/{version-Cn26llLs.js.map → version-D0hwRoO4.js.map} +1 -1
  66. package/dist/css/web-mojo.css +1 -1
  67. package/dist/docit.cjs.js +1 -1
  68. package/dist/docit.cjs.js.map +1 -1
  69. package/dist/docit.es.js +1 -1
  70. package/dist/docit.es.js.map +1 -1
  71. package/dist/index.cjs.js +1 -1
  72. package/dist/index.cjs.js.map +1 -1
  73. package/dist/index.es.js +1 -1
  74. package/dist/index.es.js.map +1 -1
  75. package/dist/lightbox.cjs.js +1 -1
  76. package/dist/lightbox.cjs.js.map +1 -1
  77. package/dist/lightbox.es.js +1 -1
  78. package/dist/lightbox.es.js.map +1 -1
  79. package/dist/map.cjs.js +1 -1
  80. package/dist/map.es.js +1 -1
  81. package/dist/timeline.es.js +1 -1
  82. package/dist/web-mojo.lite.iife.js +1226 -2
  83. package/dist/web-mojo.lite.iife.js.map +1 -1
  84. package/dist/web-mojo.lite.iife.min.js +95 -74
  85. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  86. package/package.json +1 -1
  87. package/dist/chunks/ChatView-D1-ev2qA.js +0 -2
  88. package/dist/chunks/ChatView-D1-ev2qA.js.map +0 -1
  89. package/dist/chunks/ChatView-DesGp57e.js +0 -2
  90. package/dist/chunks/ChatView-DesGp57e.js.map +0 -1
  91. package/dist/chunks/ContextMenu-CsQGpSrv.js +0 -3
  92. package/dist/chunks/ContextMenu-CsQGpSrv.js.map +0 -1
  93. package/dist/chunks/ContextMenu-dqFxitXY.js +0 -3
  94. package/dist/chunks/ContextMenu-dqFxitXY.js.map +0 -1
  95. package/dist/chunks/FormView-BQnvMheR.js +0 -3
  96. package/dist/chunks/FormView-BQnvMheR.js.map +0 -1
  97. package/dist/chunks/FormView-C7FnbmEI.js +0 -3
  98. package/dist/chunks/FormView-C7FnbmEI.js.map +0 -1
  99. package/dist/chunks/TokenManager-8JM2qj_1.js +0 -2
  100. package/dist/chunks/TokenManager-D-9tqubS.js +0 -2
@@ -1,4 +1,4 @@
1
- var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=e.defaultRoute||"home",this.routes=[],this.currentRoute=null,this.eventEmitter=e.eventEmitter||null,this.boundHandlePopState=this.handlePopState.bind(this)}start(){window.addEventListener("popstate",this.boundHandlePopState),this.handleCurrentLocation()}stop(){window.removeEventListener("popstate",this.boundHandlePopState)}addRoute(e,t){this.routes.push({pattern:this.normalizePattern(e),regex:this.patternToRegex(e),pageName:t,paramNames:this.extractParamNames(e)})}async navigate(e,t={}){const{replace:s=!1,state:i=null,trigger:a=!0}=t,{pageName:r,queryParams:n}=this.parseInput(e);a&&await this.handleRouteChange(r,n)}back(){window.history.back()}forward(){window.history.forward()}getCurrentRoute(){return this.currentRoute}getCurrentPath(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();return this.buildPublicUrl(e,t)}handlePopState(e){this.allowPopState?this.handleCurrentLocation():console.warn("PopStateEvent is not allowed")}async handleCurrentLocation(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();await this.handleRouteChange(e,t)}async handleRouteChange(e,t){const s="/"+e,i=this.matchRoute(s),a=this.buildPublicUrl(e,t);return i?(this.currentRoute=i,this.eventEmitter&&this.eventEmitter.emit("route:changed",{path:a,pageName:i.pageName,params:i.params,query:t,route:i}),i):(console.log("No route matched for page:",e),this.eventEmitter&&this.eventEmitter.emit("route:notfound",{path:a}),null)}matchRoute(e){for(const t of this.routes){const s=e.match(t.regex);if(s){const i={};return t.paramNames.forEach((a,r)=>{i[a]=s[r+1]}),{...t,params:i,path:e}}}return null}parseInput(e){let t=this.defaultRoute,s={};if(!e)return{pageName:t,queryParams:s};try{if(e.includes("?")){const[i,a]=e.split("?",2),r=new URLSearchParams(a);if(r.has("page")){t=r.get("page")||this.defaultRoute;for(const[n,o]of r)n!=="page"&&(s[n]=o)}else{i.startsWith("/")?t=i.substring(1)||this.defaultRoute:t=i||this.defaultRoute;for(const[n,o]of r)s[n]=o}}else e.startsWith("/")?t=e.substring(1)||this.defaultRoute:t=e}catch(i){console.warn("Failed to parse input:",e,i),t=this.defaultRoute,s={}}return{pageName:t,queryParams:s}}parseCurrentUrl(){const e=new URLSearchParams(window.location.search),t=e.get("page")||this.defaultRoute,s={};for(const[i,a]of e)i!=="page"&&(s[i]=a);return{pageName:t,queryParams:s}}buildPublicUrl(e,t={}){const s=new URLSearchParams;return s.set("page",e),Object.entries(t).forEach(([i,a])=>{a!=null&&a!==""&&s.set(i,String(a))}),"?"+s.toString()}updateBrowserUrl(e,t,s,i){const a=new URL(window.location.origin+window.location.pathname);a.searchParams.set("page",e),Object.entries(t).forEach(([n,o])=>{o!=null&&o!==""&&a.searchParams.set(n,String(o))});const r=a.toString();s?window.history.replaceState(i,"",r):window.history.pushState(i,"",r)}patternToRegex(e){let t=e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/\/:([^/?]+)\?/g,"(?:/([^/]+))?").replace(/:([^/]+)/g,"([^/]+)");return new RegExp(`^${t}$`)}extractParamNames(e){return(e.match(/:([^/?]+)\??/g)||[]).map(s=>s.replace(/[:?]/g,""))}normalizePattern(e){return e.startsWith("/")?e:`/${e}`}updateUrl(e={},t={}){const{replace:s=!1}=t,{pageName:i}=this.parseCurrentUrl();this.updateBrowserUrl(i,e,s)}buildUrl(e,t={}){return this.buildPublicUrl(e,t)}doRoutesMatch(e,t){if(!e||!t)return!1;const{pageName:s}=this.parseInput(e),{pageName:i}=this.parseInput(t);return s===i}}class Ee{constructor(){this.listeners={},this.onceListeners={},this.maxListeners=100,this.debugMode=!1,this.eventStats={}}on(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.on(s,t)),this):(this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].length>=this.maxListeners&&console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${e}`),this.listeners[e].push(t),this)}once(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.once(s,t)),this):(this.onceListeners[e]||(this.onceListeners[e]=[]),this.onceListeners[e].push(t),this)}off(e,t){if(Array.isArray(e))return e.forEach(s=>this.off(s,t)),this;if(!t)return delete this.listeners[e],delete this.onceListeners[e],this;if(this.listeners[e]){const s=this.listeners[e].indexOf(t);s!==-1&&(this.listeners[e].splice(s,1),this.listeners[e].length===0&&delete this.listeners[e])}if(this.onceListeners[e]){const s=this.onceListeners[e].indexOf(t);s!==-1&&(this.onceListeners[e].splice(s,1),this.onceListeners[e].length===0&&delete this.onceListeners[e])}return this}emit(e,t){this.updateEventStats(e),this.debugMode&&console.log(`[EventBus] Emitting: ${e}`,t);const s=[];return this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]),this.debugMode&&s.length>0&&console.log(`[EventBus] ${s.length} listener(s) for '${e}'`),s.forEach(i=>{try{i(t,e)&&(e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault())}catch(a){console.error(`Error in event listener for '${e}':`,a),this.emitError(a,e,i)}}),this}async emitAsync(e,t){const s=[];this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]);const i=s.map(a=>new Promise(r=>{try{const n=a(t,e);r(n)}catch(n){console.error(`Error in async event listener for '${e}':`,n),this.emitError(n,e,a),r()}}));return await Promise.all(i),this}removeAllListeners(){return this.listeners={},this.onceListeners={},this}listenerCount(e){const t=this.listeners[e]?this.listeners[e].length:0,s=this.onceListeners[e]?this.onceListeners[e].length:0;return t+s}eventNames(){const e=Object.keys(this.listeners),t=Object.keys(this.onceListeners);return[...new Set([...e,...t])]}setMaxListeners(e){if(typeof e!="number"||e<0)throw new Error("Max listeners must be a non-negative number");return this.maxListeners=e,this}namespace(e){const t=s=>`${e}:${s}`;return{on:(s,i)=>this.on(t(s),i),once:(s,i)=>this.once(t(s),i),off:(s,i)=>this.off(t(s),i),emit:(s,i)=>this.emit(t(s),i),emitAsync:(s,i)=>this.emitAsync(t(s),i)}}use(e){if(typeof e!="function")throw new Error("Middleware must be a function");const t=this.emit;return this.emit=(s,i)=>{try{const a=e(s,i);if(a===!1)return this;const r=a!==void 0?a:i;return t.call(this,s,r)}catch(a){return console.error("Error in event middleware:",a),t.call(this,s,i)}},this}emitError(e,t,s){t!=="error"&&setTimeout(()=>{this.emit("error",{error:e,originalEvent:t,callback:s.toString()})},0)}waitFor(e,t=null){return new Promise((s,i)=>{let a=null;const r=()=>{a&&clearTimeout(a)},n=o=>{r(),s(o)};this.once(e,n),t&&(a=setTimeout(()=>{this.off(e,n),i(new Error(`Timeout waiting for event: ${e}`))},t))})}debug(e=!0){return this.debugMode=e,console.log(e?"[EventBus] Debug mode enabled":"[EventBus] Debug mode disabled"),this}getStats(){const e=this.eventNames(),t={totalEvents:e.length,totalListeners:0,events:{},emissions:{...this.eventStats}};return e.forEach(s=>{const i=this.listenerCount(s);t.events[s]=i,t.totalListeners+=i}),t}updateEventStats(e){this.eventStats[e]||(this.eventStats[e]={count:0,firstEmission:Date.now(),lastEmission:null}),this.eventStats[e].count++,this.eventStats[e].lastEmission=Date.now()}getEventStats(e){const t=this.eventStats[e];return t?{...t,listenerCount:this.listenerCount(e),avgEmissionsPerMinute:this.calculateEmissionRate(t)}:null}calculateEmissionRate(e){if(!e.firstEmission||!e.lastEmission)return 0;const t=e.lastEmission-e.firstEmission;if(t===0)return 0;const s=t/(1e3*60);return Math.round(e.count/s*100)/100}resetStats(){return this.eventStats={},this}getTopEvents(e=10){return Object.entries(this.eventStats).map(([t,s])=>({event:t,count:s.count,rate:this.calculateEmissionRate(s),listeners:this.listenerCount(t)})).sort((t,s)=>s.count-t.count).slice(0,e)}debugInfo(){console.group("[EventBus] Debug Information"),console.log("Debug Mode:",this.debugMode),console.log("Max Listeners:",this.maxListeners);const e=this.getStats();return console.log("Total Events:",e.totalEvents),console.log("Total Listeners:",e.totalListeners),Object.keys(this.eventStats).length>0&&console.log("Top Events:",this.getTopEvents(5)),console.groupEnd(),this}}class ke{constructor(){this.config={baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"},trackDevice:!0,duidHeader:"X-Mojo-UID",duidTransport:"header"},this.interceptors={request:[],response:[]},this.duid=null,this.config.trackDevice&&this._initializeDuid()}_initializeDuid(){const e="mojo_device_uid";try{let t=localStorage.getItem(e);t?this.duid=t:(this.duid=this._generateDuid(),localStorage.setItem(e,this.duid))}catch(t){console.error("Could not access localStorage to get/set DUID.",t),this.duid=this._generateDuid()}}_generateDuid(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}configure(e){e.baseUrl&&(e.baseURL=e.baseUrl);const t=this.config.trackDevice;this.config={...this.config,...e,headers:{...this.config.headers,...e.headers}},this.config.trackDevice&&!t&&this._initializeDuid()}addInterceptor(e,t){this.interceptors[e]&&this.interceptors[e].push(t)}buildUrl(e){if(e.startsWith("http://")||e.startsWith("https://"))return e;const t=this.config.baseURL.endsWith("/")?this.config.baseURL.slice(0,-1):this.config.baseURL,s=e.startsWith("/")?e:`/${e}`;return`${t}${s}`}categorizeError(e,t=0){if(e.name==="TypeError"&&e.message.includes("fetch"))return{reason:"not_reachable",message:"Service is not reachable - please check your connection"};if(e.name==="AbortError")return{reason:"cancelled",message:"Request was cancelled"};if(e.name==="TimeoutError"||e.message.includes("timeout"))return{reason:"timed_out",message:"Request timed out - please try again"};if(t>=400){if(t===400)return{reason:"bad_request",message:"Invalid request data"};if(t===401)return{reason:"unauthorized",message:"Authentication required"};if(t===403)return{reason:"forbidden",message:"Access denied"};if(t===404)return{reason:"not_found",message:"Resource not found"};if(t===409)return{reason:"conflict",message:"Resource conflict"};if(t===422)return{reason:"validation_error",message:"Validation failed"};if(t===429)return{reason:"rate_limited",message:"Too many requests - please wait"};if(t>=500)return{reason:"server_error",message:"Server error - please try again later"};if(t>=400)return{reason:"client_error",message:"Request error"}}return e.message.includes("CORS")?{reason:"cors_error",message:"Cross-origin request blocked"}:e.message.includes("DNS")||e.message.includes("ENOTFOUND")?{reason:"dns_error",message:"Unable to resolve server address"}:{reason:"unknown_error",message:`Network error: ${e.message}`}}buildQueryString(e={}){const t=new URLSearchParams;Object.entries(e).forEach(([i,a])=>{a!=null&&(Array.isArray(a)?a.forEach(r=>t.append(`${i}[]`,r)):t.append(i,a))});const s=t.toString();return s?`?${s}`:""}async processRequestInterceptors(e){let t={...e};for(const s of this.interceptors.request)try{t=await s(t)}catch(i){throw console.error("Request interceptor error:",i),i}return t}async processResponseInterceptors(e,t){let s={success:e.ok,status:e.status,statusText:e.statusText,headers:Object.fromEntries(e.headers.entries()),data:null,errors:null,message:null,reason:null};try{const i=e.headers.get("content-type");if(i&&i.includes("application/json")){const a=await e.json();if(s.data=a,!e.ok){const r=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=a.errors||{},s.message=a.message||r.message,s.reason=r.reason}}else if(s.data=await e.text(),!e.ok){const a=this.categorizeError(new Error("HTTP Error"),e.status);s.message=a.message,s.reason=a.reason}}catch{s.errors={parse:"Failed to parse response"},s.message="Invalid response format"}for(const i of this.interceptors.response)try{s=await i(s,t)}catch(a){console.error("Response interceptor error:",a)}return s}async request(e,t,s=null,i={},a={}){let r={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(i),headers:{...this.config.headers,...a.headers},data:s,options:{timeout:this.config.timeout,...a}};if(r=await this.processRequestInterceptors(r),this.config.trackDevice&&this.duid)if(this.config.duidTransport==="header")r.headers[this.config.duidHeader]=this.duid;else if(r.method==="GET"){const l=new URL(r.url);l.searchParams.append("duid",this.duid),r.url=l.toString()}else r.data&&typeof r.data=="object"&&!(r.data instanceof FormData)&&(r.data.duid=this.duid);const n={method:r.method,headers:r.headers},o=[];r.options.timeout&&o.push(AbortSignal.timeout(r.options.timeout)),r.options.signal&&o.push(r.options.signal),o.length>1?n.signal=AbortSignal.any?AbortSignal.any(o):o[0]:o.length===1&&(n.signal=o[0]),r.data&&["POST","PUT","PATCH"].includes(r.method)&&(r.data instanceof FormData?(n.body=r.data,delete n.headers["Content-Type"]):typeof r.data=="object"?n.body=JSON.stringify(r.data):n.body=r.data);try{const l=await fetch(r.url,n);return await this.processResponseInterceptors(l,r)}catch(l){if(l.name==="AbortError")throw l;const c=this.categorizeError(l),d={success:!1,status:0,statusText:"Network Error",headers:{},data:null,errors:{network:l.message},message:c.message,reason:c.reason},h={ok:!1,status:0,statusText:"Network Error",headers:new Headers,json:async()=>({}),text:async()=>""};return await this.processResponseInterceptors(h,r),d}}async GET(e,t={},s={}){return this.request("GET",e,null,t,s)}async POST(e,t={},s={},i={}){return this.request("POST",e,t,s,i)}async PUT(e,t={},s={},i={}){return this.request("PUT",e,t,s,i)}async PATCH(e,t={},s={},i={}){return this.request("PATCH",e,t,s,i)}async DELETE(e,t={},s={}){return this.request("DELETE",e,null,t,s)}async download(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=r.headers.get("content-disposition");let o=s.filename||"download";if(n){const m=n.match(/filename="?(.+)"?/);m&&m.length>1&&(o=m[1])}const l=r.body.getReader(),c=new ReadableStream({start(m){function p(){return l.read().then(({done:g,value:y})=>{if(g){m.close();return}return m.enqueue(y),p()})}return p()}}),d=await new Response(c).blob(),h=window.URL.createObjectURL(d),f=document.createElement("a");return f.style.display="none",f.href=h,f.download=o,document.body.appendChild(f),f.click(),window.URL.revokeObjectURL(h),f.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async downloadBlob(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=await r.blob(),o=r.headers.get("content-disposition");let l=s.filename||"download";if(o){const h=o.match(/filename="?(.+)"?/);h&&h.length>1&&(l=h[1])}const c=window.URL.createObjectURL(n),d=document.createElement("a");return d.style.display="none",d.href=c,d.download=l,document.body.appendChild(d),d.click(),window.URL.revokeObjectURL(c),d.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async upload(e,t,s={}){return new Promise((i,a)=>{if(!(t instanceof File)){a(new Error("Only single File objects are supported for legacy backend compatibility"));return}const r=new XMLHttpRequest;s.onProgress&&typeof s.onProgress=="function"&&(r.upload.onprogress=s.onProgress),r.onload=function(){r.status>=200&&r.status<300?i({data:r.response,status:r.status,statusText:r.statusText,xhr:r}):a(new Error(`Upload failed: ${r.status} ${r.statusText}`))},r.onerror=function(){a(new Error("Upload failed: Network error"))},r.ontimeout=function(){a(new Error("Upload failed: Timeout"))},r.open("PUT",e),r.setRequestHeader("Content-Type",t.type),s.timeout&&(r.timeout=s.timeout),r.send(t)})}async uploadMultipart(e,t,s={},i={}){const a=new FormData;if(t instanceof FileList)Array.from(t).forEach((r,n)=>{a.append(`file_${n}`,r)});else if(t instanceof File)a.append("file",t);else if(t instanceof FormData)return this.POST(e,t,{},i);return Object.entries(s).forEach(([r,n])=>{a.append(r,n)}),this.POST(e,a,{},i)}setAuthToken(e,t="Bearer"){e?this.config.headers.Authorization=`${t} ${e}`:delete this.config.headers.Authorization}clearAuth(){delete this.config.headers.Authorization}isRetryableError(e){return["not_reachable","timed_out","server_error","dns_error"].includes(e.reason)}requiresAuth(e){return e.reason==="unauthorized"}isNetworkError(e){return["not_reachable","timed_out","cancelled","cors_error","dns_error"].includes(e.reason)}getUserMessage(e){return e.message?e.message:{not_reachable:"Unable to connect to the server. Please check your internet connection.",timed_out:"The request took too long. Please try again.",cancelled:"The request was cancelled.",unauthorized:"Please log in to continue.",forbidden:"You don't have permission to perform this action.",not_found:"The requested resource was not found.",validation_error:"Please check your input and try again.",rate_limited:"Too many requests. Please wait a moment before trying again.",server_error:"Server error. Please try again later.",cors_error:"Access blocked by security policy.",dns_error:"Unable to reach the server.",unknown_error:"An unexpected error occurred."}[e.reason]||"An error occurred. Please try again."}}const z=new ke;class K{constructor(e={}){this.config=e,this.initPluginRegistry(),this.name=e.name||"MOJO App",this.version=e.version||"1.0.0",this.debug=e.debug||!1,this.container=e.container||"#app",this.layoutType=e.layout||"portal",this.layoutConfig=e.layoutConfig||{},e.sidebar&&(this.layoutConfig.sidebarConfig=e.sidebar),e.topbar&&(this.layoutConfig.topbarConfig=e.topbar),this.layout=null,this.layoutConfig.containerId=this.container||this.containerId||"#app",this.pageContainer=e.pageContainer||"#page-container",this.basePath=e.basePath||"",this.routerMode=e.routerMode||e.router?.mode||"param",this.basePath=e.basePath||e.router?.base||"",this.defaultRoute=e.defaultRoute||"home",this.session=e.session||{},this.router=null,this.navigation=e.navigation||{},this.state={currentPage:null,previousPage:null,loading:!1},this.events=new Ee,this.rest=z,e.api&&this.rest.configure(e.api),this.router=new te({mode:this.routerMode==="param"?"params":this.routerMode,basePath:this.basePath,defaultRoute:this.defaultRoute,eventEmitter:this.events}),this.events.on("route:changed",async t=>{const{pageName:s,params:i,query:a}=t;await this.showPage(s,a,i,{fromRouter:!0})}),typeof window<"u"&&(window.MOJO=window.MOJO||{},window.MOJO.router=this.router),this.setupFocusTracking(),this.pageCache=new Map,this.pageClasses=new Map,this.componentClasses=new Map,this.modelClasses=new Map,this.currentPage=null,this.isStarted=!1,window.matchUUID?window[window.matchUUID]=this:window.MOJO?window.MOJO.app=this:window.__app__=this}async start(){if(this.isStarted){console.warn("WebApp already started");return}try{this.setupPageContainer(),this.validateDefaultRoute(),await this.setupRouter(),this.isStarted=!0,this.router.allowPopState=!1,this.events.emit("app:ready",{app:this})}catch(e){throw console.error(`Failed to start ${this.name}:`,e),this.showError("Failed to start application"),e}}async setupRouter(){if(!this.router){console.error("Router not initialized");return}this.events.on("route:notfound",async e=>{console.warn(`Route not found: ${e.path}`),this._show404(e.path)}),this.router.start(),console.log(`Router started in ${this.routerMode} mode`)}setupPageContainer(){const e=typeof this.container=="string"?document.querySelector(this.container):this.container;e&&!e.querySelector("#page-container")&&(e.innerHTML='<div id="page-container"></div>'),this.pageContainer="#page-container"}registerPage(e,t,s={}){if(typeof e!="string"||!e)return console.error("registerPage: pageName must be a non-empty string"),this;if(typeof t!="function")return console.error("registerPage: PageClass must be a constructor function"),this;if(s.containerId||(s.containerId=this.pageContainer),this.pageClasses.set(e,{PageClass:t,constructorOptions:s}),this.router){let i=s.route||`/${e}`;i.startsWith("/")||(i=`/${i}`),s.route=i,this.router.addRoute(i,e)}return this}getPage(e){return this.pageCache.get(e)}getPagePermissions(e){if(this.pageCache.has(e))return this.pageCache.get(e).permissions;const t=this.pageClasses.get(e);if(!t)return null;const{PageClass:s,constructorOptions:i}=t;return i?i.permissions:null}getOrCreatePage(e){if(this.pageCache.has(e))return this.pageCache.get(e);const t=this.pageClasses.get(e);if(!t)return console.error(`Page not registered: ${e}`),null;const{PageClass:s,constructorOptions:i}=t;try{const a={pageName:e,...i,app:this},r=new s(a);return i.route&&(r.route=i.route),this.pageCache.set(e,r),console.log(`Created page: ${e} with route: ${r.route}`),r}catch(a){return console.error(`Failed to create page ${e}:`,a),null}}async showPage(e,t={},s={},i={}){const{fromRouter:a=!1,replace:r=!1,force:n=!1}=i;try{let o,l;typeof e=="string"?(l=e,o=this.getOrCreatePage(e)):e&&typeof e=="object"&&(o=e,l=e.pageName);const c=this.currentPage;if(!o){this._show404(l,s,t,a);return}if(this.events.emit("page:showing",{page:o,pageName:o.pageName,params:s,query:t,fromRouter:a}),!o.canEnter()){this._showDeniedPage(o,s,t,a);return}c&&c!==o&&await this._exitOldPage(c),await o.onParams(s,t),c!==o&&await o.onEnter(),o.syncUrl(),this.events.emit("page:show",{page:o,pageName:o.pageName,params:s,query:t,fromRouter:a}),await o.render(),this.currentPage=o,console.log(`✅ Showing page: ${o.pageName}`,{query:t,params:s})}catch(o){console.error("Error in showPage:",o),this.showError(`Failed to load page: ${o.message}`),e!=="error"&&await this.showPage("error",{},{error:o,originalPage:e},{fromRouter:a})}}async _show404(e,t,s,i){const a=this.getOrCreatePage("404");a&&(a.setInfo&&a.setInfo(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:404",{page:null,pageName:e,params:t,query:s,fromRouter:i}))}async _showDeniedPage(e,t,s,i){const a=this.getOrCreatePage("denied");a.setDeniedPage&&a.setDeniedPage(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:denied",{page:e,pageName:e.pageName,params:t,query:s,fromRouter:i})}async _exitOldPage(e){if(e)try{await e.onExit(),await e.unmount(),this.events.emit("page:hide",{page:e})}catch(t){console.error(`Error exiting page ${e.pageName}:`,t)}}async navigate(e,t={},s={}){if(!this.router){console.error("Router not initialized");return}let i=e;if(Object.keys(t).length>0){const a=new URLSearchParams(t).toString();i+=(e.includes("?")?"&":"?")+a}return await this.router.navigate(i,s)}async navigateToDefault(e={}){return await this.showPage(this.defaultRoute,{},{},e)}back(){this.router?this.router.back():console.warn("Router not initialized")}forward(){this.router?this.router.forward():console.warn("Router not initialized")}getCurrentPage(){return this.currentPage}getPageContainer(){return this.layout&&this.layout.getPageContainer?this.layout.getPageContainer():typeof this.pageContainer=="string"?document.querySelector(this.pageContainer):this.pageContainer}async showError(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Error",{size:"md",class:"text-danger"})}catch(t){this.events.emit("notification",{message:e,type:"error"}),typeof window<"u"&&window?.console&&console.error("[WebApp] showError fallback:",t),typeof window<"u"&&alert(`Error: ${e}`)}}async showSuccess(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Success",{size:"md",class:"text-success"})}catch(t){this.events.emit("notification",{message:e,type:"success"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showSuccess fallback:",t),typeof window<"u"&&alert(`Success: ${e}`)}}async showInfo(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Information",{size:"md",class:"text-info"})}catch(t){this.events.emit("notification",{message:e,type:"info"}),typeof window<"u"&&window?.console&&console.info("[WebApp] showInfo fallback:",t),typeof window<"u"&&alert(`Info: ${e}`)}}async showWarning(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Warning",{size:"md",class:"text-warning"})}catch(t){this.events.emit("notification",{message:e,type:"warning"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showWarning fallback:",t),typeof window<"u"&&alert(`Warning: ${e}`)}}showNotification(e,t="info"){this.events.emit("notification",{message:e,type:t})}async showLoading(e={}){typeof e=="string"&&(e={message:e});try{(await Promise.resolve().then(()=>_)).default.showBusy(e)}catch(t){typeof window<"u"&&window?.console&&console.warn("[WebApp] showLoading fallback:",t,e),this.events.emit("notification",{message:e.message||"Loading...",type:"info"})}}async hideLoading(){try{(await Promise.resolve().then(()=>_)).default.hideBusy()}catch(e){typeof window<"u"&&window?.console&&console.warn("[WebApp] hideLoading fallback:",e)}}async showModelView(e,t={}){try{return await(await Promise.resolve().then(()=>_)).default.showModelView(e,t)}catch(s){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showModelForm failed:",s),s}}async showModelForm(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showModelForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showModelForm failed:",t),t}}async showForm(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showForm failed:",t),t}}async showDialog(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async showAlert(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async confirm(e,t="Confirm",s={}){return await(await Promise.resolve().then(()=>_)).default.confirm(e,t,s)}setupFocusTracking(){if(typeof window>"u")return;this.isFocused=!document.hidden;const e=()=>{const i=this.isFocused;this.isFocused=!document.hidden,i!==this.isFocused&&(this.isFocused?this.events.emit("browser:focus"):this.events.emit("browser:blur"))},t=()=>{this.isFocused||(this.isFocused=!0,this.events.emit("browser:focus"))},s=()=>{this.isFocused&&(this.isFocused=!1,this.events.emit("browser:blur"))};document.addEventListener("visibilitychange",e),window.addEventListener("focus",t),window.addEventListener("blur",s),this._focusHandlers={visibilitychange:e,focus:t,blur:s}}setupErrorHandling(){window.addEventListener("error",e=>{console.error("Global error:",e.error),this.debug&&this.showError(`Error: ${e.error?.message||"Unknown error"}`)}),window.addEventListener("unhandledrejection",e=>{console.error("Unhandled promise rejection:",e.reason),this.debug&&this.showError(`Promise rejected: ${e.reason?.message||"Unknown error"}`)})}escapeHtml(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}getState(e){return e?this.state[e]:this.state}setState(e){const t={...this.state};Object.assign(this.state,e),this.events.emit("state:changed",{oldState:t,newState:this.state,updates:e})}registerComponent(e,t){this.componentClasses.set(e,t)}getComponent(e){return this.componentClasses.get(e)}registerModel(e,t){this.modelClasses.set(e,t)}getModel(e){return this.modelClasses.get(e)}setupRest(){this.rest=z,z.configure(this.api)}async destroy(){console.log("Destroying WebApp..."),this.router&&this.router.stop(),this._focusHandlers&&typeof window<"u"&&(document.removeEventListener("visibilitychange",this._focusHandlers.visibilitychange),window.removeEventListener("focus",this._focusHandlers.focus),window.removeEventListener("blur",this._focusHandlers.blur));const e=Array.from(this.pageCache.values());if(await Promise.allSettled(e.map(async t=>{try{t.destroy&&await t.destroy()}catch(s){console.error("Error destroying page:",s)}})),this.layout&&this.layout.destroy)try{await this.layout.destroy()}catch(t){console.error("Error destroying layout:",t)}this.pageCache.clear(),this.pageClasses.clear(),this.componentClasses.clear(),this.modelClasses.clear(),typeof window<"u"&&window.MOJO&&delete window.MOJO.router,this.isStarted=!1,console.log(`✨ ${this.name} destroyed`)}buildPagePath(e,t,s){let i=e.route||`/${e.pageName.toLowerCase()}`;if(Object.keys(t).forEach(a=>{(typeof t[a]=="string"||typeof t[a]=="number")&&(i=i.replace(`:${a}`,t[a]))}),s&&Object.keys(s).length>0){const a=new URLSearchParams(s).toString();i+=(i.includes("?")?"&":"?")+a}return i}validateDefaultRoute(){this.pageClasses.has(this.defaultRoute)?console.log(`✅ Default route '${this.defaultRoute}' is registered`):(console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`),console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`),console.warn(" Or change default route: new WebApp({ defaultRoute: 'your-page' });"))}findFallbackPage(){const e=["404","error","denied"];for(const[t]of this.pageClasses.entries())if(!e.includes(t))return t;return null}static create(e={}){return new K(e)}initPluginRegistry(){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.app=this)}static registerPlugin(e,t){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.plugins[e]=t,console.debug(`MOJO Plugin registered: ${e}`))}}const Ae="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==";class Ie{constructor(){this.formatters=new Map,this.registerBuiltInFormatters()}escapeHtml(e){if(e==null)return"";const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};return String(e).replace(/[&<>"']/g,s=>t[s])}registerBuiltInFormatters(){this.register("date",this.date.bind(this)),this.register("time",this.time.bind(this)),this.register("datetime",this.datetime.bind(this)),this.register("datetime_tz",this.datetime_tz.bind(this)),this.register("datatime_tz",this.datetime_tz.bind(this)),this.register("date_range",this.date_range.bind(this)),this.register("datetime_range",this.datetime_range.bind(this)),this.register("relative",this.relative.bind(this)),this.register("fromNow",this.relative.bind(this)),this.register("relative_short",this.relative_short.bind(this)),this.register("iso",this.iso.bind(this)),this.register("epoch",e=>{if(e==null||e==="")return e;const t=parseFloat(e);return isNaN(t)?e:t*1e3}),this.register("number",this.number.bind(this)),this.register("currency",this.currency.bind(this)),this.register("percent",this.percent.bind(this)),this.register("filesize",this.filesize.bind(this)),this.register("ordinal",this.ordinal.bind(this)),this.register("compact",this.compact.bind(this)),this.register("add",this.add.bind(this)),this.register("subtract",this.subtract.bind(this)),this.register("multiply",this.multiply.bind(this)),this.register("divide",this.divide.bind(this)),this.register("sub",this.subtract.bind(this)),this.register("mult",this.multiply.bind(this)),this.register("div",this.divide.bind(this)),this.register("uppercase",e=>String(e).toUpperCase()),this.register("lowercase",e=>String(e).toLowerCase()),this.register("upper",e=>String(e).toUpperCase()),this.register("lower",e=>String(e).toLowerCase()),this.register("capitalize",this.capitalize.bind(this)),this.register("caps",this.capitalize.bind(this)),this.register("replace",this.replace.bind(this)),this.register("truncate",this.truncate.bind(this)),this.register("truncate_middle",this.truncate_middle.bind(this)),this.register("truncate_front",this.truncate_front.bind(this)),this.register("slug",this.slug.bind(this)),this.register("initials",this.initials.bind(this)),this.register("mask",this.mask.bind(this)),this.register("hex",this.hex.bind(this)),this.register("tohex",this.hex.bind(this)),this.register("unhex",this.unhex.bind(this)),this.register("fromhex",this.unhex.bind(this)),this.register("email",this.email.bind(this)),this.register("phone",this.phone.bind(this)),this.register("url",this.url.bind(this)),this.register("badge",this.badge.bind(this)),this.register("badgeClass",this.badgeClass.bind(this)),this.register("status",this.status.bind(this)),this.register("status_text",this.status_text.bind(this)),this.register("status_icon",this.status_icon.bind(this)),this.register("boolean",this.boolean.bind(this)),this.register("bool",this.bool.bind(this)),this.register("yesno",e=>this.boolean(e,"Yes","No")),this.register("yesnoicon",this.yesnoicon.bind(this)),this.register("icon",this.icon.bind(this)),this.register("avatar",this.avatar.bind(this)),this.register("image",this.image.bind(this)),this.register("tooltip",this.tooltip.bind(this)),this.register("linkify",this.linkify.bind(this)),this.register("clipboard",this.clipboard.bind(this)),this.register("default",this.default.bind(this)),this.register("equals",this.equals.bind(this)),this.register("json",this.json.bind(this)),this.register("raw",e=>e),this.register("custom",(e,t)=>typeof t=="function"?t(e):e),this.register("iter",this.iter.bind(this)),this.register("keys",e=>e&&typeof e=="object"&&!Array.isArray(e)?Object.keys(e):null),this.register("values",e=>e&&typeof e=="object"&&!Array.isArray(e)?Object.values(e):null),this.register("plural",this.plural.bind(this)),this.register("list",this.formatList.bind(this)),this.register("duration",this.duration.bind(this)),this.register("hash",this.hash.bind(this)),this.register("stripHtml",this.stripHtml.bind(this)),this.register("highlight",this.highlight.bind(this)),this.register("nl2br",this.nl2br.bind(this)),this.register("code",this.code.bind(this)),this.register("pre",e=>`<pre class="bg-light p-2 rounded border">${this.escapeHtml(String(e))}</pre>`)}relative_short(e){return this.relative(e,!0)}linkify(e,t={}){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a={urls:!0,emails:!0,target:"_blank",rel:"noopener noreferrer"},r=t&&typeof t=="object"?{...a,...t}:a;let n=i;if(r.urls!==!1){const o=/(^|\s)((?:https?:\/\/|www\.)[^\s<]+)/gi;n=n.replace(o,(l,c,d)=>{const h=d.startsWith("www.")?`https://${d}`:d;return`${c}<a href="${h}" target="${r.target}" rel="${r.rel}">${d}</a>`})}if(r.emails!==!1){const o=/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;n=n.replace(o,l=>`<a href="mailto:${l}">${l}</a>`)}return n}clipboard(e,t="text"){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a=t!=="icon-only",r=`
1
+ var MOJO=(function(D){"use strict";class ie{constructor(e={}){this.defaultRoute=e.defaultRoute||"home",this.routes=[],this.currentRoute=null,this.eventEmitter=e.eventEmitter||null,this.boundHandlePopState=this.handlePopState.bind(this)}start(){window.addEventListener("popstate",this.boundHandlePopState),this.handleCurrentLocation()}stop(){window.removeEventListener("popstate",this.boundHandlePopState)}addRoute(e,t){this.routes.push({pattern:this.normalizePattern(e),regex:this.patternToRegex(e),pageName:t,paramNames:this.extractParamNames(e)})}async navigate(e,t={}){const{replace:s=!1,state:i=null,trigger:a=!0}=t,{pageName:r,queryParams:n}=this.parseInput(e);a&&await this.handleRouteChange(r,n)}back(){window.history.back()}forward(){window.history.forward()}getCurrentRoute(){return this.currentRoute}getCurrentPath(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();return this.buildPublicUrl(e,t)}handlePopState(e){this.allowPopState?this.handleCurrentLocation():console.warn("PopStateEvent is not allowed")}async handleCurrentLocation(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();await this.handleRouteChange(e,t)}async handleRouteChange(e,t){const s="/"+e,i=this.matchRoute(s),a=this.buildPublicUrl(e,t);return i?(this.currentRoute=i,this.eventEmitter&&this.eventEmitter.emit("route:changed",{path:a,pageName:i.pageName,params:i.params,query:t,route:i}),i):(console.log("No route matched for page:",e),this.eventEmitter&&this.eventEmitter.emit("route:notfound",{path:a}),null)}matchRoute(e){for(const t of this.routes){const s=e.match(t.regex);if(s){const i={};return t.paramNames.forEach((a,r)=>{i[a]=s[r+1]}),{...t,params:i,path:e}}}return null}parseInput(e){let t=this.defaultRoute,s={};if(!e)return{pageName:t,queryParams:s};try{if(e.includes("?")){const[i,a]=e.split("?",2),r=new URLSearchParams(a);if(r.has("page")){t=r.get("page")||this.defaultRoute;for(const[n,o]of r)n!=="page"&&(s[n]=o)}else{i.startsWith("/")?t=i.substring(1)||this.defaultRoute:t=i||this.defaultRoute;for(const[n,o]of r)s[n]=o}}else e.startsWith("/")?t=e.substring(1)||this.defaultRoute:t=e}catch(i){console.warn("Failed to parse input:",e,i),t=this.defaultRoute,s={}}return{pageName:t,queryParams:s}}parseCurrentUrl(){const e=new URLSearchParams(window.location.search),t=e.get("page")||this.defaultRoute,s={};for(const[i,a]of e)i!=="page"&&(s[i]=a);return{pageName:t,queryParams:s}}buildPublicUrl(e,t={}){const s=new URLSearchParams;return s.set("page",e),Object.entries(t).forEach(([i,a])=>{a!=null&&a!==""&&s.set(i,String(a))}),"?"+s.toString()}updateBrowserUrl(e,t,s,i){const a=new URL(window.location.origin+window.location.pathname);a.searchParams.set("page",e),Object.entries(t).forEach(([n,o])=>{o!=null&&o!==""&&a.searchParams.set(n,String(o))});const r=a.toString();s?window.history.replaceState(i,"",r):window.history.pushState(i,"",r)}patternToRegex(e){let t=e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/\/:([^/?]+)\?/g,"(?:/([^/]+))?").replace(/:([^/]+)/g,"([^/]+)");return new RegExp(`^${t}$`)}extractParamNames(e){return(e.match(/:([^/?]+)\??/g)||[]).map(s=>s.replace(/[:?]/g,""))}normalizePattern(e){return e.startsWith("/")?e:`/${e}`}updateUrl(e={},t={}){const{replace:s=!1}=t,{pageName:i}=this.parseCurrentUrl();this.updateBrowserUrl(i,e,s)}buildUrl(e,t={}){return this.buildPublicUrl(e,t)}doRoutesMatch(e,t){if(!e||!t)return!1;const{pageName:s}=this.parseInput(e),{pageName:i}=this.parseInput(t);return s===i}}class _e{constructor(){this.listeners={},this.onceListeners={},this.maxListeners=100,this.debugMode=!1,this.eventStats={}}on(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.on(s,t)),this):(this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].length>=this.maxListeners&&console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${e}`),this.listeners[e].push(t),this)}once(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.once(s,t)),this):(this.onceListeners[e]||(this.onceListeners[e]=[]),this.onceListeners[e].push(t),this)}off(e,t){if(Array.isArray(e))return e.forEach(s=>this.off(s,t)),this;if(!t)return delete this.listeners[e],delete this.onceListeners[e],this;if(this.listeners[e]){const s=this.listeners[e].indexOf(t);s!==-1&&(this.listeners[e].splice(s,1),this.listeners[e].length===0&&delete this.listeners[e])}if(this.onceListeners[e]){const s=this.onceListeners[e].indexOf(t);s!==-1&&(this.onceListeners[e].splice(s,1),this.onceListeners[e].length===0&&delete this.onceListeners[e])}return this}emit(e,t){this.updateEventStats(e),this.debugMode&&console.log(`[EventBus] Emitting: ${e}`,t);const s=[];return this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]),this.debugMode&&s.length>0&&console.log(`[EventBus] ${s.length} listener(s) for '${e}'`),s.forEach(i=>{try{i(t,e)&&(e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault())}catch(a){console.error(`Error in event listener for '${e}':`,a),this.emitError(a,e,i)}}),this}async emitAsync(e,t){const s=[];this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]);const i=s.map(a=>new Promise(r=>{try{const n=a(t,e);r(n)}catch(n){console.error(`Error in async event listener for '${e}':`,n),this.emitError(n,e,a),r()}}));return await Promise.all(i),this}removeAllListeners(){return this.listeners={},this.onceListeners={},this}listenerCount(e){const t=this.listeners[e]?this.listeners[e].length:0,s=this.onceListeners[e]?this.onceListeners[e].length:0;return t+s}eventNames(){const e=Object.keys(this.listeners),t=Object.keys(this.onceListeners);return[...new Set([...e,...t])]}setMaxListeners(e){if(typeof e!="number"||e<0)throw new Error("Max listeners must be a non-negative number");return this.maxListeners=e,this}namespace(e){const t=s=>`${e}:${s}`;return{on:(s,i)=>this.on(t(s),i),once:(s,i)=>this.once(t(s),i),off:(s,i)=>this.off(t(s),i),emit:(s,i)=>this.emit(t(s),i),emitAsync:(s,i)=>this.emitAsync(t(s),i)}}use(e){if(typeof e!="function")throw new Error("Middleware must be a function");const t=this.emit;return this.emit=(s,i)=>{try{const a=e(s,i);if(a===!1)return this;const r=a!==void 0?a:i;return t.call(this,s,r)}catch(a){return console.error("Error in event middleware:",a),t.call(this,s,i)}},this}emitError(e,t,s){t!=="error"&&setTimeout(()=>{this.emit("error",{error:e,originalEvent:t,callback:s.toString()})},0)}waitFor(e,t=null){return new Promise((s,i)=>{let a=null;const r=()=>{a&&clearTimeout(a)},n=o=>{r(),s(o)};this.once(e,n),t&&(a=setTimeout(()=>{this.off(e,n),i(new Error(`Timeout waiting for event: ${e}`))},t))})}debug(e=!0){return this.debugMode=e,console.log(e?"[EventBus] Debug mode enabled":"[EventBus] Debug mode disabled"),this}getStats(){const e=this.eventNames(),t={totalEvents:e.length,totalListeners:0,events:{},emissions:{...this.eventStats}};return e.forEach(s=>{const i=this.listenerCount(s);t.events[s]=i,t.totalListeners+=i}),t}updateEventStats(e){this.eventStats[e]||(this.eventStats[e]={count:0,firstEmission:Date.now(),lastEmission:null}),this.eventStats[e].count++,this.eventStats[e].lastEmission=Date.now()}getEventStats(e){const t=this.eventStats[e];return t?{...t,listenerCount:this.listenerCount(e),avgEmissionsPerMinute:this.calculateEmissionRate(t)}:null}calculateEmissionRate(e){if(!e.firstEmission||!e.lastEmission)return 0;const t=e.lastEmission-e.firstEmission;if(t===0)return 0;const s=t/(1e3*60);return Math.round(e.count/s*100)/100}resetStats(){return this.eventStats={},this}getTopEvents(e=10){return Object.entries(this.eventStats).map(([t,s])=>({event:t,count:s.count,rate:this.calculateEmissionRate(s),listeners:this.listenerCount(t)})).sort((t,s)=>s.count-t.count).slice(0,e)}debugInfo(){console.group("[EventBus] Debug Information"),console.log("Debug Mode:",this.debugMode),console.log("Max Listeners:",this.maxListeners);const e=this.getStats();return console.log("Total Events:",e.totalEvents),console.log("Total Listeners:",e.totalListeners),Object.keys(this.eventStats).length>0&&console.log("Top Events:",this.getTopEvents(5)),console.groupEnd(),this}}class Le{constructor(){this.config={baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"},trackDevice:!0,duidHeader:"X-Mojo-UID",duidTransport:"header"},this.interceptors={request:[],response:[]},this.duid=null,this.config.trackDevice&&this._initializeDuid()}_initializeDuid(){const e="mojo_device_uid";try{let t=localStorage.getItem(e);t?this.duid=t:(this.duid=this._generateDuid(),localStorage.setItem(e,this.duid))}catch(t){console.error("Could not access localStorage to get/set DUID.",t),this.duid=this._generateDuid()}}_generateDuid(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}configure(e){e.baseUrl&&(e.baseURL=e.baseUrl);const t=this.config.trackDevice;this.config={...this.config,...e,headers:{...this.config.headers,...e.headers}},this.config.trackDevice&&!t&&this._initializeDuid()}addInterceptor(e,t){this.interceptors[e]&&this.interceptors[e].push(t)}buildUrl(e){if(e.startsWith("http://")||e.startsWith("https://"))return e;const t=this.config.baseURL.endsWith("/")?this.config.baseURL.slice(0,-1):this.config.baseURL,s=e.startsWith("/")?e:`/${e}`;return`${t}${s}`}categorizeError(e,t=0){if(e.name==="TypeError"&&e.message.includes("fetch"))return{reason:"not_reachable",message:"Service is not reachable - please check your connection"};if(e.name==="AbortError")return{reason:"cancelled",message:"Request was cancelled"};if(e.name==="TimeoutError"||e.message.includes("timeout"))return{reason:"timed_out",message:"Request timed out - please try again"};if(t>=400){if(t===400)return{reason:"bad_request",message:"Invalid request data"};if(t===401)return{reason:"unauthorized",message:"Authentication required"};if(t===403)return{reason:"forbidden",message:"Access denied"};if(t===404)return{reason:"not_found",message:"Resource not found"};if(t===409)return{reason:"conflict",message:"Resource conflict"};if(t===422)return{reason:"validation_error",message:"Validation failed"};if(t===429)return{reason:"rate_limited",message:"Too many requests - please wait"};if(t>=500)return{reason:"server_error",message:"Server error - please try again later"};if(t>=400)return{reason:"client_error",message:"Request error"}}return e.message.includes("CORS")?{reason:"cors_error",message:"Cross-origin request blocked"}:e.message.includes("DNS")||e.message.includes("ENOTFOUND")?{reason:"dns_error",message:"Unable to resolve server address"}:{reason:"unknown_error",message:`Network error: ${e.message}`}}buildQueryString(e={}){const t=new URLSearchParams;Object.entries(e).forEach(([i,a])=>{a!=null&&(Array.isArray(a)?a.forEach(r=>t.append(`${i}[]`,r)):t.append(i,a))});const s=t.toString();return s?`?${s}`:""}async processRequestInterceptors(e){let t={...e};for(const s of this.interceptors.request)try{t=await s(t)}catch(i){throw console.error("Request interceptor error:",i),i}return t}async processResponseInterceptors(e,t){let s={success:e.ok,status:e.status,statusText:e.statusText,headers:Object.fromEntries(e.headers.entries()),data:null,errors:null,message:null,reason:null};try{const i=e.headers.get("content-type");if(i&&i.includes("application/json")){const a=await e.json();if(s.data=a,!e.ok){const r=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=a.errors||{},s.message=a.message||r.message,s.reason=r.reason}}else if(s.data=await e.text(),!e.ok){const a=this.categorizeError(new Error("HTTP Error"),e.status);s.message=a.message,s.reason=a.reason}}catch{s.errors={parse:"Failed to parse response"},s.message="Invalid response format"}for(const i of this.interceptors.response)try{s=await i(s,t)}catch(a){console.error("Response interceptor error:",a)}return s}async request(e,t,s=null,i={},a={}){let r={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(i),headers:{...this.config.headers,...a.headers},data:s,options:{timeout:this.config.timeout,...a}};if(r=await this.processRequestInterceptors(r),this.config.trackDevice&&this.duid)if(this.config.duidTransport==="header")r.headers[this.config.duidHeader]=this.duid;else if(r.method==="GET"){const l=new URL(r.url);l.searchParams.append("duid",this.duid),r.url=l.toString()}else r.data&&typeof r.data=="object"&&!(r.data instanceof FormData)&&(r.data.duid=this.duid);const n={method:r.method,headers:r.headers},o=[];r.options.timeout&&o.push(AbortSignal.timeout(r.options.timeout)),r.options.signal&&o.push(r.options.signal),o.length>1?n.signal=AbortSignal.any?AbortSignal.any(o):o[0]:o.length===1&&(n.signal=o[0]),r.data&&["POST","PUT","PATCH"].includes(r.method)&&(r.data instanceof FormData?(n.body=r.data,delete n.headers["Content-Type"]):typeof r.data=="object"?n.body=JSON.stringify(r.data):n.body=r.data);try{const l=await fetch(r.url,n);return await this.processResponseInterceptors(l,r)}catch(l){if(l.name==="AbortError")throw l;const c=this.categorizeError(l),d={success:!1,status:0,statusText:"Network Error",headers:{},data:null,errors:{network:l.message},message:c.message,reason:c.reason},h={ok:!1,status:0,statusText:"Network Error",headers:new Headers,json:async()=>({}),text:async()=>""};return await this.processResponseInterceptors(h,r),d}}async GET(e,t={},s={}){return this.request("GET",e,null,t,s)}async POST(e,t={},s={},i={}){return this.request("POST",e,t,s,i)}async PUT(e,t={},s={},i={}){return this.request("PUT",e,t,s,i)}async PATCH(e,t={},s={},i={}){return this.request("PATCH",e,t,s,i)}async DELETE(e,t={},s={}){return this.request("DELETE",e,null,t,s)}async download(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=r.headers.get("content-disposition");let o=s.filename||"download";if(n){const p=n.match(/filename="?(.+)"?/);p&&p.length>1&&(o=p[1])}const l=r.body.getReader(),c=new ReadableStream({start(p){function f(){return l.read().then(({done:g,value:y})=>{if(g){p.close();return}return p.enqueue(y),f()})}return f()}}),d=await new Response(c).blob(),h=window.URL.createObjectURL(d),m=document.createElement("a");return m.style.display="none",m.href=h,m.download=o,document.body.appendChild(m),m.click(),window.URL.revokeObjectURL(h),m.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async downloadBlob(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=await r.blob(),o=r.headers.get("content-disposition");let l=s.filename||"download";if(o){const h=o.match(/filename="?(.+)"?/);h&&h.length>1&&(l=h[1])}const c=window.URL.createObjectURL(n),d=document.createElement("a");return d.style.display="none",d.href=c,d.download=l,document.body.appendChild(d),d.click(),window.URL.revokeObjectURL(c),d.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async upload(e,t,s={}){return new Promise((i,a)=>{if(!(t instanceof File)){a(new Error("Only single File objects are supported for legacy backend compatibility"));return}const r=new XMLHttpRequest;s.onProgress&&typeof s.onProgress=="function"&&(r.upload.onprogress=s.onProgress),r.onload=function(){r.status>=200&&r.status<300?i({data:r.response,status:r.status,statusText:r.statusText,xhr:r}):a(new Error(`Upload failed: ${r.status} ${r.statusText}`))},r.onerror=function(){a(new Error("Upload failed: Network error"))},r.ontimeout=function(){a(new Error("Upload failed: Timeout"))},r.open("PUT",e),r.setRequestHeader("Content-Type",t.type),s.timeout&&(r.timeout=s.timeout),r.send(t)})}async uploadMultipart(e,t,s={},i={}){const a=new FormData;if(t instanceof FileList)Array.from(t).forEach((r,n)=>{a.append(`file_${n}`,r)});else if(t instanceof File)a.append("file",t);else if(t instanceof FormData)return this.POST(e,t,{},i);return Object.entries(s).forEach(([r,n])=>{a.append(r,n)}),this.POST(e,a,{},i)}setAuthToken(e,t="Bearer"){e?this.config.headers.Authorization=`${t} ${e}`:delete this.config.headers.Authorization}clearAuth(){delete this.config.headers.Authorization}isRetryableError(e){return["not_reachable","timed_out","server_error","dns_error"].includes(e.reason)}requiresAuth(e){return e.reason==="unauthorized"}isNetworkError(e){return["not_reachable","timed_out","cancelled","cors_error","dns_error"].includes(e.reason)}getUserMessage(e){return e.message?e.message:{not_reachable:"Unable to connect to the server. Please check your internet connection.",timed_out:"The request took too long. Please try again.",cancelled:"The request was cancelled.",unauthorized:"Please log in to continue.",forbidden:"You don't have permission to perform this action.",not_found:"The requested resource was not found.",validation_error:"Please check your input and try again.",rate_limited:"Too many requests. Please wait a moment before trying again.",server_error:"Server error. Please try again later.",cors_error:"Access blocked by security policy.",dns_error:"Unable to reach the server.",unknown_error:"An unexpected error occurred."}[e.reason]||"An error occurred. Please try again."}}const z=new Le;class ee{constructor(e={}){this.config=e,this.initPluginRegistry(),this.name=e.name||"MOJO App",this.version=e.version||"1.0.0",this.debug=e.debug||!1,this.container=e.container||"#app",this.layoutType=e.layout||"portal",this.layoutConfig=e.layoutConfig||{},e.sidebar&&(this.layoutConfig.sidebarConfig=e.sidebar),e.topbar&&(this.layoutConfig.topbarConfig=e.topbar),this.layout=null,this.layoutConfig.containerId=this.container||this.containerId||"#app",this.pageContainer=e.pageContainer||"#page-container",this.basePath=e.basePath||"",this.routerMode=e.routerMode||e.router?.mode||"param",this.basePath=e.basePath||e.router?.base||"",this.defaultRoute=e.defaultRoute||"home",this.session=e.session||{},this.router=null,this.navigation=e.navigation||{},this.state={currentPage:null,previousPage:null,loading:!1},this.events=new _e,this.rest=z,e.api&&this.rest.configure(e.api),this.router=new ie({mode:this.routerMode==="param"?"params":this.routerMode,basePath:this.basePath,defaultRoute:this.defaultRoute,eventEmitter:this.events}),this.events.on("route:changed",async t=>{const{pageName:s,params:i,query:a}=t;await this.showPage(s,a,i,{fromRouter:!0})}),typeof window<"u"&&(window.MOJO=window.MOJO||{},window.MOJO.router=this.router),this.setupFocusTracking(),this.pageCache=new Map,this.pageClasses=new Map,this.componentClasses=new Map,this.modelClasses=new Map,this.currentPage=null,this.isStarted=!1,window.matchUUID?window[window.matchUUID]=this:window.MOJO?window.MOJO.app=this:window.__app__=this}async start(){if(this.isStarted){console.warn("WebApp already started");return}try{this.setupPageContainer(),this.validateDefaultRoute(),await this.setupRouter(),this.isStarted=!0,this.router.allowPopState=!1,this.events.emit("app:ready",{app:this})}catch(e){throw console.error(`Failed to start ${this.name}:`,e),this.showError("Failed to start application"),e}}async setupRouter(){if(!this.router){console.error("Router not initialized");return}this.events.on("route:notfound",async e=>{console.warn(`Route not found: ${e.path}`),this._show404(e.path)}),this.router.start(),console.log(`Router started in ${this.routerMode} mode`)}setupPageContainer(){const e=typeof this.container=="string"?document.querySelector(this.container):this.container;e&&!e.querySelector("#page-container")&&(e.innerHTML='<div id="page-container"></div>'),this.pageContainer="#page-container"}registerPage(e,t,s={}){if(typeof e!="string"||!e)return console.error("registerPage: pageName must be a non-empty string"),this;if(typeof t!="function")return console.error("registerPage: PageClass must be a constructor function"),this;if(s.containerId||(s.containerId=this.pageContainer),this.pageClasses.set(e,{PageClass:t,constructorOptions:s}),this.router){let i=s.route||`/${e}`;i.startsWith("/")||(i=`/${i}`),s.route=i,this.router.addRoute(i,e)}return this}getPage(e){return this.pageCache.get(e)}getPagePermissions(e){if(this.pageCache.has(e))return this.pageCache.get(e).permissions;const t=this.pageClasses.get(e);if(!t)return null;const{PageClass:s,constructorOptions:i}=t;return i?i.permissions:null}getOrCreatePage(e){if(this.pageCache.has(e))return this.pageCache.get(e);const t=this.pageClasses.get(e);if(!t)return console.error(`Page not registered: ${e}`),null;const{PageClass:s,constructorOptions:i}=t;try{const a={pageName:e,...i,app:this},r=new s(a);return i.route&&(r.route=i.route),this.pageCache.set(e,r),console.log(`Created page: ${e} with route: ${r.route}`),r}catch(a){return console.error(`Failed to create page ${e}:`,a),null}}async showPage(e,t={},s={},i={}){const{fromRouter:a=!1,replace:r=!1,force:n=!1}=i;try{let o,l;typeof e=="string"?(l=e,o=this.getOrCreatePage(e)):e&&typeof e=="object"&&(o=e,l=e.pageName);const c=this.currentPage;if(!o){this._show404(l,s,t,a);return}if(this.events.emit("page:showing",{page:o,pageName:o.pageName,params:s,query:t,fromRouter:a}),!o.canEnter()){this._showDeniedPage(o,s,t,a);return}c&&c!==o&&await this._exitOldPage(c),await o.onParams(s,t),c!==o&&await o.onEnter(),o.syncUrl(),this.events.emit("page:show",{page:o,pageName:o.pageName,params:s,query:t,fromRouter:a}),await o.render(),this.currentPage=o,console.log(`✅ Showing page: ${o.pageName}`,{query:t,params:s})}catch(o){console.error("Error in showPage:",o),this.showError(`Failed to load page: ${o.message}`),e!=="error"&&await this.showPage("error",{},{error:o,originalPage:e},{fromRouter:a})}}async _show404(e,t,s,i){const a=this.getOrCreatePage("404");a&&(a.setInfo&&a.setInfo(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:404",{page:null,pageName:e,params:t,query:s,fromRouter:i}))}async _showDeniedPage(e,t,s,i){const a=this.getOrCreatePage("denied");a.setDeniedPage&&a.setDeniedPage(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:denied",{page:e,pageName:e.pageName,params:t,query:s,fromRouter:i})}async _exitOldPage(e){if(e)try{await e.onExit(),await e.unmount(),this.events.emit("page:hide",{page:e})}catch(t){console.error(`Error exiting page ${e.pageName}:`,t)}}async navigate(e,t={},s={}){if(!this.router){console.error("Router not initialized");return}let i=e;if(Object.keys(t).length>0){const a=new URLSearchParams(t).toString();i+=(e.includes("?")?"&":"?")+a}return await this.router.navigate(i,s)}async navigateToDefault(e={}){return await this.showPage(this.defaultRoute,{},{},e)}back(){this.router?this.router.back():console.warn("Router not initialized")}forward(){this.router?this.router.forward():console.warn("Router not initialized")}getCurrentPage(){return this.currentPage}getPageContainer(){return this.layout&&this.layout.getPageContainer?this.layout.getPageContainer():typeof this.pageContainer=="string"?document.querySelector(this.pageContainer):this.pageContainer}async showError(e){try{await(await Promise.resolve().then(()=>L)).default.alert(e,"Error",{size:"md",class:"text-danger"})}catch(t){this.events.emit("notification",{message:e,type:"error"}),typeof window<"u"&&window?.console&&console.error("[WebApp] showError fallback:",t),typeof window<"u"&&alert(`Error: ${e}`)}}async showSuccess(e){try{await(await Promise.resolve().then(()=>L)).default.alert(e,"Success",{size:"md",class:"text-success"})}catch(t){this.events.emit("notification",{message:e,type:"success"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showSuccess fallback:",t),typeof window<"u"&&alert(`Success: ${e}`)}}async showInfo(e){try{await(await Promise.resolve().then(()=>L)).default.alert(e,"Information",{size:"md",class:"text-info"})}catch(t){this.events.emit("notification",{message:e,type:"info"}),typeof window<"u"&&window?.console&&console.info("[WebApp] showInfo fallback:",t),typeof window<"u"&&alert(`Info: ${e}`)}}async showWarning(e){try{await(await Promise.resolve().then(()=>L)).default.alert(e,"Warning",{size:"md",class:"text-warning"})}catch(t){this.events.emit("notification",{message:e,type:"warning"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showWarning fallback:",t),typeof window<"u"&&alert(`Warning: ${e}`)}}showNotification(e,t="info"){this.events.emit("notification",{message:e,type:t})}async showLoading(e={}){typeof e=="string"&&(e={message:e});try{(await Promise.resolve().then(()=>L)).default.showBusy(e)}catch(t){typeof window<"u"&&window?.console&&console.warn("[WebApp] showLoading fallback:",t,e),this.events.emit("notification",{message:e.message||"Loading...",type:"info"})}}async hideLoading(){try{(await Promise.resolve().then(()=>L)).default.hideBusy()}catch(e){typeof window<"u"&&window?.console&&console.warn("[WebApp] hideLoading fallback:",e)}}async showModelView(e,t={}){try{return await(await Promise.resolve().then(()=>L)).default.showModelView(e,t)}catch(s){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showModelForm failed:",s),s}}async showModelForm(e={}){try{return await(await Promise.resolve().then(()=>L)).default.showModelForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showModelForm failed:",t),t}}async showForm(e={}){try{return await(await Promise.resolve().then(()=>L)).default.showForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showForm failed:",t),t}}async showDialog(e={}){try{return await(await Promise.resolve().then(()=>L)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async showAlert(e={}){try{return await(await Promise.resolve().then(()=>L)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async confirm(e,t="Confirm",s={}){return await(await Promise.resolve().then(()=>L)).default.confirm(e,t,s)}setupFocusTracking(){if(typeof window>"u")return;this.isFocused=!document.hidden;const e=()=>{const i=this.isFocused;this.isFocused=!document.hidden,i!==this.isFocused&&(this.isFocused?this.events.emit("browser:focus"):this.events.emit("browser:blur"))},t=()=>{this.isFocused||(this.isFocused=!0,this.events.emit("browser:focus"))},s=()=>{this.isFocused&&(this.isFocused=!1,this.events.emit("browser:blur"))};document.addEventListener("visibilitychange",e),window.addEventListener("focus",t),window.addEventListener("blur",s),this._focusHandlers={visibilitychange:e,focus:t,blur:s}}setupErrorHandling(){window.addEventListener("error",e=>{console.error("Global error:",e.error),this.debug&&this.showError(`Error: ${e.error?.message||"Unknown error"}`)}),window.addEventListener("unhandledrejection",e=>{console.error("Unhandled promise rejection:",e.reason),this.debug&&this.showError(`Promise rejected: ${e.reason?.message||"Unknown error"}`)})}escapeHtml(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}getState(e){return e?this.state[e]:this.state}setState(e){const t={...this.state};Object.assign(this.state,e),this.events.emit("state:changed",{oldState:t,newState:this.state,updates:e})}registerComponent(e,t){this.componentClasses.set(e,t)}getComponent(e){return this.componentClasses.get(e)}registerModel(e,t){this.modelClasses.set(e,t)}getModel(e){return this.modelClasses.get(e)}setupRest(){this.rest=z,z.configure(this.api)}async destroy(){console.log("Destroying WebApp..."),this.router&&this.router.stop(),this._focusHandlers&&typeof window<"u"&&(document.removeEventListener("visibilitychange",this._focusHandlers.visibilitychange),window.removeEventListener("focus",this._focusHandlers.focus),window.removeEventListener("blur",this._focusHandlers.blur));const e=Array.from(this.pageCache.values());if(await Promise.allSettled(e.map(async t=>{try{t.destroy&&await t.destroy()}catch(s){console.error("Error destroying page:",s)}})),this.layout&&this.layout.destroy)try{await this.layout.destroy()}catch(t){console.error("Error destroying layout:",t)}this.pageCache.clear(),this.pageClasses.clear(),this.componentClasses.clear(),this.modelClasses.clear(),typeof window<"u"&&window.MOJO&&delete window.MOJO.router,this.isStarted=!1,console.log(`✨ ${this.name} destroyed`)}buildPagePath(e,t,s){let i=e.route||`/${e.pageName.toLowerCase()}`;if(Object.keys(t).forEach(a=>{(typeof t[a]=="string"||typeof t[a]=="number")&&(i=i.replace(`:${a}`,t[a]))}),s&&Object.keys(s).length>0){const a=new URLSearchParams(s).toString();i+=(i.includes("?")?"&":"?")+a}return i}validateDefaultRoute(){this.pageClasses.has(this.defaultRoute)?console.log(`✅ Default route '${this.defaultRoute}' is registered`):(console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`),console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`),console.warn(" Or change default route: new WebApp({ defaultRoute: 'your-page' });"))}findFallbackPage(){const e=["404","error","denied"];for(const[t]of this.pageClasses.entries())if(!e.includes(t))return t;return null}static create(e={}){return new ee(e)}initPluginRegistry(){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.app=this)}static registerPlugin(e,t){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.plugins[e]=t,console.debug(`MOJO Plugin registered: ${e}`))}}const Pe="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==";class Ne{constructor(){this.formatters=new Map,this.registerBuiltInFormatters()}escapeHtml(e){if(e==null)return"";const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};return String(e).replace(/[&<>"']/g,s=>t[s])}registerBuiltInFormatters(){this.register("date",this.date.bind(this)),this.register("time",this.time.bind(this)),this.register("datetime",this.datetime.bind(this)),this.register("datetime_tz",this.datetime_tz.bind(this)),this.register("datatime_tz",this.datetime_tz.bind(this)),this.register("date_range",this.date_range.bind(this)),this.register("datetime_range",this.datetime_range.bind(this)),this.register("relative",this.relative.bind(this)),this.register("fromNow",this.relative.bind(this)),this.register("relative_short",this.relative_short.bind(this)),this.register("iso",this.iso.bind(this)),this.register("epoch",e=>{if(e==null||e==="")return e;const t=parseFloat(e);return isNaN(t)?e:t*1e3}),this.register("number",this.number.bind(this)),this.register("currency",this.currency.bind(this)),this.register("percent",this.percent.bind(this)),this.register("filesize",this.filesize.bind(this)),this.register("ordinal",this.ordinal.bind(this)),this.register("compact",this.compact.bind(this)),this.register("add",this.add.bind(this)),this.register("subtract",this.subtract.bind(this)),this.register("multiply",this.multiply.bind(this)),this.register("divide",this.divide.bind(this)),this.register("sub",this.subtract.bind(this)),this.register("mult",this.multiply.bind(this)),this.register("div",this.divide.bind(this)),this.register("uppercase",e=>String(e).toUpperCase()),this.register("lowercase",e=>String(e).toLowerCase()),this.register("upper",e=>String(e).toUpperCase()),this.register("lower",e=>String(e).toLowerCase()),this.register("capitalize",this.capitalize.bind(this)),this.register("caps",this.capitalize.bind(this)),this.register("replace",this.replace.bind(this)),this.register("truncate",this.truncate.bind(this)),this.register("truncate_middle",this.truncate_middle.bind(this)),this.register("truncate_front",this.truncate_front.bind(this)),this.register("slug",this.slug.bind(this)),this.register("initials",this.initials.bind(this)),this.register("mask",this.mask.bind(this)),this.register("hex",this.hex.bind(this)),this.register("tohex",this.hex.bind(this)),this.register("unhex",this.unhex.bind(this)),this.register("fromhex",this.unhex.bind(this)),this.register("email",this.email.bind(this)),this.register("phone",this.phone.bind(this)),this.register("url",this.url.bind(this)),this.register("badge",this.badge.bind(this)),this.register("badgeClass",this.badgeClass.bind(this)),this.register("status",this.status.bind(this)),this.register("status_text",this.status_text.bind(this)),this.register("status_icon",this.status_icon.bind(this)),this.register("boolean",this.boolean.bind(this)),this.register("bool",this.bool.bind(this)),this.register("yesno",e=>this.boolean(e,"Yes","No")),this.register("yesnoicon",this.yesnoicon.bind(this)),this.register("icon",this.icon.bind(this)),this.register("avatar",this.avatar.bind(this)),this.register("image",this.image.bind(this)),this.register("tooltip",this.tooltip.bind(this)),this.register("linkify",this.linkify.bind(this)),this.register("clipboard",this.clipboard.bind(this)),this.register("default",this.default.bind(this)),this.register("equals",this.equals.bind(this)),this.register("json",this.json.bind(this)),this.register("raw",e=>e),this.register("custom",(e,t)=>typeof t=="function"?t(e):e),this.register("iter",this.iter.bind(this)),this.register("keys",e=>e&&typeof e=="object"&&!Array.isArray(e)?Object.keys(e):null),this.register("values",e=>e&&typeof e=="object"&&!Array.isArray(e)?Object.values(e):null),this.register("plural",this.plural.bind(this)),this.register("list",this.formatList.bind(this)),this.register("duration",this.duration.bind(this)),this.register("hash",this.hash.bind(this)),this.register("stripHtml",this.stripHtml.bind(this)),this.register("highlight",this.highlight.bind(this)),this.register("nl2br",this.nl2br.bind(this)),this.register("code",this.code.bind(this)),this.register("pre",e=>`<pre class="bg-light p-2 rounded border">${this.escapeHtml(String(e))}</pre>`)}relative_short(e){return this.relative(e,!0)}linkify(e,t={}){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a={urls:!0,emails:!0,target:"_blank",rel:"noopener noreferrer"},r=t&&typeof t=="object"?{...a,...t}:a;let n=i;if(r.urls!==!1){const o=/(^|\s)((?:https?:\/\/|www\.)[^\s<]+)/gi;n=n.replace(o,(l,c,d)=>{const h=d.startsWith("www.")?`https://${d}`:d;return`${c}<a href="${h}" target="${r.target}" rel="${r.rel}">${d}</a>`})}if(r.emails!==!1){const o=/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;n=n.replace(o,l=>`<a href="mailto:${l}">${l}</a>`)}return n}clipboard(e,t="text"){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a=t!=="icon-only",r=`
2
2
  <button type="button"
3
3
  class="btn btn-sm btn-outline-secondary ms-1 p-0 border-0 bg-transparent"
4
4
  title="Copy"
@@ -11,14 +11,14 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
11
11
  ${a?`<span class="font-monospace">${i}</span>`:""}
12
12
  ${r}
13
13
  </span>
14
- `}nl2br(e){return e==null?"":this.escapeHtml(String(e)).replace(/\r\n|\r|\n/g,"<br>")}code(e,t=""){if(e==null)return"";const s=t?`language-${this.escapeHtml(String(t))}`:"",i=this.escapeHtml(String(e));return`<pre class="bg-light p-2 rounded border"><code class="${s}">${i}</code></pre>`}register(e,t){if(typeof t!="function")throw new Error(`Formatter must be a function, got ${typeof t}`);return this.formatters.set(e.toLowerCase(),t),this}apply(e,t,...s){try{const i=this.formatters.get(e.toLowerCase());return i?i(t,...s):(console.warn(`Formatter '${e}' not found`),t)}catch(i){return console.error(`Error in formatter '${e}':`,i),t}}pipe(e,t,s=null){return t?this.parsePipeString(t,s).reduce((a,r)=>this.apply(r.name,a,...r.args),e):e}parsePipeString(e,t=null){const s=[],i=e.split("|").map(a=>a.trim());for(const a of i){const r=this.parseFormatter(a,t);r&&s.push(r)}return s}parseFormatter(e,t=null){const s=e.match(/^([a-zA-Z_]\w*)\s*\((.*)\)$/);if(s){const[,a,r]=s,n=r?this.parseArguments(r,t):[];return{name:a,args:n}}const i=e.match(/^([a-zA-Z_]\w*)(?::(.+))?$/);if(i){const[,a,r]=i,n=r?this.parseColonArguments(r,t):[];return{name:a,args:n}}return null}parseArguments(e,t=null){const s=[];let i="",a=!1,r=null,n=0;for(let o=0;o<e.length;o++){const l=e[o];!a&&(l==='"'||l==="'")?(a=!0,r=l,i+=l):a&&l===r&&e[o-1]!=="\\"?(a=!1,r=null,i+=l):!a&&l==="{"?(n++,i+=l):!a&&l==="}"?(n--,i+=l):!a&&n===0&&l===","?(s.push(this.parseValue(i.trim(),t)),i=""):i+=l}return i.trim()&&s.push(this.parseValue(i.trim(),t)),s}parseColonArguments(e,t=null){const s=[];let i="",a=!1,r=null;for(let n=0;n<e.length;n++){const o=e[n];!a&&(o==='"'||o==="'")?(a=!0,r=o,i+=o):a&&o===r&&e[n-1]!=="\\"?(a=!1,r=null,i+=o):!a&&o===":"?(s.push(this.parseValue(i.trim(),t)),i=""):i+=o}return i.trim()&&s.push(this.parseValue(i.trim(),t)),s}parseValue(e,t=null){if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(e==="true")return!0;if(e==="false")return!1;if(e==="null")return null;if(e!=="undefined"){if(!isNaN(e)&&e!=="")return Number(e);if(e.startsWith("{")&&e.endsWith("}"))try{return JSON.parse(e)}catch{}if(t&&this.isIdentifier(e)){if(!e.includes(".")&&Object.prototype.hasOwnProperty.call(t,e))return t[e];if(t.get&&typeof t.get=="function"){const s=t.get(e);if(s!==void 0)return s}if(t.getContextValue&&typeof t.getContextValue=="function"){const s=t.getContextValue(e);if(s!==void 0)return s}if(e.includes(".")){const s=window.MOJOUtils||(typeof require<"u"?require("./MOJOUtils.js").default:null);if(s){const i=s.getNestedValue(t,e);if(i!==void 0)return i}}}return e}}isIdentifier(e){return/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(e)}date(e,t="MM/DD/YYYY"){if(!e)return"";e=this.normalizeEpoch(e);let s;if(e instanceof Date)s=e;else if(typeof e=="string")if(/^\d{4}-\d{2}-\d{2}$/.test(e)){const[n,o,l]=e.split("-").map(Number);s=new Date(n,o-1,l)}else s=new Date(e);else s=new Date(e);if(isNaN(s.getTime()))return String(e);const i={YYYY:s.getFullYear(),YY:String(s.getFullYear()).slice(-2),MMMM:s.toLocaleDateString("en-US",{month:"long"}),MMM:s.toLocaleDateString("en-US",{month:"short"}),MM:String(s.getMonth()+1).padStart(2,"0"),M:s.getMonth()+1,dddd:s.toLocaleDateString("en-US",{weekday:"long"}),ddd:s.toLocaleDateString("en-US",{weekday:"short"}),DD:String(s.getDate()).padStart(2,"0"),D:s.getDate()};let a=t;const r=new RegExp(`(${Object.keys(i).join("|")})`,"g");return a=a.replace(r,n=>i[n]||n),a}time(e,t="HH:mm:ss"){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const i=s.getHours(),a={HH:String(i).padStart(2,"0"),H:i,hh:String(i%12||12).padStart(2,"0"),h:i%12||12,mm:String(s.getMinutes()).padStart(2,"0"),m:s.getMinutes(),ss:String(s.getSeconds()).padStart(2,"0"),s:s.getSeconds(),A:i>=12?"PM":"AM",a:i>=12?"pm":"am"};let r=t;const n=Object.keys(a).sort((o,l)=>l.length-o.length);for(const o of n)r=r.replace(new RegExp(o,"g"),a[o]);return r}datetime(e,t="MM/DD/YYYY",s="HH:mm:ss"){e=this.normalizeEpoch(e);const i=this.date(e,t),a=this.time(e,s);return i&&a?`${i} ${a}`:""}datetime_tz(e,t="MM/DD/YYYY",s="HH:mm:ss",i={}){if(!e)return"";e=this.normalizeEpoch(e);const a=e instanceof Date?e:new Date(e);if(isNaN(a.getTime()))return String(e);const r=i&&i.locale||"en-US",n=i&&i.timeZone?i.timeZone:void 0,o=()=>{let T="";try{const j=new Intl.DateTimeFormat(r,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(P=>P.type==="timeZoneName");if(T=j?j.value:"",T&&/^GMT[+-]/i.test(T))try{const R=new Intl.DateTimeFormat(r,{timeStyle:"short",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(Ke=>Ke.type==="timeZoneName");R&&R.value&&!/^GMT[+-]/i.test(R.value)&&(T=R.value)}catch{}if(T&&/\s/.test(T)){const P=T.split(/\s+/).map(R=>R[0]).join("").toUpperCase();P.length>=2&&P.length<=4&&(T=P)}}catch{T=""}return T};if(!n){const T=this.date(a,t),O=this.time(a,s),j=o();return T&&O?`${T} ${O} ${j}`.trim():""}const l=new Intl.DateTimeFormat(r,{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(a),c=T=>{const O=l.find(j=>j.type===T);return O?O.value:""},d=c("year"),h=c("month"),f=c("day"),m=c("hour"),p=c("minute"),g=c("second"),y=h?String(parseInt(h,10)):"",b=f?String(parseInt(f,10)):"",w=m?String(parseInt(m,10)):"",C=m?parseInt(m,10)%12||12:"",$=m?parseInt(m,10)>=12?"PM":"AM":"",S=$?$.toLowerCase():"",M=new Intl.DateTimeFormat(r,{timeZone:n,month:"long"}).format(a),L=new Intl.DateTimeFormat(r,{timeZone:n,month:"short"}).format(a),A=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"long"}).format(a),U=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"short"}).format(a),pe={YYYY:d,YY:d?d.slice(-2):"",MMMM:M,MMM:L,MM:h,M:y,dddd:A,ddd:U,DD:f,D:b},ge={HH:m,H:w,hh:C!==""?String(C).padStart(2,"0"):"",h:C!==""?String(C):"",mm:p,m:p?String(parseInt(p,10)):"",ss:g,s:g?String(parseInt(g,10)):"",A:$,a:S},I=(T,O)=>{if(!T)return"";const j=new RegExp(`(${Object.keys(O).sort((P,R)=>R.length-P.length).join("|")})`,"g");return T.replace(j,P=>O[P]??P)},N=I(t,pe),De=I(s,ge),Ge=o();return N&&De?`${N} ${De} ${Ge}`.trim():""}normalizeEpoch(e){if(typeof e!="number"&&(e=Number(e)),isNaN(e))return"";if(e<1e11)return e*1e3;if(e>1e12&&e<1e13)return e;throw new Error("Value doesn't look like epoch seconds or ms")}date_range(e,t=null,s="MM/DD/YYYY"){if(!e)return"";const i=t||new Date,a=this.date(e,s),r=this.date(i,s);return!a||!r?"":`${a} - ${r}`}datetime_range(e,t=null,s="MM/DD/YYYY",i="HH:mm"){if(!e)return"";const a=t||new Date,r=this.datetime(e,s,i),n=this.datetime(a,s,i);return!r||!n?"":`${r} - ${n}`}relative(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const a=s-new Date,r=Math.abs(a),n=Math.floor(r/1e3),o=Math.floor(n/60),l=Math.floor(o/60),c=Math.floor(l/24),d=a>0;if(t)return c>365?Math.floor(c/365)+"y":c>30?Math.floor(c/30)+"mo":c>7?Math.floor(c/7)+"w":c>0?c+"d":l>0?l+"h":o>0?o+"m":"now";if(c>365){const h=Math.floor(c/365),f=d?"in ":"",m=d?"":" ago";return f+h+" year"+(h>1?"s":"")+m}if(c>30){const h=Math.floor(c/30),f=d?"in ":"",m=d?"":" ago";return f+h+" month"+(h>1?"s":"")+m}if(c>7){const h=Math.floor(c/7),f=d?"in ":"",m=d?"":" ago";return f+h+" week"+(h>1?"s":"")+m}if(c===1)return d?"tomorrow":"yesterday";if(c>0){const h=d?"in ":"",f=d?"":" ago";return h+c+" days"+f}if(l>0){const h=d?"in ":"",f=d?"":" ago";return h+l+" hour"+(l>1?"s":"")+f}if(o>0){const h=d?"in ":"",f=d?"":" ago";return h+o+" minute"+(o>1?"s":"")+f}if(n>30){const h=d?"in ":"",f=d?"":" ago";return h+n+" seconds"+f}return"just now"}iso(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);return isNaN(s.getTime())?String(e):t?s.toISOString().split("T")[0]:s.toISOString()}number(e,t=2,s="en-US"){const i=parseFloat(e);return isNaN(i)?String(e):i.toLocaleString(s,{minimumFractionDigits:t,maximumFractionDigits:t})}currency(e,t="$",s=2){const i=parseInt(e);if(isNaN(i))return String(e);const a=Math.abs(i).toString(),r=i<0?"-":"";let n,o;a.length<=2?(n="0",o=a.padStart(2,"0")):(n=a.slice(0,-2),o=a.slice(-2)),n=n.replace(/\B(?=(\d{3})+(?!\d))/g,",");let l;if(s===0)parseInt(o)>=50&&(n=(parseInt(n.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),l=n;else if(s===2)l=`${n}.${o}`;else{const c=o.slice(0,s).padEnd(s,"0");l=`${n}.${c}`}return r+t+l}percent(e,t=0,s=!0){const i=parseFloat(e);if(isNaN(i))return String(e);const a=s?i*100:i;return this.number(a,t)+"%"}filesize(e,t=!1,s=1){const i=parseInt(e);if(isNaN(i))return String(e);const a=t?["B","KiB","MiB","GiB","TiB"]:["B","KB","MB","GB","TB"],r=t?1024:1e3;let n=i,o=0;for(;n>=r&&o<a.length-1;)n/=r,o++;const l=o===0?0:s;return`${n.toFixed(l)} ${a[o]}`}ordinal(e,t=!1){const s=parseInt(e);if(isNaN(s))return String(e);const i=s%10,a=s%100;let r="th";return i===1&&a!==11?r="st":i===2&&a!==12?r="nd":i===3&&a!==13&&(r="rd"),t?r:s+r}compact(e,t=1){const s=parseFloat(e);if(isNaN(s))return String(e);const i=Math.abs(s),a=s<0?"-":"";return i>=1e9?a+(i/1e9).toFixed(t)+"B":i>=1e6?a+(i/1e6).toFixed(t)+"M":i>=1e3?a+(i/1e3).toFixed(t)+"K":String(s)}add(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s+i}subtract(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s-i}multiply(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s*i}divide(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)||i===0?e:s/i}capitalize(e,t=!0){const s=String(e);return s?t?s.replace(/\b\w/g,i=>i.toUpperCase()):s.charAt(0).toUpperCase()+s.slice(1):""}replace(e,t,s="",i="g"){if(e==null)return"";const a=String(e);if(t==null||t==="")return a;if(t instanceof RegExp)return a.replace(t,String(s));const r=String(t),n=r.match(/^\/(.+)\/([a-z]*)$/i);if(n){const[,o,l]=n;try{return a.replace(new RegExp(o,l),String(s))}catch{}}return String(i).includes("g")?a.split(r).join(String(s)):a.replace(r,String(s))}truncate(e,t=50,s="..."){const i=String(e);return i.length<=t?i:i.substring(0,t)+s}truncate_front(e,t=8,s="..."){const i=String(e);return i.length<=t?i:`${s}${i.slice(-t)}`}truncate_middle(e,t=8,s="***"){const i=String(e);if(i.length<=t)return i;const a=Math.floor(t/2),r=i.substring(0,a),n=i.substring(i.length-a);return`${r}${s}${n}`}slug(e,t="-"){return String(e).toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,t).replace(new RegExp(`${t}+`,"g"),t).replace(new RegExp(`^${t}|${t}$`,"g"),"")}initials(e,t=2){return String(e).split(/\s+/).filter(a=>a.length>0).slice(0,t).map(a=>a.charAt(0).toUpperCase()).join("")}mask(e,t="*",s=4){const i=String(e);if(i.length<=s)return i;const a=t.repeat(Math.max(0,i.length-s)),r=i.slice(-s);return a+r}email(e,t={}){const s=String(e).trim();if(!s)return"";if(t.link===!1)return s;const i=t.subject?`?subject=${encodeURIComponent(t.subject)}`:"",a=t.body?`&body=${encodeURIComponent(t.body)}`:"",r=t.class?` class="${t.class}"`:"";return`<a href="mailto:${s}${i}${a}"${r}>${s}</a>`}phone(e,t="US",s=!0){let i=String(e).replace(/\D/g,""),a=i;return t==="US"&&(i.length===10?a=`(${i.slice(0,3)}) ${i.slice(3,6)}-${i.slice(6)}`:i.length===11&&i[0]==="1"&&(a=`+1 (${i.slice(1,4)}) ${i.slice(4,7)}-${i.slice(7)}`)),s?`<a href="tel:${i}">${a}</a>`:a}url(e,t=null,s=!0){let i=String(e).trim();return i?(/^https?:\/\//.test(i)||(i="https://"+i),`<a href="${i}"${s?' target="_blank"':""}${s?' rel="noopener noreferrer"':""}>${t||i}</a>`):""}badge(e,t="auto"){if(Array.isArray(e))return e.map(r=>this.badge(r,t)).join(" ");const s=String(e);if(typeof t=="string"&&t.includes("=")){const r=Object.fromEntries(t.split(",").map(l=>l.split("=").map(c=>c.trim()))),n=r[s]||r[s.toLowerCase()]||this.inferBadgeType(s);return`<span class="badge ${n?`bg-${n}`:"bg-secondary"}">${s}</span>`}const i=t==="auto"?this.inferBadgeType(s):t;return`<span class="badge ${i?`bg-${i}`:"bg-secondary"}">${s}</span>`}badgeClass(e,t="auto"){const s=String(e),i=t==="auto"?this.inferBadgeType(s):t;return i?`bg-${i}`:"bg-secondary"}inferBadgeType(e){const t=e.toLowerCase();return["active","pass","success","complete","completed","approved","done","true","on","yes"].includes(t)?"success":["error","failed","fail","rejected","deleted","cancelled","false","off","no","declined"].includes(t)?"danger":["warning","pending","review","processing","uploading"].includes(t)?"warning":["info","new","draft"].includes(t)?"info":(["inactive","disabled","archived","suspended"].includes(t),"secondary")}status(e){return this._status(e)}status_icon(e){return this._status(e,{},{},!1,!0)}status_text(e){return this._status(e,{},{},!0,!1)}_status(e,t={},s={},i=!1,a=!1){const r=String(e).toLowerCase(),n={active:"bi bi-check-circle-fill",approved:"bi bi-check-circle-fill",declined:"bi bi-x-circle-fill",inactive:"bi bi-pause-circle-fill",pending:"bi bi-clock-fill",success:"bi bi-check-circle-fill",error:"bi bi-exclamation-triangle-fill",warning:"bi bi-exclamation-triangle-fill"},o={active:"success",approved:"success",declined:"danger",inactive:"secondary",pending:"warning",success:"success",error:"danger",warning:"warning"},l=t[r]||n[r]||"",c=s[r]||o[r]||"secondary";let d="";!i&&l&&(d=`<i class="${l}"></i>`);let h="";return a||(h=e),`<span class="text-${c}">${d}${d?" ":""}${h}</span>`}boolean(e,t="True",s="False",i=!1){const a=e?t:s;return i?`<span class="text-${e?"success":"danger"}">${a}</span>`:a}bool(e){return e==null||e===0||e===""||e===!1||e==="false"?!1:e===!0||e==="true"?!0:!(Array.isArray(e)&&e.length===0||e&&typeof e=="object"&&e.constructor===Object&&Object.keys(e).length===0)}icon(e,t={}){const s=String(e).toLowerCase(),i=t[s]||"";return i?`<i class="${i}"></i>`:""}yesnoicon(e,t="bi bi-check-circle-fill text-success",s="bi bi-x-circle-fill text-danger"){return e?`<i class="${t}"></i>`:`<i class="${s}"></i>`}image(e,t="thumbnail",s="img-fluid",i=""){const a=this._extractImageUrl(e,t);return a?`<img src="${a}" class="${s}" alt="${i}" />`:""}avatar(e,t="md",s="rounded-circle",i=""){const a=this._extractImageUrl(e,"square_sm")||Ae,r={xs:"width: 1.5rem; height: 1.5rem;",sm:"width: 2rem; height: 2rem;",md:"width: 3rem; height: 3rem;",lg:"width: 4rem; height: 4rem;",xl:"width: 5rem; height: 5rem;"},n=r[t]||r.md,l=`object-fit-cover ${s}`.trim();return`<img src="${a}" class="${l}" style="${n}" alt="${i}" />`}tooltip(e,t="",s="top",i=""){if(e==null)return"";const a=String(e),r=i==="html"?t:this.escapeHtml(t);return`<span data-bs-toggle="tooltip" data-bs-placement="${s}" ${i==="html"?'data-bs-html="true"':""} data-bs-title="${r}">${a}</span>`}_extractImageUrl(e,t="thumbnail"){if(!e)return null;if(typeof e=="string")return e;if(typeof e=="object"){if(e.attributes&&(e=e.attributes),t==="thumbnail"&&e.thumbnail&&typeof e.thumbnail=="string")return e.thumbnail;if(e.renditions&&typeof e.renditions=="object"){const s=e.renditions[t];if(s&&s.url)return s.url;const i=Object.values(e.renditions);if(i.length>0&&i[0].url)return i[0].url}if(e.url)return e.url}return null}default(e,t=""){return e==null||e===""?t:e}equals(e,t,s,i=""){return e==t?s:i}plural(e,t,s=null,i=!0){if(e==null||t===null||t===void 0)return i?`${e} ${t}`:t||"";const a=parseInt(e);if(isNaN(a))return i?`${e} ${t}`:t||"";const r=Math.abs(a)===1?t:s||t+"s";return i?`${a} ${r}`:r}formatList(e,t={}){if(!Array.isArray(e))return String(e);const{conjunction:s="and",limit:i=null,moreText:a="others"}=t;if(e.length===0)return"";if(e.length===1)return String(e[0]);let r=e.slice(),n=!1;if(i&&e.length>i&&(r=e.slice(0,i),n=!0),n){const o=e.length-i;return`${r.join(", ")}, ${s} ${o} ${a}`}return r.length===2?`${r[0]} ${s} ${r[1]}`:`${r.slice(0,-1).join(", ")}, ${s} ${r[r.length-1]}`}duration(e,t="ms",s=!1,i=2){if(e==null)return"";const a=parseFloat(e);if(isNaN(a))return String(e);let r;switch(t){case"s":case"sec":case"seconds":r=a*1e3;break;case"m":case"min":case"minutes":r=a*6e4;break;case"h":case"hr":case"hours":r=a*36e5;break;case"d":case"day":case"days":r=a*864e5;break;default:r=a}const n=[{name:"day",short:"d",value:864e5},{name:"hour",short:"h",value:36e5},{name:"minute",short:"m",value:6e4},{name:"second",short:"s",value:1e3}];if(r===0)return s?"0s":"0 seconds";const o=Math.abs(r),l=r<0?"-":"",c=[];let d=o;for(const h of n)if(d>=h.value){const f=Math.floor(d/h.value);d=d%h.value;const m=s?h.short:f===1?h.name:h.name+"s";if(c.push(s?`${f}${m}`:`${f} ${m}`),c.length>=i)break}return c.length===0?s?`${Math.round(o)}ms`:`${Math.round(o)} milliseconds`:l+(s?c.join(""):c.join(" "))}hash(e,t=8,s="",i="..."){if(e==null)return"";const a=String(e);return a.length<=t?s+a:s+a.substring(0,t)+i}stripHtml(e){return e==null?"":String(e).replace(/<[^>]*>/g,"")}highlight(e,t,s="highlight"){if(e==null||!t)return String(e||"");const i=String(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`(${i})`,"gi");return String(e).replace(a,`<mark class="${s}">$1</mark>`)}hex(e,t=!1,s=!1){if(e==null)return"";let i="";const a=r=>Array.from(r).map(n=>n.toString(16).padStart(2,"0")).join("");if(typeof e=="number"){let r=Math.abs(Math.trunc(e)).toString(16);r.length%2&&(r="0"+r),i=r}else if(e instanceof Uint8Array)i=a(e);else if(e instanceof ArrayBuffer)i=a(new Uint8Array(e));else if(Array.isArray(e)&&e.every(r=>typeof r=="number"))i=a(Uint8Array.from(e.map(r=>r&255)));else{const n=new TextEncoder().encode(String(e));i=a(n)}return t&&(i=i.toUpperCase()),(s?"0x":"")+i}unhex(e){if(e==null)return"";let t=String(e).trim();if((t.startsWith("0x")||t.startsWith("0X"))&&(t=t.slice(2)),t=t.replace(/\s+/g,""),t.length===0)return"";t.length%2!==0&&(t="0"+t);const s=new Uint8Array(t.length/2);for(let i=0;i<t.length;i+=2){const a=parseInt(t.slice(i,i+2),16);if(Number.isNaN(a))return String(e);s[i/2]=a}try{return new TextDecoder().decode(s)}catch{let a="";for(const r of s)a+=String.fromCharCode(r);return a}}json(e,t=2){try{return JSON.stringify(e,null,t)}catch{return String(e)}}has(e){return this.formatters.has(e.toLowerCase())}unregister(e){return this.formatters.delete(e.toLowerCase())}listFormatters(){return Array.from(this.formatters.keys()).sort()}iter(e){return e==null?[]:Array.isArray(e)?e:typeof e=="object"?Object.entries(e).map(([t,s])=>({key:t,value:s})):[{key:"0",value:e}]}}const V=new Ie;window.dataFormatter=V;class x{static getContextData(e,t){if(!t||e==null)return;let s=t,i="",a=0,r=-1;for(let o=0;o<t.length;o++){const l=t[o];if(l==="(")a++;else if(l===")")a--;else if(l==="|"&&a===0){r=o;break}}r>-1&&(s=t.substring(0,r).trim(),i=t.substring(r+1).trim());const n=this.getNestedValue(e,s);return i?V.pipe(n,i,e):n}static getNestedValue(e,t){if(!t||e==null)return;if(!t.includes(".")){if(t in e){const a=e[t];return typeof a=="function"?a.call(e):a}return}const s=t.split(".");let i=e;for(let a=0;a<s.length;a++){const r=s[a];if(i==null)return;if(a===0)if(i.hasOwnProperty(r)){const n=i[r];typeof n=="function"?i=n.call(e):i=n}else return;else{if(i&&typeof i.getContextValue=="function"){const n=s.slice(a).join(".");return i.getContextValue(n)}if(Array.isArray(i)&&!isNaN(r))i=i[parseInt(r)];else if(i.hasOwnProperty(r))i=i[r];else if(typeof i[r]=="function")i=i[r].call(i);else return}}return i}static isNullOrUndefined(e){return e==null}static deepClone(e){if(e===null||typeof e!="object")return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof Array)return e.map(t=>this.deepClone(t));if(e instanceof Object){const t={};for(const s in e)e.hasOwnProperty(s)&&(t[s]=this.deepClone(e[s]));return t}}static deepMerge(e,...t){if(!t.length)return e;const s=t.shift();if(this.isObject(e)&&this.isObject(s))for(const i in s)this.isObject(s[i])?(e[i]||Object.assign(e,{[i]:{}}),this.deepMerge(e[i],s[i])):Object.assign(e,{[i]:s[i]});return this.deepMerge(e,...t)}static isObject(e){return e&&typeof e=="object"&&!Array.isArray(e)}static debounce(e,t){let s;return function(...a){const r=()=>{clearTimeout(s),e(...a)};clearTimeout(s),s=setTimeout(r,t)}}static throttle(e,t){let s;return function(...i){s||(e.apply(this,i),s=!0,setTimeout(()=>s=!1,t))}}static generateId(e=""){const t=Date.now().toString(36),s=Math.random().toString(36).substr(2,9);return e?`${e}_${t}_${s}`:`${t}_${s}`}static escapeHtml(e){const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(e).replace(/[&<>"'`=\/]/g,s=>t[s])}static checkPasswordStrength(e){if(!e||typeof e!="string")return{score:0,strength:"invalid",feedback:["Password must be a non-empty string"],details:{length:0,hasLowercase:!1,hasUppercase:!1,hasNumbers:!1,hasSpecialChars:!1,hasCommonPatterns:!1,isCommonPassword:!1}};const t=[],s={length:e.length,hasLowercase:/[a-z]/.test(e),hasUppercase:/[A-Z]/.test(e),hasNumbers:/[0-9]/.test(e),hasSpecialChars:/[^a-zA-Z0-9]/.test(e),hasCommonPatterns:!1,isCommonPassword:!1};let i=0;e.length<6?t.push("Password should be at least 6 characters long"):e.length<8?(i+=1,t.push("Consider using at least 8 characters for better security")):e.length<12?i+=3:i+=4,s.hasLowercase?i+=1:t.push("Include lowercase letters"),s.hasUppercase?i+=1:t.push("Include uppercase letters"),s.hasNumbers?i+=1:t.push("Include numbers"),s.hasSpecialChars?i+=2:t.push("Include special characters (!@#$%^&* etc.)");const a=[/123/,/abc/i,/qwerty/i,/asdf/i,/(.)\1{2,}/,/password/i,/admin/i,/user/i,/login/i];for(const o of a)if(o.test(e)){s.hasCommonPatterns=!0,i-=1,t.push("Avoid common patterns and dictionary words");break}["123456","password","123456789","12345678","12345","1234567","1234567890","qwerty","abc123","111111","123123","admin","letmein","welcome","monkey","password123","123qwe","qwerty123","000000","dragon","sunshine","princess","azerty","1234","iloveyou","trustno1","superman","shadow","master","jennifer"].includes(e.toLowerCase())&&(s.isCommonPassword=!0,i=Math.max(0,i-3),t.push("This password is too common and easily guessed"));let n;return i<2?n="very-weak":i<4?n="weak":i<6?n="fair":i<8?n="good":n="strong",i>=7&&t.length===0?t.push("Strong password! Consider using a password manager."):i>=5&&t.length<=1&&t.push("Good password strength. Consider adding more variety."),{score:Math.max(0,i),strength:n,feedback:t,details:s}}static generatePassword(e={}){const s={...{length:12,includeLowercase:!0,includeUppercase:!0,includeNumbers:!0,includeSpecialChars:!0,customChars:"",excludeAmbiguous:!1},...e};if(s.length<4)throw new Error("Password length must be at least 4 characters");let i="abcdefghijklmnopqrstuvwxyz",a="ABCDEFGHIJKLMNOPQRSTUVWXYZ",r="0123456789",n="!@#$%^&*()_+-=[]{}|;:,.<>?";s.excludeAmbiguous&&(i=i.replace(/[il]/g,""),a=a.replace(/[IOL]/g,""),r=r.replace(/[01]/g,""),n=n.replace(/[|]/g,""));let o="";const l=[];if(s.customChars?o=s.customChars:(s.includeLowercase&&(o+=i,l.push(i[Math.floor(Math.random()*i.length)])),s.includeUppercase&&(o+=a,l.push(a[Math.floor(Math.random()*a.length)])),s.includeNumbers&&(o+=r,l.push(r[Math.floor(Math.random()*r.length)])),s.includeSpecialChars&&(o+=n,l.push(n[Math.floor(Math.random()*n.length)]))),!o)throw new Error("No character types selected for password generation");let c="";for(const d of l)c+=d;for(let d=c.length;d<s.length;d++)c+=o[Math.floor(Math.random()*o.length)];return c.split("").sort(()=>Math.random()-.5).join("")}static parseQueryString(e){const t={},s=new URLSearchParams(e);for(const[i,a]of s.entries())t[i]=a;return t}static toQueryString(e){return new URLSearchParams(e).toString()}static wrapData(e,t=null,s=3){return!e||typeof e!="object"||e instanceof Date||e instanceof RegExp||e instanceof Error||s<=0||typeof e.getContextValue=="function"?e:Array.isArray(e)?e.map(i=>i&&typeof i=="object"&&!i.getContextValue?new se(i,t):i):new se(e,t)}}class se{constructor(e,t=null){if(Object.defineProperty(this,"_data",{value:e,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(this,"_rootContext",{value:t,writable:!1,enumerable:!1,configurable:!1}),e&&typeof e=="object"){for(const s in e)if(e.hasOwnProperty(s)){const i=e[s];this[s]=x.wrapData(i,t)}}}getContextValue(e){let t=e,s="",i=0,a=-1;for(let n=0;n<e.length;n++){const o=e[n];if(o==="(")i++;else if(o===")")i--;else if(o==="|"&&i===0){a=n;break}}a>-1&&(t=e.substring(0,a).trim(),s=e.substring(a+1).trim());let r;return t in this&&t!=="_data"&&t!=="_rootContext"?r=this[t]:r=x.getNestedValue(this._data,t),s&&r!==void 0?V.pipe(r,s,this._rootContext||this._data):r}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}x.DataWrapper=se,typeof window<"u"&&(window.utils=x);const Te=Object.prototype.toString,B=Array.isArray||function(u){return Te.call(u)==="[object Array]"},Y=function(u){return typeof u=="function"},be=function(u){return u!==null&&typeof u=="object"};function Me(){return typeof window>"u"?null:window.MOJO?.dataFormatter?window.MOJO.dataFormatter:window.dataFormatter?window.dataFormatter:null}const ye=function(u){const e={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(u).replace(/[&<>"'`=\/]/g,function(t){return e[t]})};class we{constructor(e){this.string=e,this.tail=e,this.pos=0}eos(){return this.tail===""}scan(e){const t=this.tail.match(e);if(!t||t.index!==0)return"";const s=t[0];return this.tail=this.tail.substring(s.length),this.pos+=s.length,s}scanUntil(e){const t=this.tail.search(e);let s;switch(t){case-1:s=this.tail,this.tail="";break;case 0:s="";break;default:s=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=s.length,s}}class Q{constructor(e,t){this.view=e,this.cache={".":this.view},this.parent=t,this.view?._cacheId||this.view&&typeof this.view=="object"&&(this.view._cacheId=Math.random().toString(36).substring(2))}push(e){return new Q(e,this)}lookup(e){if(this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;if(this.renderCache.has(i))return this.renderCache.get(i)}if(e===".")return this.view;if(e&&e.startsWith(".")){let i=e.substring(1),a=!1,r=null;const n=i.indexOf("|");if(n!==-1&&(r=i.substring(n+1).trim(),i=i.substring(0,n).trim()),i.endsWith("|iter")&&(i=i.substring(0,i.length-5),a=!0),this.view&&typeof this.view=="object"){let o;if(typeof this.view.getContextValue=="function")try{o=this.view.getContextValue(i),o!==void 0&&Y(o)&&(o=o.call(this.view))}catch{o=void 0}if(o===void 0&&i in this.view&&(o=this.view[i],Y(o)&&(o=o.call(this.view))),r&&o!==void 0)try{const l=Me();l&&typeof l.pipe=="function"&&(o=l.pipe(o,r,this))}catch{}return B(o)?a?o:o.length>0:be(o)?a?Object.entries(o).map(([l,c])=>({key:l,value:c})):Object.keys(o).length>0:o}return}const t=this.cache;let s;if(t.hasOwnProperty(e))s=t[e];else{let i=this,a,r,n,o=!1;for(;i;){if(i.view&&typeof i.view.getContextValue=="function")try{a=i.view.getContextValue(e),a!==void 0&&(o=!0)}catch{o=!1}if(!o)if(e.indexOf(".")>0)for(a=i.view,r=e.split("."),n=0;a!=null&&n<r.length;)if(a&&typeof a.getContextValue=="function"&&n<r.length)try{const l=r.slice(n).join(".");a=a.getContextValue(l),n=r.length,a!==void 0&&(o=!0)}catch{n===r.length-1&&(o=ie(a,r[n])||ve(a,r[n])),a=a[r[n++]]}else n===r.length-1&&(o=ie(a,r[n])||ve(a,r[n])),a=a[r[n++]];else a=i.view[e],o=ie(i.view,e);if(o){s=a;break}i=i.parent}t[e]=s}if(Y(s)&&(s=s.call(this.view)),this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;this.renderCache.set(i,s)}return s}}function ie(u,e){return u!=null&&typeof u=="object"&&e in u}function ve(u,e){return u!=null&&typeof u!="object"&&u.hasOwnProperty&&u.hasOwnProperty(e)}class Ce{constructor(){this.templateCache=new Map}clearCache(){this.templateCache.clear()}parse(e,t){t=t||["{{","}}"];const s=e+":"+t.join(":");let i=this.templateCache.get(s);return i==null&&(i=this.parseTemplate(e,t),this.templateCache.set(s,i)),i}parseTemplate(e,t){if(!e)return[];const s=t[0],i=t[1],a=new we(e),r=[];let n,o,l,c,d;const h=new RegExp(ae(s)+"\\s*"),f=new RegExp("\\s*"+ae(i)),m=new RegExp("\\s*"+ae("}"+i));for(;!a.eos();){if(n=a.pos,l=a.scanUntil(h),l)for(let p=0;p<l.length;++p)c=l.charAt(p),c===`
15
- `?r.push(["text",c]):r.push(["text",c]);if(!a.scan(h))break;if(o=a.scan(/[#^\/>{&=!]/),o||(o="name"),a.scan(/\s*/),o==="="?(l=a.scanUntil(/\s*=/),a.scan(/\s*=/),a.scanUntil(f)):o==="{"?(l=a.scanUntil(m),a.scan(m),o="&"):l=a.scanUntil(f),a.scan(f),o==="#"||o==="^")d=[o,l,n,a.pos],r.push(d);else if(o==="/"){let p;for(let g=r.length-1;g>=0;--g)if((r[g][0]==="#"||r[g][0]==="^")&&r[g][1]===l){p=r[g];break}p&&p.length===4&&p.push(a.pos),r.push([o,l,n,a.pos])}else r.push([o,l,n,a.pos])}return this.nestSections(this.squashTokens(r))}squashTokens(e){const t=[];let s,i;for(let a=0;a<e.length;++a)s=e[a],s&&(s[0]==="text"&&i&&i[0]==="text"?(i[1]+=s[1],i[3]=s[3]):(t.push(s),i=s));return t}nestSections(e){const t=[];let s=t;const i=[];for(let a=0;a<e.length;++a){const r=e[a];switch(r[0]){case"#":case"^":const n=[r[0],r[1],r[2],r[3],[],r[4]||null];s.push(n),i.push(n),s=n[4];break;case"/":const o=i.pop();o&&(o[5]=r[2],s=i.length>0?i[i.length-1][4]:t);break;default:s.push(r)}}return t}render(e,t,s,i){const a=this.getConfigTags(i)||["{{","}}"],r=this.parse(e,a),n=new Map;return this.renderTokens(r,new Q(t),s,e,i,n)}renderTokens(e,t,s,i,a,r){r&&!t.renderCache&&(t.renderCache=r);let n="";for(let o=0;o<e.length;++o){const l=e[o];let c;switch(l[0]){case"#":if(c=t.lookup(l[1]),!c)continue;const d=l[4];if(!d||!B(d)){console.warn(`MUSTACHE WARNING - Section ${l[1]} has no child tokens:`,l);continue}if(B(c))for(let h=0;h<c.length;++h){const f=t.push(c[h]);t.renderCache&&(f.renderCache=t.renderCache);const m=this.renderTokens(d,f,s,i,a,r);n+=m}else if(typeof c=="object"||typeof c=="string"||typeof c=="number"){const h=t.push(c);t.renderCache&&(h.renderCache=t.renderCache),n+=this.renderTokens(d,h,s,i,a,r)}else if(Y(c)){const h=i==null?null:i.slice(l[3],l[5]);c=c.call(t.view,h,f=>this.render(f,t.view,s,a)),c!=null&&(n+=c)}else c&&(n+=this.renderTokens(d,t,s,i,a,r));break;case"^":if(c=t.lookup(l[1]),!c||B(c)&&c.length===0){const h=l[4];h&&B(h)&&(n+=this.renderTokens(h,t,s,i,a,r))}break;case">":if(!s)continue;c=Y(s)?s(l[1]):s[l[1]],c!=null&&(n+=this.render(c,t.view,s,a));break;case"&":c=t.lookup(l[1]),c!=null&&(n+=c);break;case"name":c=t.lookup(l[1]),c!=null&&(n+=ye(c));break;case"text":n+=l[1];break}}return n}getConfigTags(e){return be(e)&&B(e.tags)?e.tags:null}}function ae(u){return u.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const re=new Ce,E={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:we,Context:Q,Writer:Ce,escape:ye,clearCache(){return re.clearCache()},parse(u,e){return re.parse(u,e)},render(u,e,t,s){if(typeof u!="string")throw new TypeError('Invalid template! Template should be a "string"');return e&&typeof e=="object"&&!e.getContextValue&&typeof e.toJSON!="function"&&(e=x.wrapData(e)),re.render(u,e,t,s)}};class Ve{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=new Map}bind(e){if(this.unbind(),!e)return;const t=async n=>{const o=n.target.closest("[data-action]");if(o&&this.shouldHandle(o,n)){const c=o.getAttribute("data-action");if(this.hideTooltip(o),await this.dispatch(c,n,o)){n.preventDefault(),n.stopPropagation(),n.handledByChild=!0;return}}const l=n.target.closest("a[href], [data-page]");if(l&&!l.hasAttribute("data-action")&&this.shouldHandle(l,n)){if(n.ctrlKey||n.metaKey||n.shiftKey||n.button===1)return;if(l.tagName==="A"){const c=l.getAttribute("href");if(c&&c!=="#"&&!c.startsWith("#")&&(this.view.isExternalLink(c)||l.hasAttribute("data-external")))return}this.hideTooltip(l),n.preventDefault(),n.stopPropagation(),n.handledByChild=!0,l.hasAttribute("data-page")?await this.view.handlePageNavigation(l):await this.view.handleHrefNavigation(l)}},s=n=>{const o=n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n))return;const l=o.getAttribute("data-change-action");this.dispatchChange(l,n,o).then(c=>{c&&(n.stopPropagation(),n.handledByChild=!0)})},i=n=>{const o=n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n)||!n.target.matches('[data-filter="live-search"]'))return;const c=o.getAttribute("data-change-action"),d=parseInt(o.getAttribute("data-filter-debounce"))||300,h=`${c}-${o.getAttribute("data-container")||"default"}`;this.debounceTimers.has(h)&&clearTimeout(this.debounceTimers.get(h));const f=setTimeout(()=>{this.debounceTimers.delete(h),this.dispatchChange(c,n,o).then(m=>{m&&(n.stopPropagation(),n.handledByChild=!0)})},d);this.debounceTimers.set(h,f)},a=n=>{if(n.target.matches('[data-filter="search"]'))return;const o=n.target.closest("[data-keydown-action]")||n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n))return;let l=["Enter"];if(o.getAttribute("data-change-keys")&&(l=o.getAttribute("data-change-keys").split(",").map(c=>c.trim())),l.includes("*")||l.includes(n.key)){const c=o.getAttribute("data-keydown-action")||o.getAttribute("data-change-action");this.dispatch(c,n,o).then(d=>{d&&(n.preventDefault(),n.stopPropagation(),n.handledByChild=!0)})}},r=n=>{const o=n.target.closest("form[data-action]");if(!o||!this.shouldHandle(o,n))return;n.preventDefault();const l=o.getAttribute("data-action");this.dispatch(l,n,o)};e.addEventListener("click",t),e.addEventListener("change",s),e.addEventListener("input",i),e.addEventListener("keydown",a),e.addEventListener("submit",r),this.domListeners.push({el:e,type:"click",fn:t},{el:e,type:"change",fn:s},{el:e,type:"input",fn:i},{el:e,type:"keydown",fn:a},{el:e,type:"submit",fn:r})}unbind(){for(const{el:e,type:t,fn:s}of this.domListeners)e.removeEventListener(t,s);this.domListeners=[];for(const e of this.debounceTimers.values())clearTimeout(e);this.debounceTimers.clear()}hideDropdown(e){const s=e.closest(".dropdown-menu").closest(".dropdown");if(!s)return;const i=s.querySelector('[data-bs-toggle="dropdown"]');i&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(i)?.hide()}hideTooltip(e){if(e&&window.bootstrap?.Tooltip){const t=window.bootstrap.Tooltip.getInstance(e);t&&t.dispose()}}hideAllTooltips(){window.bootstrap?.Tooltip&&document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.hide()})}async dispatch(e,t,s){const i=this.view,a=l=>l.includes("-")?l.split("-").map(c=>c[0].toUpperCase()+c.slice(1)).join(""):l[0].toUpperCase()+l.slice(1),r=`handleAction${a(e)}`;if(typeof i[r]=="function")try{return t.preventDefault(),await i[r](t,s),!0}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const n=`onAction${a(e)}`;if(typeof i[n]=="function")try{return await i[n](t,s)?(s.closest(".dropdown-menu")&&this.hideDropdown(s),t.preventDefault(),t.stopPropagation(),!0):!1}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const o=`onPassThruAction${a(e)}`;if(typeof i[o]=="function")try{return await i[o](t,s),!1}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}if(typeof i.onActionDefault=="function")try{return await i.onActionDefault(e,t,s)}catch(l){return console.error(`Error in default action handler for ${e}:`,l),i.handleActionError(e,l,t,s),!0}return i.emit?.(`action:${e}`,{action:e,event:t,element:s}),!1}async dispatchChange(e,t,s){const i=this.view,r=`onChange${(n=>n.includes("-")?n.split("-").map(o=>o[0].toUpperCase()+o.slice(1)).join(""):n[0].toUpperCase()+n.slice(1))(e)}`;if(typeof i[r]=="function")try{return await i[r](t,s),!0}catch(n){return console.error(`Error in onChange ${e}:`,n),i.handleActionError?.(e,n,t,s),!0}return await this.dispatch(e,t,s)}shouldHandle(e,t){return!!(this.owns(e)||this.contains(e)&&!t.handledByChild)}owns(e){const t=this.view.element;if(!t||!t.contains(e))return!1;for(const s of Object.values(this.view.children))if(s.element&&s.element.contains(e))return!1;return!0}contains(e){return!!this.view.element&&this.view.element.contains(e)}}const ne={on(u,e,t){this._listeners||(this._listeners={}),this._listeners[u]||(this._listeners[u]=[]);const s={callback:e,context:t,fn:t?e.bind(t):e};return this._listeners[u].push(s),this},off(u,e,t){return!this._listeners||!this._listeners[u]?this:(e?(this._listeners[u]=this._listeners[u].filter(s=>s.callback!==e||arguments.length===3&&s.context!==t),this._listeners[u].length===0&&delete this._listeners[u]):delete this._listeners[u],this)},once(u,e,t){const s=(...i)=>{this.off(u,s),(t?e.bind(t):e).apply(t||this,i)};return this.on(u,s),this},emit(u,...e){if(!this._listeners||!this._listeners[u])return this;const t=this._listeners[u].slice();for(const s of t)try{s.fn.apply(s.context||this,e)}catch(i){console&&console.error&&console.error(`Error in ${u} event handler:`,i)}return this}};typeof window<"u"&&(window.Mustache=E);class v{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??v._genId(),this.containerId=e.containerId??null,this.container=e.container??null,typeof this.container=="string"&&(this.containerId=this.container,this.container=null),this.parent=e.parent??null,this.children=e.children??{},this.template=e.template||e.templateUrl||"",this.data=e.data??{},this.isRendering=!1,this.lastRenderTime=0,this.mounted=!1,this.debug=e.debug??!1,this.app=e.app??null,this.cacheTemplate=e.cacheTemplate??!0,this.enableTooltips=e.enableTooltips??!1,this.options={...e},this.element=this._ensureElement(),this.events=new Ve(this),e.model&&this.setModel(e.model)}async onInit(){}async onInitView(){this.initialized||(this.initialized=!0,await this.onInit())}async onBeforeRender(){}async onAfterRender(){}async onBeforeMount(){}async onAfterMount(){}async onBeforeUnmount(){}async onAfterUnmount(){}async onBeforeDestroy(){}async onAfterDestroy(){}setModel(e={}){let t=e!==this.model;if(!t)return this;this.model&&this.model.off&&this.model.off("change",this._onModelChange,this),this.model=e,this.model&&this.model.on&&this.model.on("change",this._onModelChange,this);for(const s in this.children){const i=this.children[s];i&&typeof i.setModel=="function"&&i.setModel(e)}return t&&this._onModelChange(),this}_onModelChange(){this.isMounted()&&this.render()}setTemplate(e){return this.template=e??"",this}addChild(e,t){try{if(!e||typeof e!="object")return this;t&&((t.containerId||t.container)&&(e.containerId=t.containerId||t.container),t.id&&(e.id=t.id)),e.parent=this,this.getApp()&&(e.app=this.app),this.children[e.id]=e}catch(s){v._warn("addChild error",s)}return e}removeChild(e){try{const t=typeof e=="string"?e:e&&e.id;if(!t)return this;const s=this.children[t];s&&(Promise.resolve(s.destroy()).catch(i=>v._warn("removeChild destroy error",i)),delete this.children[t])}catch(t){v._warn("removeChild error",t)}return this}getChild(e){return this.children[e]}async updateData(e,t=!1){return Object.assign(this.data,e),t&&this.isMounted()&&await this.render(),this}toggleClass(e,t){return t===void 0&&(t=!this.element.classList.contains(e)),this.element.classList.toggle(e,t),this}addClass(e){return this.element.classList.add(e),this}setClass(e){return this.element.className=e,this}removeClass(e){return this.element.classList.remove(e),this}canRender(){if(this.isRendering)return!1;const e=Date.now();if(this.options.renderCooldown>0&&e-this.lastRenderTime<this.options.renderCooldown)return v._warn(`View ${this.id}: Render called too quickly, cooldown active`),!1;if(this.options.noAppend&&this.parent)if(this.parent.contains(this.containerId||this.container)){if(this.containerId&&!document.getElementById(this.containerId))return!1;if(this.container&&!document.contains(this.container))return!1}else return!1;return!0}async render(e=!0,t=null){const s=Date.now();if(!this.canRender())return this;this.isRendering=!0,this.lastRenderTime=s;try{this.initialized||await this.onInitView(),this.unbindEvents(),await this.onBeforeRender(),this.getViewData&&(this.data=await this.getViewData());const i=await this.renderTemplate();this.element.innerHTML=i,e&&!this.isMounted()&&await this.mount(t),await this._renderChildren(),await this.onAfterRender(),this.bindEvents()}catch(i){v._warn(`Render error in ${this.id}`,i)}finally{this.isRendering=!1}return this}async _renderChildren(){for(const e in this.children){const t=this.children[e];t&&(t.parent=this,await Promise.resolve(t.render()).catch(s=>v._warn(`Child render error (${e})`,s)))}}async _unmountChildren(){for(const e in this.children){const t=this.children[e];t&&t.unbindEvents()}}isMounted(){return this.element?.isConnected}getChildElementById(e,t=null){const s=e.startsWith("#")?e.substring(1):e;return t?t.querySelector(`#${s}`):this.element.querySelector(`#${s}`)}getChildElement(e){if(e.startsWith("#"))return this.getChildElementById(e);let t=this.element?.querySelector(`[data-container="${e}"]`);return t||this.getChildElementById(e)}getContainer(){return this.replaceById?this.parent?this.parent.getChildElementById(this.id):null:this.containerId?this.parent?this.parent.getChildElement(this.containerId):this.getChildElementById(this.containerId,document.body):null}async mount(e=null){if(await this.onBeforeMount(),e||(e=this.getContainer()),this.containerId&&!e){console.error(`Container not found for ${this.containerId}`);return}e&&this.replaceById?e.replaceWith(this.element):e?e.replaceChildren(this.element):!this.containerId&&this.parent?this.parent.element.appendChild(this.element):!this.containerId&&!this.parent&&this.options.allowAppendToBody?(console.log("APPENDING TO BODY!!!!"),document.body.appendChild(this.element)):console.error(`Container not found for ${this.containerId}`),await this.onAfterMount(),this.mounted=!0}async unmount(){!this.element||!this.element.parentNode||(await this.onBeforeUnmount(),await this._unmountChildren(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.events.unbind(),await this.onAfterUnmount(),this.mounted=!1)}async destroy(){try{this.events.unbind();for(const e in this.children){const t=this.children[e];t&&await Promise.resolve(t.destroy()).catch(s=>v._warn(`Child destroy error (${e})`,s))}this.mounted=!1,this.element&&this.element.parentNode&&(await this.onBeforeDestroy(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),await this.onAfterDestroy())}catch(e){v._warn(`Destroy error in ${this.id}`,e)}}_ensureElement(){try{if(this.element&&this.element.tagName?.toLowerCase()===this.tagName)return this._syncAttrs(),this.element;const e=document.createElement(this.tagName);return this.element=e,this.el=e,this._syncAttrs(),e}catch(e){v._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||v._genId(),t}}_syncAttrs(){try{if(!this.element)return;this.id&&(this.element.id=this.id),this.element.className=this.className||"",this.style==null?this.element.removeAttribute("style"):this.element.style.cssText=String(this.style)}catch(e){v._warn("_syncAttrs error",e)}}bindEvents(){this.events.bind(this.element),this.enableTooltips&&this.initializeTooltips()}unbindEvents(){this.events.unbind(),this.enableTooltips&&this.disposeTooltips()}async renderTemplate(){const e=await this.getTemplate();if(!e)return"";const t=this.getPartials();return E.render(e,this,t)}renderTemplateString(e,t,s){return E.render(e,t,s)}getPartials(){return{}}async getTemplate(){if(this._templateCache&&this.cacheTemplate)return this._templateCache;const e=this.template||this.templateUrl;if(!e)throw new Error("Template not found");let t="";if(typeof e=="string")if(e.includes("<")||e.includes("{"))t=e;else try{let s=e;this.app||(this.app=this.getApp()),this.app&&this.app.basePath&&!s.startsWith("/")&&!s.startsWith("http://")&&!s.startsWith("https://")&&(s=`${this.app.basePath.endsWith("/")?this.app.basePath.slice(0,-1):this.app.basePath}/${s}`);const i=await fetch(s);if(!i.ok)throw new Error(`HTTP ${i.status}: ${i.statusText}`);t=await i.text()}catch(s){v._warn(`Failed to load template from ${e}: ${s}`),this.showError?.(`Failed to load template from ${e}: ${s.message}`)}else typeof e=="function"&&(t=await this.template(this.data,this.state));return this.cacheTemplate&&t&&(this._templateCache=t),t}getContextValue(e){const t=x.getContextData(this,e);return e&&e.startsWith("data.")&&t&&typeof t=="object"?x.wrapData(t,this):e&&e.startsWith("model.")&&e!=="model"&&t&&typeof t=="object"&&typeof t.getContextValue!="function"?x.wrapData(t,null):t}async handlePageNavigation(e){const t=e.getAttribute("data-page"),s=e.getAttribute("data-params");let i={};if(s)try{i=JSON.parse(s)}catch{console.warn("Invalid JSON in data-params:",s)}const a=this.getApp();if(a){a.showPage(t,i);return}const r=this.findRouter();r&&typeof r.navigateToPage=="function"?await r.navigateToPage(t,i):console.error(`No router found for page navigation to '${t}'`)}async handleHrefNavigation(e){const t=e.getAttribute("href");if(this.isExternalLink(t)||e.hasAttribute("data-external"))return;const s=this.findRouter();if(s){if(s.options&&s.options.mode==="param"&&t.startsWith("?")){const a="/"+t;await s.navigate(a);return}if(s.options&&s.options.mode==="hash"&&t.startsWith("#")){await s.navigate(t);return}const i=this.hrefToRoutePath(t);await s.navigate(i)}else console.warn("No router found for navigation, using default behavior"),window.location.href=t}isExternalLink(e){return e?e.startsWith("/")&&this.getApp()?!e.startsWith(this.findRouter().basePath):e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//"):!0}hrefToRoutePath(e){if(e.startsWith("/")){const t=this.findRouter();if(t&&t.options&&t.options.base){const s=t.options.base;if(e.startsWith(s))return e.substring(s.length)||"/"}return e}return e.startsWith("./")?e.substring(2):e}findRouter(){return this.getApp(),this.app?.router||null}getApp(){if(this.app)return this.app;const e=[window.__app__,window.MOJO?.app,window.APP,window.app,window.WebApp,window.matchUUID?window[window.matchUUID]():window[window.matchUUID]];return this.app=e.find(t=>t&&typeof t.showPage=="function")||null,this.app}handleActionError(e,t,s,i){this.showError(`Action '${e}' failed: ${t}`,s,i)}escapeHtml(e){if(typeof e!="string")return e;const t=document.createElement("div");return t.textContent=e,t.innerHTML}contains(e){if(typeof e=="string"){if(!this.element)return!1;e=document.getElementById(e)}return e?this.element.contains(e):!1}initializeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.disposeTooltips(),[...this.element.querySelectorAll('[data-bs-toggle="tooltip"]')].map(t=>{const s=t.getAttribute("data-tooltip-theme"),i=t.getAttribute("data-tooltip-size");let a="";s&&(a+=`tooltip-${s} `),i&&(a+=`tooltip-${i}`);const r={},n=a.trim();return n&&(r.customClass=n),new window.bootstrap.Tooltip(t,r)})}disposeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.element.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.dispose()})}async showError(e){console.error(`View ${this.id} error:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showError=="function"){await t.showError(e);return}alert(`Error: ${e}`)}async showSuccess(e){this.debug&&console.log(`View ${this.id} success:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showSuccess=="function"){await t.showSuccess(e);return}alert(`Success: ${e}`)}async showInfo(e){console.info(`View ${this.id} info:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showInfo=="function"){await t.showInfo(e);return}alert(`Info: ${e}`)}async showWarning(e){console.warn(`View ${this.id} warning:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showWarning=="function"){await t.showWarning(e);return}alert(`Warning: ${e}`)}async onActionCopyToClipboard(e,t){try{const i=(t?.closest("[data-clipboard]")||t)?.getAttribute("data-clipboard")||"";if(!i)return!0;if(navigator.clipboard&&window.isSecureContext)await navigator.clipboard.writeText(i);else{const n=document.createElement("textarea");n.value=i,document.body.appendChild(n),n.select(),document.execCommand("copy"),document.body.removeChild(n)}const a=t.querySelector("i"),r=a&&a.className;return a&&(a.className="bi bi-check",setTimeout(()=>{a.className=r},1e3)),!0}catch(s){return console.warn("Copy to clipboard failed:",s),!0}}static _genId(){return`view_${Math.random().toString(36).substr(2,9)}`}static _warn(e,t){try{t?console.warn(`[View] ${e}:`,t):console.warn(`[View] ${e}`)}catch{}}}Object.assign(v.prototype,ne);class W extends v{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",!this.id&&this.constructor.pageName&&!e.pageName&&(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,console.log(`Page ${this.pageName} constructed with route: ${this.route}`)}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&typeof document<"u"&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} entered`)}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} exiting`)}async onGroupChange(e){}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){console.log(`Default action '${e}' triggered on page: ${this.pageName}`)}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){!e||!this.element||(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element&&this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&(t.type==="checkbox"?e[t.name]=t.checked:t.type==="radio"?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e}restoreFormData(e){!e||!this.element||Object.entries(e).forEach(([t,s])=>{const i=this.element.querySelector(`[name="${t}"]`);if(i)if(i.type==="checkbox")i.checked=s;else if(i.type==="radio"){const a=this.element.querySelector(`[name="${t}"][value="${s}"]`);a&&(a.checked=!0)}else i.value=s})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if(!(typeof document>"u")){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([t,s])=>{if(t!=="title"&&t!=="description"){let i=document.querySelector(`meta[name="${t}"]`);i||(i=document.createElement("meta"),i.name=t,document.head.appendChild(i)),i.content=s}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`
14
+ `}nl2br(e){return e==null?"":this.escapeHtml(String(e)).replace(/\r\n|\r|\n/g,"<br>")}code(e,t=""){if(e==null)return"";const s=t?`language-${this.escapeHtml(String(t))}`:"",i=this.escapeHtml(String(e));return`<pre class="bg-light p-2 rounded border"><code class="${s}">${i}</code></pre>`}register(e,t){if(typeof t!="function")throw new Error(`Formatter must be a function, got ${typeof t}`);return this.formatters.set(e.toLowerCase(),t),this}apply(e,t,...s){try{const i=this.formatters.get(e.toLowerCase());return i?i(t,...s):(console.warn(`Formatter '${e}' not found`),t)}catch(i){return console.error(`Error in formatter '${e}':`,i),t}}pipe(e,t,s=null){return t?this.parsePipeString(t,s).reduce((a,r)=>this.apply(r.name,a,...r.args),e):e}parsePipeString(e,t=null){const s=[],i=e.split("|").map(a=>a.trim());for(const a of i){const r=this.parseFormatter(a,t);r&&s.push(r)}return s}parseFormatter(e,t=null){const s=e.match(/^([a-zA-Z_]\w*)\s*\((.*)\)$/);if(s){const[,a,r]=s,n=r?this.parseArguments(r,t):[];return{name:a,args:n}}const i=e.match(/^([a-zA-Z_]\w*)(?::(.+))?$/);if(i){const[,a,r]=i,n=r?this.parseColonArguments(r,t):[];return{name:a,args:n}}return null}parseArguments(e,t=null){const s=[];let i="",a=!1,r=null,n=0;for(let o=0;o<e.length;o++){const l=e[o];!a&&(l==='"'||l==="'")?(a=!0,r=l,i+=l):a&&l===r&&e[o-1]!=="\\"?(a=!1,r=null,i+=l):!a&&l==="{"?(n++,i+=l):!a&&l==="}"?(n--,i+=l):!a&&n===0&&l===","?(s.push(this.parseValue(i.trim(),t)),i=""):i+=l}return i.trim()&&s.push(this.parseValue(i.trim(),t)),s}parseColonArguments(e,t=null){const s=[];let i="",a=!1,r=null;for(let n=0;n<e.length;n++){const o=e[n];!a&&(o==='"'||o==="'")?(a=!0,r=o,i+=o):a&&o===r&&e[n-1]!=="\\"?(a=!1,r=null,i+=o):!a&&o===":"?(s.push(this.parseValue(i.trim(),t)),i=""):i+=o}return i.trim()&&s.push(this.parseValue(i.trim(),t)),s}parseValue(e,t=null){if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(e==="true")return!0;if(e==="false")return!1;if(e==="null")return null;if(e!=="undefined"){if(!isNaN(e)&&e!=="")return Number(e);if(e.startsWith("{")&&e.endsWith("}"))try{return JSON.parse(e)}catch{}if(t&&this.isIdentifier(e)){if(!e.includes(".")&&Object.prototype.hasOwnProperty.call(t,e))return t[e];if(t.get&&typeof t.get=="function"){const s=t.get(e);if(s!==void 0)return s}if(t.getContextValue&&typeof t.getContextValue=="function"){const s=t.getContextValue(e);if(s!==void 0)return s}if(e.includes(".")){const s=window.MOJOUtils||(typeof require<"u"?require("./MOJOUtils.js").default:null);if(s){const i=s.getNestedValue(t,e);if(i!==void 0)return i}}}return e}}isIdentifier(e){return/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(e)}date(e,t="MM/DD/YYYY"){if(!e)return"";e=this.normalizeEpoch(e);let s;if(e instanceof Date)s=e;else if(typeof e=="string")if(/^\d{4}-\d{2}-\d{2}$/.test(e)){const[n,o,l]=e.split("-").map(Number);s=new Date(n,o-1,l)}else s=new Date(e);else s=new Date(e);if(isNaN(s.getTime()))return String(e);const i={YYYY:s.getFullYear(),YY:String(s.getFullYear()).slice(-2),MMMM:s.toLocaleDateString("en-US",{month:"long"}),MMM:s.toLocaleDateString("en-US",{month:"short"}),MM:String(s.getMonth()+1).padStart(2,"0"),M:s.getMonth()+1,dddd:s.toLocaleDateString("en-US",{weekday:"long"}),ddd:s.toLocaleDateString("en-US",{weekday:"short"}),DD:String(s.getDate()).padStart(2,"0"),D:s.getDate()};let a=t;const r=new RegExp(`(${Object.keys(i).join("|")})`,"g");return a=a.replace(r,n=>i[n]||n),a}time(e,t="HH:mm:ss"){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const i=s.getHours(),a={HH:String(i).padStart(2,"0"),H:i,hh:String(i%12||12).padStart(2,"0"),h:i%12||12,mm:String(s.getMinutes()).padStart(2,"0"),m:s.getMinutes(),ss:String(s.getSeconds()).padStart(2,"0"),s:s.getSeconds(),A:i>=12?"PM":"AM",a:i>=12?"pm":"am"};let r=t;const n=Object.keys(a).sort((o,l)=>l.length-o.length);for(const o of n)r=r.replace(new RegExp(o,"g"),a[o]);return r}datetime(e,t="MM/DD/YYYY",s="HH:mm:ss"){e=this.normalizeEpoch(e);const i=this.date(e,t),a=this.time(e,s);return i&&a?`${i} ${a}`:""}datetime_tz(e,t="MM/DD/YYYY",s="HH:mm:ss",i={}){if(!e)return"";e=this.normalizeEpoch(e);const a=e instanceof Date?e:new Date(e);if(isNaN(a.getTime()))return String(e);const r=i&&i.locale||"en-US",n=i&&i.timeZone?i.timeZone:void 0,o=()=>{let I="";try{const j=new Intl.DateTimeFormat(r,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(P=>P.type==="timeZoneName");if(I=j?j.value:"",I&&/^GMT[+-]/i.test(I))try{const R=new Intl.DateTimeFormat(r,{timeStyle:"short",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(lt=>lt.type==="timeZoneName");R&&R.value&&!/^GMT[+-]/i.test(R.value)&&(I=R.value)}catch{}if(I&&/\s/.test(I)){const P=I.split(/\s+/).map(R=>R[0]).join("").toUpperCase();P.length>=2&&P.length<=4&&(I=P)}}catch{I=""}return I};if(!n){const I=this.date(a,t),O=this.time(a,s),j=o();return I&&O?`${I} ${O} ${j}`.trim():""}const l=new Intl.DateTimeFormat(r,{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(a),c=I=>{const O=l.find(j=>j.type===I);return O?O.value:""},d=c("year"),h=c("month"),m=c("day"),p=c("hour"),f=c("minute"),g=c("second"),y=h?String(parseInt(h,10)):"",b=m?String(parseInt(m,10)):"",w=p?String(parseInt(p,10)):"",C=p?parseInt(p,10)%12||12:"",$=p?parseInt(p,10)>=12?"PM":"AM":"",S=$?$.toLowerCase():"",M=new Intl.DateTimeFormat(r,{timeZone:n,month:"long"}).format(a),_=new Intl.DateTimeFormat(r,{timeZone:n,month:"short"}).format(a),A=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"long"}).format(a),G=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"short"}).format(a),Ce={YYYY:d,YY:d?d.slice(-2):"",MMMM:M,MMM:_,MM:h,M:y,dddd:A,ddd:G,DD:m,D:b},xe={HH:p,H:w,hh:C!==""?String(C).padStart(2,"0"):"",h:C!==""?String(C):"",mm:f,m:f?String(parseInt(f,10)):"",ss:g,s:g?String(parseInt(g,10)):"",A:$,a:S},k=(I,O)=>{if(!I)return"";const j=new RegExp(`(${Object.keys(O).sort((P,R)=>R.length-P.length).join("|")})`,"g");return I.replace(j,P=>O[P]??P)},N=k(t,Ce),Ve=k(s,xe),ot=o();return N&&Ve?`${N} ${Ve} ${ot}`.trim():""}normalizeEpoch(e){if(typeof e!="number"&&(e=Number(e)),isNaN(e))return"";if(e<1e11)return e*1e3;if(e>1e12&&e<1e13)return e;throw new Error("Value doesn't look like epoch seconds or ms")}date_range(e,t=null,s="MM/DD/YYYY"){if(!e)return"";const i=t||new Date,a=this.date(e,s),r=this.date(i,s);return!a||!r?"":`${a} - ${r}`}datetime_range(e,t=null,s="MM/DD/YYYY",i="HH:mm"){if(!e)return"";const a=t||new Date,r=this.datetime(e,s,i),n=this.datetime(a,s,i);return!r||!n?"":`${r} - ${n}`}relative(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const a=s-new Date,r=Math.abs(a),n=Math.floor(r/1e3),o=Math.floor(n/60),l=Math.floor(o/60),c=Math.floor(l/24),d=a>0;if(t)return c>365?Math.floor(c/365)+"y":c>30?Math.floor(c/30)+"mo":c>7?Math.floor(c/7)+"w":c>0?c+"d":l>0?l+"h":o>0?o+"m":"now";if(c>365){const h=Math.floor(c/365),m=d?"in ":"",p=d?"":" ago";return m+h+" year"+(h>1?"s":"")+p}if(c>30){const h=Math.floor(c/30),m=d?"in ":"",p=d?"":" ago";return m+h+" month"+(h>1?"s":"")+p}if(c>7){const h=Math.floor(c/7),m=d?"in ":"",p=d?"":" ago";return m+h+" week"+(h>1?"s":"")+p}if(c===1)return d?"tomorrow":"yesterday";if(c>0){const h=d?"in ":"",m=d?"":" ago";return h+c+" days"+m}if(l>0){const h=d?"in ":"",m=d?"":" ago";return h+l+" hour"+(l>1?"s":"")+m}if(o>0){const h=d?"in ":"",m=d?"":" ago";return h+o+" minute"+(o>1?"s":"")+m}if(n>30){const h=d?"in ":"",m=d?"":" ago";return h+n+" seconds"+m}return"just now"}iso(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);return isNaN(s.getTime())?String(e):t?s.toISOString().split("T")[0]:s.toISOString()}number(e,t=2,s="en-US"){const i=parseFloat(e);return isNaN(i)?String(e):i.toLocaleString(s,{minimumFractionDigits:t,maximumFractionDigits:t})}currency(e,t="$",s=2){const i=parseInt(e);if(isNaN(i))return String(e);const a=Math.abs(i).toString(),r=i<0?"-":"";let n,o;a.length<=2?(n="0",o=a.padStart(2,"0")):(n=a.slice(0,-2),o=a.slice(-2)),n=n.replace(/\B(?=(\d{3})+(?!\d))/g,",");let l;if(s===0)parseInt(o)>=50&&(n=(parseInt(n.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),l=n;else if(s===2)l=`${n}.${o}`;else{const c=o.slice(0,s).padEnd(s,"0");l=`${n}.${c}`}return r+t+l}percent(e,t=0,s=!0){const i=parseFloat(e);if(isNaN(i))return String(e);const a=s?i*100:i;return this.number(a,t)+"%"}filesize(e,t=!1,s=1){const i=parseInt(e);if(isNaN(i))return String(e);const a=t?["B","KiB","MiB","GiB","TiB"]:["B","KB","MB","GB","TB"],r=t?1024:1e3;let n=i,o=0;for(;n>=r&&o<a.length-1;)n/=r,o++;const l=o===0?0:s;return`${n.toFixed(l)} ${a[o]}`}ordinal(e,t=!1){const s=parseInt(e);if(isNaN(s))return String(e);const i=s%10,a=s%100;let r="th";return i===1&&a!==11?r="st":i===2&&a!==12?r="nd":i===3&&a!==13&&(r="rd"),t?r:s+r}compact(e,t=1){const s=parseFloat(e);if(isNaN(s))return String(e);const i=Math.abs(s),a=s<0?"-":"";return i>=1e9?a+(i/1e9).toFixed(t)+"B":i>=1e6?a+(i/1e6).toFixed(t)+"M":i>=1e3?a+(i/1e3).toFixed(t)+"K":String(s)}add(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s+i}subtract(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s-i}multiply(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s*i}divide(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)||i===0?e:s/i}capitalize(e,t=!0){const s=String(e);return s?t?s.replace(/\b\w/g,i=>i.toUpperCase()):s.charAt(0).toUpperCase()+s.slice(1):""}replace(e,t,s="",i="g"){if(e==null)return"";const a=String(e);if(t==null||t==="")return a;if(t instanceof RegExp)return a.replace(t,String(s));const r=String(t),n=r.match(/^\/(.+)\/([a-z]*)$/i);if(n){const[,o,l]=n;try{return a.replace(new RegExp(o,l),String(s))}catch{}}return String(i).includes("g")?a.split(r).join(String(s)):a.replace(r,String(s))}truncate(e,t=50,s="..."){const i=String(e);return i.length<=t?i:i.substring(0,t)+s}truncate_front(e,t=8,s="..."){const i=String(e);return i.length<=t?i:`${s}${i.slice(-t)}`}truncate_middle(e,t=8,s="***"){const i=String(e);if(i.length<=t)return i;const a=Math.floor(t/2),r=i.substring(0,a),n=i.substring(i.length-a);return`${r}${s}${n}`}slug(e,t="-"){return String(e).toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,t).replace(new RegExp(`${t}+`,"g"),t).replace(new RegExp(`^${t}|${t}$`,"g"),"")}initials(e,t=2){return String(e).split(/\s+/).filter(a=>a.length>0).slice(0,t).map(a=>a.charAt(0).toUpperCase()).join("")}mask(e,t="*",s=4){const i=String(e);if(i.length<=s)return i;const a=t.repeat(Math.max(0,i.length-s)),r=i.slice(-s);return a+r}email(e,t={}){const s=String(e).trim();if(!s)return"";if(t.link===!1)return s;const i=t.subject?`?subject=${encodeURIComponent(t.subject)}`:"",a=t.body?`&body=${encodeURIComponent(t.body)}`:"",r=t.class?` class="${t.class}"`:"";return`<a href="mailto:${s}${i}${a}"${r}>${s}</a>`}phone(e,t="US",s=!0){let i=String(e).replace(/\D/g,""),a=i;return t==="US"&&(i.length===10?a=`(${i.slice(0,3)}) ${i.slice(3,6)}-${i.slice(6)}`:i.length===11&&i[0]==="1"&&(a=`+1 (${i.slice(1,4)}) ${i.slice(4,7)}-${i.slice(7)}`)),s?`<a href="tel:${i}">${a}</a>`:a}url(e,t=null,s=!0){let i=String(e).trim();return i?(/^https?:\/\//.test(i)||(i="https://"+i),`<a href="${i}"${s?' target="_blank"':""}${s?' rel="noopener noreferrer"':""}>${t||i}</a>`):""}badge(e,t="auto"){if(Array.isArray(e))return e.map(r=>this.badge(r,t)).join(" ");const s=String(e);if(typeof t=="string"&&t.includes("=")){const r=Object.fromEntries(t.split(",").map(l=>l.split("=").map(c=>c.trim()))),n=r[s]||r[s.toLowerCase()]||this.inferBadgeType(s);return`<span class="badge ${n?`bg-${n}`:"bg-secondary"}">${s}</span>`}const i=t==="auto"?this.inferBadgeType(s):t;return`<span class="badge ${i?`bg-${i}`:"bg-secondary"}">${s}</span>`}badgeClass(e,t="auto"){const s=String(e),i=t==="auto"?this.inferBadgeType(s):t;return i?`bg-${i}`:"bg-secondary"}inferBadgeType(e){const t=e.toLowerCase();return["active","pass","success","complete","completed","approved","done","true","on","yes"].includes(t)?"success":["error","failed","fail","rejected","deleted","cancelled","false","off","no","declined"].includes(t)?"danger":["warning","pending","review","processing","uploading"].includes(t)?"warning":["info","new","draft"].includes(t)?"info":(["inactive","disabled","archived","suspended"].includes(t),"secondary")}status(e){return this._status(e)}status_icon(e){return this._status(e,{},{},!1,!0)}status_text(e){return this._status(e,{},{},!0,!1)}_status(e,t={},s={},i=!1,a=!1){const r=String(e).toLowerCase(),n={active:"bi bi-check-circle-fill",approved:"bi bi-check-circle-fill",declined:"bi bi-x-circle-fill",inactive:"bi bi-pause-circle-fill",pending:"bi bi-clock-fill",success:"bi bi-check-circle-fill",error:"bi bi-exclamation-triangle-fill",warning:"bi bi-exclamation-triangle-fill"},o={active:"success",approved:"success",declined:"danger",inactive:"secondary",pending:"warning",success:"success",error:"danger",warning:"warning"},l=t[r]||n[r]||"",c=s[r]||o[r]||"secondary";let d="";!i&&l&&(d=`<i class="${l}"></i>`);let h="";return a||(h=e),`<span class="text-${c}">${d}${d?" ":""}${h}</span>`}boolean(e,t="True",s="False",i=!1){const a=e?t:s;return i?`<span class="text-${e?"success":"danger"}">${a}</span>`:a}bool(e){return e==null||e===0||e===""||e===!1||e==="false"?!1:e===!0||e==="true"?!0:!(Array.isArray(e)&&e.length===0||e&&typeof e=="object"&&e.constructor===Object&&Object.keys(e).length===0)}icon(e,t={}){const s=String(e).toLowerCase(),i=t[s]||"";return i?`<i class="${i}"></i>`:""}yesnoicon(e,t="bi bi-check-circle-fill text-success",s="bi bi-x-circle-fill text-danger"){return e?`<i class="${t}"></i>`:`<i class="${s}"></i>`}image(e,t="thumbnail",s="img-fluid",i=""){const a=this._extractImageUrl(e,t);return a?`<img src="${a}" class="${s}" alt="${i}" />`:""}avatar(e,t="md",s="rounded-circle",i=""){const a=this._extractImageUrl(e,"square_sm")||Pe,r={xs:"width: 1.5rem; height: 1.5rem;",sm:"width: 2rem; height: 2rem;",md:"width: 3rem; height: 3rem;",lg:"width: 4rem; height: 4rem;",xl:"width: 5rem; height: 5rem;"},n=r[t]||r.md,l=`object-fit-cover ${s}`.trim();return`<img src="${a}" class="${l}" style="${n}" alt="${i}" />`}tooltip(e,t="",s="top",i=""){if(e==null)return"";const a=String(e),r=i==="html"?t:this.escapeHtml(t);return`<span data-bs-toggle="tooltip" data-bs-placement="${s}" ${i==="html"?'data-bs-html="true"':""} data-bs-title="${r}">${a}</span>`}_extractImageUrl(e,t="thumbnail"){if(!e)return null;if(typeof e=="string")return e;if(typeof e=="object"){if(e.attributes&&(e=e.attributes),t==="thumbnail"&&e.thumbnail&&typeof e.thumbnail=="string")return e.thumbnail;if(e.renditions&&typeof e.renditions=="object"){const s=e.renditions[t];if(s&&s.url)return s.url;const i=Object.values(e.renditions);if(i.length>0&&i[0].url)return i[0].url}if(e.url)return e.url}return null}default(e,t=""){return e==null||e===""?t:e}equals(e,t,s,i=""){return e==t?s:i}plural(e,t,s=null,i=!0){if(e==null||t===null||t===void 0)return i?`${e} ${t}`:t||"";const a=parseInt(e);if(isNaN(a))return i?`${e} ${t}`:t||"";const r=Math.abs(a)===1?t:s||t+"s";return i?`${a} ${r}`:r}formatList(e,t={}){if(!Array.isArray(e))return String(e);const{conjunction:s="and",limit:i=null,moreText:a="others"}=t;if(e.length===0)return"";if(e.length===1)return String(e[0]);let r=e.slice(),n=!1;if(i&&e.length>i&&(r=e.slice(0,i),n=!0),n){const o=e.length-i;return`${r.join(", ")}, ${s} ${o} ${a}`}return r.length===2?`${r[0]} ${s} ${r[1]}`:`${r.slice(0,-1).join(", ")}, ${s} ${r[r.length-1]}`}duration(e,t="ms",s=!1,i=2){if(e==null)return"";const a=parseFloat(e);if(isNaN(a))return String(e);let r;switch(t){case"s":case"sec":case"seconds":r=a*1e3;break;case"m":case"min":case"minutes":r=a*6e4;break;case"h":case"hr":case"hours":r=a*36e5;break;case"d":case"day":case"days":r=a*864e5;break;default:r=a}const n=[{name:"day",short:"d",value:864e5},{name:"hour",short:"h",value:36e5},{name:"minute",short:"m",value:6e4},{name:"second",short:"s",value:1e3}];if(r===0)return s?"0s":"0 seconds";const o=Math.abs(r),l=r<0?"-":"",c=[];let d=o;for(const h of n)if(d>=h.value){const m=Math.floor(d/h.value);d=d%h.value;const p=s?h.short:m===1?h.name:h.name+"s";if(c.push(s?`${m}${p}`:`${m} ${p}`),c.length>=i)break}return c.length===0?s?`${Math.round(o)}ms`:`${Math.round(o)} milliseconds`:l+(s?c.join(""):c.join(" "))}hash(e,t=8,s="",i="..."){if(e==null)return"";const a=String(e);return a.length<=t?s+a:s+a.substring(0,t)+i}stripHtml(e){return e==null?"":String(e).replace(/<[^>]*>/g,"")}highlight(e,t,s="highlight"){if(e==null||!t)return String(e||"");const i=String(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`(${i})`,"gi");return String(e).replace(a,`<mark class="${s}">$1</mark>`)}hex(e,t=!1,s=!1){if(e==null)return"";let i="";const a=r=>Array.from(r).map(n=>n.toString(16).padStart(2,"0")).join("");if(typeof e=="number"){let r=Math.abs(Math.trunc(e)).toString(16);r.length%2&&(r="0"+r),i=r}else if(e instanceof Uint8Array)i=a(e);else if(e instanceof ArrayBuffer)i=a(new Uint8Array(e));else if(Array.isArray(e)&&e.every(r=>typeof r=="number"))i=a(Uint8Array.from(e.map(r=>r&255)));else{const n=new TextEncoder().encode(String(e));i=a(n)}return t&&(i=i.toUpperCase()),(s?"0x":"")+i}unhex(e){if(e==null)return"";let t=String(e).trim();if((t.startsWith("0x")||t.startsWith("0X"))&&(t=t.slice(2)),t=t.replace(/\s+/g,""),t.length===0)return"";t.length%2!==0&&(t="0"+t);const s=new Uint8Array(t.length/2);for(let i=0;i<t.length;i+=2){const a=parseInt(t.slice(i,i+2),16);if(Number.isNaN(a))return String(e);s[i/2]=a}try{return new TextDecoder().decode(s)}catch{let a="";for(const r of s)a+=String.fromCharCode(r);return a}}json(e,t=2){try{return JSON.stringify(e,null,t)}catch{return String(e)}}has(e){return this.formatters.has(e.toLowerCase())}unregister(e){return this.formatters.delete(e.toLowerCase())}listFormatters(){return Array.from(this.formatters.keys()).sort()}iter(e){return e==null?[]:Array.isArray(e)?e:typeof e=="object"?Object.entries(e).map(([t,s])=>({key:t,value:s})):[{key:"0",value:e}]}}const V=new Ne;window.dataFormatter=V;class x{static getContextData(e,t){if(!t||e==null)return;let s=t,i="",a=0,r=-1;for(let o=0;o<t.length;o++){const l=t[o];if(l==="(")a++;else if(l===")")a--;else if(l==="|"&&a===0){r=o;break}}r>-1&&(s=t.substring(0,r).trim(),i=t.substring(r+1).trim());const n=this.getNestedValue(e,s);return i?V.pipe(n,i,e):n}static getNestedValue(e,t){if(!t||e==null)return;if(!t.includes(".")){if(t in e){const a=e[t];return typeof a=="function"?a.call(e):a}return}const s=t.split(".");let i=e;for(let a=0;a<s.length;a++){const r=s[a];if(i==null)return;if(a===0)if(i.hasOwnProperty(r)){const n=i[r];typeof n=="function"?i=n.call(e):i=n}else return;else{if(i&&typeof i.getContextValue=="function"){const n=s.slice(a).join(".");return i.getContextValue(n)}if(Array.isArray(i)&&!isNaN(r))i=i[parseInt(r)];else if(i.hasOwnProperty(r))i=i[r];else if(typeof i[r]=="function")i=i[r].call(i);else return}}return i}static isNullOrUndefined(e){return e==null}static deepClone(e){if(e===null||typeof e!="object")return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof Array)return e.map(t=>this.deepClone(t));if(e instanceof Object){const t={};for(const s in e)e.hasOwnProperty(s)&&(t[s]=this.deepClone(e[s]));return t}}static deepMerge(e,...t){if(!t.length)return e;const s=t.shift();if(this.isObject(e)&&this.isObject(s))for(const i in s)this.isObject(s[i])?(e[i]||Object.assign(e,{[i]:{}}),this.deepMerge(e[i],s[i])):Object.assign(e,{[i]:s[i]});return this.deepMerge(e,...t)}static isObject(e){return e&&typeof e=="object"&&!Array.isArray(e)}static debounce(e,t){let s;return function(...a){const r=()=>{clearTimeout(s),e(...a)};clearTimeout(s),s=setTimeout(r,t)}}static throttle(e,t){let s;return function(...i){s||(e.apply(this,i),s=!0,setTimeout(()=>s=!1,t))}}static generateId(e=""){const t=Date.now().toString(36),s=Math.random().toString(36).substr(2,9);return e?`${e}_${t}_${s}`:`${t}_${s}`}static escapeHtml(e){const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(e).replace(/[&<>"'`=\/]/g,s=>t[s])}static checkPasswordStrength(e){if(!e||typeof e!="string")return{score:0,strength:"invalid",feedback:["Password must be a non-empty string"],details:{length:0,hasLowercase:!1,hasUppercase:!1,hasNumbers:!1,hasSpecialChars:!1,hasCommonPatterns:!1,isCommonPassword:!1}};const t=[],s={length:e.length,hasLowercase:/[a-z]/.test(e),hasUppercase:/[A-Z]/.test(e),hasNumbers:/[0-9]/.test(e),hasSpecialChars:/[^a-zA-Z0-9]/.test(e),hasCommonPatterns:!1,isCommonPassword:!1};let i=0;e.length<6?t.push("Password should be at least 6 characters long"):e.length<8?(i+=1,t.push("Consider using at least 8 characters for better security")):e.length<12?i+=3:i+=4,s.hasLowercase?i+=1:t.push("Include lowercase letters"),s.hasUppercase?i+=1:t.push("Include uppercase letters"),s.hasNumbers?i+=1:t.push("Include numbers"),s.hasSpecialChars?i+=2:t.push("Include special characters (!@#$%^&* etc.)");const a=[/123/,/abc/i,/qwerty/i,/asdf/i,/(.)\1{2,}/,/password/i,/admin/i,/user/i,/login/i];for(const o of a)if(o.test(e)){s.hasCommonPatterns=!0,i-=1,t.push("Avoid common patterns and dictionary words");break}["123456","password","123456789","12345678","12345","1234567","1234567890","qwerty","abc123","111111","123123","admin","letmein","welcome","monkey","password123","123qwe","qwerty123","000000","dragon","sunshine","princess","azerty","1234","iloveyou","trustno1","superman","shadow","master","jennifer"].includes(e.toLowerCase())&&(s.isCommonPassword=!0,i=Math.max(0,i-3),t.push("This password is too common and easily guessed"));let n;return i<2?n="very-weak":i<4?n="weak":i<6?n="fair":i<8?n="good":n="strong",i>=7&&t.length===0?t.push("Strong password! Consider using a password manager."):i>=5&&t.length<=1&&t.push("Good password strength. Consider adding more variety."),{score:Math.max(0,i),strength:n,feedback:t,details:s}}static generatePassword(e={}){const s={...{length:12,includeLowercase:!0,includeUppercase:!0,includeNumbers:!0,includeSpecialChars:!0,customChars:"",excludeAmbiguous:!1},...e};if(s.length<4)throw new Error("Password length must be at least 4 characters");let i="abcdefghijklmnopqrstuvwxyz",a="ABCDEFGHIJKLMNOPQRSTUVWXYZ",r="0123456789",n="!@#$%^&*()_+-=[]{}|;:,.<>?";s.excludeAmbiguous&&(i=i.replace(/[il]/g,""),a=a.replace(/[IOL]/g,""),r=r.replace(/[01]/g,""),n=n.replace(/[|]/g,""));let o="";const l=[];if(s.customChars?o=s.customChars:(s.includeLowercase&&(o+=i,l.push(i[Math.floor(Math.random()*i.length)])),s.includeUppercase&&(o+=a,l.push(a[Math.floor(Math.random()*a.length)])),s.includeNumbers&&(o+=r,l.push(r[Math.floor(Math.random()*r.length)])),s.includeSpecialChars&&(o+=n,l.push(n[Math.floor(Math.random()*n.length)]))),!o)throw new Error("No character types selected for password generation");let c="";for(const d of l)c+=d;for(let d=c.length;d<s.length;d++)c+=o[Math.floor(Math.random()*o.length)];return c.split("").sort(()=>Math.random()-.5).join("")}static parseQueryString(e){const t={},s=new URLSearchParams(e);for(const[i,a]of s.entries())t[i]=a;return t}static toQueryString(e){return new URLSearchParams(e).toString()}static wrapData(e,t=null,s=3){return!e||typeof e!="object"||e instanceof Date||e instanceof RegExp||e instanceof Error||s<=0||typeof e.getContextValue=="function"?e:Array.isArray(e)?e.map(i=>i&&typeof i=="object"&&!i.getContextValue?new ae(i,t):i):new ae(e,t)}}class ae{constructor(e,t=null){if(Object.defineProperty(this,"_data",{value:e,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(this,"_rootContext",{value:t,writable:!1,enumerable:!1,configurable:!1}),e&&typeof e=="object"){for(const s in e)if(e.hasOwnProperty(s)){const i=e[s];this[s]=x.wrapData(i,t)}}}getContextValue(e){let t=e,s="",i=0,a=-1;for(let n=0;n<e.length;n++){const o=e[n];if(o==="(")i++;else if(o===")")i--;else if(o==="|"&&i===0){a=n;break}}a>-1&&(t=e.substring(0,a).trim(),s=e.substring(a+1).trim());let r;return t in this&&t!=="_data"&&t!=="_rootContext"?r=this[t]:r=x.getNestedValue(this._data,t),s&&r!==void 0?V.pipe(r,s,this._rootContext||this._data):r}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}x.DataWrapper=ae,typeof window<"u"&&(window.utils=x);const Oe=Object.prototype.toString,Y=Array.isArray||function(u){return Oe.call(u)==="[object Array]"},Z=function(u){return typeof u=="function"},$e=function(u){return u!==null&&typeof u=="object"};function He(){return typeof window>"u"?null:window.MOJO?.dataFormatter?window.MOJO.dataFormatter:window.dataFormatter?window.dataFormatter:null}const Se=function(u){const e={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(u).replace(/[&<>"'`=\/]/g,function(t){return e[t]})};class Fe{constructor(e){this.string=e,this.tail=e,this.pos=0}eos(){return this.tail===""}scan(e){const t=this.tail.match(e);if(!t||t.index!==0)return"";const s=t[0];return this.tail=this.tail.substring(s.length),this.pos+=s.length,s}scanUntil(e){const t=this.tail.search(e);let s;switch(t){case-1:s=this.tail,this.tail="";break;case 0:s="";break;default:s=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=s.length,s}}class te{constructor(e,t){this.view=e,this.cache={".":this.view},this.parent=t,this.view?._cacheId||this.view&&typeof this.view=="object"&&(this.view._cacheId=Math.random().toString(36).substring(2))}push(e){return new te(e,this)}lookup(e){if(this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;if(this.renderCache.has(i))return this.renderCache.get(i)}if(e===".")return this.view;if(e&&e.startsWith(".")){let i=e.substring(1),a=!1,r=null;const n=i.indexOf("|");if(n!==-1&&(r=i.substring(n+1).trim(),i=i.substring(0,n).trim()),i.endsWith("|iter")&&(i=i.substring(0,i.length-5),a=!0),this.view&&typeof this.view=="object"){let o;if(typeof this.view.getContextValue=="function")try{o=this.view.getContextValue(i),o!==void 0&&Z(o)&&(o=o.call(this.view))}catch{o=void 0}if(o===void 0&&i in this.view&&(o=this.view[i],Z(o)&&(o=o.call(this.view))),r&&o!==void 0)try{const l=He();l&&typeof l.pipe=="function"&&(o=l.pipe(o,r,this))}catch{}return Y(o)?a?o:o.length>0:$e(o)?a?Object.entries(o).map(([l,c])=>({key:l,value:c})):Object.keys(o).length>0:o}return}const t=this.cache;let s;if(t.hasOwnProperty(e))s=t[e];else{let i=this,a,r,n,o=!1;for(;i;){if(i.view&&typeof i.view.getContextValue=="function")try{a=i.view.getContextValue(e),a!==void 0&&(o=!0)}catch{o=!1}if(!o)if(e.indexOf(".")>0)for(a=i.view,r=e.split("."),n=0;a!=null&&n<r.length;)if(a&&typeof a.getContextValue=="function"&&n<r.length)try{const l=r.slice(n).join(".");a=a.getContextValue(l),n=r.length,a!==void 0&&(o=!0)}catch{n===r.length-1&&(o=re(a,r[n])||De(a,r[n])),a=a[r[n++]]}else n===r.length-1&&(o=re(a,r[n])||De(a,r[n])),a=a[r[n++]];else a=i.view[e],o=re(i.view,e);if(o){s=a;break}i=i.parent}t[e]=s}if(Z(s)&&(s=s.call(this.view)),this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;this.renderCache.set(i,s)}return s}}function re(u,e){return u!=null&&typeof u=="object"&&e in u}function De(u,e){return u!=null&&typeof u!="object"&&u.hasOwnProperty&&u.hasOwnProperty(e)}class Ee{constructor(){this.templateCache=new Map}clearCache(){this.templateCache.clear()}parse(e,t){t=t||["{{","}}"];const s=e+":"+t.join(":");let i=this.templateCache.get(s);return i==null&&(i=this.parseTemplate(e,t),this.templateCache.set(s,i)),i}parseTemplate(e,t){if(!e)return[];const s=t[0],i=t[1],a=new Fe(e),r=[];let n,o,l,c,d;const h=new RegExp(ne(s)+"\\s*"),m=new RegExp("\\s*"+ne(i)),p=new RegExp("\\s*"+ne("}"+i));for(;!a.eos();){if(n=a.pos,l=a.scanUntil(h),l)for(let f=0;f<l.length;++f)c=l.charAt(f),c===`
15
+ `?r.push(["text",c]):r.push(["text",c]);if(!a.scan(h))break;if(o=a.scan(/[#^\/>{&=!]/),o||(o="name"),a.scan(/\s*/),o==="="?(l=a.scanUntil(/\s*=/),a.scan(/\s*=/),a.scanUntil(m)):o==="{"?(l=a.scanUntil(p),a.scan(p),o="&"):l=a.scanUntil(m),a.scan(m),o==="#"||o==="^")d=[o,l,n,a.pos],r.push(d);else if(o==="/"){let f;for(let g=r.length-1;g>=0;--g)if((r[g][0]==="#"||r[g][0]==="^")&&r[g][1]===l){f=r[g];break}f&&f.length===4&&f.push(a.pos),r.push([o,l,n,a.pos])}else r.push([o,l,n,a.pos])}return this.nestSections(this.squashTokens(r))}squashTokens(e){const t=[];let s,i;for(let a=0;a<e.length;++a)s=e[a],s&&(s[0]==="text"&&i&&i[0]==="text"?(i[1]+=s[1],i[3]=s[3]):(t.push(s),i=s));return t}nestSections(e){const t=[];let s=t;const i=[];for(let a=0;a<e.length;++a){const r=e[a];switch(r[0]){case"#":case"^":const n=[r[0],r[1],r[2],r[3],[],r[4]||null];s.push(n),i.push(n),s=n[4];break;case"/":const o=i.pop();o&&(o[5]=r[2],s=i.length>0?i[i.length-1][4]:t);break;default:s.push(r)}}return t}render(e,t,s,i){const a=this.getConfigTags(i)||["{{","}}"],r=this.parse(e,a),n=new Map;return this.renderTokens(r,new te(t),s,e,i,n)}renderTokens(e,t,s,i,a,r){r&&!t.renderCache&&(t.renderCache=r);let n="";for(let o=0;o<e.length;++o){const l=e[o];let c;switch(l[0]){case"#":if(c=t.lookup(l[1]),!c)continue;const d=l[4];if(!d||!Y(d)){console.warn(`MUSTACHE WARNING - Section ${l[1]} has no child tokens:`,l);continue}if(Y(c))for(let h=0;h<c.length;++h){const m=t.push(c[h]);t.renderCache&&(m.renderCache=t.renderCache);const p=this.renderTokens(d,m,s,i,a,r);n+=p}else if(typeof c=="object"||typeof c=="string"||typeof c=="number"){const h=t.push(c);t.renderCache&&(h.renderCache=t.renderCache),n+=this.renderTokens(d,h,s,i,a,r)}else if(Z(c)){const h=i==null?null:i.slice(l[3],l[5]);c=c.call(t.view,h,m=>this.render(m,t.view,s,a)),c!=null&&(n+=c)}else c&&(n+=this.renderTokens(d,t,s,i,a,r));break;case"^":if(c=t.lookup(l[1]),!c||Y(c)&&c.length===0){const h=l[4];h&&Y(h)&&(n+=this.renderTokens(h,t,s,i,a,r))}break;case">":if(!s)continue;c=Z(s)?s(l[1]):s[l[1]],c!=null&&(n+=this.render(c,t.view,s,a));break;case"&":c=t.lookup(l[1]),c!=null&&(n+=c);break;case"name":c=t.lookup(l[1]),c!=null&&(n+=Se(c));break;case"text":n+=l[1];break}}return n}getConfigTags(e){return $e(e)&&Y(e.tags)?e.tags:null}}function ne(u){return u.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const oe=new Ee,E={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:Fe,Context:te,Writer:Ee,escape:Se,clearCache(){return oe.clearCache()},parse(u,e){return oe.parse(u,e)},render(u,e,t,s){if(typeof u!="string")throw new TypeError('Invalid template! Template should be a "string"');return e&&typeof e=="object"&&!e.getContextValue&&typeof e.toJSON!="function"&&(e=x.wrapData(e)),oe.render(u,e,t,s)}};class qe{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=new Map}bind(e){if(this.unbind(),!e)return;const t=async n=>{const o=n.target.closest("[data-action]");if(o&&this.shouldHandle(o,n)){const c=o.getAttribute("data-action");if(this.hideTooltip(o),await this.dispatch(c,n,o)){n.preventDefault(),n.stopPropagation(),n.handledByChild=!0;return}}const l=n.target.closest("a[href], [data-page]");if(l&&!l.hasAttribute("data-action")&&this.shouldHandle(l,n)){if(n.ctrlKey||n.metaKey||n.shiftKey||n.button===1)return;if(l.tagName==="A"){const c=l.getAttribute("href");if(c&&c!=="#"&&!c.startsWith("#")&&(this.view.isExternalLink(c)||l.hasAttribute("data-external")))return}this.hideTooltip(l),n.preventDefault(),n.stopPropagation(),n.handledByChild=!0,l.hasAttribute("data-page")?await this.view.handlePageNavigation(l):await this.view.handleHrefNavigation(l)}},s=n=>{const o=n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n))return;const l=o.getAttribute("data-change-action");this.dispatchChange(l,n,o).then(c=>{c&&(n.stopPropagation(),n.handledByChild=!0)})},i=n=>{const o=n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n)||!n.target.matches('[data-filter="live-search"]'))return;const c=o.getAttribute("data-change-action"),d=parseInt(o.getAttribute("data-filter-debounce"))||300,h=`${c}-${o.getAttribute("data-container")||"default"}`;this.debounceTimers.has(h)&&clearTimeout(this.debounceTimers.get(h));const m=setTimeout(()=>{this.debounceTimers.delete(h),this.dispatchChange(c,n,o).then(p=>{p&&(n.stopPropagation(),n.handledByChild=!0)})},d);this.debounceTimers.set(h,m)},a=n=>{if(n.target.matches('[data-filter="search"]'))return;const o=n.target.closest("[data-keydown-action]")||n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n))return;let l=["Enter"];if(o.getAttribute("data-change-keys")&&(l=o.getAttribute("data-change-keys").split(",").map(c=>c.trim())),l.includes("*")||l.includes(n.key)){const c=o.getAttribute("data-keydown-action")||o.getAttribute("data-change-action");this.dispatch(c,n,o).then(d=>{d&&(n.preventDefault(),n.stopPropagation(),n.handledByChild=!0)})}},r=n=>{const o=n.target.closest("form[data-action]");if(!o||!this.shouldHandle(o,n))return;n.preventDefault();const l=o.getAttribute("data-action");this.dispatch(l,n,o)};e.addEventListener("click",t),e.addEventListener("change",s),e.addEventListener("input",i),e.addEventListener("keydown",a),e.addEventListener("submit",r),this.domListeners.push({el:e,type:"click",fn:t},{el:e,type:"change",fn:s},{el:e,type:"input",fn:i},{el:e,type:"keydown",fn:a},{el:e,type:"submit",fn:r})}unbind(){for(const{el:e,type:t,fn:s}of this.domListeners)e.removeEventListener(t,s);this.domListeners=[];for(const e of this.debounceTimers.values())clearTimeout(e);this.debounceTimers.clear()}hideDropdown(e){const s=e.closest(".dropdown-menu").closest(".dropdown");if(!s)return;const i=s.querySelector('[data-bs-toggle="dropdown"]');i&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(i)?.hide()}hideTooltip(e){if(e&&window.bootstrap?.Tooltip){const t=window.bootstrap.Tooltip.getInstance(e);t&&t.dispose()}}hideAllTooltips(){window.bootstrap?.Tooltip&&document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.hide()})}async dispatch(e,t,s){const i=this.view,a=l=>l.includes("-")?l.split("-").map(c=>c[0].toUpperCase()+c.slice(1)).join(""):l[0].toUpperCase()+l.slice(1),r=`handleAction${a(e)}`;if(typeof i[r]=="function")try{return t.preventDefault(),await i[r](t,s),!0}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const n=`onAction${a(e)}`;if(typeof i[n]=="function")try{return await i[n](t,s)?(s.closest(".dropdown-menu")&&this.hideDropdown(s),t.preventDefault(),t.stopPropagation(),!0):!1}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const o=`onPassThruAction${a(e)}`;if(typeof i[o]=="function")try{return await i[o](t,s),!1}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}if(typeof i.onActionDefault=="function")try{return await i.onActionDefault(e,t,s)}catch(l){return console.error(`Error in default action handler for ${e}:`,l),i.handleActionError(e,l,t,s),!0}return i.emit?.(`action:${e}`,{action:e,event:t,element:s}),!1}async dispatchChange(e,t,s){const i=this.view,r=`onChange${(n=>n.includes("-")?n.split("-").map(o=>o[0].toUpperCase()+o.slice(1)).join(""):n[0].toUpperCase()+n.slice(1))(e)}`;if(typeof i[r]=="function")try{return await i[r](t,s),!0}catch(n){return console.error(`Error in onChange ${e}:`,n),i.handleActionError?.(e,n,t,s),!0}return await this.dispatch(e,t,s)}shouldHandle(e,t){return!!(this.owns(e)||this.contains(e)&&!t.handledByChild)}owns(e){const t=this.view.element;if(!t||!t.contains(e))return!1;for(const s of Object.values(this.view.children))if(s.element&&s.element.contains(e))return!1;return!0}contains(e){return!!this.view.element&&this.view.element.contains(e)}}const le={on(u,e,t){this._listeners||(this._listeners={}),this._listeners[u]||(this._listeners[u]=[]);const s={callback:e,context:t,fn:t?e.bind(t):e};return this._listeners[u].push(s),this},off(u,e,t){return!this._listeners||!this._listeners[u]?this:(e?(this._listeners[u]=this._listeners[u].filter(s=>s.callback!==e||arguments.length===3&&s.context!==t),this._listeners[u].length===0&&delete this._listeners[u]):delete this._listeners[u],this)},once(u,e,t){const s=(...i)=>{this.off(u,s),(t?e.bind(t):e).apply(t||this,i)};return this.on(u,s),this},emit(u,...e){if(!this._listeners||!this._listeners[u])return this;const t=this._listeners[u].slice();for(const s of t)try{s.fn.apply(s.context||this,e)}catch(i){console&&console.error&&console.error(`Error in ${u} event handler:`,i)}return this}};typeof window<"u"&&(window.Mustache=E);class v{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??v._genId(),this.containerId=e.containerId??null,this.container=e.container??null,typeof this.container=="string"&&(this.containerId=this.container,this.container=null),this.parent=e.parent??null,this.children=e.children??{},this.template=e.template||e.templateUrl||"",this.data=e.data??{},this.isRendering=!1,this.lastRenderTime=0,this.mounted=!1,this.debug=e.debug??!1,this.app=e.app??null,this.cacheTemplate=e.cacheTemplate??!0,this.enableTooltips=e.enableTooltips??!1,this.options={...e},this.element=this._ensureElement(),this.events=new qe(this),e.model&&this.setModel(e.model)}async onInit(){}async onInitView(){this.initialized||(this.initialized=!0,await this.onInit())}async onBeforeRender(){}async onAfterRender(){}async onBeforeMount(){}async onAfterMount(){}async onBeforeUnmount(){}async onAfterUnmount(){}async onBeforeDestroy(){}async onAfterDestroy(){}setModel(e={}){let t=e!==this.model;if(!t)return this;this.model&&this.model.off&&this.model.off("change",this._onModelChange,this),this.model=e,this.model&&this.model.on&&this.model.on("change",this._onModelChange,this);for(const s in this.children){const i=this.children[s];i&&typeof i.setModel=="function"&&i.setModel(e)}return t&&this._onModelChange(),this}_onModelChange(){this.isMounted()&&this.render()}setTemplate(e){return this.template=e??"",this}addChild(e,t){try{if(!e||typeof e!="object")return this;t&&((t.containerId||t.container)&&(e.containerId=t.containerId||t.container),t.id&&(e.id=t.id)),e.parent=this,this.getApp()&&(e.app=this.app),this.children[e.id]=e}catch(s){v._warn("addChild error",s)}return e}removeChild(e){try{const t=typeof e=="string"?e:e&&e.id;if(!t)return this;const s=this.children[t];s&&(Promise.resolve(s.destroy()).catch(i=>v._warn("removeChild destroy error",i)),delete this.children[t])}catch(t){v._warn("removeChild error",t)}return this}getChild(e){return this.children[e]}async updateData(e,t=!1){return Object.assign(this.data,e),t&&this.isMounted()&&await this.render(),this}toggleClass(e,t){return t===void 0&&(t=!this.element.classList.contains(e)),this.element.classList.toggle(e,t),this}addClass(e){return this.element.classList.add(e),this}setClass(e){return this.element.className=e,this}removeClass(e){return this.element.classList.remove(e),this}canRender(){if(this.isRendering)return!1;const e=Date.now();if(this.options.renderCooldown>0&&e-this.lastRenderTime<this.options.renderCooldown)return v._warn(`View ${this.id}: Render called too quickly, cooldown active`),!1;if(this.options.noAppend&&this.parent)if(this.parent.contains(this.containerId||this.container)){if(this.containerId&&!document.getElementById(this.containerId))return!1;if(this.container&&!document.contains(this.container))return!1}else return!1;return!0}async render(e=!0,t=null){const s=Date.now();if(!this.canRender())return this;this.isRendering=!0,this.lastRenderTime=s;try{this.initialized||await this.onInitView(),this.unbindEvents(),await this.onBeforeRender(),this.getViewData&&(this.data=await this.getViewData());const i=await this.renderTemplate();this.element.innerHTML=i,e&&!this.isMounted()&&await this.mount(t),await this._renderChildren(),await this.onAfterRender(),this.bindEvents()}catch(i){v._warn(`Render error in ${this.id}`,i)}finally{this.isRendering=!1}return this}async _renderChildren(){for(const e in this.children){const t=this.children[e];t&&(t.parent=this,await Promise.resolve(t.render()).catch(s=>v._warn(`Child render error (${e})`,s)))}}async _unmountChildren(){for(const e in this.children){const t=this.children[e];t&&t.unbindEvents()}}isMounted(){return this.element?.isConnected}getChildElementById(e,t=null){const s=e.startsWith("#")?e.substring(1):e;return t?t.querySelector(`#${s}`):this.element.querySelector(`#${s}`)}getChildElement(e){if(e.startsWith("#"))return this.getChildElementById(e);let t=this.element?.querySelector(`[data-container="${e}"]`);return t||this.getChildElementById(e)}getContainer(){return this.replaceById?this.parent?this.parent.getChildElementById(this.id):null:this.containerId?this.parent?this.parent.getChildElement(this.containerId):this.getChildElementById(this.containerId,document.body):null}async mount(e=null){if(await this.onBeforeMount(),e||(e=this.getContainer()),this.containerId&&!e){console.error(`Container not found for ${this.containerId}`);return}e&&this.replaceById?e.replaceWith(this.element):e?e.replaceChildren(this.element):!this.containerId&&this.parent?this.parent.element.appendChild(this.element):!this.containerId&&!this.parent&&this.options.allowAppendToBody?(console.log("APPENDING TO BODY!!!!"),document.body.appendChild(this.element)):console.error(`Container not found for ${this.containerId}`),await this.onAfterMount(),this.mounted=!0}async unmount(){!this.element||!this.element.parentNode||(await this.onBeforeUnmount(),await this._unmountChildren(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.events.unbind(),await this.onAfterUnmount(),this.mounted=!1)}async destroy(){try{this.events.unbind();for(const e in this.children){const t=this.children[e];t&&await Promise.resolve(t.destroy()).catch(s=>v._warn(`Child destroy error (${e})`,s))}this.mounted=!1,this.element&&this.element.parentNode&&(await this.onBeforeDestroy(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),await this.onAfterDestroy())}catch(e){v._warn(`Destroy error in ${this.id}`,e)}}_ensureElement(){try{if(this.element&&this.element.tagName?.toLowerCase()===this.tagName)return this._syncAttrs(),this.element;const e=document.createElement(this.tagName);return this.element=e,this.el=e,this._syncAttrs(),e}catch(e){v._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||v._genId(),t}}_syncAttrs(){try{if(!this.element)return;this.id&&(this.element.id=this.id),this.element.className=this.className||"",this.style==null?this.element.removeAttribute("style"):this.element.style.cssText=String(this.style)}catch(e){v._warn("_syncAttrs error",e)}}bindEvents(){this.events.bind(this.element),this.enableTooltips&&this.initializeTooltips()}unbindEvents(){this.events.unbind(),this.enableTooltips&&this.disposeTooltips()}async renderTemplate(){const e=await this.getTemplate();if(!e)return"";const t=this.getPartials();return E.render(e,this,t)}renderTemplateString(e,t,s){return E.render(e,t,s)}getPartials(){return{}}async getTemplate(){if(this._templateCache&&this.cacheTemplate)return this._templateCache;const e=this.template||this.templateUrl;if(!e)throw new Error("Template not found");let t="";if(typeof e=="string")if(e.includes("<")||e.includes("{"))t=e;else try{let s=e;this.app||(this.app=this.getApp()),this.app&&this.app.basePath&&!s.startsWith("/")&&!s.startsWith("http://")&&!s.startsWith("https://")&&(s=`${this.app.basePath.endsWith("/")?this.app.basePath.slice(0,-1):this.app.basePath}/${s}`);const i=await fetch(s);if(!i.ok)throw new Error(`HTTP ${i.status}: ${i.statusText}`);t=await i.text()}catch(s){v._warn(`Failed to load template from ${e}: ${s}`),this.showError?.(`Failed to load template from ${e}: ${s.message}`)}else typeof e=="function"&&(t=await this.template(this.data,this.state));return this.cacheTemplate&&t&&(this._templateCache=t),t}getContextValue(e){const t=x.getContextData(this,e);return e&&e.startsWith("data.")&&t&&typeof t=="object"?x.wrapData(t,this):e&&e.startsWith("model.")&&e!=="model"&&t&&typeof t=="object"&&typeof t.getContextValue!="function"?x.wrapData(t,null):t}async handlePageNavigation(e){const t=e.getAttribute("data-page"),s=e.getAttribute("data-params");let i={};if(s)try{i=JSON.parse(s)}catch{console.warn("Invalid JSON in data-params:",s)}const a=this.getApp();if(a){a.showPage(t,i);return}const r=this.findRouter();r&&typeof r.navigateToPage=="function"?await r.navigateToPage(t,i):console.error(`No router found for page navigation to '${t}'`)}async handleHrefNavigation(e){const t=e.getAttribute("href");if(this.isExternalLink(t)||e.hasAttribute("data-external"))return;const s=this.findRouter();if(s){if(s.options&&s.options.mode==="param"&&t.startsWith("?")){const a="/"+t;await s.navigate(a);return}if(s.options&&s.options.mode==="hash"&&t.startsWith("#")){await s.navigate(t);return}const i=this.hrefToRoutePath(t);await s.navigate(i)}else console.warn("No router found for navigation, using default behavior"),window.location.href=t}isExternalLink(e){return e?e.startsWith("/")&&this.getApp()?!e.startsWith(this.findRouter().basePath):e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//"):!0}hrefToRoutePath(e){if(e.startsWith("/")){const t=this.findRouter();if(t&&t.options&&t.options.base){const s=t.options.base;if(e.startsWith(s))return e.substring(s.length)||"/"}return e}return e.startsWith("./")?e.substring(2):e}findRouter(){return this.getApp(),this.app?.router||null}getApp(){if(this.app)return this.app;const e=[window.__app__,window.MOJO?.app,window.APP,window.app,window.WebApp,window.matchUUID?window[window.matchUUID]():window[window.matchUUID]];return this.app=e.find(t=>t&&typeof t.showPage=="function")||null,this.app}handleActionError(e,t,s,i){this.showError(`Action '${e}' failed: ${t}`,s,i)}escapeHtml(e){if(typeof e!="string")return e;const t=document.createElement("div");return t.textContent=e,t.innerHTML}contains(e){if(typeof e=="string"){if(!this.element)return!1;e=document.getElementById(e)}return e?this.element.contains(e):!1}initializeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.disposeTooltips(),[...this.element.querySelectorAll('[data-bs-toggle="tooltip"]')].map(t=>{const s=t.getAttribute("data-tooltip-theme"),i=t.getAttribute("data-tooltip-size");let a="";s&&(a+=`tooltip-${s} `),i&&(a+=`tooltip-${i}`);const r={},n=a.trim();return n&&(r.customClass=n),new window.bootstrap.Tooltip(t,r)})}disposeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.element.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.dispose()})}async showError(e){console.error(`View ${this.id} error:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showError=="function"){await t.showError(e);return}alert(`Error: ${e}`)}async showSuccess(e){this.debug&&console.log(`View ${this.id} success:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showSuccess=="function"){await t.showSuccess(e);return}alert(`Success: ${e}`)}async showInfo(e){console.info(`View ${this.id} info:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showInfo=="function"){await t.showInfo(e);return}alert(`Info: ${e}`)}async showWarning(e){console.warn(`View ${this.id} warning:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showWarning=="function"){await t.showWarning(e);return}alert(`Warning: ${e}`)}async onActionCopyToClipboard(e,t){try{const i=(t?.closest("[data-clipboard]")||t)?.getAttribute("data-clipboard")||"";if(!i)return!0;if(navigator.clipboard&&window.isSecureContext)await navigator.clipboard.writeText(i);else{const n=document.createElement("textarea");n.value=i,document.body.appendChild(n),n.select(),document.execCommand("copy"),document.body.removeChild(n)}const a=t.querySelector("i"),r=a&&a.className;return a&&(a.className="bi bi-check",setTimeout(()=>{a.className=r},1e3)),!0}catch(s){return console.warn("Copy to clipboard failed:",s),!0}}static _genId(){return`view_${Math.random().toString(36).substr(2,9)}`}static _warn(e,t){try{t?console.warn(`[View] ${e}:`,t):console.warn(`[View] ${e}`)}catch{}}}Object.assign(v.prototype,le);class K extends v{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",!this.id&&this.constructor.pageName&&!e.pageName&&(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,console.log(`Page ${this.pageName} constructed with route: ${this.route}`)}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&typeof document<"u"&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} entered`)}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} exiting`)}async onGroupChange(e){}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){console.log(`Default action '${e}' triggered on page: ${this.pageName}`)}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){!e||!this.element||(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element&&this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&(t.type==="checkbox"?e[t.name]=t.checked:t.type==="radio"?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e}restoreFormData(e){!e||!this.element||Object.entries(e).forEach(([t,s])=>{const i=this.element.querySelector(`[name="${t}"]`);if(i)if(i.type==="checkbox")i.checked=s;else if(i.type==="radio"){const a=this.element.querySelector(`[name="${t}"][value="${s}"]`);a&&(a.checked=!0)}else i.value=s})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if(!(typeof document>"u")){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([t,s])=>{if(t!=="title"&&t!=="description"){let i=document.querySelector(`meta[name="${t}"]`);i||(i=document.createElement("meta"),i.name=t,document.head.appendChild(i)),i.content=s}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`
16
16
  ${e}
17
17
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
18
18
  `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},5e3)}}showSuccess(e){if(super.showSuccess(e),this.element){const t=document.createElement("div");t.className="alert alert-success alert-dismissible fade show",t.innerHTML=`
19
19
  ${e}
20
20
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
21
- `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),typeof document<"u"&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),typeof document<"u"&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){if(this.app&&this.app.router)return this.app.router.navigate(e,s);if(typeof window<"u"&&window.MOJO?.router)return window.MOJO.router.navigate(e,s);console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return typeof e=="string"&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class t extends W{constructor(i={}){super({...e,...i})}}return t.template=e.template,t.pageName=e.pageName,t.route=e.route,t}}class X{constructor(e={},t={}){this.endpoint=t.endpoint||this.constructor.endpoint||"",this.id=e.id||null,this.attributes={...e},this._=this.attributes,this.originalAttributes={...e},this.errors={},this.loading=!1,this.rest=z,this.options={idAttribute:"id",timestamps:!0,...t}}getContextValue(e){return this.get(e)}get(e){return!e.includes(".")&&!e.includes("|")&&this[e]!==void 0?typeof this[e]=="function"?this[e]():this[e]:x.getContextData(this.attributes,e)}set(e,t,s={}){const i=JSON.parse(JSON.stringify(this.attributes));let a=!1;if(e!=null){if(typeof e=="object"){for(const[r,n]of Object.entries(e))a=this._setNestedAttribute(r,n)||a;e.id!==void 0&&(this.id=e.id)}else e==="id"?(this.id=t,a=!0):a=this._setNestedAttribute(e,t);if(a&&!s.silent)if(this.emit("change",this),typeof e=="string")this.emit(`change:${e}`,t,this);else for(const[r,n]of Object.entries(e)){const o=this._getNestedValue(r);JSON.stringify(this._getNestedValue(r,i))!==JSON.stringify(o)&&this.emit(`change:${r}`,o,this)}}}_setNestedAttribute(e,t){if(!e.includes(".")){const l=this.attributes[e];return this.attributes[e]=t,this[e]=t,l!==t}const s=e.split("."),i=s[0];(!this.attributes[i]||typeof this.attributes[i]!="object")&&(this.attributes[i]={}),(!this[i]||typeof this[i]!="object")&&(this[i]={});const a=this._getNestedValue(e);let r=this.attributes[i],n=this[i];for(let l=1;l<s.length-1;l++){const c=s[l];(!r[c]||typeof r[c]!="object")&&(r[c]={}),(!n[c]||typeof n[c]!="object")&&(n[c]={}),r=r[c],n=n[c]}const o=s[s.length-1];return r[o]=t,n[o]=t,JSON.stringify(a)!==JSON.stringify(t)}_getNestedValue(e,t=this.attributes){if(!e.includes("."))return t[e];const s=e.split(".");let i=t;for(const a of s){if(i==null||typeof i!="object")return;i=i[a]}return i}getData(){return this.attributes}getId(){return this.id}async fetch(e={}){let t=e.url;if(!t){const r=e.id||this.getId();if(!r&&this.options.requiresId!==!1)throw new Error("Model: ID is required for fetching");t=this.buildUrl(r)}const s=JSON.stringify({url:t,params:e.params});if(e.debounceMs&&e.debounceMs>0)return this._debouncedFetch(s,e);if(this.currentRequest&&this.currentRequestKey!==s&&(console.info("Model: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===s)return console.info("Model: Duplicate request in progress, returning existing promise"),this.currentRequest;const i=Date.now();if(this.lastFetchTime&&i-this.lastFetchTime<100)return console.info("Model: Rate limited, skipping fetch"),this;this.loading=!0,this.errors={},this.lastFetchTime=i,this.currentRequestKey=s,this.abortController=new AbortController,this.currentRequest=this._performFetch(t,e,this.abortController);try{return await this.currentRequest}catch(r){if(r.name==="AbortError")return console.info("Model: Request was cancelled"),this;throw r}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _debouncedFetch(e,t){return this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((s,i)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const a=await this.fetch({...t,debounceMs:0});s(a)}catch(a){i(a)}},t.debounceMs)})}async _performFetch(e,t,s){try{t.graph&&(!t.params||!t.params.graph)&&(t.params||(t.params={}),t.params.graph=t.graph);const i=await this.rest.GET(e,t.params,{signal:s.signal});return i.success?i.data.status?(this.originalAttributes={...this.attributes},i.data.data&&this.set(i.data.data),this.errors={}):this.errors=i.data:this.errors=i.errors||{},i}catch(i){if(i.name==="AbortError")throw console.info("Model: Fetch was cancelled"),i;return this.errors={fetch:i.message},{success:!1,error:i.message,status:i.status||500}}finally{this.loading=!1}}async save(e,t={}){const s=!this.id,i=s?"POST":"PUT",a=s?this.buildUrl():this.buildUrl(this.id);this.loading=!0,this.errors={};try{const r=await this.rest[i](a,e,t.params);return r.success?r.data.status?(this.originalAttributes={...this.attributes},this.set(r.data.data),this.errors={}):this.errors=r.data:this.errors=r.errors||{},r}catch(r){return{success:!1,error:r.message,status:r.status||500}}finally{this.loading=!1}}async destroy(e={}){if(!this.id)return this.errors={destroy:"Cannot destroy model without ID"},{success:!1,error:"Cannot destroy model without ID",status:400};const t=this.buildUrl(this.id);this.loading=!0,this.errors={};try{const s=await this.rest.DELETE(t,e.params);return s.success?(this.attributes={},this.originalAttributes={},this.id=null,this.errors={}):this.errors=s.errors||{},s}catch(s){return this.errors={destroy:s.message},{success:!1,error:s.message,status:s.status||500}}finally{this.loading=!1}}isDirty(){return JSON.stringify(this.attributes)!==JSON.stringify(this.originalAttributes)}getChangedAttributes(){const e={};for(const[t,s]of Object.entries(this.attributes))this.originalAttributes[t]!==s&&(e[t]=s);return e}reset(){this.attributes={...this.originalAttributes},this._=this.attributes,this.errors={}}buildUrl(e=null){let t=this.endpoint;return e&&(t=t.endsWith("/")?`${t}${e}`:`${t}/${e}`),t}toJSON(){return{id:this.id,...this.attributes}}validate(){if(this.errors={},this.constructor.validations)for(const[e,t]of Object.entries(this.constructor.validations))this.validateField(e,t);return Object.keys(this.errors).length===0}validateField(e,t){const s=this.get(e),i=Array.isArray(t)?t:[t];for(const a of i)if(typeof a=="function"){const r=a(s,this);if(r!==!0){this.errors[e]=r||`${e} is invalid`;break}}else if(typeof a=="object"){if(a.required&&(s==null||s==="")){this.errors[e]=a.message||`${e} is required`;break}if(a.minLength&&s&&s.length<a.minLength){this.errors[e]=a.message||`${e} must be at least ${a.minLength} characters`;break}if(a.maxLength&&s&&s.length>a.maxLength){this.errors[e]=a.message||`${e} must be no more than ${a.maxLength} characters`;break}if(a.pattern&&s&&!a.pattern.test(s)){this.errors[e]=a.message||`${e} format is invalid`;break}}}static async find(e,t={}){const s=new this({},t);return await s.fetch({id:e,...t}),s}static create(e={},t={}){return new this(e,t)}cancel(){return this.currentRequest&&this.abortController?(console.info("Model: Manually cancelling active request"),this.abortController.abort(),!0):this.debouncedFetchTimeout?(clearTimeout(this.debouncedFetchTimeout),this.debouncedFetchTimeout=null,!0):!1}isFetching(){return!!this.currentRequest}async showError(e){await Dialog.alert(e,"Error",{size:"md",class:"text-danger"})}}Object.assign(X.prototype,ne);class J{constructor(e={},t=null){if(Array.isArray(e)?(t=e,e=t||{}):t=t||e.data||[],this.ModelClass=e.ModelClass||X,this.models=[],this.loading=!1,this.errors={},this.meta={},this.rest=z,t&&this.add(t),this.params={start:0,size:e.size||10,...e.params},this.endpoint=e.endpoint||this.ModelClass.endpoint||"",!this.endpoint){let s=new this.ModelClass;this.endpoint=s.endpoint}this.restEnabled=!!this.endpoint,e.restEnabled!==void 0&&(this.restEnabled=e.restEnabled),this.options={parse:!0,reset:!0,preloaded:!1,...e}}getModelName(){return this.ModelClass.name}async fetch(e={}){const t=JSON.stringify({...this.params,...e});if(this.currentRequest&&this.currentRequestKey!==t&&(console.info("Collection: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===t)return console.info("Collection: Duplicate request in progress, returning existing promise"),this.currentRequest;const s=Date.now();if(this.options.rateLimiting&&this.lastFetchTime&&s-this.lastFetchTime<100)return console.info("Collection: Rate limited, skipping fetch"),{success:!0,message:"Rate limited, skipping fetch",data:{data:this.toJSON()}};if(!this.restEnabled)return console.info("Collection: REST disabled, skipping fetch"),{success:!0,message:"REST disabled, skipping fetch",data:{data:this.toJSON()}};if(this.options.preloaded&&this.models.length>0)return console.info("Collection: Using preloaded data, skipping fetch"),{success:!0,message:"Using preloaded data, skipping fetch",data:{data:this.toJSON()}};const a=this.buildUrl();this.loading=!0,this.errors={},this.lastFetchTime=s,this.currentRequestKey=t,this.abortController=new AbortController,this.currentRequest=this._performFetch(a,e,this.abortController);try{return await this.currentRequest}catch(r){return r.name==="AbortError"?(console.info("Collection: Request was cancelled"),{success:!1,error:"Request cancelled",status:0}):{success:!1,error:r.message,status:r.status||500}}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _performFetch(e,t,s){const i={...this.params,...t};console.log("Fetching collection data from",e,i);try{this.emit("fetch:start");const a=await this.rest.GET(e,i,{signal:s.signal});if(a.success&&a.data.status){const r=this.options.parse?this.parse(a):a.data;(this.options.reset||t.reset!==!1)&&this.reset(),this.add(r,{silent:t.silent}),this.errors={},this.emit("fetch:success")}else a.data&&a.data.error?(this.errors=a.data,this.emit("fetch:error",{message:a.data.error,error:a.data})):(this.errors=a.errors||{},this.emit("fetch:error",{error:a.errors}));return a}catch(a){return a.name==="AbortError"?(console.info("Collection: Fetch was cancelled"),{success:!1,error:"Request cancelled",status:0}):(this.errors={fetch:a.message},this.emit("fetch:error",{message:a.message,error:a}),{success:!1,error:a.message,status:a.status||500})}finally{this.loading=!1,this.emit("fetch:end")}}async updateParams(e,t=!1,s=0){return await this.setParams({...this.params,...e},t,s)}async setParams(e,t=!1,s=0){return this.params=e,t&&this.restEnabled?s>0?(this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((i,a)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const r=await this.fetch();i(r)}catch(r){a(r)}},s)})):this.fetch():Promise.resolve(this)}async fetchOne(e,t={}){if(!e)return console.warn("Collection: fetchOne requires an ID"),null;if(!this.restEnabled)return console.info("Collection: REST disabled, cannot fetch single item"),null;try{const s=new this.ModelClass({id:e},{endpoint:this.endpoint,collection:this}),i=await s.fetch(t);if(i.success){if(t.addToCollection===!0){const a=this.get(s.id);a?t.merge!==!1&&a.set(s.attributes):this.add(s,{silent:t.silent})}return s}else return console.warn("Collection: fetchOne failed -",i.error||"Unknown error"),null}catch(s){return console.error("Collection: fetchOne error -",s.message),null}}async download(e="json",t={}){if(!this.restEnabled)return console.warn("Collection: REST is not enabled, cannot download from remote."),{success:!1,message:"Remote downloads are not enabled for this collection."};const s=this.buildUrl(),i={...this.params};delete i.start,delete i.size,i.download_format=e;const a=`export-${this.getModelName().toLowerCase()}`,r=this._buildDateRangeSuffix(i),n=`${a}${r}.${e}`,l={json:"application/json",csv:"text/csv"}[e]||"*/*";return i.filename=n,this.rest.download(s,i,{...t,filename:n,headers:{Accept:l}})}_buildDateRangeSuffix(e={}){const t=e.dr_start,s=e.dr_end;if(!t&&!s)return"";const i=n=>n?String(n).replace(/[^\dA-Za-z_-]/g,"-"):"",a=[],r=e.dr_field||"daterange";return a.push(i(r)),t&&a.push(`from-${i(e.dr_start)}`),s&&a.push(`to-${i(e.dr_end)}`),`-${a.filter(Boolean).join("-")}`}parse(e){return e.data&&Array.isArray(e.data.data)?(this.meta={size:e.data.size||10,start:e.data.start||0,count:e.data.count||0,status:e.data.status,graph:e.data.graph,...e.meta},e.data.data):Array.isArray(e.data)?e.data:Array.isArray(e)?e:[e]}add(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r;a instanceof this.ModelClass?r=a:r=new this.ModelClass(a,{endpoint:this.endpoint,collection:this});const n=this.models.findIndex(o=>o.id===r.id);n!==-1?t.merge!==!1&&this.models[n].set(r.attributes):(this.models.push(r),i.push(r))}return!t.silent&&i.length>0&&(this.emit("add",{models:i,collection:this}),this.emit("update",{collection:this})),i}remove(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r=-1;if(typeof a=="string"||typeof a=="number"?r=this.models.findIndex(n=>n.id==a):r=this.models.indexOf(a),r!==-1){const n=this.models.splice(r,1)[0];i.push(n)}}return!t.silent&&i.length>0&&(this.emit("remove",{models:i,collection:this}),this.emit("update",{collection:this})),i}reset(e=null,t={}){const s=[...this.models];return this.models=[],e&&this.add(e,{silent:!0,...t}),t.silent||this.emit("reset",{collection:this,previousModels:s}),this}get(e){return this.models.find(t=>t.id==e)}at(e){return this.models[e]}length(){return this.models.length}isEmpty(){return this.models.length===0}where(e){return typeof e=="function"?this.models.filter(e):typeof e=="object"?this.models.filter(t=>Object.entries(e).every(([s,i])=>t.get(s)===i)):[]}findWhere(e){const t=this.where(e);return t.length>0?t[0]:void 0}forEach(e,t){if(typeof e!="function")throw new TypeError("Callback must be a function");return this.models.forEach((s,i)=>{e.call(t,s,i,this)}),this}sort(e,t={}){if(typeof e=="string"){const s=e;e=(i,a)=>{const r=i.get(s),n=a.get(s);return r<n?-1:r>n?1:0}}return this.models.sort(e),t.silent||this.trigger("sort",{collection:this}),this}toJSON(){return this.models.map(e=>e.toJSON())}cancel(){return this.currentRequest&&this.abortController?(console.info("Collection: Manually cancelling active request"),this.abortController.abort(),!0):!1}isFetching(){return!!this.currentRequest}buildUrl(){return this.endpoint}*[Symbol.iterator](){for(const e of this.models)yield e}static fromArray(e,t=[],s={}){const i=new this(e,s);return i.add(t,{silent:!0}),i}}Object.assign(J.prototype,ne);class H{static _plugins=[];static _renderers=new Map;static register(e){return!e||typeof e!="object"?(console.warn("[FormPlugins] register called with invalid plugin:",e),()=>{}):!e.id||typeof e.id!="string"?(console.warn('[FormPlugins] plugin must have a unique string "id"',e),()=>{}):(this.unregister(e.id),e.fieldTypes&&typeof e.fieldTypes=="object"&&Object.entries(e.fieldTypes).forEach(([t,s])=>{typeof s=="function"?this._renderers.set(t,{renderer:s,pluginId:e.id}):console.warn(`[FormPlugins] renderer for type "${t}" is not a function`)}),this._plugins.push(e),()=>this.unregister(e.id))}static unregister(e){if(e){this._plugins=this._plugins.filter(t=>t.id!==e);for(const[t,s]of this._renderers.entries())s?.pluginId===e&&this._renderers.delete(t)}}static getRenderer(e){return this._renderers.get(e)?.renderer||null}static hasRenderer(e){return this._renderers.has(e)}static getPlugins(){return[...this._plugins]}static _invoke(e,t,...s){const i=e?.[t];if(typeof i=="function")try{return i.apply(e,s)}catch(a){console.error(`[FormPlugins] ${t} error from plugin "${e.id}":`,a)}}static onFormBuilderInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormBuilderInit",e))}static onFormViewInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormViewInit",e))}static onFormViewAfterRender(e){this._plugins.forEach(t=>this._invoke(t,"onAfterRender",e))}static onFieldInit(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldInit",e,t,s))}static onFieldChange(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldChange",e,t,s))}}class Z{constructor(e={}){this.fields=e.fields||[],this.structureOnly=e.structureOnly||!1,this.fields.forEach(t=>{t.cols&&!t.columns?(t.columns=t.cols,delete t.cols):t.columns||(t.columns=12),t.type==="group"&&t.fields&&t.fields.forEach(s=>{s.cols&&!s.columns?(s.columns=s.cols,delete s.cols):s.columns||(s.columns=12)})}),this.options={formClass:"needs-validation",formMethod:"POST",formAction:"",groupClass:"row mb-3",fieldWrapper:"",labelClass:"form-label",inputClass:"form-control",errorClass:"invalid-feedback",helpClass:"form-text",submitButton:!1,resetButton:!1,...e.options},this.buttons=e.buttons||[],this.data=e.data||{},this.errors=e.errors||{},this.initializeTemplates()}initializeTemplates(){H.onFormBuilderInit?.(this),this.templates={input:`
21
+ `,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),typeof document<"u"&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),typeof document<"u"&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){if(this.app&&this.app.router)return this.app.router.navigate(e,s);if(typeof window<"u"&&window.MOJO?.router)return window.MOJO.router.navigate(e,s);console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return typeof e=="string"&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class t extends K{constructor(i={}){super({...e,...i})}}return t.template=e.template,t.pageName=e.pageName,t.route=e.route,t}}class B{constructor(e={},t={}){this.endpoint=t.endpoint||this.constructor.endpoint||"",this.id=e.id||null,this.attributes={...e},this._=this.attributes,this.originalAttributes={...e},this.errors={},this.loading=!1,this.rest=z,this.options={idAttribute:"id",timestamps:!0,...t}}getContextValue(e){return this.get(e)}get(e){return!e.includes(".")&&!e.includes("|")&&this[e]!==void 0?typeof this[e]=="function"?this[e]():this[e]:x.getContextData(this.attributes,e)}set(e,t,s={}){const i=JSON.parse(JSON.stringify(this.attributes));let a=!1;if(e!=null){if(typeof e=="object"){for(const[r,n]of Object.entries(e))a=this._setNestedAttribute(r,n)||a;e.id!==void 0&&(this.id=e.id)}else e==="id"?(this.id=t,a=!0):a=this._setNestedAttribute(e,t);if(a&&!s.silent)if(this.emit("change",this),typeof e=="string")this.emit(`change:${e}`,t,this);else for(const[r,n]of Object.entries(e)){const o=this._getNestedValue(r);JSON.stringify(this._getNestedValue(r,i))!==JSON.stringify(o)&&this.emit(`change:${r}`,o,this)}}}_setNestedAttribute(e,t){if(!e.includes(".")){const l=this.attributes[e];return this.attributes[e]=t,this[e]=t,l!==t}const s=e.split("."),i=s[0];(!this.attributes[i]||typeof this.attributes[i]!="object")&&(this.attributes[i]={}),(!this[i]||typeof this[i]!="object")&&(this[i]={});const a=this._getNestedValue(e);let r=this.attributes[i],n=this[i];for(let l=1;l<s.length-1;l++){const c=s[l];(!r[c]||typeof r[c]!="object")&&(r[c]={}),(!n[c]||typeof n[c]!="object")&&(n[c]={}),r=r[c],n=n[c]}const o=s[s.length-1];return r[o]=t,n[o]=t,JSON.stringify(a)!==JSON.stringify(t)}_getNestedValue(e,t=this.attributes){if(!e.includes("."))return t[e];const s=e.split(".");let i=t;for(const a of s){if(i==null||typeof i!="object")return;i=i[a]}return i}getData(){return this.attributes}getId(){return this.id}async fetch(e={}){let t=e.url;if(!t){const r=e.id||this.getId();if(!r&&this.options.requiresId!==!1)throw new Error("Model: ID is required for fetching");t=this.buildUrl(r)}const s=JSON.stringify({url:t,params:e.params});if(e.debounceMs&&e.debounceMs>0)return this._debouncedFetch(s,e);if(this.currentRequest&&this.currentRequestKey!==s&&(console.info("Model: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===s)return console.info("Model: Duplicate request in progress, returning existing promise"),this.currentRequest;const i=Date.now();if(this.lastFetchTime&&i-this.lastFetchTime<100)return console.info("Model: Rate limited, skipping fetch"),this;this.loading=!0,this.errors={},this.lastFetchTime=i,this.currentRequestKey=s,this.abortController=new AbortController,this.currentRequest=this._performFetch(t,e,this.abortController);try{return await this.currentRequest}catch(r){if(r.name==="AbortError")return console.info("Model: Request was cancelled"),this;throw r}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _debouncedFetch(e,t){return this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((s,i)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const a=await this.fetch({...t,debounceMs:0});s(a)}catch(a){i(a)}},t.debounceMs)})}async _performFetch(e,t,s){try{t.graph&&(!t.params||!t.params.graph)&&(t.params||(t.params={}),t.params.graph=t.graph);const i=await this.rest.GET(e,t.params,{signal:s.signal});return i.success?i.data.status?(this.originalAttributes={...this.attributes},i.data.data&&this.set(i.data.data),this.errors={}):this.errors=i.data:this.errors=i.errors||{},i}catch(i){if(i.name==="AbortError")throw console.info("Model: Fetch was cancelled"),i;return this.errors={fetch:i.message},{success:!1,error:i.message,status:i.status||500}}finally{this.loading=!1}}async save(e,t={}){const s=!this.id,i=s?"POST":"PUT",a=s?this.buildUrl():this.buildUrl(this.id);this.loading=!0,this.errors={};try{const r=await this.rest[i](a,e,t.params);return r.success?r.data.status?(this.originalAttributes={...this.attributes},this.set(r.data.data),this.errors={}):this.errors=r.data:this.errors=r.errors||{},r}catch(r){return{success:!1,error:r.message,status:r.status||500}}finally{this.loading=!1}}async destroy(e={}){if(!this.id)return this.errors={destroy:"Cannot destroy model without ID"},{success:!1,error:"Cannot destroy model without ID",status:400};const t=this.buildUrl(this.id);this.loading=!0,this.errors={};try{const s=await this.rest.DELETE(t,e.params);return s.success?(this.attributes={},this.originalAttributes={},this.id=null,this.errors={}):this.errors=s.errors||{},s}catch(s){return this.errors={destroy:s.message},{success:!1,error:s.message,status:s.status||500}}finally{this.loading=!1}}isDirty(){return JSON.stringify(this.attributes)!==JSON.stringify(this.originalAttributes)}getChangedAttributes(){const e={};for(const[t,s]of Object.entries(this.attributes))this.originalAttributes[t]!==s&&(e[t]=s);return e}reset(){this.attributes={...this.originalAttributes},this._=this.attributes,this.errors={}}buildUrl(e=null){let t=this.endpoint;return e&&(t=t.endsWith("/")?`${t}${e}`:`${t}/${e}`),t}toJSON(){return{id:this.id,...this.attributes}}validate(){if(this.errors={},this.constructor.validations)for(const[e,t]of Object.entries(this.constructor.validations))this.validateField(e,t);return Object.keys(this.errors).length===0}validateField(e,t){const s=this.get(e),i=Array.isArray(t)?t:[t];for(const a of i)if(typeof a=="function"){const r=a(s,this);if(r!==!0){this.errors[e]=r||`${e} is invalid`;break}}else if(typeof a=="object"){if(a.required&&(s==null||s==="")){this.errors[e]=a.message||`${e} is required`;break}if(a.minLength&&s&&s.length<a.minLength){this.errors[e]=a.message||`${e} must be at least ${a.minLength} characters`;break}if(a.maxLength&&s&&s.length>a.maxLength){this.errors[e]=a.message||`${e} must be no more than ${a.maxLength} characters`;break}if(a.pattern&&s&&!a.pattern.test(s)){this.errors[e]=a.message||`${e} format is invalid`;break}}}static async find(e,t={}){const s=new this({},t);return await s.fetch({id:e,...t}),s}static create(e={},t={}){return new this(e,t)}cancel(){return this.currentRequest&&this.abortController?(console.info("Model: Manually cancelling active request"),this.abortController.abort(),!0):this.debouncedFetchTimeout?(clearTimeout(this.debouncedFetchTimeout),this.debouncedFetchTimeout=null,!0):!1}isFetching(){return!!this.currentRequest}async showError(e){await Dialog.alert(e,"Error",{size:"md",class:"text-danger"})}}Object.assign(B.prototype,le);class W{constructor(e={},t=null){if(Array.isArray(e)?(t=e,e=t||{}):t=t||e.data||[],this.ModelClass=e.ModelClass||B,this.models=[],this.loading=!1,this.errors={},this.meta={},this.rest=z,t&&this.add(t),this.params={start:0,size:e.size||10,...e.params},this.endpoint=e.endpoint||this.ModelClass.endpoint||"",!this.endpoint){let s=new this.ModelClass;this.endpoint=s.endpoint}this.restEnabled=!!this.endpoint,e.restEnabled!==void 0&&(this.restEnabled=e.restEnabled),this.options={parse:!0,reset:!0,preloaded:!1,...e}}getModelName(){return this.ModelClass.name}async fetch(e={}){const t=JSON.stringify({...this.params,...e});if(this.currentRequest&&this.currentRequestKey!==t&&(console.info("Collection: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===t)return console.info("Collection: Duplicate request in progress, returning existing promise"),this.currentRequest;const s=Date.now();if(this.options.rateLimiting&&this.lastFetchTime&&s-this.lastFetchTime<100)return console.info("Collection: Rate limited, skipping fetch"),{success:!0,message:"Rate limited, skipping fetch",data:{data:this.toJSON()}};if(!this.restEnabled)return console.info("Collection: REST disabled, skipping fetch"),{success:!0,message:"REST disabled, skipping fetch",data:{data:this.toJSON()}};if(this.options.preloaded&&this.models.length>0)return console.info("Collection: Using preloaded data, skipping fetch"),{success:!0,message:"Using preloaded data, skipping fetch",data:{data:this.toJSON()}};const a=this.buildUrl();this.loading=!0,this.errors={},this.lastFetchTime=s,this.currentRequestKey=t,this.abortController=new AbortController,this.currentRequest=this._performFetch(a,e,this.abortController);try{return await this.currentRequest}catch(r){return r.name==="AbortError"?(console.info("Collection: Request was cancelled"),{success:!1,error:"Request cancelled",status:0}):{success:!1,error:r.message,status:r.status||500}}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _performFetch(e,t,s){const i={...this.params,...t};console.log("Fetching collection data from",e,i);try{this.emit("fetch:start");const a=await this.rest.GET(e,i,{signal:s.signal});if(a.success&&a.data.status){const r=this.options.parse?this.parse(a):a.data;(this.options.reset||t.reset!==!1)&&this.reset(),this.add(r,{silent:t.silent}),this.errors={},this.emit("fetch:success")}else a.data&&a.data.error?(this.errors=a.data,this.emit("fetch:error",{message:a.data.error,error:a.data})):(this.errors=a.errors||{},this.emit("fetch:error",{error:a.errors}));return a}catch(a){return a.name==="AbortError"?(console.info("Collection: Fetch was cancelled"),{success:!1,error:"Request cancelled",status:0}):(this.errors={fetch:a.message},this.emit("fetch:error",{message:a.message,error:a}),{success:!1,error:a.message,status:a.status||500})}finally{this.loading=!1,this.emit("fetch:end")}}async updateParams(e,t=!1,s=0){return await this.setParams({...this.params,...e},t,s)}async setParams(e,t=!1,s=0){return this.params=e,t&&this.restEnabled?s>0?(this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((i,a)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const r=await this.fetch();i(r)}catch(r){a(r)}},s)})):this.fetch():Promise.resolve(this)}async fetchOne(e,t={}){if(!e)return console.warn("Collection: fetchOne requires an ID"),null;if(!this.restEnabled)return console.info("Collection: REST disabled, cannot fetch single item"),null;try{const s=new this.ModelClass({id:e},{endpoint:this.endpoint,collection:this}),i=await s.fetch(t);if(i.success){if(t.addToCollection===!0){const a=this.get(s.id);a?t.merge!==!1&&a.set(s.attributes):this.add(s,{silent:t.silent})}return s}else return console.warn("Collection: fetchOne failed -",i.error||"Unknown error"),null}catch(s){return console.error("Collection: fetchOne error -",s.message),null}}async download(e="json",t={}){if(!this.restEnabled)return console.warn("Collection: REST is not enabled, cannot download from remote."),{success:!1,message:"Remote downloads are not enabled for this collection."};const s=this.buildUrl(),i={...this.params};delete i.start,delete i.size,i.download_format=e;const a=`export-${this.getModelName().toLowerCase()}`,r=this._buildDateRangeSuffix(i),n=`${a}${r}.${e}`,l={json:"application/json",csv:"text/csv"}[e]||"*/*";return i.filename=n,this.rest.download(s,i,{...t,filename:n,headers:{Accept:l}})}_buildDateRangeSuffix(e={}){const t=e.dr_start,s=e.dr_end;if(!t&&!s)return"";const i=n=>n?String(n).replace(/[^\dA-Za-z_-]/g,"-"):"",a=[],r=e.dr_field||"daterange";return a.push(i(r)),t&&a.push(`from-${i(e.dr_start)}`),s&&a.push(`to-${i(e.dr_end)}`),`-${a.filter(Boolean).join("-")}`}parse(e){return e.data&&Array.isArray(e.data.data)?(this.meta={size:e.data.size||10,start:e.data.start||0,count:e.data.count||0,status:e.data.status,graph:e.data.graph,...e.meta},e.data.data):Array.isArray(e.data)?e.data:Array.isArray(e)?e:[e]}add(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r;a instanceof this.ModelClass?r=a:r=new this.ModelClass(a,{endpoint:this.endpoint,collection:this});const n=this.models.findIndex(o=>o.id===r.id);n!==-1?t.merge!==!1&&this.models[n].set(r.attributes):(this.models.push(r),i.push(r))}return!t.silent&&i.length>0&&(this.emit("add",{models:i,collection:this}),this.emit("update",{collection:this})),i}remove(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r=-1;if(typeof a=="string"||typeof a=="number"?r=this.models.findIndex(n=>n.id==a):r=this.models.indexOf(a),r!==-1){const n=this.models.splice(r,1)[0];i.push(n)}}return!t.silent&&i.length>0&&(this.emit("remove",{models:i,collection:this}),this.emit("update",{collection:this})),i}reset(e=null,t={}){const s=[...this.models];return this.models=[],e&&this.add(e,{silent:!0,...t}),t.silent||this.emit("reset",{collection:this,previousModels:s}),this}get(e){return this.models.find(t=>t.id==e)}at(e){return this.models[e]}length(){return this.models.length}isEmpty(){return this.models.length===0}where(e){return typeof e=="function"?this.models.filter(e):typeof e=="object"?this.models.filter(t=>Object.entries(e).every(([s,i])=>t.get(s)===i)):[]}findWhere(e){const t=this.where(e);return t.length>0?t[0]:void 0}forEach(e,t){if(typeof e!="function")throw new TypeError("Callback must be a function");return this.models.forEach((s,i)=>{e.call(t,s,i,this)}),this}sort(e,t={}){if(typeof e=="string"){const s=e;e=(i,a)=>{const r=i.get(s),n=a.get(s);return r<n?-1:r>n?1:0}}return this.models.sort(e),t.silent||this.trigger("sort",{collection:this}),this}toJSON(){return this.models.map(e=>e.toJSON())}cancel(){return this.currentRequest&&this.abortController?(console.info("Collection: Manually cancelling active request"),this.abortController.abort(),!0):!1}isFetching(){return!!this.currentRequest}buildUrl(){return this.endpoint}*[Symbol.iterator](){for(const e of this.models)yield e}static fromArray(e,t=[],s={}){const i=new this(e,s);return i.add(t,{silent:!0}),i}}Object.assign(W.prototype,le);class H{static _plugins=[];static _renderers=new Map;static register(e){return!e||typeof e!="object"?(console.warn("[FormPlugins] register called with invalid plugin:",e),()=>{}):!e.id||typeof e.id!="string"?(console.warn('[FormPlugins] plugin must have a unique string "id"',e),()=>{}):(this.unregister(e.id),e.fieldTypes&&typeof e.fieldTypes=="object"&&Object.entries(e.fieldTypes).forEach(([t,s])=>{typeof s=="function"?this._renderers.set(t,{renderer:s,pluginId:e.id}):console.warn(`[FormPlugins] renderer for type "${t}" is not a function`)}),this._plugins.push(e),()=>this.unregister(e.id))}static unregister(e){if(e){this._plugins=this._plugins.filter(t=>t.id!==e);for(const[t,s]of this._renderers.entries())s?.pluginId===e&&this._renderers.delete(t)}}static getRenderer(e){return this._renderers.get(e)?.renderer||null}static hasRenderer(e){return this._renderers.has(e)}static getPlugins(){return[...this._plugins]}static _invoke(e,t,...s){const i=e?.[t];if(typeof i=="function")try{return i.apply(e,s)}catch(a){console.error(`[FormPlugins] ${t} error from plugin "${e.id}":`,a)}}static onFormBuilderInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormBuilderInit",e))}static onFormViewInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormViewInit",e))}static onFormViewAfterRender(e){this._plugins.forEach(t=>this._invoke(t,"onAfterRender",e))}static onFieldInit(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldInit",e,t,s))}static onFieldChange(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldChange",e,t,s))}}class Q{constructor(e={}){this.fields=e.fields||[],this.structureOnly=e.structureOnly||!1,this.fields.forEach(t=>{t.cols&&!t.columns?(t.columns=t.cols,delete t.cols):t.columns||(t.columns=12),t.type==="group"&&t.fields&&t.fields.forEach(s=>{s.cols&&!s.columns?(s.columns=s.cols,delete s.cols):s.columns||(s.columns=12)})}),this.options={formClass:"needs-validation",formMethod:"POST",formAction:"",groupClass:"row mb-3",fieldWrapper:"",labelClass:"form-label",inputClass:"form-control",errorClass:"invalid-feedback",helpClass:"form-text",submitButton:!1,resetButton:!1,...e.options},this.buttons=e.buttons||[],this.data=e.data||{},this.errors=e.errors||{},this.initializeTemplates()}initializeTemplates(){H.onFormBuilderInit?.(this),this.templates={input:`
22
22
  <div class="mojo-form-control">
23
23
  {{#label}}
24
24
  <label for="{{fieldId}}" class="{{labelClass}}">
@@ -192,7 +192,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
192
192
  {{#error}}<div class="{{errorClass}}">{{error}}</div>{{/error}}
193
193
  </div>
194
194
  `,image:`
195
- <div class="mojo-form-control">
195
+ <div class="mojo-form-control" style="display: flex; flex-direction: column; align-items: center;">
196
196
  {{#label}}
197
197
  <label for="{{fieldId}}" class="{{labelClass}}">
198
198
  {{label}}{{#required}}<span class="text-danger">*</span>{{/required}}
@@ -366,14 +366,14 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
366
366
  </div>
367
367
  </div>
368
368
  </div>
369
- `}buildFieldHTML(e){const{type:t,columns:s,class:i=""}=e;let a="";const r=H&&typeof H.getRenderer=="function"?H.getRenderer(t):null;if(typeof r=="function")try{const o=r(this,e);o!=null&&(a=String(o))}catch(o){console.error("FormPlugins custom renderer error:",o)}if(!a)switch(t){case"text":a=this.renderTextField(e);break;case"email":a=this.renderEmailField(e);break;case"password":a=this.renderPasswordField(e);break;case"number":a=this.renderNumberField(e);break;case"tel":a=this.renderTelField(e);break;case"url":a=this.renderUrlField(e);break;case"search":a=this.renderSearchField(e);break;case"hex":a=this.renderHexField(e);break;case"textarea":a=this.renderTextareaField(e);break;case"htmlpreview":a=this.renderHtmlPreviewField(e);break;case"json":a=this.renderJsonField(e);break;case"select":a=this.renderSelectField(e);break;case"multiselect":a=this.renderMultiSelectField(e);break;case"checkbox":a=this.renderCheckboxField(e);break;case"toggle":case"switch":a=this.renderSwitchField(e);break;case"radio":a=this.renderRadioField(e);break;case"date":a=this.renderDateField(e);break;case"datetime":a=this.renderDateTimeField(e);break;case"time":a=this.renderTimeField(e);break;case"file":a=this.renderFileField(e);break;case"image":a=this.renderImageField(e);break;case"color":a=this.renderColorField(e);break;case"range":a=this.renderRangeField(e);break;case"hidden":a=this.renderHiddenField(e);break;case"button":a=this.renderButton(e);break;case"divider":a=this.renderDivider(e);break;case"html":a=this.renderHtmlField(e);break;case"heading":case"header":a=this.renderHeaderField(e);break;case"tag":case"tags":a=this.renderTagField(e);break;case"collection":a=this.renderCollectionField(e);break;case"collectionmultiselect":case"collection-multiselect":a=this.renderCollectionMultiSelectField(e);break;case"datepicker":a=this.renderDatePickerField(e);break;case"daterange":a=this.renderDateRangeField(e);break;case"checklistdropdown":a=this.renderChecklistDropdownField(e);break;case"buttongroup":a=this.renderButtonGroupField(e);break;case"combo":case"combobox":case"autocomplete":a=this.renderComboField(e);break;case"tabset":a=this.renderTabsetField(e);break;default:console.warn(`Unknown field type: ${t}`),a=this.renderTextField(e)}let n;return this.isAutoSizingField(e)?n=`col ${i}`.trim():n=`col-${s} ${i}`.trim(),`<div class="${n}">${a}</div>`}getFieldId(e){return e?`field_${e.replace(/[.\s\[\]]/g,"_")}`:`field_${Math.random().toString(36).substr(2,9)}`}renderTextField(e){return this.renderInputField(e,"text")}renderEmailField(e){return this.renderInputField(e,"email")}renderPasswordField(e){const t=e.passwordUsage||"current",s=t==="new"||t==="new-password"?"new-password":"current-password",i={...e.attributes||{},autocomplete:e.attributes&&e.attributes.autocomplete||s};return this.renderInputField({...e,showToggle:e.showToggle!==!1,attributes:i},"password")}renderNumberField(e){const{min:t,max:s,step:i=1,...a}=e,r=[];return t!==void 0&&r.push(`min="${t}"`),s!==void 0&&r.push(`max="${s}"`),i!==void 0&&r.push(`step="${i}"`),this.renderInputField({...a,attributes:{...a.attributes,...r.reduce((n,o)=>{const[l,c]=o.split("=");return n[l]=c.replace(/"/g,""),n},{})}},"number")}renderTelField(e){return this.renderInputField(e,"tel")}renderUrlField(e){return this.renderInputField(e,"url")}renderSearchField(e){const t={...e,attributes:{"data-filter":"live-search","data-change-action":"filter-search","data-filter-debounce":e.debounce||"300",...e.attributes}};return this.renderInputField(t,"search")}renderHexField(e){const{hexType:t="color",allowPrefix:s=!0,minLength:i,maxLength:a,...r}=e;let n,o,l,c,d;switch(t){case"color":n=s?"^#?[0-9A-Fa-f]{6}$":"^[0-9A-Fa-f]{6}$",o=6,l=s?7:6,c=s?"#FF0000":"FF0000",d=d||"Enter a valid hex color (e.g., "+c+")";break;case"color-short":n=s?"^#?[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$":"^[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$",o=s?4:3,l=s?7:6,c=s?"#F00 or #FF0000":"F00 or FF0000",d=d||"Enter a valid hex color (3 or 6 digits)";break;case"string":n="^[0-9A-Fa-f]+$",o=i||1,l=a||64,c="ABCDEF123456",d=d||"Only hexadecimal characters (0-9, A-F) allowed";break;default:n=s?"^#?[0-9A-Fa-f]+$":"^[0-9A-Fa-f]+$",o=i||1,l=a||64,c=s?"#ABCDEF or ABCDEF":"ABCDEF",d=d||"Enter hexadecimal characters only"}const h={...r,pattern:n,minLength:o,maxLength:l,placeholder:r.placeholder||c,help:r.help||d,attributes:{"data-hex-type":t,"data-allow-prefix":s,style:"text-transform: uppercase;",...r.attributes}};return this.renderInputField(h,"text")}renderInputField(e,t="text"){const{name:s,label:i,value:a="",placeholder:r="",required:n=!1,disabled:o=!1,readonly:l=!1,class:c="",attributes:d={},help:h=e.helpText||e.help||""}=e,f=`${this.options.inputClass} ${c}`.trim(),m=this.errors[s],p=this.getFieldValue(s)??a,g=Object.entries(d).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),y=this.getFieldId(s),b={labelClass:this.options.labelClass,inputClass:f,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:y,name:s,type:t,fieldValue:this.escapeHtml(p),label:i?this.escapeHtml(i):null,placeholder:r?this.escapeHtml(r):null,help:h?this.escapeHtml(h):null,error:m?this.escapeHtml(m):null,required:n,disabled:o,readonly:l,attrs:g,showCopy:!!e.showCopy};if(t==="password"&&(e.showToggle||e.strengthMeter||e.capsLockWarning)){const w={...b,showToggle:!!e.showToggle,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return E.render(this.templates.password,w)}if(t==="password"){const w={...b,showToggle:e.showToggle!==!1,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return E.render(this.templates.password,w)}return E.render(this.templates.input,b)}renderTextareaField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:o=!1,rows:l=3,cols:c,class:d="",attributes:h={},help:f=e.helpText||e.help||""}=e,m=`${this.options.inputClass} ${d}`.trim(),p=this.errors[t],g=this.getFieldValue(t)??i,y=Object.entries(h).map(([C,$])=>`${C}="${this.escapeHtml($)}"`).join(" "),b=this.getFieldId(t),w={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:b,name:t,fieldValue:g,label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:f?this.escapeHtml(f):null,error:p?this.escapeHtml(p):null,rows:l||3,required:r,disabled:n,readonly:o,showCopy:!!e.showCopy,attrs:y};return E.render(this.templates.textarea,w)}renderHtmlPreviewField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:o=!1,rows:l=5,class:c="",attributes:d={},help:h=e.helpText||e.help||""}=e,f=`${this.options.inputClass} ${c}`.trim(),m=this.errors[t],p=this.getFieldValue(t)??i,g=Object.entries(d).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),y=this.getFieldId(t),b={labelClass:this.options.labelClass,inputClass:f,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:y,name:t,fieldValue:p,label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:h?this.escapeHtml(h):null,error:m?this.escapeHtml(m):null,rows:l||5,required:r,disabled:n,readonly:o,attrs:g,showCopy:!!e.showCopy};return E.render(this.templates.htmlpreview,b)}renderJsonField(e){const{name:t,label:s,placeholder:i="",required:a=!1,disabled:r=!1,readonly:n=!1,rows:o=3,class:l="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=this.getFieldValue(e.name)??e.value??{};let f=h;if(typeof h=="object"&&h!==null)try{f=JSON.stringify(h,null,2)}catch{f="{}"}else typeof h!="string"&&(f=String(h));const m=`${this.options.inputClass} ${l}`.trim(),p=this.errors[t],g=this.getFieldId(t),y=Object.entries({...c,"data-field-type":"json"}).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),b={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,fieldValue:f,label:s?this.escapeHtml(s):null,placeholder:i?this.escapeHtml(i):null,help:d?this.escapeHtml(d):null,error:p?this.escapeHtml(p):null,rows:o||3,required:a,disabled:r,readonly:n,attrs:y};return E.render(this.templates.textarea,b)}renderSelectField(e){const{name:t,label:s,options:i=[],value:a="",required:r=!1,disabled:n=!1,multiple:o=!1,searchable:l=!1,class:c="",attributes:d={},help:h=e.helpText||e.help||"",start:f=e.start,end:m=e.end,step:p=e.step,format:g=e.format,prefix:y=e.prefix,suffix:b=e.suffix}=e,w=`form-select ${c}`.trim(),C=this.errors[t],S=this.getFieldValue(t)??a,M=Object.entries(d).map(([I,N])=>`${I}="${this.escapeHtml(N)}"`).join(" "),L=this.getFieldId(t);let A=[...i];if(f!==void 0&&m!==void 0){const I=p!==void 0?p:1,N=this.generateSelectOptions(f,m,I,{format:g,prefix:y,suffix:b});A=[...A,...N]}let U="";Array.isArray(A)&&(U=A.map(I=>{if(typeof I=="string"){const N=I===S?"selected":"";return`<option value="${this.escapeHtml(I)}" ${N}>${this.escapeHtml(I)}</option>`}else if(I&&typeof I=="object"){const N=I.value==S?"selected":"";return`<option value="${this.escapeHtml(I.value)}" ${N}>${this.escapeHtml(I.label||I.text||I.value)}</option>`}return""}).join(""));const pe=l?`
369
+ `}buildFieldHTML(e){const{type:t,columns:s,class:i=""}=e;let a="";const r=H&&typeof H.getRenderer=="function"?H.getRenderer(t):null;if(typeof r=="function")try{const o=r(this,e);o!=null&&(a=String(o))}catch(o){console.error("FormPlugins custom renderer error:",o)}if(!a)switch(t){case"text":a=this.renderTextField(e);break;case"email":a=this.renderEmailField(e);break;case"password":a=this.renderPasswordField(e);break;case"number":a=this.renderNumberField(e);break;case"tel":a=this.renderTelField(e);break;case"url":a=this.renderUrlField(e);break;case"search":a=this.renderSearchField(e);break;case"hex":a=this.renderHexField(e);break;case"textarea":a=this.renderTextareaField(e);break;case"htmlpreview":a=this.renderHtmlPreviewField(e);break;case"json":a=this.renderJsonField(e);break;case"select":a=this.renderSelectField(e);break;case"multiselect":a=this.renderMultiSelectField(e);break;case"checkbox":a=this.renderCheckboxField(e);break;case"toggle":case"switch":a=this.renderSwitchField(e);break;case"radio":a=this.renderRadioField(e);break;case"date":a=this.renderDateField(e);break;case"datetime":a=this.renderDateTimeField(e);break;case"time":a=this.renderTimeField(e);break;case"file":a=this.renderFileField(e);break;case"image":a=this.renderImageField(e);break;case"color":a=this.renderColorField(e);break;case"range":a=this.renderRangeField(e);break;case"hidden":a=this.renderHiddenField(e);break;case"button":a=this.renderButton(e);break;case"divider":a=this.renderDivider(e);break;case"html":a=this.renderHtmlField(e);break;case"heading":case"header":a=this.renderHeaderField(e);break;case"tag":case"tags":a=this.renderTagField(e);break;case"collection":a=this.renderCollectionField(e);break;case"collectionmultiselect":case"collection-multiselect":a=this.renderCollectionMultiSelectField(e);break;case"datepicker":a=this.renderDatePickerField(e);break;case"daterange":a=this.renderDateRangeField(e);break;case"checklistdropdown":a=this.renderChecklistDropdownField(e);break;case"buttongroup":a=this.renderButtonGroupField(e);break;case"combo":case"combobox":case"autocomplete":a=this.renderComboField(e);break;case"tabset":a=this.renderTabsetField(e);break;default:console.warn(`Unknown field type: ${t}`),a=this.renderTextField(e)}let n;return this.isAutoSizingField(e)?n=`col ${i}`.trim():n=`col-${s} ${i}`.trim(),`<div class="${n}">${a}</div>`}getFieldId(e){return e?`field_${e.replace(/[.\s\[\]]/g,"_")}`:`field_${Math.random().toString(36).substr(2,9)}`}renderTextField(e){return this.renderInputField(e,"text")}renderEmailField(e){return this.renderInputField(e,"email")}renderPasswordField(e){const t=e.passwordUsage||"current",s=t==="new"||t==="new-password"?"new-password":"current-password",i={...e.attributes||{},autocomplete:e.attributes&&e.attributes.autocomplete||s};return this.renderInputField({...e,showToggle:e.showToggle!==!1,attributes:i},"password")}renderNumberField(e){const{min:t,max:s,step:i=1,...a}=e,r=[];return t!==void 0&&r.push(`min="${t}"`),s!==void 0&&r.push(`max="${s}"`),i!==void 0&&r.push(`step="${i}"`),this.renderInputField({...a,attributes:{...a.attributes,...r.reduce((n,o)=>{const[l,c]=o.split("=");return n[l]=c.replace(/"/g,""),n},{})}},"number")}renderTelField(e){return this.renderInputField(e,"tel")}renderUrlField(e){return this.renderInputField(e,"url")}renderSearchField(e){const t={...e,attributes:{"data-filter":"live-search","data-change-action":"filter-search","data-filter-debounce":e.debounce||"300",...e.attributes}};return this.renderInputField(t,"search")}renderHexField(e){const{hexType:t="color",allowPrefix:s=!0,minLength:i,maxLength:a,...r}=e;let n,o,l,c,d;switch(t){case"color":n=s?"^#?[0-9A-Fa-f]{6}$":"^[0-9A-Fa-f]{6}$",o=6,l=s?7:6,c=s?"#FF0000":"FF0000",d=d||"Enter a valid hex color (e.g., "+c+")";break;case"color-short":n=s?"^#?[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$":"^[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$",o=s?4:3,l=s?7:6,c=s?"#F00 or #FF0000":"F00 or FF0000",d=d||"Enter a valid hex color (3 or 6 digits)";break;case"string":n="^[0-9A-Fa-f]+$",o=i||1,l=a||64,c="ABCDEF123456",d=d||"Only hexadecimal characters (0-9, A-F) allowed";break;default:n=s?"^#?[0-9A-Fa-f]+$":"^[0-9A-Fa-f]+$",o=i||1,l=a||64,c=s?"#ABCDEF or ABCDEF":"ABCDEF",d=d||"Enter hexadecimal characters only"}const h={...r,pattern:n,minLength:o,maxLength:l,placeholder:r.placeholder||c,help:r.help||d,attributes:{"data-hex-type":t,"data-allow-prefix":s,style:"text-transform: uppercase;",...r.attributes}};return this.renderInputField(h,"text")}renderInputField(e,t="text"){const{name:s,label:i,value:a="",placeholder:r="",required:n=!1,disabled:o=!1,readonly:l=!1,class:c="",attributes:d={},help:h=e.helpText||e.help||""}=e,m=`${this.options.inputClass} ${c}`.trim(),p=this.errors[s],f=this.getFieldValue(s)??a,g=Object.entries(d).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),y=this.getFieldId(s),b={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:y,name:s,type:t,fieldValue:this.escapeHtml(f),label:i?this.escapeHtml(i):null,placeholder:r?this.escapeHtml(r):null,help:h?this.escapeHtml(h):null,error:p?this.escapeHtml(p):null,required:n,disabled:o,readonly:l,attrs:g,showCopy:!!e.showCopy};if(t==="password"&&(e.showToggle||e.strengthMeter||e.capsLockWarning)){const w={...b,showToggle:!!e.showToggle,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return E.render(this.templates.password,w)}if(t==="password"){const w={...b,showToggle:e.showToggle!==!1,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return E.render(this.templates.password,w)}return E.render(this.templates.input,b)}renderTextareaField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:o=!1,rows:l=3,cols:c,class:d="",attributes:h={},help:m=e.helpText||e.help||""}=e,p=`${this.options.inputClass} ${d}`.trim(),f=this.errors[t],g=this.getFieldValue(t)??i,y=Object.entries(h).map(([C,$])=>`${C}="${this.escapeHtml($)}"`).join(" "),b=this.getFieldId(t),w={labelClass:this.options.labelClass,inputClass:p,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:b,name:t,fieldValue:g,label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:m?this.escapeHtml(m):null,error:f?this.escapeHtml(f):null,rows:l||3,required:r,disabled:n,readonly:o,showCopy:!!e.showCopy,attrs:y};return E.render(this.templates.textarea,w)}renderHtmlPreviewField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:o=!1,rows:l=5,class:c="",attributes:d={},help:h=e.helpText||e.help||""}=e,m=`${this.options.inputClass} ${c}`.trim(),p=this.errors[t],f=this.getFieldValue(t)??i,g=Object.entries(d).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),y=this.getFieldId(t),b={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:y,name:t,fieldValue:f,label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:h?this.escapeHtml(h):null,error:p?this.escapeHtml(p):null,rows:l||5,required:r,disabled:n,readonly:o,attrs:g,showCopy:!!e.showCopy};return E.render(this.templates.htmlpreview,b)}renderJsonField(e){const{name:t,label:s,placeholder:i="",required:a=!1,disabled:r=!1,readonly:n=!1,rows:o=3,class:l="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=this.getFieldValue(e.name)??e.value??{};let m=h;if(typeof h=="object"&&h!==null)try{m=JSON.stringify(h,null,2)}catch{m="{}"}else typeof h!="string"&&(m=String(h));const p=`${this.options.inputClass} ${l}`.trim(),f=this.errors[t],g=this.getFieldId(t),y=Object.entries({...c,"data-field-type":"json"}).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),b={labelClass:this.options.labelClass,inputClass:p,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,fieldValue:m,label:s?this.escapeHtml(s):null,placeholder:i?this.escapeHtml(i):null,help:d?this.escapeHtml(d):null,error:f?this.escapeHtml(f):null,rows:o||3,required:a,disabled:r,readonly:n,attrs:y};return E.render(this.templates.textarea,b)}renderSelectField(e){const{name:t,label:s,options:i=[],value:a="",required:r=!1,disabled:n=!1,multiple:o=!1,searchable:l=!1,class:c="",attributes:d={},help:h=e.helpText||e.help||"",start:m=e.start,end:p=e.end,step:f=e.step,format:g=e.format,prefix:y=e.prefix,suffix:b=e.suffix}=e,w=`form-select ${c}`.trim(),C=this.errors[t],S=this.getFieldValue(t)??a,M=Object.entries(d).map(([k,N])=>`${k}="${this.escapeHtml(N)}"`).join(" "),_=this.getFieldId(t);let A=[...i];if(m!==void 0&&p!==void 0){const k=f!==void 0?f:1,N=this.generateSelectOptions(m,p,k,{format:g,prefix:y,suffix:b});A=[...A,...N]}let G="";Array.isArray(A)&&(G=A.map(k=>{if(typeof k=="string"){const N=k===S?"selected":"";return`<option value="${this.escapeHtml(k)}" ${N}>${this.escapeHtml(k)}</option>`}else if(k&&typeof k=="object"){const N=k.value==S?"selected":"";return`<option value="${this.escapeHtml(k.value)}" ${N}>${this.escapeHtml(k.label||k.text||k.value)}</option>`}return""}).join(""));const Ce=l?`
370
370
  <input type="text"
371
371
  class="form-control form-control-sm mb-2"
372
372
  placeholder="Search options..."
373
373
  data-filter="live-search"
374
374
  data-change-action="filter-select-options"
375
- data-target="${L}">
376
- `:"",ge={labelClass:this.options.labelClass,inputClass:w,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:L,name:t,label:s?this.escapeHtml(s):null,help:h?this.escapeHtml(h):null,error:C?this.escapeHtml(C):null,searchInput:l?pe:null,optionsHTML:U,required:r,disabled:n,multiple:o,attrs:M};return E.render(this.templates.select,ge)}renderMultiSelectField(e){const{name:t,label:s,options:i=[],value:a=[],required:r=!1,disabled:n=!1,maxHeight:o=300,help:l=e.helpText||e.help||""}=e,c=e.placeholder||e.placeHolder||"Select...";this.getFieldId(t);const d=this.errors[t],h=e.value??this.getFieldValue(t)??a;return`
375
+ data-target="${_}">
376
+ `:"",xe={labelClass:this.options.labelClass,inputClass:w,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:_,name:t,label:s?this.escapeHtml(s):null,help:h?this.escapeHtml(h):null,error:C?this.escapeHtml(C):null,searchInput:l?Ce:null,optionsHTML:G,required:r,disabled:n,multiple:o,attrs:M};return E.render(this.templates.select,xe)}renderMultiSelectField(e){const{name:t,label:s,options:i=[],value:a=[],required:r=!1,disabled:n=!1,maxHeight:o=300,help:l=e.helpText||e.help||""}=e,c=e.placeholder||e.placeHolder||"Select...";this.getFieldId(t);const d=this.errors[t],h=e.value??this.getFieldValue(t)??a;return`
377
377
  <div class="multiselect-placeholder"
378
378
  data-field-name="${t}"
379
379
  data-field-type="multiselect"
@@ -383,11 +383,11 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
383
383
  multiple
384
384
  ${n?"disabled":""}
385
385
  ${r?"required":""}>
386
- ${i.map(f=>{const m=typeof f=="string"?f:f.value,p=typeof f=="string"?f:f.label||f.value,g=Array.isArray(h)&&h.includes(m)?"selected":"";return`<option value="${this.escapeHtml(m)}" ${g}>${this.escapeHtml(p)}</option>`}).join("")}
386
+ ${i.map(m=>{const p=typeof m=="string"?m:m.value,f=typeof m=="string"?m:m.label||m.value,g=Array.isArray(h)&&h.includes(p)?"selected":"";return`<option value="${this.escapeHtml(p)}" ${g}>${this.escapeHtml(f)}</option>`}).join("")}
387
387
  </select>
388
388
  <small class="form-text text-muted">This will be enhanced with MultiSelectDropdown component</small>
389
389
  </div>
390
- `}renderCheckboxField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,class:n="",attributes:o={},help:l=e.helpText||e.help||""}=e,c=this.errors[t],d=this.getFieldValue(t)??i,h=d===!0||d==="true"||d==="1",f=Object.entries(o).map(([g,y])=>`${g}="${this.escapeHtml(y)}"`).join(" "),m=this.getFieldId(t),p={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:m,name:t,label:this.escapeHtml(s),help:l?this.escapeHtml(l):null,error:c?this.escapeHtml(c):null,value:this.escapeHtml(i),fieldClass:n,checked:h,required:a,disabled:r,attrs:f};return E.render(this.templates.checkbox,p)}renderSwitchField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,size:n="md",class:o="",attributes:l={},help:c=e.helpText||e.help||""}=e,d=this.errors[t],h=this.getFieldValue(t)??i,f=h===!0||h==="true"||h==="1",m=Object.entries(l).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),p=this.getFieldId(t),g=n!=="md"?`form-switch-${n}`:"",y={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:p,name:t,label:this.escapeHtml(s),help:c?this.escapeHtml(c):null,error:d?this.escapeHtml(d):null,value:this.escapeHtml(i),sizeClass:g,fieldClass:o,checked:f,required:a,disabled:r,attrs:m};return E.render(this.templates.switch,y)}renderRadioField(e){const{name:t,label:s,options:i=[],value:a="",disabled:r=!1,inline:n=!1,class:o="",attributes:l={},help:c=e.helpText||e.help||""}=e,d=this.errors[t],h=this.getFieldValue(t)??a,f=Object.entries(l).map(([p,g])=>`${p}="${this.escapeHtml(g)}"`).join(" ");let m="";return Array.isArray(i)&&(m=i.map((p,g)=>{const y=`${t}_${g}`,b=typeof p=="string"?p:p.value,w=typeof p=="string"?p:p.label||p.text||p.value,C=b===h?"checked":"";return`
390
+ `}renderCheckboxField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,class:n="",attributes:o={},help:l=e.helpText||e.help||""}=e,c=this.errors[t],d=this.getFieldValue(t)??i,h=d===!0||d==="true"||d==="1",m=Object.entries(o).map(([g,y])=>`${g}="${this.escapeHtml(y)}"`).join(" "),p=this.getFieldId(t),f={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:p,name:t,label:this.escapeHtml(s),help:l?this.escapeHtml(l):null,error:c?this.escapeHtml(c):null,value:this.escapeHtml(i),fieldClass:n,checked:h,required:a,disabled:r,attrs:m};return E.render(this.templates.checkbox,f)}renderSwitchField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,size:n="md",class:o="",attributes:l={},help:c=e.helpText||e.help||""}=e,d=this.errors[t],h=this.getFieldValue(t)??i,m=h===!0||h==="true"||h==="1",p=Object.entries(l).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),f=this.getFieldId(t),g=n!=="md"?`form-switch-${n}`:"",y={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:f,name:t,label:this.escapeHtml(s),help:c?this.escapeHtml(c):null,error:d?this.escapeHtml(d):null,value:this.escapeHtml(i),sizeClass:g,fieldClass:o,checked:m,required:a,disabled:r,attrs:p};return E.render(this.templates.switch,y)}renderRadioField(e){const{name:t,label:s,options:i=[],value:a="",disabled:r=!1,inline:n=!1,class:o="",attributes:l={},help:c=e.helpText||e.help||""}=e,d=this.errors[t],h=this.getFieldValue(t)??a,m=Object.entries(l).map(([f,g])=>`${f}="${this.escapeHtml(g)}"`).join(" ");let p="";return Array.isArray(i)&&(p=i.map((f,g)=>{const y=`${t}_${g}`,b=typeof f=="string"?f:f.value,w=typeof f=="string"?f:f.label||f.text||f.value,C=b===h?"checked":"";return`
391
391
  <div class="form-check ${n?"form-check-inline":""}">
392
392
  <input
393
393
  type="radio"
@@ -398,7 +398,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
398
398
  ${C}
399
399
  ${r?"disabled":""}
400
400
 
401
- ${f}
401
+ ${m}
402
402
  >
403
403
  <label class="form-check-label" for="${y}">
404
404
  ${this.escapeHtml(w)}
@@ -409,13 +409,13 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
409
409
  ${s?`<fieldset>
410
410
  <legend class="${this.options.labelClass}">${this.escapeHtml(s)}</legend>
411
411
  <div class="${o}">
412
- ${m}
412
+ ${p}
413
413
  </div>
414
- </fieldset>`:`<div class="${o}">${m}</div>`}
414
+ </fieldset>`:`<div class="${o}">${p}</div>`}
415
415
  ${c?`<div class="${this.options.helpClass}">${this.escapeHtml(c)}</div>`:""}
416
416
  ${d?`<div class="${this.options.errorClass}">${this.escapeHtml(d)}</div>`:""}
417
417
  </div>
418
- `}renderDateField(e){return this.renderInputField(e,"date")}renderDateTimeField(e){return this.renderInputField(e,"datetime-local")}renderTimeField(e){return this.renderInputField(e,"time")}renderFileField(e){const{name:t,label:s,required:i=!1,disabled:a=!1,multiple:r=!1,accept:n="*/*",class:o="",attributes:l={},help:c=e.helpText||e.help||""}=e,d=`${this.options.inputClass} ${o}`.trim(),h=this.errors[t],f=Object.entries(l).map(([g,y])=>`${g}="${this.escapeHtml(y)}"`).join(" "),m=this.getFieldId(t),p={labelClass:this.options.labelClass,inputClass:d,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:m,name:t,label:s?this.escapeHtml(s):null,help:c?this.escapeHtml(c):null,error:h?this.escapeHtml(h):null,accept:n,required:i,disabled:a,multiple:r,attrs:f};return E.render(this.templates.file,p)}renderImageField(e){const{name:t,label:s,required:i=!1,disabled:a=!1,accept:r="image/*",class:n="",attributes:o={},help:l=e.helpText||e.help||"",size:c="md",allowDrop:d=!0,placeholder:h="Drop image here or click to upload"}=e,f=`${this.options.inputClass} ${n}`.trim(),m=this.errors[t],p=this.getFieldId(t),g=`${p}_dropzone`,y=`${p}_preview`,b={xs:{width:48,height:48,containerClass:"image-field-xs"},sm:{width:96,height:96,containerClass:"image-field-sm"},md:{width:150,height:150,containerClass:"image-field-md"},lg:{width:200,height:200,containerClass:"image-field-lg"},xl:{width:300,height:300,containerClass:"image-field-xl"}},w=b[c]||b.md,C=Object.entries(o).map(([L,A])=>`${L}="${this.escapeHtml(A)}"`).join(" "),$=this.getFieldValue(t),S=this.extractImageUrl($,c),M={labelClass:this.options.labelClass,inputClass:f,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:p,name:t,label:s?this.escapeHtml(s):null,help:l?this.escapeHtml(l):null,error:m?this.escapeHtml(m):null,dropZoneId:g,previewId:y,containerClass:w.containerClass,width:w.width,height:w.height,accept:r,imageUrl:S,placeholderText:a?"No image":this.escapeHtml(h),cursor:a?"default":"pointer",allowDrop:d,showRemove:!a,required:i,disabled:a,attrs:C};return E.render(this.templates.image,M)}extractImageUrl(e,t="md"){if(!e)return null;if(typeof e=="string")return e;if(typeof e=="object"&&e.url){if(e.renditions){const s={xs:["thumbnail_sm","thumbnail","square_sm"],sm:["thumbnail","thumbnail_sm","square_sm"],md:["thumbnail_md","thumbnail","thumbnail_lg"],lg:["thumbnail_lg","thumbnail_md","thumbnail"],xl:["original","thumbnail_lg"]},i=s[t]||s.md;for(const a of i)if(e.renditions[a]&&e.renditions[a].url)return e.renditions[a].url}return e.url}return null}renderColorField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:o=!1,class:l="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=`${this.options.inputClass} ${l}`.trim(),f=this.errors[t],m=this.getFieldValue(t)??i,p=Object.entries(c).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),g=this.getFieldId(t),y={labelClass:this.options.labelClass,inputClass:h,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,fieldValue:this.escapeHtml(m),label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:d?this.escapeHtml(d):null,error:f?this.escapeHtml(f):null,required:r,disabled:n,readonly:o,attrs:p};return E.render(this.templates.color,y)}renderRangeField(e){const{name:t,label:s,min:i=0,max:a=100,step:r=1,value:n=i,disabled:o=!1,class:l="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=`${this.options.inputClass} ${l}`.trim(),f=this.errors[t],m=this.getFieldValue(t)??n,p=Object.entries(c).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),g=this.getFieldId(t),y={labelClass:this.options.labelClass,inputClass:h,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,label:s?this.escapeHtml(s):null,help:d?this.escapeHtml(d):null,error:f?this.escapeHtml(f):null,min:i,max:a,step:r,fieldValue:m,disabled:o,attrs:p};return E.render(this.templates.range,y)}renderHiddenField(e){const{name:t,value:s=""}=e,i=this.getFieldValue(t)??s;return`<input type="hidden" name="${t}" value="${this.escapeHtml(i)}">`}renderButton(e){const{name:t="",label:s="Button",type:i="button",action:a="",class:r="btn-secondary",disabled:n=!1,attributes:o={}}=e;let l=a;l||(i==="submit"?l="submit-form":i==="reset"&&(l="reset-form"));const c=Object.entries(o).map(([d,h])=>`${d}="${this.escapeHtml(h)}"`).join(" ");return`
418
+ `}renderDateField(e){return this.renderInputField(e,"date")}renderDateTimeField(e){return this.renderInputField(e,"datetime-local")}renderTimeField(e){return this.renderInputField(e,"time")}renderFileField(e){const{name:t,label:s,required:i=!1,disabled:a=!1,multiple:r=!1,accept:n="*/*",class:o="",attributes:l={},help:c=e.helpText||e.help||""}=e,d=`${this.options.inputClass} ${o}`.trim(),h=this.errors[t],m=Object.entries(l).map(([g,y])=>`${g}="${this.escapeHtml(y)}"`).join(" "),p=this.getFieldId(t),f={labelClass:this.options.labelClass,inputClass:d,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:p,name:t,label:s?this.escapeHtml(s):null,help:c?this.escapeHtml(c):null,error:h?this.escapeHtml(h):null,accept:n,required:i,disabled:a,multiple:r,attrs:m};return E.render(this.templates.file,f)}renderImageField(e){const{name:t,label:s,required:i=!1,disabled:a=!1,accept:r="image/*",class:n="",attributes:o={},help:l=e.helpText||e.help||"",size:c="md",allowDrop:d=!0,placeholder:h="Drop image here or click to upload"}=e,m=`${this.options.inputClass} ${n}`.trim(),p=this.errors[t],f=this.getFieldId(t),g=`${f}_dropzone`,y=`${f}_preview`,b={xs:{width:48,height:48,containerClass:"image-field-xs"},sm:{width:96,height:96,containerClass:"image-field-sm"},md:{width:150,height:150,containerClass:"image-field-md"},lg:{width:200,height:200,containerClass:"image-field-lg"},xl:{width:300,height:300,containerClass:"image-field-xl"}},w=b[c]||b.md,C=Object.entries(o).map(([_,A])=>`${_}="${this.escapeHtml(A)}"`).join(" "),$=this.getFieldValue(t),S=this.extractImageUrl($,c),M={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:f,name:t,label:s?this.escapeHtml(s):null,help:l?this.escapeHtml(l):null,error:p?this.escapeHtml(p):null,dropZoneId:g,previewId:y,containerClass:w.containerClass,width:w.width,height:w.height,accept:r,imageUrl:S,placeholderText:a?"No image":this.escapeHtml(h),cursor:a?"default":"pointer",allowDrop:d,showRemove:!a,required:i,disabled:a,attrs:C};return E.render(this.templates.image,M)}extractImageUrl(e,t="md"){if(!e)return null;if(typeof e=="string")return e;if(typeof e=="object"&&e.url){if(e.renditions){const s={xs:["thumbnail_sm","thumbnail","square_sm"],sm:["thumbnail","thumbnail_sm","square_sm"],md:["thumbnail_md","thumbnail","thumbnail_lg"],lg:["thumbnail_lg","thumbnail_md","thumbnail"],xl:["original","thumbnail_lg"]},i=s[t]||s.md;for(const a of i)if(e.renditions[a]&&e.renditions[a].url)return e.renditions[a].url}return e.url}return null}renderColorField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:o=!1,class:l="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=`${this.options.inputClass} ${l}`.trim(),m=this.errors[t],p=this.getFieldValue(t)??i,f=Object.entries(c).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),g=this.getFieldId(t),y={labelClass:this.options.labelClass,inputClass:h,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,fieldValue:this.escapeHtml(p),label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:d?this.escapeHtml(d):null,error:m?this.escapeHtml(m):null,required:r,disabled:n,readonly:o,attrs:f};return E.render(this.templates.color,y)}renderRangeField(e){const{name:t,label:s,min:i=0,max:a=100,step:r=1,value:n=i,disabled:o=!1,class:l="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=`${this.options.inputClass} ${l}`.trim(),m=this.errors[t],p=this.getFieldValue(t)??n,f=Object.entries(c).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),g=this.getFieldId(t),y={labelClass:this.options.labelClass,inputClass:h,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,label:s?this.escapeHtml(s):null,help:d?this.escapeHtml(d):null,error:m?this.escapeHtml(m):null,min:i,max:a,step:r,fieldValue:p,disabled:o,attrs:f};return E.render(this.templates.range,y)}renderHiddenField(e){const{name:t,value:s=""}=e,i=this.getFieldValue(t)??s;return`<input type="hidden" name="${t}" value="${this.escapeHtml(i)}">`}renderButton(e){const{name:t="",label:s="Button",type:i="button",action:a="",class:r="btn-secondary",disabled:n=!1,attributes:o={}}=e;let l=a;l||(i==="submit"?l="submit-form":i==="reset"&&(l="reset-form"));const c=Object.entries(o).map(([d,h])=>`${d}="${this.escapeHtml(h)}"`).join(" ");return`
419
419
  <button
420
420
  type="button"
421
421
  ${t?`name="${t}"`:""}
@@ -439,34 +439,34 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
439
439
  <div class="form-actions mt-3">
440
440
  ${e}
441
441
  </div>
442
- `:""}getFieldValue(e){return this.structureOnly?"":x.getContextData(this.data,e)}renderTagField(e){const{name:t,label:s,value:i="",placeholder:a="Add tags...",required:r=!1,disabled:n=!1,readonly:o=!1,maxTags:l=50,allowDuplicates:c=!1,separator:d=",",help:h=e.helpText||e.help||""}=e,f=this.getFieldId(t),m=this.errors[t],p=this.getFieldValue(t)??i;return`
442
+ `:""}getFieldValue(e){return this.structureOnly?"":x.getContextData(this.data,e)}renderTagField(e){const{name:t,label:s,value:i="",placeholder:a="Add tags...",required:r=!1,disabled:n=!1,readonly:o=!1,maxTags:l=50,allowDuplicates:c=!1,separator:d=",",help:h=e.helpText||e.help||""}=e,m=this.getFieldId(t),p=this.errors[t],f=this.getFieldValue(t)??i;return`
443
443
  <div class="mojo-form-control">
444
- ${s?`<label for="${f}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
444
+ ${s?`<label for="${m}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
445
445
  <div class="tag-input-placeholder"
446
446
  data-field-name="${t}"
447
447
  data-field-type="tag"
448
- data-field-config='${JSON.stringify({name:t,value:p,placeholder:a,maxTags:l,allowDuplicates:c,separator:d,disabled:n,readonly:o,required:r})}'>
448
+ data-field-config='${JSON.stringify({name:t,value:f,placeholder:a,maxTags:l,allowDuplicates:c,separator:d,disabled:n,readonly:o,required:r})}'>
449
449
  <input type="text"
450
- id="${f}"
450
+ id="${m}"
451
451
  name="${t}_display"
452
- class="${this.options.inputClass}${m?" is-invalid":""}"
452
+ class="${this.options.inputClass}${p?" is-invalid":""}"
453
453
  placeholder="${this.escapeHtml(a)}"
454
454
  ${n?"disabled":""}
455
455
  ${o?"readonly":""}
456
456
 
457
- <input type="hidden" name="${t}" value="${this.escapeHtml(p)}">
457
+ <input type="hidden" name="${t}" value="${this.escapeHtml(f)}">
458
458
  <small class="form-text text-muted">This will be enhanced with TagInput component</small>
459
459
  </div>
460
460
  ${h?`<div class="${this.options.helpClass}">${this.escapeHtml(h)}</div>`:""}
461
- ${m?`<div class="${this.options.errorClass}">${this.escapeHtml(m)}</div>`:""}
461
+ ${p?`<div class="${this.options.errorClass}">${this.escapeHtml(p)}</div>`:""}
462
462
  </div>
463
- `}renderCollectionField(e){const{name:t,label:s,value:i="",placeholder:a="Search...",required:r=!1,disabled:n=!1,readonly:o=!1,Collection:l,labelField:c="name",valueField:d="id",maxItems:h=10,emptyFetch:f=!1,debounceMs:m=300,requiresActiveGroup:p=!1,help:g=e.helpText||e.help||""}=e,y=this.getFieldId(t),b=this.errors[t],w=this.getFieldValue(t)??i;return`
463
+ `}renderCollectionField(e){const{name:t,label:s,value:i="",placeholder:a="Search...",required:r=!1,disabled:n=!1,readonly:o=!1,Collection:l,labelField:c="name",valueField:d="id",maxItems:h=10,emptyFetch:m=!1,debounceMs:p=300,requiresActiveGroup:f=!1,help:g=e.helpText||e.help||""}=e,y=this.getFieldId(t),b=this.errors[t],w=this.getFieldValue(t)??i;return`
464
464
  <div class="mojo-form-control">
465
465
  ${s?`<label for="${y}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
466
466
  <div class="collection-select-placeholder"
467
467
  data-field-name="${t}"
468
468
  data-field-type="collection"
469
- data-field-config='${JSON.stringify({name:t,value:w,placeholder:a,labelField:c,valueField:d,maxItems:h,emptyFetch:f,debounceMs:m,disabled:n,readonly:o,required:r,requiresActiveGroup:p})}'>
469
+ data-field-config='${JSON.stringify({name:t,value:w,placeholder:a,labelField:c,valueField:d,maxItems:h,emptyFetch:m,debounceMs:p,disabled:n,readonly:o,required:r,requiresActiveGroup:f})}'>
470
470
  <input type="text"
471
471
  id="${y}"
472
472
  name="${t}_display"
@@ -481,30 +481,30 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
481
481
  ${g?`<div class="${this.options.helpClass}">${this.escapeHtml(g)}</div>`:""}
482
482
  ${b?`<div class="${this.options.errorClass}">${this.escapeHtml(b)}</div>`:""}
483
483
  </div>
484
- `}renderCollectionMultiSelectField(e){const{name:t,label:s,value:i=[],required:a=!1,disabled:r=!1,Collection:n,collectionParams:o={},labelField:l="name",valueField:c="id",excludeIds:d=[],ignoreIds:h=[],size:f=8,maxHeight:m=null,showSelectAll:p=!0,enableSearch:g=!1,searchPlaceholder:y="Search...",searchDebounce:b=400,requiresActiveGroup:w=!1,help:C=e.helpText||e.help||""}=e;this.getFieldId(t);const $=this.errors[t],S=this.getFieldValue(t)??i;return`
484
+ `}renderCollectionMultiSelectField(e){const{name:t,label:s,value:i=[],required:a=!1,disabled:r=!1,Collection:n,collectionParams:o={},labelField:l="name",valueField:c="id",excludeIds:d=[],ignoreIds:h=[],size:m=8,maxHeight:p=null,showSelectAll:f=!0,enableSearch:g=!1,searchPlaceholder:y="Search...",searchDebounce:b=400,requiresActiveGroup:w=!1,help:C=e.helpText||e.help||""}=e;this.getFieldId(t);const $=this.errors[t],S=this.getFieldValue(t)??i;return`
485
485
  <div class="mojo-form-control">
486
486
  ${s?`<label class="${this.options.labelClass}">${this.escapeHtml(s)}${a?'<span class="text-danger">*</span>':""}</label>`:""}
487
487
  <div class="collection-multiselect-placeholder"
488
488
  data-field-name="${t}"
489
489
  data-field-type="collectionmultiselect"
490
- data-field-config='${JSON.stringify({name:t,value:S,labelField:l,valueField:c,excludeIds:d,ignoreIds:h,size:f,maxHeight:m,showSelectAll:p,enableSearch:g,searchPlaceholder:y,searchDebounce:b,disabled:r,required:a,requiresActiveGroup:w})}'>
490
+ data-field-config='${JSON.stringify({name:t,value:S,labelField:l,valueField:c,excludeIds:d,ignoreIds:h,size:m,maxHeight:p,showSelectAll:f,enableSearch:g,searchPlaceholder:y,searchDebounce:b,disabled:r,required:a,requiresActiveGroup:w})}'>
491
491
  <input type="hidden" name="${t}" value="${this.escapeHtml(JSON.stringify(S))}">
492
492
  <small class="form-text text-muted">This will be enhanced with CollectionMultiSelect component</small>
493
493
  </div>
494
494
  ${C?`<div class="${this.options.helpClass}">${this.escapeHtml(C)}</div>`:""}
495
495
  ${$?`<div class="${this.options.errorClass}">${this.escapeHtml($)}</div>`:""}
496
496
  </div>
497
- `}renderDatePickerField(e){const{name:t,label:s,value:i="",placeholder:a="Select date...",required:r=!1,disabled:n=!1,readonly:o=!1,min:l=null,max:c=null,format:d="YYYY-MM-DD",displayFormat:h="MMM DD, YYYY",help:f=e.helpText||e.help||""}=e,m=this.getFieldId(t),p=this.errors[t],g=this.getFieldValue(t)??i;return`
497
+ `}renderDatePickerField(e){const{name:t,label:s,value:i="",placeholder:a="Select date...",required:r=!1,disabled:n=!1,readonly:o=!1,min:l=null,max:c=null,format:d="YYYY-MM-DD",displayFormat:h="MMM DD, YYYY",help:m=e.helpText||e.help||""}=e,p=this.getFieldId(t),f=this.errors[t],g=this.getFieldValue(t)??i;return`
498
498
  <div class="mojo-form-control">
499
- ${s?`<label for="${m}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
499
+ ${s?`<label for="${p}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
500
500
  <div class="date-picker-placeholder"
501
501
  data-field-name="${t}"
502
502
  data-field-type="datepicker"
503
503
  data-field-config='${JSON.stringify({name:t,value:g,placeholder:a,min:l,max:c,format:d,displayFormat:h,disabled:n,readonly:o,required:r})}'>
504
504
  <input type="date"
505
- id="${m}"
505
+ id="${p}"
506
506
  name="${t}"
507
- class="${this.options.inputClass}${p?" is-invalid":""}"
507
+ class="${this.options.inputClass}${f?" is-invalid":""}"
508
508
  value="${this.escapeHtml(g)}"
509
509
  placeholder="${this.escapeHtml(a)}"
510
510
  ${l?`min="${l}"`:""}
@@ -515,26 +515,26 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
515
515
 
516
516
  <small class="form-text text-muted">This will be enhanced with Easepick DatePicker</small>
517
517
  </div>
518
- ${f?`<div class="${this.options.helpClass}">${this.escapeHtml(f)}</div>`:""}
519
- ${p?`<div class="${this.options.errorClass}">${this.escapeHtml(p)}</div>`:""}
518
+ ${m?`<div class="${this.options.helpClass}">${this.escapeHtml(m)}</div>`:""}
519
+ ${f?`<div class="${this.options.errorClass}">${this.escapeHtml(f)}</div>`:""}
520
520
  </div>
521
- `}renderDateRangeField(e){const{name:t,startName:s,endName:i,fieldName:a,label:r,startDate:n="",endDate:o="",placeholder:l="Select date range...",required:c=!1,disabled:d=!1,readonly:h=!1,min:f=null,max:m=null,format:p="YYYY-MM-DD",displayFormat:g="MMM DD, YYYY",outputFormat:y="date",separator:b=" - ",help:w=e.helpText||e.help||""}=e,C=this.getFieldId(t||s||"daterange"),$=this.errors[t],S=s||(t?t+"_start":""),M=i||(t?t+"_end":""),L=this.getFieldValue(S)||n,A=this.getFieldValue(M)||o;return`
521
+ `}renderDateRangeField(e){const{name:t,startName:s,endName:i,fieldName:a,label:r,startDate:n="",endDate:o="",placeholder:l="Select date range...",required:c=!1,disabled:d=!1,readonly:h=!1,min:m=null,max:p=null,format:f="YYYY-MM-DD",displayFormat:g="MMM DD, YYYY",outputFormat:y="date",separator:b=" - ",help:w=e.helpText||e.help||""}=e,C=this.getFieldId(t||s||"daterange"),$=this.errors[t],S=s||(t?t+"_start":""),M=i||(t?t+"_end":""),_=this.getFieldValue(S)||n,A=this.getFieldValue(M)||o;return`
522
522
  <div class="mojo-form-control">
523
523
  ${r?`<label for="${C}" class="${this.options.labelClass}">${this.escapeHtml(r)}${c?'<span class="text-danger">*</span>':""}</label>`:""}
524
524
  <div class="date-range-picker-placeholder"
525
525
  data-field-name="${t||s||"daterange"}"
526
526
  data-field-type="daterange"
527
- data-field-config='${JSON.stringify({name:t,startName:s,endName:i,fieldName:a,startDate:L,endDate:A,placeholder:l,min:f,max:m,format:p,displayFormat:g,outputFormat:y,separator:b,disabled:d,readonly:h,required:c})}'>
527
+ data-field-config='${JSON.stringify({name:t,startName:s,endName:i,fieldName:a,startDate:_,endDate:A,placeholder:l,min:m,max:p,format:f,displayFormat:g,outputFormat:y,separator:b,disabled:d,readonly:h,required:c})}'>
528
528
  <div class="row g-2">
529
529
  <div class="col">
530
530
  <input type="date"
531
531
  id="${C}_start"
532
532
  name="${t}_start"
533
533
  class="${this.options.inputClass}${$?" is-invalid":""}"
534
- value="${this.escapeHtml(L)}"
534
+ value="${this.escapeHtml(_)}"
535
535
  placeholder="Start date..."
536
- ${f?`min="${f}"`:""}
537
- ${m?`max="${m}"`:""}
536
+ ${m?`min="${m}"`:""}
537
+ ${p?`max="${p}"`:""}
538
538
  ${d?"disabled":""}
539
539
  ${h?"readonly":""}
540
540
  ${c?"required":""}
@@ -550,8 +550,8 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
550
550
  class="${this.options.inputClass}${$?" is-invalid":""}"
551
551
  value="${this.escapeHtml(A)}"
552
552
  placeholder="End date..."
553
- ${f?`min="${f}"`:""}
554
- ${m?`max="${m}"`:""}
553
+ ${m?`min="${m}"`:""}
554
+ ${p?`max="${p}"`:""}
555
555
  ${d?"disabled":""}
556
556
  ${h?"readonly":""}
557
557
  ${c?"required":""}
@@ -594,14 +594,14 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
594
594
  ${this.escapeHtml(l.label||`Tab ${c+1}`)}
595
595
  </button>
596
596
  </li>
597
- `}).join(""),o=t.map((l,c)=>{const d=`${r}-pane-${c}`,h=c===0,f=(l.fields||[]).map(m=>m.type==="group"?this.buildGroupHTML(m):this.buildFieldHTML(m)).join("");return`
597
+ `}).join(""),o=t.map((l,c)=>{const d=`${r}-pane-${c}`,h=c===0,m=(l.fields||[]).map(p=>p.type==="group"?this.buildGroupHTML(p):this.buildFieldHTML(p)).join("");return`
598
598
  <div class="tab-pane fade ${h?"show active":""}"
599
599
  id="${d}"
600
600
  role="tabpanel"
601
601
  aria-labelledby="${d}-tab"
602
602
  data-tab-index="${c}">
603
603
  <div class="row">
604
- ${f}
604
+ ${m}
605
605
  </div>
606
606
  </div>
607
607
  `}).join("");return`
@@ -613,7 +613,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
613
613
  ${o}
614
614
  </div>
615
615
  </div>
616
- `}generateSelectOptions(e,t,s=1,i={}){const{format:a,prefix:r="",suffix:n=""}=i,o=[],l=e<=t?Math.abs(s):-Math.abs(s);for(let c=e;e<=t?c<=t:c>=t;c+=l){let d=String(c);if(typeof a=="function")d=a(c);else if(a==="padded"||a==="pad"){const h=String(Math.max(Math.abs(e),Math.abs(t))).length;d=String(c).padStart(h,"0")}else a==="ordinal"&&(d=this.formatOrdinal(c));d=`${r}${d}${n}`,o.push({value:c,label:d})}return o}formatOrdinal(e){const t=e%10,s=e%100;return t===1&&s!==11?e+"st":t===2&&s!==12?e+"nd":t===3&&s!==13?e+"rd":e+"th"}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}}const Le={enableFileDrop(u={}){if(this._fileDropConfig={acceptedTypes:u.acceptedTypes||["*/*"],maxFileSize:u.maxFileSize||10*1024*1024,dropZoneSelector:u.dropZoneSelector||null,visualFeedback:u.visualFeedback!==!1,multiple:u.multiple||!1,validateOnDrop:u.validateOnDrop!==!1,dragOverClass:u.dragOverClass||"drag-over",dragActiveClass:u.dragActiveClass||"drag-active"},this._fileDropState={isDragActive:!1,dragCounter:0},this._boundFileDropHandlers={dragEnter:this._onFileDropDragEnter.bind(this),dragOver:this._onFileDropDragOver.bind(this),dragLeave:this._onFileDropDragLeave.bind(this),drop:this._onFileDropDrop.bind(this),preventDefault:this._onFileDropPreventDefault.bind(this)},this.element)this._setupFileDropListeners();else{const t=this.onAfterRender.bind(this);this.onAfterRender=async()=>{await t(),this._setupFileDropListeners()}}const e=this.onBeforeDestroy.bind(this);this.onBeforeDestroy=async()=>{this._cleanupFileDropListeners(),await e()}},_setupFileDropListeners(){if(!this._fileDropConfig)return;const u=this._getFileDropZone();if(!u){console.warn("FileDropMixin: Drop zone not found");return}this._fileDropZone=u,u.addEventListener("dragenter",this._boundFileDropHandlers.dragEnter),u.addEventListener("dragover",this._boundFileDropHandlers.dragOver),u.addEventListener("dragleave",this._boundFileDropHandlers.dragLeave),u.addEventListener("drop",this._boundFileDropHandlers.drop),["dragenter","dragover","dragleave","drop"].forEach(e=>{document.addEventListener(e,this._boundFileDropHandlers.preventDefault)})},_cleanupFileDropListeners(){this._boundFileDropHandlers&&(this._fileDropZone&&(this._fileDropZone.removeEventListener("dragenter",this._boundFileDropHandlers.dragEnter),this._fileDropZone.removeEventListener("dragover",this._boundFileDropHandlers.dragOver),this._fileDropZone.removeEventListener("dragleave",this._boundFileDropHandlers.dragLeave),this._fileDropZone.removeEventListener("drop",this._boundFileDropHandlers.drop)),["dragenter","dragover","dragleave","drop"].forEach(u=>{document.removeEventListener(u,this._boundFileDropHandlers.preventDefault)}),this._fileDropZone=null,this._boundFileDropHandlers=null,this._fileDropConfig=null,this._fileDropState=null)},_getFileDropZone(){return this._fileDropConfig.dropZoneSelector?this.element.querySelector(this._fileDropConfig.dropZoneSelector):this.element},_onFileDropPreventDefault(u){u.preventDefault(),u.stopPropagation()},_onFileDropDragEnter(u){this._onFileDropPreventDefault(u),this._fileDropState.dragCounter++,this._fileDropState.isDragActive||(this._fileDropState.isDragActive=!0,this._applyFileDropVisualFeedback(!0))},_onFileDropDragOver(u){this._onFileDropPreventDefault(u),u.dataTransfer.dropEffect="copy"},_onFileDropDragLeave(u){this._onFileDropPreventDefault(u),this._fileDropState.dragCounter--,this._fileDropState.dragCounter<=0&&(this._fileDropState.isDragActive=!1,this._fileDropState.dragCounter=0,this._applyFileDropVisualFeedback(!1))},async _onFileDropDrop(u){this._onFileDropPreventDefault(u),this._fileDropState.isDragActive=!1,this._fileDropState.dragCounter=0,this._applyFileDropVisualFeedback(!1);const e=Array.from(u.dataTransfer.files);if(e.length===0)return;const t=this._fileDropConfig.multiple?e:[e[0]];let s={valid:!0,errors:[]};if(this._fileDropConfig.validateOnDrop&&(s=this._validateFileDropFiles(t),!s.valid)){typeof this.onFileDropError=="function"&&await this.onFileDropError(new Error(s.errors.join(", ")),u,t);return}if(typeof this.onFileDrop=="function")try{await this.onFileDrop(t,u,s)}catch(i){typeof this.onFileDropError=="function"?await this.onFileDropError(i,u,t):console.error("FileDropMixin: Error in onFileDrop callback:",i)}else console.warn("FileDropMixin: No onFileDrop method found on view")},_applyFileDropVisualFeedback(u){if(!this._fileDropConfig.visualFeedback||!this._fileDropZone)return;const{dragOverClass:e,dragActiveClass:t}=this._fileDropConfig;u?this._fileDropZone.classList.add(e,t):this._fileDropZone.classList.remove(e,t)},_validateFileDropFiles(u){const e=[],t=this._fileDropConfig;for(const s of u){if(!this._isFileDropTypeAccepted(s.type)){e.push(`File type "${s.type}" is not accepted for file "${s.name}"`);continue}s.size>t.maxFileSize&&e.push(`File "${s.name}" (${this._formatFileDropSize(s.size)}) exceeds maximum size (${this._formatFileDropSize(t.maxFileSize)})`)}return{valid:e.length===0,errors:e}},_isFileDropTypeAccepted(u){const{acceptedTypes:e}=this._fileDropConfig;return e.includes("*/*")?!0:e.some(t=>{if(t===u)return!0;if(t.endsWith("/*")){const s=t.split("/")[0];return u.startsWith(s+"/")}return!1})},_formatFileDropSize(u){if(u===0)return"0 Bytes";const e=1024,t=["Bytes","KB","MB","GB"],s=Math.floor(Math.log(u)/Math.log(e));return parseFloat((u/Math.pow(e,s)).toFixed(2))+" "+t[s]}};function _e(u){Object.assign(u.prototype,Le)}class oe extends v{constructor(e={}){const{name:t,value:s="",placeholder:i="Add tags...",maxTags:a=50,allowDuplicates:r=!1,separator:n=",",trimTags:o=!0,minLength:l=1,maxLength:c=50,disabled:d=!1,readonly:h=!1,class:f="",tagClass:m="badge bg-primary",inputClass:p="form-control",...g}=e;super({tagName:"div",className:`tag-input-view ${f}`,...g}),this.name=t,this.placeholder=i,this.maxTags=a,this.allowDuplicates=r,this.separator=n,this.trimTags=o,this.minLength=l,this.maxLength=c,this.disabled=d,this.readonly=h,this.tagClass=m,this.inputClass=p,this.tags=[],this.focusedTagIndex=-1,s&&(this.tags=this.parseTagString(s))}async renderTemplate(){const e=this.renderTags(),t=this.renderHiddenInput(),s=this.renderInput();return`
616
+ `}generateSelectOptions(e,t,s=1,i={}){const{format:a,prefix:r="",suffix:n=""}=i,o=[],l=e<=t?Math.abs(s):-Math.abs(s);for(let c=e;e<=t?c<=t:c>=t;c+=l){let d=String(c);if(typeof a=="function")d=a(c);else if(a==="padded"||a==="pad"){const h=String(Math.max(Math.abs(e),Math.abs(t))).length;d=String(c).padStart(h,"0")}else a==="ordinal"&&(d=this.formatOrdinal(c));d=`${r}${d}${n}`,o.push({value:c,label:d})}return o}formatOrdinal(e){const t=e%10,s=e%100;return t===1&&s!==11?e+"st":t===2&&s!==12?e+"nd":t===3&&s!==13?e+"rd":e+"th"}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}}const je={enableFileDrop(u={}){if(this._fileDropConfig={acceptedTypes:u.acceptedTypes||["*/*"],maxFileSize:u.maxFileSize||10*1024*1024,dropZoneSelector:u.dropZoneSelector||null,visualFeedback:u.visualFeedback!==!1,multiple:u.multiple||!1,validateOnDrop:u.validateOnDrop!==!1,dragOverClass:u.dragOverClass||"drag-over",dragActiveClass:u.dragActiveClass||"drag-active"},this._fileDropState={isDragActive:!1,dragCounter:0},this._boundFileDropHandlers={dragEnter:this._onFileDropDragEnter.bind(this),dragOver:this._onFileDropDragOver.bind(this),dragLeave:this._onFileDropDragLeave.bind(this),drop:this._onFileDropDrop.bind(this),preventDefault:this._onFileDropPreventDefault.bind(this)},this.element)this._setupFileDropListeners();else{const t=this.onAfterRender.bind(this);this.onAfterRender=async()=>{await t(),this._setupFileDropListeners()}}const e=this.onBeforeDestroy.bind(this);this.onBeforeDestroy=async()=>{this._cleanupFileDropListeners(),await e()}},_setupFileDropListeners(){if(!this._fileDropConfig)return;const u=this._getFileDropZone();if(!u){console.warn("FileDropMixin: Drop zone not found");return}this._fileDropZone=u,u.addEventListener("dragenter",this._boundFileDropHandlers.dragEnter),u.addEventListener("dragover",this._boundFileDropHandlers.dragOver),u.addEventListener("dragleave",this._boundFileDropHandlers.dragLeave),u.addEventListener("drop",this._boundFileDropHandlers.drop),["dragenter","dragover","dragleave","drop"].forEach(e=>{document.addEventListener(e,this._boundFileDropHandlers.preventDefault)})},_cleanupFileDropListeners(){this._boundFileDropHandlers&&(this._fileDropZone&&(this._fileDropZone.removeEventListener("dragenter",this._boundFileDropHandlers.dragEnter),this._fileDropZone.removeEventListener("dragover",this._boundFileDropHandlers.dragOver),this._fileDropZone.removeEventListener("dragleave",this._boundFileDropHandlers.dragLeave),this._fileDropZone.removeEventListener("drop",this._boundFileDropHandlers.drop)),["dragenter","dragover","dragleave","drop"].forEach(u=>{document.removeEventListener(u,this._boundFileDropHandlers.preventDefault)}),this._fileDropZone=null,this._boundFileDropHandlers=null,this._fileDropConfig=null,this._fileDropState=null)},_getFileDropZone(){return this._fileDropConfig.dropZoneSelector?this.element.querySelector(this._fileDropConfig.dropZoneSelector):this.element},_onFileDropPreventDefault(u){u.preventDefault(),u.stopPropagation()},_onFileDropDragEnter(u){this._onFileDropPreventDefault(u),this._fileDropState.dragCounter++,this._fileDropState.isDragActive||(this._fileDropState.isDragActive=!0,this._applyFileDropVisualFeedback(!0))},_onFileDropDragOver(u){this._onFileDropPreventDefault(u),u.dataTransfer.dropEffect="copy"},_onFileDropDragLeave(u){this._onFileDropPreventDefault(u),this._fileDropState.dragCounter--,this._fileDropState.dragCounter<=0&&(this._fileDropState.isDragActive=!1,this._fileDropState.dragCounter=0,this._applyFileDropVisualFeedback(!1))},async _onFileDropDrop(u){this._onFileDropPreventDefault(u),this._fileDropState.isDragActive=!1,this._fileDropState.dragCounter=0,this._applyFileDropVisualFeedback(!1);const e=Array.from(u.dataTransfer.files);if(e.length===0)return;const t=this._fileDropConfig.multiple?e:[e[0]];let s={valid:!0,errors:[]};if(this._fileDropConfig.validateOnDrop&&(s=this._validateFileDropFiles(t),!s.valid)){typeof this.onFileDropError=="function"&&await this.onFileDropError(new Error(s.errors.join(", ")),u,t);return}if(typeof this.onFileDrop=="function")try{await this.onFileDrop(t,u,s)}catch(i){typeof this.onFileDropError=="function"?await this.onFileDropError(i,u,t):console.error("FileDropMixin: Error in onFileDrop callback:",i)}else console.warn("FileDropMixin: No onFileDrop method found on view")},_applyFileDropVisualFeedback(u){if(!this._fileDropConfig.visualFeedback||!this._fileDropZone)return;const{dragOverClass:e,dragActiveClass:t}=this._fileDropConfig;u?this._fileDropZone.classList.add(e,t):this._fileDropZone.classList.remove(e,t)},_validateFileDropFiles(u){const e=[],t=this._fileDropConfig;for(const s of u){if(!this._isFileDropTypeAccepted(s.type)){e.push(`File type "${s.type}" is not accepted for file "${s.name}"`);continue}s.size>t.maxFileSize&&e.push(`File "${s.name}" (${this._formatFileDropSize(s.size)}) exceeds maximum size (${this._formatFileDropSize(t.maxFileSize)})`)}return{valid:e.length===0,errors:e}},_isFileDropTypeAccepted(u){const{acceptedTypes:e}=this._fileDropConfig;return e.includes("*/*")?!0:e.some(t=>{if(t===u)return!0;if(t.endsWith("/*")){const s=t.split("/")[0];return u.startsWith(s+"/")}return!1})},_formatFileDropSize(u){if(u===0)return"0 Bytes";const e=1024,t=["Bytes","KB","MB","GB"],s=Math.floor(Math.log(u)/Math.log(e));return parseFloat((u/Math.pow(e,s)).toFixed(2))+" "+t[s]}};function Re(u){Object.assign(u.prototype,je)}class ce extends v{constructor(e={}){const{name:t,value:s="",placeholder:i="Add tags...",maxTags:a=50,allowDuplicates:r=!1,separator:n=",",trimTags:o=!0,minLength:l=1,maxLength:c=50,disabled:d=!1,readonly:h=!1,class:m="",tagClass:p="badge bg-primary",inputClass:f="form-control",...g}=e;super({tagName:"div",className:`tag-input-view ${m}`,...g}),this.name=t,this.placeholder=i,this.maxTags=a,this.allowDuplicates=r,this.separator=n,this.trimTags=o,this.minLength=l,this.maxLength=c,this.disabled=d,this.readonly=h,this.tagClass=p,this.inputClass=f,this.tags=[],this.focusedTagIndex=-1,s&&(this.tags=this.parseTagString(s))}async renderTemplate(){const e=this.renderTags(),t=this.renderHiddenInput(),s=this.renderInput();return`
617
617
  <div class="tag-input-container">
618
618
  <div class="tag-input-wrapper border rounded p-2"
619
619
  data-action="focus-input"
@@ -659,7 +659,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
659
659
  value="${this.escapeHtml(this.getTagString())}"
660
660
  class="tag-input-hidden">
661
661
  `:""}async onAfterRender(){await super.onAfterRender(),this.updateTagCount()}async onActionFocusInput(e,t){this.focus()}focus(){const e=this.element.querySelector(".tag-input-field");e&&!this.disabled&&e.focus(),this.focusedTagIndex=-1}async onActionRemoveTag(e,t){e.stopPropagation();const s=parseInt(t.getAttribute("data-tag-index"));s>=0&&s<this.tags.length&&await this.removeTag(s)}async onChangeInputChange(e,t){const s=t.value,i=s.slice(-1);if(i===this.separator||i===`
662
- `){e.preventDefault();const a=s.slice(0,-1);a.trim()&&(await this.addTag(a),t.value="");return}}bindEvents(){this.__bnd_keydown||(this.__bnd_keydown=this.handleInputKeydown.bind(this)),this.element.addEventListener("keydown",this.__bnd_keydown),this.events.bind(this.element)}unbindEvents(){this.__bnd_keydown&&this.element.removeEventListener("keydown",this.__bnd_keydown),this.events.unbind()}handleInputKeydown(e){const t=e.target,s=t.value||"";switch(e.key){case"Enter":case"Tab":case",":s.trim()&&(e.preventDefault(),this.addTag(s),t.value="");break;case"Backspace":s===""&&this.tags.length>0&&(e.preventDefault(),this.focusedTagIndex>=0?(this.removeTag(this.focusedTagIndex),this.focusedTagIndex==0?this.focus():this.focusTag(this.focusedTagIndex-1)):this.removeTag(this.tags.length-1));break;case"ArrowLeft":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex-1;i>=0?this.focusTag(i):this.focus()}else this.focusTag(this.tags.length-1);break;case"ArrowRight":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex+1;i<this.tags.length?this.focusTag(i):this.focus()}else this.focusTag(0);break;case"Escape":t.value="",t.blur();break}}async addTag(e){if(this.readonly||this.disabled)return!1;const t=this.trimTags?e.trim():e;return this.isValidTag(t)?!this.allowDuplicates&&this.tags.includes(t)?(this.showTagError(`Tag "${t}" already exists`),!1):this.tags.length>=this.maxTags?(this.showTagError(`Maximum ${this.maxTags} tags allowed`),!1):(this.tags.push(t),await this.updateDisplay(),this.emit("tag:added",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0):!1}async removeTag(e){if(this.readonly||this.disabled)return!1;if(e>=0&&e<this.tags.length){const t=this.tags[e];return this.tags.splice(e,1),await this.updateDisplay(),this.emit("tag:removed",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0}return!1}async removeTagByValue(e){const t=this.tags.indexOf(e);return t>=0?await this.removeTag(t):!1}async clearTags(){if(this.readonly||this.disabled)return!1;const e=[...this.tags];return this.tags=[],await this.updateDisplay(),this.emit("tags:cleared",{oldTags:e}),this.emit("change",{value:"",tags:[]}),!0}async setTags(e){let t=[];Array.isArray(e)?t=e:typeof e=="string"&&(t=this.parseTagString(e)),t=t.filter(s=>this.isValidTag(s)).slice(0,this.maxTags),this.allowDuplicates||(t=[...new Set(t)]),this.tags=t,await this.updateDisplay(),this.emit("tags:set",{tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags})}isValidTag(e){return!(typeof e!="string"||e.length<this.minLength||e.length>this.maxLength||e.trim()==="")}parseTagString(e){return e?e.split(this.separator).map(t=>this.trimTags?t.trim():t).filter(t=>t.length>0):[]}getTagString(){return this.tags.join(this.separator)}getTags(){return[...this.tags]}focusTag(e){const t=this.element.querySelectorAll(".tag-item");t[e]&&(this.focusedTagIndex=e,console.log(`Focused tag index: ${e}`),t[e].focus())}async updateDisplay(){const e=this.element.querySelector(".tags-container");e&&(e.innerHTML=this.renderTags());const t=this.element.querySelector(".tag-input-hidden");t&&(t.value=this.getTagString()),this.updateTagCount()}updateTagCount(){const e=this.element.querySelector(".tag-count");e&&(e.textContent=this.tags.length)}showTagError(e){let t=this.element.querySelector(".tag-error");if(!t){t=document.createElement("div"),t.className="tag-error small text-danger mt-1";const s=this.element.querySelector(".tag-input-feedback");s&&s.parentNode.insertBefore(t,s.nextSibling)}t.textContent=e,setTimeout(()=>{t.parentNode&&t.remove()},3e3)}setEnabled(e){this.disabled=!e;const t=this.element.querySelector(".tag-input-field");t&&(t.disabled=this.disabled);const s=this.element.querySelector(".tag-input-wrapper");s&&s.classList.toggle("disabled",this.disabled)}setReadonly(e){this.readonly=e;const t=this.element.querySelector(".tag-input-field");t&&(t.style.display=e?"none":""),this.element.querySelectorAll(".tag-remove").forEach(i=>{i.style.display=e?"none":""})}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}getFormValue(){return this.getTagString()}async setFormValue(e){await this.setTags(e)}static create(e={}){return new oe(e)}}class Pe extends v{constructor(e={}){super({tagName:"div",className:"collection-dropdown-view dropdown-menu show w-100 position-absolute",style:"max-height: 250px; overflow-y: auto; z-index: 1000;",template:`
662
+ `){e.preventDefault();const a=s.slice(0,-1);a.trim()&&(await this.addTag(a),t.value="");return}}bindEvents(){this.__bnd_keydown||(this.__bnd_keydown=this.handleInputKeydown.bind(this)),this.element.addEventListener("keydown",this.__bnd_keydown),this.events.bind(this.element)}unbindEvents(){this.__bnd_keydown&&this.element.removeEventListener("keydown",this.__bnd_keydown),this.events.unbind()}handleInputKeydown(e){const t=e.target,s=t.value||"";switch(e.key){case"Enter":case"Tab":case",":s.trim()&&(e.preventDefault(),this.addTag(s),t.value="");break;case"Backspace":s===""&&this.tags.length>0&&(e.preventDefault(),this.focusedTagIndex>=0?(this.removeTag(this.focusedTagIndex),this.focusedTagIndex==0?this.focus():this.focusTag(this.focusedTagIndex-1)):this.removeTag(this.tags.length-1));break;case"ArrowLeft":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex-1;i>=0?this.focusTag(i):this.focus()}else this.focusTag(this.tags.length-1);break;case"ArrowRight":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex+1;i<this.tags.length?this.focusTag(i):this.focus()}else this.focusTag(0);break;case"Escape":t.value="",t.blur();break}}async addTag(e){if(this.readonly||this.disabled)return!1;const t=this.trimTags?e.trim():e;return this.isValidTag(t)?!this.allowDuplicates&&this.tags.includes(t)?(this.showTagError(`Tag "${t}" already exists`),!1):this.tags.length>=this.maxTags?(this.showTagError(`Maximum ${this.maxTags} tags allowed`),!1):(this.tags.push(t),await this.updateDisplay(),this.emit("tag:added",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0):!1}async removeTag(e){if(this.readonly||this.disabled)return!1;if(e>=0&&e<this.tags.length){const t=this.tags[e];return this.tags.splice(e,1),await this.updateDisplay(),this.emit("tag:removed",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0}return!1}async removeTagByValue(e){const t=this.tags.indexOf(e);return t>=0?await this.removeTag(t):!1}async clearTags(){if(this.readonly||this.disabled)return!1;const e=[...this.tags];return this.tags=[],await this.updateDisplay(),this.emit("tags:cleared",{oldTags:e}),this.emit("change",{value:"",tags:[]}),!0}async setTags(e){let t=[];Array.isArray(e)?t=e:typeof e=="string"&&(t=this.parseTagString(e)),t=t.filter(s=>this.isValidTag(s)).slice(0,this.maxTags),this.allowDuplicates||(t=[...new Set(t)]),this.tags=t,await this.updateDisplay(),this.emit("tags:set",{tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags})}isValidTag(e){return!(typeof e!="string"||e.length<this.minLength||e.length>this.maxLength||e.trim()==="")}parseTagString(e){return e?e.split(this.separator).map(t=>this.trimTags?t.trim():t).filter(t=>t.length>0):[]}getTagString(){return this.tags.join(this.separator)}getTags(){return[...this.tags]}focusTag(e){const t=this.element.querySelectorAll(".tag-item");t[e]&&(this.focusedTagIndex=e,console.log(`Focused tag index: ${e}`),t[e].focus())}async updateDisplay(){const e=this.element.querySelector(".tags-container");e&&(e.innerHTML=this.renderTags());const t=this.element.querySelector(".tag-input-hidden");t&&(t.value=this.getTagString()),this.updateTagCount()}updateTagCount(){const e=this.element.querySelector(".tag-count");e&&(e.textContent=this.tags.length)}showTagError(e){let t=this.element.querySelector(".tag-error");if(!t){t=document.createElement("div"),t.className="tag-error small text-danger mt-1";const s=this.element.querySelector(".tag-input-feedback");s&&s.parentNode.insertBefore(t,s.nextSibling)}t.textContent=e,setTimeout(()=>{t.parentNode&&t.remove()},3e3)}setEnabled(e){this.disabled=!e;const t=this.element.querySelector(".tag-input-field");t&&(t.disabled=this.disabled);const s=this.element.querySelector(".tag-input-wrapper");s&&s.classList.toggle("disabled",this.disabled)}setReadonly(e){this.readonly=e;const t=this.element.querySelector(".tag-input-field");t&&(t.style.display=e?"none":""),this.element.querySelectorAll(".tag-remove").forEach(i=>{i.style.display=e?"none":""})}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}getFormValue(){return this.getTagString()}async setFormValue(e){await this.setTags(e)}static create(e={}){return new ce(e)}}class ze extends v{constructor(e={}){super({tagName:"div",className:"collection-dropdown-view dropdown-menu show w-100 position-absolute",style:"max-height: 250px; overflow-y: auto; z-index: 1000;",template:`
663
663
  {{#data.loading}}
664
664
  <div class="dropdown-item text-center">
665
665
  <div class="spinner-border spinner-border-sm" role="status">
@@ -687,7 +687,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
687
687
  </div>
688
688
  {{/data.showNoResults}}
689
689
  {{/data.loading}}
690
- `,...e}),this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.selectedValue=e.selectedValue||"",this.loading=e.loading||!1,this.hasSearched=e.hasSearched||!1,this.focusedIndex=e.focusedIndex||-1}async getViewData(){const e=this.collection?this.collection.toJSON().map((t,s)=>{const i=x.getNestedValue(t,this.labelField),a=x.getNestedValue(t,this.valueField);return{...t,labelField:i,valueField:a,isSelected:a==this.selectedValue,isFocused:s===this.focusedIndex,index:s}}):[];return{loading:this.loading,hasSearched:this.hasSearched,showNoResults:!this.loading&&this.hasSearched&&e.length===0,items:e}}async handleActionSelectItem(e,t){e.preventDefault();const s=t.getAttribute("data-value"),i=t.getAttribute("data-label");this.emit("item-selected",{value:s,label:i})}updateState(e){Object.assign(this,e)}updateFocusedItem(e){this.focusedIndex=e,this.element?.querySelectorAll('.dropdown-item[data-action="select-item"]')?.forEach((s,i)=>{s.classList.toggle("bg-light",i===this.focusedIndex)})}getItemCount(){return this.collection?this.collection.length():0}getFocusedItem(){return this.focusedIndex>=0&&this.collection&&this.collection.toJSON()[this.focusedIndex]||null}}class Ne extends v{constructor(e={}){super({className:"collection-select-view",template:`
690
+ `,...e}),this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.selectedValue=e.selectedValue||"",this.loading=e.loading||!1,this.hasSearched=e.hasSearched||!1,this.focusedIndex=e.focusedIndex||-1}async getViewData(){const e=this.collection?this.collection.toJSON().map((t,s)=>{const i=x.getNestedValue(t,this.labelField),a=x.getNestedValue(t,this.valueField);return{...t,labelField:i,valueField:a,isSelected:a==this.selectedValue,isFocused:s===this.focusedIndex,index:s}}):[];return{loading:this.loading,hasSearched:this.hasSearched,showNoResults:!this.loading&&this.hasSearched&&e.length===0,items:e}}async handleActionSelectItem(e,t){e.preventDefault();const s=t.getAttribute("data-value"),i=t.getAttribute("data-label");this.emit("item-selected",{value:s,label:i})}updateState(e){Object.assign(this,e)}updateFocusedItem(e){this.focusedIndex=e,this.element?.querySelectorAll('.dropdown-item[data-action="select-item"]')?.forEach((s,i)=>{s.classList.toggle("bg-light",i===this.focusedIndex)})}getItemCount(){return this.collection?this.collection.length():0}getFocusedItem(){return this.focusedIndex>=0&&this.collection&&this.collection.toJSON()[this.focusedIndex]||null}}class Be extends v{constructor(e={}){super({className:"collection-select-view",template:`
691
691
  <div class="position-relative">
692
692
  <input type="text"
693
693
  class="form-control {{#data.hasError}}is-invalid{{/data.hasError}} {{#data.showClear}}pe-5{{/data.showClear}}"
@@ -715,14 +715,14 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
715
715
  <div class="invalid-feedback">{{data.errorMessage}}</div>
716
716
  {{/data.hasError}}
717
717
  </div>
718
- `,...e}),this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.maxItems=e.maxItems||10,this.placeholder=e.placeholder||"Search...",this.debounceMs=e.debounceMs||400,this.name=e.name||"collection_select",this.emptyFetch=e.emptyFetch!==!1,this.requiresActiveGroup=e.requiresActiveGroup||!1,this.selectedValue=e.value||"0",this.selectedLabel="",this.searchValue="",this.showDropdown=!1,this.loading=!1,this.hasSearched=!1,this.focusedIndex=-1,this.hasError=!1,this.errorMessage="",this.selectedValue&&typeof this.selectedValue=="object"&&(this.selectedLabel=x.getNestedValue(this.selectedValue,this.labelField)||"",this.selectedValue=x.getNestedValue(this.selectedValue,this.valueField)||"0"),this.searchTimer=null,this.dropdownView=null,this.defaultParams={},this.defaultParamsOption=e.defaultParams||null,this.handleDocumentClick=this.handleDocumentClick.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleInputEvents=this.handleInputEvents.bind(this)}onInit(){this.collection&&this.setupCollection()}setupCollection(){if(this.defaultParams={...this.collection.params},this.collection.params.size=this.maxItems,this.defaultParams.size=this.maxItems,this.defaultParamsOption){const e=typeof this.defaultParamsOption=="function"?this.defaultParamsOption():this.defaultParamsOption;e&&typeof e=="object"&&(Object.assign(this.defaultParams,e),Object.assign(this.collection.params,e))}if(this.requiresActiveGroup){const e=this.getApp();e&&e.activeGroup&&e.activeGroup.id&&(this.collection.params.group=e.activeGroup.id,this.defaultParams.group=e.activeGroup.id)}this.collection.on("fetch:start",()=>{this.loading=!0,this.showDropdown=!0,this.updateDropdown()}),this.collection.on("fetch:end",()=>{this.loading=!1,this.showDropdown=!0,this.updateDropdown()}),this.selectedValue&&this.loadSelectedItem(),this.emptyFetch&&this.collection.isEmpty()&&this.performInitialFetch()}async performInitialFetch(){if(this.collection)try{const e={...this.defaultParams};delete e.search,await this.collection.updateParams(e,!0)}catch(e){console.error("Initial fetch error:",e)}}async loadSelectedItem(){try{if(!this.selectedValue||this.selectedValue=="0"||this.selectedLabel)return;const e=this.collection?.get(this.selectedValue);if(e){this.selectedLabel=this.getFieldValue(e,this.labelField),this.render(!1);return}let t=await this.collection.fetchOne(this.selectedValue);t&&(this.selectedLabel=this.getFieldValue(t,this.labelField)||`${t.constructor.name} #${t.id}`,this.render(!1))}catch(e){console.error("Error loading selected item:",e)}}async getViewData(){let e="";return this.showDropdown&&this.hasSearched?e=this.searchValue:this.selectedValue&&this.selectedLabel&&(e=this.selectedLabel),{name:this.name,placeholder:this.placeholder,displayValue:e,selectedValue:this.selectedValue,showClear:!!(this.selectedValue&&this.selectedValue!=="0"&&this.selectedLabel),hasError:this.hasError,errorMessage:this.errorMessage}}async onAfterRender(){await super.onAfterRender();const e=this.getInput();e&&(e.addEventListener("input",this.handleInputEvents),e.addEventListener("focus",this.handleInputEvents),e.addEventListener("keydown",this.handleKeyDown)),document.addEventListener("click",this.handleDocumentClick),this.createDropdownView()}async onBeforeDestroy(){await super.onBeforeDestroy();const e=this.getInput();e&&(e.removeEventListener("input",this.handleInputEvents),e.removeEventListener("focus",this.handleInputEvents),e.removeEventListener("keydown",this.handleKeyDown)),document.removeEventListener("click",this.handleDocumentClick),this.searchTimer&&clearTimeout(this.searchTimer),this.dropdownView&&this.dropdownView.destroy()}createDropdownView(){this.dropdownView&&this.dropdownView.destroy(),this.dropdownView=new Pe({collection:this.collection,labelField:this.labelField,valueField:this.valueField,selectedValue:this.selectedValue,loading:this.loading,hasSearched:this.hasSearched,focusedIndex:this.focusedIndex}),this.dropdownView.on("item-selected",e=>{this.selectItem(e.value,e.label)})}async handleInputEvents(e){const t=e.target;e.type==="focus"?(this.showDropdown=!0,!this.hasSearched&&this.emptyFetch&&this.collection?.isEmpty()&&this.performInitialFetch(),this.updateDropdown()):e.type==="input"&&(this.searchValue=t.value,this.showDropdown=!0,this.hasSearched=!0,this.focusedIndex=-1,this.searchValue!==this.selectedLabel&&(this.selectedValue="0",this.selectedLabel="",this.emit("change",{value:"0",label:""})),this.searchTimer&&clearTimeout(this.searchTimer),this.searchTimer=setTimeout(()=>{this.performSearch()},this.debounceMs),this.updateDropdown())}async handleActionClearSelection(e,t){e.preventDefault(),e.stopPropagation(),this.clearSelection()}clearSelection(){this.selectedValue="0",this.selectedLabel="",this.searchValue="",this.showDropdown=!1,this.hasError=!1,this.focusedIndex=-1,this.hasSearched=!1;const e=this.getInput();e&&(e.value="",e.focus());const t=this.getHiddenInput();t&&(t.value="0"),this.updateDropdown(),this.render(),this.emit("change",{value:"0",label:""})}async performSearch(){if(this.collection)try{const e={...this.defaultParams};this.searchValue&&this.searchValue.trim()&&(e.search=this.searchValue.trim()),await this.collection.updateParams(e,!0)}catch(e){console.error("Search error:",e),this.loading=!1,this.updateDropdown()}}updateDropdown(){if(this.dropdownView)if(this.dropdownView.updateState({selectedValue:this.selectedValue,loading:this.loading,hasSearched:this.hasSearched,focusedIndex:this.focusedIndex}),this.showDropdown)if(this.dropdownView.isMounted())this.dropdownView.render();else{const e=this.element?.querySelector(".dropdown-container");e&&this.dropdownView.render(!0,e)}else this.dropdownView.isMounted()&&(this.dropdownView.destroy(),this.createDropdownView())}selectItem(e,t){this.selectedValue=e,this.selectedLabel=t,this.searchValue="",this.showDropdown=!1,this.hasError=!1,this.focusedIndex=-1,this.hasSearched=!1;const s=this.getInput();s&&(s.value=t);const i=this.getHiddenInput();i&&(i.value=e),this.updateDropdown(),this.emit("change",{value:e,label:t})}handleDocumentClick(e){this.element?.contains(e.target)||(this.showDropdown=!1,this.focusedIndex=-1,this.updateDropdown())}handleKeyDown(e){if(!this.showDropdown||!this.collection)return;const t=this.dropdownView?.getItemCount()||0;switch(e.key){case"ArrowDown":e.preventDefault(),this.focusedIndex=Math.min(this.focusedIndex+1,t-1),this.dropdownView?.updateFocusedItem(this.focusedIndex);break;case"ArrowUp":e.preventDefault(),this.focusedIndex=Math.max(this.focusedIndex-1,0),this.dropdownView?.updateFocusedItem(this.focusedIndex);break;case"Enter":{e.preventDefault();const s=this.dropdownView?.getFocusedItem();s&&this.selectItem(s[this.valueField],s[this.labelField]);break}case"Escape":e.preventDefault(),this.showDropdown=!1,this.focusedIndex=-1,this.updateDropdown();break}}getInput(){return this.element?.querySelector('input[type="text"]')}getHiddenInput(){return this.element?.querySelector('input[type="hidden"]')}setValue(e,t=""){this.selectedValue=e,this.selectedLabel=t,this.searchValue="",this.hasError=!1,this.hasSearched=!1;const s=this.getInput();s&&(s.value=t);const i=this.getHiddenInput();i&&(i.value=e)}getValue(){return this.selectedValue===0||this.selectedValue==="0"?null:this.selectedValue}getLabel(){return this.selectedLabel}setError(e){this.hasError=!0,this.errorMessage=e,this.render()}clearError(){this.hasError=!1,this.errorMessage="",this.render()}focus(){const e=this.getInput();e&&e.focus()}getFormValue(){return this.selectedValue===0||this.selectedValue==="0"?null:this.selectedValue}setFormValue(e){let t=e,s="";t&&typeof t=="object"&&(s=x.getNestedValue(t,this.labelField)||"",t=x.getNestedValue(t,this.valueField)),t=t||"0",t!=this.selectedValue&&(this.selectedValue=t,this.selectedLabel=s,this.searchValue="",this.hasSearched=!1,this.showDropdown=!1,this.hasError=!1,this.selectedValue&&this.selectedValue!=="0"?(this.selectedLabel||(this.selectedLabel=`${this.collection.getModelName()} #${this.selectedValue}`),this.loadSelectedItem()):this.render())}getFieldValue(e,t){if(!(!e||!t)){if(typeof e.get=="function"){const s=e.get(t);return s===void 0&&t.includes(".")?x.getNestedValue(e,t):s}return x.getNestedValue(e,t)}}}class Oe extends v{constructor(e={}){super({tagName:"div",className:"collection-multiselect-search",template:`
718
+ `,...e}),this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.maxItems=e.maxItems||10,this.placeholder=e.placeholder||"Search...",this.debounceMs=e.debounceMs||400,this.name=e.name||"collection_select",this.emptyFetch=e.emptyFetch!==!1,this.requiresActiveGroup=e.requiresActiveGroup||!1,this.selectedValue=e.value||"0",this.selectedLabel="",this.searchValue="",this.showDropdown=!1,this.loading=!1,this.hasSearched=!1,this.focusedIndex=-1,this.hasError=!1,this.errorMessage="",this.selectedValue&&typeof this.selectedValue=="object"&&(this.selectedLabel=x.getNestedValue(this.selectedValue,this.labelField)||"",this.selectedValue=x.getNestedValue(this.selectedValue,this.valueField)||"0"),this.searchTimer=null,this.dropdownView=null,this.defaultParams={},this.defaultParamsOption=e.defaultParams||null,this.handleDocumentClick=this.handleDocumentClick.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleInputEvents=this.handleInputEvents.bind(this)}onInit(){this.collection&&this.setupCollection()}setupCollection(){if(this.defaultParams={...this.collection.params},this.collection.params.size=this.maxItems,this.defaultParams.size=this.maxItems,this.defaultParamsOption){const e=typeof this.defaultParamsOption=="function"?this.defaultParamsOption():this.defaultParamsOption;e&&typeof e=="object"&&(Object.assign(this.defaultParams,e),Object.assign(this.collection.params,e))}if(this.requiresActiveGroup){const e=this.getApp();e&&e.activeGroup&&e.activeGroup.id&&(this.collection.params.group=e.activeGroup.id,this.defaultParams.group=e.activeGroup.id)}this.collection.on("fetch:start",()=>{this.loading=!0,this.showDropdown=!0,this.updateDropdown()}),this.collection.on("fetch:end",()=>{this.loading=!1,this.showDropdown=!0,this.updateDropdown()}),this.selectedValue&&this.loadSelectedItem(),this.emptyFetch&&this.collection.isEmpty()&&this.performInitialFetch()}async performInitialFetch(){if(this.collection)try{const e={...this.defaultParams};delete e.search,await this.collection.updateParams(e,!0)}catch(e){console.error("Initial fetch error:",e)}}async loadSelectedItem(){try{if(!this.selectedValue||this.selectedValue=="0"||this.selectedLabel)return;const e=this.collection?.get(this.selectedValue);if(e){this.selectedLabel=this.getFieldValue(e,this.labelField),this.render(!1);return}let t=await this.collection.fetchOne(this.selectedValue);t&&(this.selectedLabel=this.getFieldValue(t,this.labelField)||`${t.constructor.name} #${t.id}`,this.render(!1))}catch(e){console.error("Error loading selected item:",e)}}async getViewData(){let e="";return this.showDropdown&&this.hasSearched?e=this.searchValue:this.selectedValue&&this.selectedLabel&&(e=this.selectedLabel),{name:this.name,placeholder:this.placeholder,displayValue:e,selectedValue:this.selectedValue,showClear:!!(this.selectedValue&&this.selectedValue!=="0"&&this.selectedLabel),hasError:this.hasError,errorMessage:this.errorMessage}}async onAfterRender(){await super.onAfterRender();const e=this.getInput();e&&(e.addEventListener("input",this.handleInputEvents),e.addEventListener("focus",this.handleInputEvents),e.addEventListener("keydown",this.handleKeyDown)),document.addEventListener("click",this.handleDocumentClick),this.createDropdownView()}async onBeforeDestroy(){await super.onBeforeDestroy();const e=this.getInput();e&&(e.removeEventListener("input",this.handleInputEvents),e.removeEventListener("focus",this.handleInputEvents),e.removeEventListener("keydown",this.handleKeyDown)),document.removeEventListener("click",this.handleDocumentClick),this.searchTimer&&clearTimeout(this.searchTimer),this.dropdownView&&this.dropdownView.destroy()}createDropdownView(){this.dropdownView&&this.dropdownView.destroy(),this.dropdownView=new ze({collection:this.collection,labelField:this.labelField,valueField:this.valueField,selectedValue:this.selectedValue,loading:this.loading,hasSearched:this.hasSearched,focusedIndex:this.focusedIndex}),this.dropdownView.on("item-selected",e=>{this.selectItem(e.value,e.label)})}async handleInputEvents(e){const t=e.target;e.type==="focus"?(this.showDropdown=!0,!this.hasSearched&&this.emptyFetch&&this.collection?.isEmpty()&&this.performInitialFetch(),this.updateDropdown()):e.type==="input"&&(this.searchValue=t.value,this.showDropdown=!0,this.hasSearched=!0,this.focusedIndex=-1,this.searchValue!==this.selectedLabel&&(this.selectedValue="0",this.selectedLabel="",this.emit("change",{value:"0",label:""})),this.searchTimer&&clearTimeout(this.searchTimer),this.searchTimer=setTimeout(()=>{this.performSearch()},this.debounceMs),this.updateDropdown())}async handleActionClearSelection(e,t){e.preventDefault(),e.stopPropagation(),this.clearSelection()}clearSelection(){this.selectedValue="0",this.selectedLabel="",this.searchValue="",this.showDropdown=!1,this.hasError=!1,this.focusedIndex=-1,this.hasSearched=!1;const e=this.getInput();e&&(e.value="",e.focus());const t=this.getHiddenInput();t&&(t.value="0"),this.updateDropdown(),this.render(),this.emit("change",{value:"0",label:""})}async performSearch(){if(this.collection)try{const e={...this.defaultParams};this.searchValue&&this.searchValue.trim()&&(e.search=this.searchValue.trim()),await this.collection.updateParams(e,!0)}catch(e){console.error("Search error:",e),this.loading=!1,this.updateDropdown()}}updateDropdown(){if(this.dropdownView)if(this.dropdownView.updateState({selectedValue:this.selectedValue,loading:this.loading,hasSearched:this.hasSearched,focusedIndex:this.focusedIndex}),this.showDropdown)if(this.dropdownView.isMounted())this.dropdownView.render();else{const e=this.element?.querySelector(".dropdown-container");e&&this.dropdownView.render(!0,e)}else this.dropdownView.isMounted()&&(this.dropdownView.destroy(),this.createDropdownView())}selectItem(e,t){this.selectedValue=e,this.selectedLabel=t,this.searchValue="",this.showDropdown=!1,this.hasError=!1,this.focusedIndex=-1,this.hasSearched=!1;const s=this.getInput();s&&(s.value=t);const i=this.getHiddenInput();i&&(i.value=e),this.updateDropdown(),this.emit("change",{value:e,label:t})}handleDocumentClick(e){this.element?.contains(e.target)||(this.showDropdown=!1,this.focusedIndex=-1,this.updateDropdown())}handleKeyDown(e){if(!this.showDropdown||!this.collection)return;const t=this.dropdownView?.getItemCount()||0;switch(e.key){case"ArrowDown":e.preventDefault(),this.focusedIndex=Math.min(this.focusedIndex+1,t-1),this.dropdownView?.updateFocusedItem(this.focusedIndex);break;case"ArrowUp":e.preventDefault(),this.focusedIndex=Math.max(this.focusedIndex-1,0),this.dropdownView?.updateFocusedItem(this.focusedIndex);break;case"Enter":{e.preventDefault();const s=this.dropdownView?.getFocusedItem();s&&this.selectItem(s[this.valueField],s[this.labelField]);break}case"Escape":e.preventDefault(),this.showDropdown=!1,this.focusedIndex=-1,this.updateDropdown();break}}getInput(){return this.element?.querySelector('input[type="text"]')}getHiddenInput(){return this.element?.querySelector('input[type="hidden"]')}setValue(e,t=""){this.selectedValue=e,this.selectedLabel=t,this.searchValue="",this.hasError=!1,this.hasSearched=!1;const s=this.getInput();s&&(s.value=t);const i=this.getHiddenInput();i&&(i.value=e)}getValue(){return this.selectedValue===0||this.selectedValue==="0"?null:this.selectedValue}getLabel(){return this.selectedLabel}setError(e){this.hasError=!0,this.errorMessage=e,this.render()}clearError(){this.hasError=!1,this.errorMessage="",this.render()}focus(){const e=this.getInput();e&&e.focus()}getFormValue(){return this.selectedValue===0||this.selectedValue==="0"?null:this.selectedValue}setFormValue(e){let t=e,s="";t&&typeof t=="object"&&(s=x.getNestedValue(t,this.labelField)||"",t=x.getNestedValue(t,this.valueField)),t=t||"0",t!=this.selectedValue&&(this.selectedValue=t,this.selectedLabel=s,this.searchValue="",this.hasSearched=!1,this.showDropdown=!1,this.hasError=!1,this.selectedValue&&this.selectedValue!=="0"?(this.selectedLabel||(this.selectedLabel=`${this.collection.getModelName()} #${this.selectedValue}`),this.loadSelectedItem()):this.render())}getFieldValue(e,t){if(!(!e||!t)){if(typeof e.get=="function"){const s=e.get(t);return s===void 0&&t.includes(".")?x.getNestedValue(e,t):s}return x.getNestedValue(e,t)}}}class Ue extends v{constructor(e={}){super({tagName:"div",className:"collection-multiselect-search",template:`
719
719
  <input type="text"
720
720
  class="form-control form-control-sm mb-2"
721
721
  placeholder="{{placeholder}}"
722
722
  data-change-action="search"
723
723
  data-filter="live-search"
724
724
  data-filter-debounce="{{debounce}}" />
725
- `,...e}),this.placeholder=e.placeholder||"Search...",this.debounce=e.debounce||400}async onChangeSearch(e,t){const s=t.value.trim();this.emit("search",s)}getValue(){return this.element?.querySelector("input")?.value||""}clear(){const e=this.element?.querySelector("input");e&&(e.value="")}}class He extends v{constructor(e={}){const s=!!e.customItemTemplate?"{{{customContent}}}":'<span {{#disabled}}class="text-muted"{{/disabled}}>{{label}}</span>';super({tagName:"div",className:"collection-multiselect-items",template:`
725
+ `,...e}),this.placeholder=e.placeholder||"Search...",this.debounce=e.debounce||400}async onChangeSearch(e,t){const s=t.value.trim();this.emit("search",s)}getValue(){return this.element?.querySelector("input")?.value||""}clear(){const e=this.element?.querySelector("input");e&&(e.value="")}}class Ye extends v{constructor(e={}){const s=!!e.customItemTemplate?"{{{customContent}}}":'<span {{#disabled}}class="text-muted"{{/disabled}}>{{label}}</span>';super({tagName:"div",className:"collection-multiselect-items",template:`
726
726
  {{#loading}}
727
727
  <div class="text-center py-3">
728
728
  <div class="spinner-border spinner-border-sm" role="status">
@@ -774,7 +774,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
774
774
  </div>
775
775
  {{/^items.length}}
776
776
  {{/loading}}
777
- `,...e}),this.items=e.items||[],this.loading=e.loading||!1,this.maxHeight=e.maxHeight||336,this.showSelectAll=e.showSelectAll!==!1,this.selectedCount=e.selectedCount||0,this.totalCount=e.totalCount||0,this.unselectedCount=e.unselectedCount||0,this.allSelected=e.allSelected||!1,this.noneSelected=e.noneSelected||!0,this.customItemTemplate=e.customItemTemplate||null,this.lastClickedIndex=-1}handleActionToggle(e,t){const s=t.getAttribute("data-value"),i=parseInt(t.getAttribute("data-index"),10);this.emit("toggle",{value:s,index:i,shiftKey:e.shiftKey,element:t}),this.lastClickedIndex=i}updateItemCheckbox(e,t){const s=e.querySelector("i.bi");s&&(t?(s.classList.remove("bi-square"),s.classList.add("bi-check-square-fill","text-primary")):(s.classList.remove("bi-check-square-fill","text-primary"),s.classList.add("bi-square")))}updateActionButtons(){const e=this.element?.querySelector('[data-action="select-all"]'),t=this.element?.querySelector('[data-action="deselect-all"]');e&&(e.querySelector("span"),this.allSelected?(e.classList.add("text-muted"),e.disabled=!0):(e.classList.remove("text-muted"),e.disabled=!1)),t&&(this.noneSelected?(t.classList.add("text-muted"),t.disabled=!0):(t.classList.remove("text-muted"),t.disabled=!1))}async handleActionSelectAll(e){e.preventDefault(),this.emit("select-all")}async handleActionDeselectAll(e){e.preventDefault(),this.emit("deselect-all")}updateState(e){Object.assign(this,e)}}class qe extends v{constructor(e={}){super({tagName:"div",className:"collection-multiselect-view",template:`
777
+ `,...e}),this.items=e.items||[],this.loading=e.loading||!1,this.maxHeight=e.maxHeight||336,this.showSelectAll=e.showSelectAll!==!1,this.selectedCount=e.selectedCount||0,this.totalCount=e.totalCount||0,this.unselectedCount=e.unselectedCount||0,this.allSelected=e.allSelected||!1,this.noneSelected=e.noneSelected||!0,this.customItemTemplate=e.customItemTemplate||null,this.lastClickedIndex=-1}handleActionToggle(e,t){const s=t.getAttribute("data-value"),i=parseInt(t.getAttribute("data-index"),10);this.emit("toggle",{value:s,index:i,shiftKey:e.shiftKey,element:t}),this.lastClickedIndex=i}updateItemCheckbox(e,t){const s=e.querySelector("i.bi");s&&(t?(s.classList.remove("bi-square"),s.classList.add("bi-check-square-fill","text-primary")):(s.classList.remove("bi-check-square-fill","text-primary"),s.classList.add("bi-square")))}updateActionButtons(){const e=this.element?.querySelector('[data-action="select-all"]'),t=this.element?.querySelector('[data-action="deselect-all"]');e&&(e.querySelector("span"),this.allSelected?(e.classList.add("text-muted"),e.disabled=!0):(e.classList.remove("text-muted"),e.disabled=!1)),t&&(this.noneSelected?(t.classList.add("text-muted"),t.disabled=!0):(t.classList.remove("text-muted"),t.disabled=!1))}async handleActionSelectAll(e){e.preventDefault(),this.emit("select-all")}async handleActionDeselectAll(e){e.preventDefault(),this.emit("deselect-all")}updateState(e){Object.assign(this,e)}}class We extends v{constructor(e={}){super({tagName:"div",className:"collection-multiselect-view",template:`
778
778
  <div class="mojo-form-control">
779
779
  {{#label}}
780
780
  <label class="form-label">
@@ -792,7 +792,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
792
792
  <div class="invalid-feedback d-block">{{error}}</div>
793
793
  {{/error}}
794
794
  </div>
795
- `,...e}),this.name=e.name||"collection_multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.excludeIds=e.excludeIds||[],this.ignoreIds=e.ignoreIds||[],this.itemTemplate=e.itemTemplate||null,this.collectionParams=e.collectionParams||{},this.defaultParamsOption=e.defaultParams||null,this.baseParams={},this.requiresActiveGroup=e.requiresActiveGroup||!1,this.size=e.size||8,this.maxHeight=e.maxHeight||this.size*42,this.showSelectAll=e.showSelectAll!==!1,this.enableSearch=e.enableSearch||!1,this.searchPlaceholder=e.searchPlaceholder||"Search...",this.searchDebounce=e.searchDebounce||400,this.selectedValues=Array.isArray(e.value)?e.value:[],this.loading=!1,this.items=[],this.searchView=null,this.listView=null}onInit(){this.collection&&this.setupCollection()}setupCollection(){if(this.baseParams={...this.collection.params},Object.keys(this.collectionParams).length>0&&(Object.assign(this.baseParams,this.collectionParams),Object.assign(this.collection.params,this.collectionParams)),this.defaultParamsOption){const e=typeof this.defaultParamsOption=="function"?this.defaultParamsOption():this.defaultParamsOption;e&&(Object.assign(this.baseParams,e),Object.assign(this.collection.params,e))}if(this.requiresActiveGroup){const e=this.getApp();e?.activeGroup?.id&&(this.baseParams.group=e.activeGroup.id,this.collection.params.group=e.activeGroup.id)}this.collection.on("fetch:start",()=>{this.loading=!0,this.updateListView()}),this.collection.on("fetch:end",()=>{this.loading=!1,this.buildItems(),this.updateListView()}),this.collection.isEmpty()||this.buildItems()}async onAfterRender(){await super.onAfterRender(),this.enableSearch&&this.createSearchView(),this.createListView(),this.collection?.isEmpty()&&this.collection.fetch()}createSearchView(){const e=this.element?.querySelector(".collection-multiselect-search-container");e&&(this.searchView=new Oe({placeholder:this.searchPlaceholder,debounce:this.searchDebounce}),this.searchView.on("search",t=>{this.handleSearch(t)}),this.searchView.render(!0,e))}createListView(){const e=this.element?.querySelector(".collection-multiselect-list-container");if(!e)return;const t=this.selectedValues.length,s=this.items.length,i=s-t;this.listView=new He({items:this.items,loading:this.loading,maxHeight:this.maxHeight,showSelectAll:this.showSelectAll,selectedCount:t,totalCount:s,unselectedCount:i,allSelected:t===s&&s>0,noneSelected:t===0,customItemTemplate:this.itemTemplate}),this.listView.on("toggle",a=>{this.handleToggle(a)}),this.listView.on("select-all",()=>{this.selectAll()}),this.listView.on("deselect-all",()=>{this.deselectAll()}),this.listView.render(!0,e)}updateListView(){if(this.listView){const e=this.selectedValues.length,t=this.items.length,s=t-e;this.listView.updateState({items:this.items,loading:this.loading,selectedCount:e,totalCount:t,unselectedCount:s,allSelected:e===t&&t>0,noneSelected:e===0}),this.listView.render(!1)}}buildItems(){const e=this.collection.models.filter(t=>{const s=this.getFieldValue(t,this.valueField);return!(s==null||this.excludeIds.includes(s)||this.ignoreIds.some(i=>i==s))});this.items=e.map((t,s)=>{const i=t.toJSON?t.toJSON():t,a=this.getFieldValue(t,this.valueField),r={label:this.getFieldValue(t,this.labelField),value:a,index:s,selected:this.selectedValues.some(n=>n==a),disabled:this.disabled,model:i};return this.itemTemplate&&(r.customContent=this.renderItemTemplate(r)),r})}renderItemTemplate(e){if(!this.itemTemplate)return"";try{return this.renderTemplateString(this.itemTemplate,e)}catch(t){return console.error("Error rendering item template:",t),e.label}}getFieldValue(e,t){if(!(!e||!t))return typeof e.get=="function"?e.get(t)??x.getNestedValue(e,t):x.getNestedValue(e,t)}handleSearch(e){const t={...this.baseParams};e&&(t.search=e),this.collection.updateParams(t,!0)}handleToggle({value:e,index:t,shiftKey:s,element:i}){if(s&&this.listView.lastClickedIndex>=0){const a=Math.min(this.listView.lastClickedIndex,t),r=Math.max(this.listView.lastClickedIndex,t),n=!this.items[t].selected;for(let o=a;o<=r;o++){const l=this.items[o];l.disabled||(n?this.selectedValues.includes(l.value)||this.selectedValues.push(l.value):this.selectedValues=this.selectedValues.filter(c=>c!=l.value),l.selected=n)}this.updateListView()}else{const a=this.items[t];a.selected?(this.selectedValues=this.selectedValues.filter(r=>r!=e),a.selected=!1):(this.selectedValues.push(e),a.selected=!0),i&&this.listView&&(this.listView.updateItemCheckbox(i,a.selected),this.listView.selectedCount=this.selectedValues.length,this.listView.unselectedCount=this.items.length-this.selectedValues.length,this.listView.allSelected=this.selectedValues.length===this.items.length&&this.items.length>0,this.listView.noneSelected=this.selectedValues.length===0,this.listView.updateActionButtons())}this.emit("change",{value:this.selectedValues,name:this.name})}selectAll(){this.selectedValues=this.items.filter(e=>!e.disabled).map(e=>e.value),this.items.forEach(e=>{e.disabled||(e.selected=!0)}),this.updateListView(),this.emit("change",{value:this.selectedValues,name:this.name})}deselectAll(){this.selectedValues=[],this.items.forEach(e=>e.selected=!1),this.updateListView(),this.emit("change",{value:this.selectedValues,name:this.name})}async onBeforeDestroy(){await super.onBeforeDestroy(),this.searchView&&this.searchView.destroy(),this.listView&&this.listView.destroy()}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}setExcludeIds(e){this.excludeIds=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}setIgnoreIds(e){this.ignoreIds=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}async refresh(){await this.collection.fetch()}getFormValue(){return this.selectedValues}setFormValue(e){this.setValue(e)}}class je extends v{constructor(e={}){super({tagName:"div",className:"multiselect-items",template:`
795
+ `,...e}),this.name=e.name||"collection_multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.excludeIds=e.excludeIds||[],this.ignoreIds=e.ignoreIds||[],this.itemTemplate=e.itemTemplate||null,this.collectionParams=e.collectionParams||{},this.defaultParamsOption=e.defaultParams||null,this.baseParams={},this.requiresActiveGroup=e.requiresActiveGroup||!1,this.size=e.size||8,this.maxHeight=e.maxHeight||this.size*42,this.showSelectAll=e.showSelectAll!==!1,this.enableSearch=e.enableSearch||!1,this.searchPlaceholder=e.searchPlaceholder||"Search...",this.searchDebounce=e.searchDebounce||400,this.selectedValues=Array.isArray(e.value)?e.value:[],this.loading=!1,this.items=[],this.searchView=null,this.listView=null}onInit(){this.collection&&this.setupCollection()}setupCollection(){if(this.baseParams={...this.collection.params},Object.keys(this.collectionParams).length>0&&(Object.assign(this.baseParams,this.collectionParams),Object.assign(this.collection.params,this.collectionParams)),this.defaultParamsOption){const e=typeof this.defaultParamsOption=="function"?this.defaultParamsOption():this.defaultParamsOption;e&&(Object.assign(this.baseParams,e),Object.assign(this.collection.params,e))}if(this.requiresActiveGroup){const e=this.getApp();e?.activeGroup?.id&&(this.baseParams.group=e.activeGroup.id,this.collection.params.group=e.activeGroup.id)}this.collection.on("fetch:start",()=>{this.loading=!0,this.updateListView()}),this.collection.on("fetch:end",()=>{this.loading=!1,this.buildItems(),this.updateListView()}),this.collection.isEmpty()||this.buildItems()}async onAfterRender(){await super.onAfterRender(),this.enableSearch&&this.createSearchView(),this.createListView(),this.collection?.isEmpty()&&this.collection.fetch()}createSearchView(){const e=this.element?.querySelector(".collection-multiselect-search-container");e&&(this.searchView=new Ue({placeholder:this.searchPlaceholder,debounce:this.searchDebounce}),this.searchView.on("search",t=>{this.handleSearch(t)}),this.searchView.render(!0,e))}createListView(){const e=this.element?.querySelector(".collection-multiselect-list-container");if(!e)return;const t=this.selectedValues.length,s=this.items.length,i=s-t;this.listView=new Ye({items:this.items,loading:this.loading,maxHeight:this.maxHeight,showSelectAll:this.showSelectAll,selectedCount:t,totalCount:s,unselectedCount:i,allSelected:t===s&&s>0,noneSelected:t===0,customItemTemplate:this.itemTemplate}),this.listView.on("toggle",a=>{this.handleToggle(a)}),this.listView.on("select-all",()=>{this.selectAll()}),this.listView.on("deselect-all",()=>{this.deselectAll()}),this.listView.render(!0,e)}updateListView(){if(this.listView){const e=this.selectedValues.length,t=this.items.length,s=t-e;this.listView.updateState({items:this.items,loading:this.loading,selectedCount:e,totalCount:t,unselectedCount:s,allSelected:e===t&&t>0,noneSelected:e===0}),this.listView.render(!1)}}buildItems(){const e=this.collection.models.filter(t=>{const s=this.getFieldValue(t,this.valueField);return!(s==null||this.excludeIds.includes(s)||this.ignoreIds.some(i=>i==s))});this.items=e.map((t,s)=>{const i=t.toJSON?t.toJSON():t,a=this.getFieldValue(t,this.valueField),r={label:this.getFieldValue(t,this.labelField),value:a,index:s,selected:this.selectedValues.some(n=>n==a),disabled:this.disabled,model:i};return this.itemTemplate&&(r.customContent=this.renderItemTemplate(r)),r})}renderItemTemplate(e){if(!this.itemTemplate)return"";try{return this.renderTemplateString(this.itemTemplate,e)}catch(t){return console.error("Error rendering item template:",t),e.label}}getFieldValue(e,t){if(!(!e||!t))return typeof e.get=="function"?e.get(t)??x.getNestedValue(e,t):x.getNestedValue(e,t)}handleSearch(e){const t={...this.baseParams};e&&(t.search=e),this.collection.updateParams(t,!0)}handleToggle({value:e,index:t,shiftKey:s,element:i}){if(s&&this.listView.lastClickedIndex>=0){const a=Math.min(this.listView.lastClickedIndex,t),r=Math.max(this.listView.lastClickedIndex,t),n=!this.items[t].selected;for(let o=a;o<=r;o++){const l=this.items[o];l.disabled||(n?this.selectedValues.includes(l.value)||this.selectedValues.push(l.value):this.selectedValues=this.selectedValues.filter(c=>c!=l.value),l.selected=n)}this.updateListView()}else{const a=this.items[t];a.selected?(this.selectedValues=this.selectedValues.filter(r=>r!=e),a.selected=!1):(this.selectedValues.push(e),a.selected=!0),i&&this.listView&&(this.listView.updateItemCheckbox(i,a.selected),this.listView.selectedCount=this.selectedValues.length,this.listView.unselectedCount=this.items.length-this.selectedValues.length,this.listView.allSelected=this.selectedValues.length===this.items.length&&this.items.length>0,this.listView.noneSelected=this.selectedValues.length===0,this.listView.updateActionButtons())}this.emit("change",{value:this.selectedValues,name:this.name})}selectAll(){this.selectedValues=this.items.filter(e=>!e.disabled).map(e=>e.value),this.items.forEach(e=>{e.disabled||(e.selected=!0)}),this.updateListView(),this.emit("change",{value:this.selectedValues,name:this.name})}deselectAll(){this.selectedValues=[],this.items.forEach(e=>e.selected=!1),this.updateListView(),this.emit("change",{value:this.selectedValues,name:this.name})}async onBeforeDestroy(){await super.onBeforeDestroy(),this.searchView&&this.searchView.destroy(),this.listView&&this.listView.destroy()}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}setExcludeIds(e){this.excludeIds=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}setIgnoreIds(e){this.ignoreIds=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}async refresh(){await this.collection.fetch()}getFormValue(){return this.selectedValues}setFormValue(e){this.setValue(e)}}class Je extends v{constructor(e={}){super({tagName:"div",className:"multiselect-items",template:`
796
796
  {{#items.length}}
797
797
  <div class="multiselect-list" style="max-height: {{maxHeight}}px; overflow-y: auto;">
798
798
  {{#items}}
@@ -823,7 +823,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
823
823
  <small>No options available</small>
824
824
  </div>
825
825
  {{/items.length}}
826
- `,...e}),this.items=e.items||[],this.maxHeight=e.maxHeight||300}handleActionToggle(e,t){const s=t.getAttribute("data-value"),i=parseInt(t.getAttribute("data-index"),10),a=this.items[i];if(!a||a.disabled)return;a.selected=!a.selected;const r=t.querySelector('input[type="checkbox"]');r&&(r.checked=a.selected),this.emit("toggle",{value:s,index:i,selected:a.selected})}handleActionCloseDropdown(e,t){this.emit("close-dropdown")}getValue(){return this.items.filter(e=>e.selected).map(e=>e.value)}setValue(e){const t=new Set(Array.isArray(e)?e:[e]);this.items.forEach(s=>{s.selected=t.has(s.value)}),this.render(!1)}updateItems(e){this.items=e,this.render(!1)}}class Re extends v{constructor(e={}){super({tagName:"div",className:"multiselect-dropdown",template:`
826
+ `,...e}),this.items=e.items||[],this.maxHeight=e.maxHeight||300}handleActionToggle(e,t){const s=t.getAttribute("data-value"),i=parseInt(t.getAttribute("data-index"),10),a=this.items[i];if(!a||a.disabled)return;a.selected=!a.selected;const r=t.querySelector('input[type="checkbox"]');r&&(r.checked=a.selected),this.emit("toggle",{value:s,index:i,selected:a.selected})}handleActionCloseDropdown(e,t){this.emit("close-dropdown")}getValue(){return this.items.filter(e=>e.selected).map(e=>e.value)}setValue(e){const t=new Set(Array.isArray(e)?e:[e]);this.items.forEach(s=>{s.selected=t.has(s.value)}),this.render(!1)}updateItems(e){this.items=e,this.render(!1)}}class Ge extends v{constructor(e={}){super({tagName:"div",className:"multiselect-dropdown",template:`
827
827
  <div class="mojo-form-control">
828
828
  {{#label}}
829
829
  <label class="form-label">
@@ -850,7 +850,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
850
850
  <div class="invalid-feedback d-block">{{error}}</div>
851
851
  {{/error}}
852
852
  </div>
853
- `,...e}),this.name=e.name||"multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.placeholder=e.placeholder||e.placeHolder||"Select...",this.maxHeight=e.maxHeight||300,this.showSelectedLabels=e.showSelectedLabels!==!1,this.maxLabelsToShow=e.maxLabelsToShow||3,this.options=e.options||[],this.selectedValues=Array.isArray(e.value)?e.value:[],this.buttonText=this.computeButtonText(),this.listView=null}computeButtonText(){const e=this.selectedValues.length;return e===0?this.placeholder||"Select...":this.showSelectedLabels&&e<=this.maxLabelsToShow?this.selectedValues.map(s=>{const i=this.options.find(a=>(typeof a=="string"?a:a.value)===s);return typeof i=="string"?i:i?.label||i?.value||s}).join(", "):`${e} selected`}async onAfterRender(){await super.onAfterRender(),this.createListView()}createListView(){const e=this.element?.querySelector('[data-container="items"]');if(!e)return;const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView=new je({items:t,maxHeight:this.maxHeight}),this.listView.on("toggle",s=>{this.handleToggle(s)}),this.listView.on("close-dropdown",()=>{this.closeDropdown()}),this.listView.render(!0,e)}closeDropdown(){const e=this.element?.querySelector(".dropdown-toggle");if(e&&window.bootstrap?.Dropdown){const t=window.bootstrap.Dropdown.getInstance(e);t&&t.hide()}}handleToggle(e){const{value:t,selected:s}=e;s?this.selectedValues.includes(t)||this.selectedValues.push(t):this.selectedValues=this.selectedValues.filter(i=>i!==t),this.updateButtonText(),this.emit("change",{value:this.selectedValues,name:this.name})}updateButtonText(){const e=this.element?.querySelector(".multiselect-button-text");if(!e)return;const t=this.selectedValues.length;this.buttonText=this.computeButtonText(),e.textContent=this.buttonText,t===0?e.classList.add("text-muted"):e.classList.remove("text-muted")}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:e?[e]:[],this.listView&&this.listView.setValue(this.selectedValues),this.updateButtonText()}setOptions(e){if(this.options=e,this.listView){const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView.updateItems(t)}}clear(){this.setValue([])}getFormValue(){return this.getValue()}setFormValue(e){this.setValue(e)}async onBeforeDestroy(){await super.onBeforeDestroy(),this.listView&&this.listView.destroy()}}class le extends v{constructor(e={}){const{name:t,value:s="",format:i="YYYY-MM-DD",displayFormat:a="MMM DD, YYYY",min:r=null,max:n=null,placeholder:o="Select date...",disabled:l=!1,readonly:c=!1,required:d=!1,class:h="",inputClass:f="form-control",autoApply:m=!0,inline:p=!1,...g}=e;super({tagName:"div",className:`date-picker-view ${h}`,...g}),this.name=t,this.format=i,this.displayFormat=a,this.min=r,this.max=n,this.placeholder=o,this.disabled=l,this.readonly=c,this.required=d,this.inputClass=f,this.autoApply=m,this.inline=p,this.currentValue=s,this.picker=null,this.useNative=!1,this.easepickLoaded=!1,this.checkEasepickAvailability()}async checkEasepickAvailability(){if(typeof window<"u"&&window.easepick)return this.easepickLoaded=!0,!0;try{return await this.loadEasepick(),this.easepickLoaded=!0,!0}catch(e){return console.warn("Easepick failed to load, falling back to native date input:",e),this.useNative=!0,!1}}async loadEasepick(){return new Promise((e,t)=>{if(window.easepick){e();return}const s=document.createElement("link");s.rel="stylesheet",s.href="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",document.head.appendChild(s);const i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js",i.onload=()=>{window.easepick?e():t(new Error("Easepick not available after loading"))},i.onerror=()=>t(new Error("Failed to load Easepick script")),document.head.appendChild(i)})}async renderTemplate(){const e=this.getInputId(),t=this.useNative?"date":"text",s=this.formatValueForInput(this.currentValue);return`
853
+ `,...e}),this.name=e.name||"multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.placeholder=e.placeholder||e.placeHolder||"Select...",this.maxHeight=e.maxHeight||300,this.showSelectedLabels=e.showSelectedLabels!==!1,this.maxLabelsToShow=e.maxLabelsToShow||3,this.options=e.options||[],this.selectedValues=Array.isArray(e.value)?e.value:[],this.buttonText=this.computeButtonText(),this.listView=null}computeButtonText(){const e=this.selectedValues.length;return e===0?this.placeholder||"Select...":this.showSelectedLabels&&e<=this.maxLabelsToShow?this.selectedValues.map(s=>{const i=this.options.find(a=>(typeof a=="string"?a:a.value)===s);return typeof i=="string"?i:i?.label||i?.value||s}).join(", "):`${e} selected`}async onAfterRender(){await super.onAfterRender(),this.createListView()}createListView(){const e=this.element?.querySelector('[data-container="items"]');if(!e)return;const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView=new Je({items:t,maxHeight:this.maxHeight}),this.listView.on("toggle",s=>{this.handleToggle(s)}),this.listView.on("close-dropdown",()=>{this.closeDropdown()}),this.listView.render(!0,e)}closeDropdown(){const e=this.element?.querySelector(".dropdown-toggle");if(e&&window.bootstrap?.Dropdown){const t=window.bootstrap.Dropdown.getInstance(e);t&&t.hide()}}handleToggle(e){const{value:t,selected:s}=e;s?this.selectedValues.includes(t)||this.selectedValues.push(t):this.selectedValues=this.selectedValues.filter(i=>i!==t),this.updateButtonText(),this.emit("change",{value:this.selectedValues,name:this.name})}updateButtonText(){const e=this.element?.querySelector(".multiselect-button-text");if(!e)return;const t=this.selectedValues.length;this.buttonText=this.computeButtonText(),e.textContent=this.buttonText,t===0?e.classList.add("text-muted"):e.classList.remove("text-muted")}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:e?[e]:[],this.listView&&this.listView.setValue(this.selectedValues),this.updateButtonText()}setOptions(e){if(this.options=e,this.listView){const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView.updateItems(t)}}clear(){this.setValue([])}getFormValue(){return this.getValue()}setFormValue(e){this.setValue(e)}async onBeforeDestroy(){await super.onBeforeDestroy(),this.listView&&this.listView.destroy()}}class de extends v{constructor(e={}){const{name:t,value:s="",format:i="YYYY-MM-DD",displayFormat:a="MMM DD, YYYY",min:r=null,max:n=null,placeholder:o="Select date...",disabled:l=!1,readonly:c=!1,required:d=!1,class:h="",inputClass:m="form-control",autoApply:p=!0,inline:f=!1,...g}=e;super({tagName:"div",className:`date-picker-view ${h}`,...g}),this.name=t,this.format=i,this.displayFormat=a,this.min=r,this.max=n,this.placeholder=o,this.disabled=l,this.readonly=c,this.required=d,this.inputClass=m,this.autoApply=p,this.inline=f,this.currentValue=s,this.picker=null,this.useNative=!1,this.easepickLoaded=!1,this.checkEasepickAvailability()}async checkEasepickAvailability(){if(typeof window<"u"&&window.easepick)return this.easepickLoaded=!0,!0;try{return await this.loadEasepick(),this.easepickLoaded=!0,!0}catch(e){return console.warn("Easepick failed to load, falling back to native date input:",e),this.useNative=!0,!1}}async loadEasepick(){return new Promise((e,t)=>{if(window.easepick){e();return}const s=document.createElement("link");s.rel="stylesheet",s.href="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",document.head.appendChild(s);const i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js",i.onload=()=>{window.easepick?e():t(new Error("Easepick not available after loading"))},i.onerror=()=>t(new Error("Failed to load Easepick script")),document.head.appendChild(i)})}async renderTemplate(){const e=this.getInputId(),t=this.useNative?"date":"text",s=this.formatValueForInput(this.currentValue);return`
854
854
  <div class="date-picker-container">
855
855
  <input
856
856
  type="${t}"
@@ -869,7 +869,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
869
869
  />
870
870
  <div class="date-picker-feedback"></div>
871
871
  </div>
872
- `}async onAfterRender(){await super.onAfterRender(),this.easepickLoaded&&!this.useNative?await this.initializeEasepick():this.initializeNativeFallback()}async initializeEasepick(){const e=this.getInputElement();if(!(!e||!window.easepick))try{const t={element:e,css:["https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css"],format:this.displayFormat,lang:"en-US",autoApply:this.autoApply,inline:this.inline,readonly:this.readonly,zIndex:9999};this.min&&(t.minDate=new Date(this.min)),this.max&&(t.maxDate=new Date(this.max)),t.setup=s=>{s.on("select",i=>{const a=i.detail.date;this.handleDateChange(a?this.formatDate(a,this.format):"")}),s.on("clear",()=>{this.handleDateChange("")}),s.on("show",()=>{this.emit("picker:show")}),s.on("hide",()=>{this.emit("picker:hide")})},this.picker=new window.easepick.create(t),this.currentValue&&this.picker.setDate(this.currentValue)}catch(t){console.error("Failed to initialize Easepick:",t),this.useNative=!0,this.initializeNativeFallback()}}initializeNativeFallback(){const e=this.getInputElement();e&&(e.type="date",this.currentValue&&(e.value=this.formatDate(this.currentValue,"YYYY-MM-DD")))}async onChangeDateChanged(e,t,s){const i=s.value;this.handleDateChange(i)}handleDateChange(e){const t=this.currentValue;this.currentValue=e,this.updateHiddenInput(),t!==e&&(this.emit("change",{value:e,formatted:this.formatValueForDisplay(e),oldValue:t}),this.emit("date:changed",{value:e,oldValue:t}))}formatDate(e,t=this.format){if(!e)return"";const s=new Date(e);if(isNaN(s.getTime()))return"";const i=s.getFullYear(),a=String(s.getMonth()+1).padStart(2,"0"),r=String(s.getDate()).padStart(2,"0");switch(t){case"YYYY-MM-DD":return`${i}-${a}-${r}`;case"MM/DD/YYYY":return`${a}/${r}/${i}`;case"DD/MM/YYYY":return`${r}/${a}/${i}`;case"MMM DD, YYYY":return`${["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][s.getMonth()]} ${r}, ${i}`;default:return`${i}-${a}-${r}`}}formatValueForInput(e){return e?this.useNative?this.formatDate(e,"YYYY-MM-DD"):e:""}formatValueForDisplay(e){return e?this.formatDate(e,this.displayFormat):""}getInputId(){return this.name?`datepicker_${this.name}_${Date.now()}`:`datepicker_${Date.now()}`}getInputElement(){return this.element?.querySelector("input")}updateHiddenInput(){}hasError(){return!1}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}setValue(e){if(this.currentValue=e,this.picker&&this.easepickLoaded)this.picker.setDate(e||null);else{const t=this.getInputElement();t&&(t.value=this.formatValueForInput(e))}this.emit("value:set",{value:e})}getValue(){return this.currentValue}getFormattedValue(e=this.displayFormat){return this.formatDate(this.currentValue,e)}clear(){this.setValue("")}setMin(e){if(this.min=e,this.picker&&this.easepickLoaded)this.picker.options.minDate=new Date(e);else{const t=this.getInputElement();t&&(t.min=e)}}setMax(e){if(this.max=e,this.picker&&this.easepickLoaded)this.picker.options.maxDate=new Date(e);else{const t=this.getInputElement();t&&(t.max=e)}}setEnabled(e){this.disabled=!e;const t=this.getInputElement();t&&(t.disabled=this.disabled),this.picker&&this.easepickLoaded&&this.disabled&&this.picker.hide()}setReadonly(e){this.readonly=e;const t=this.getInputElement();t&&(t.readonly=e)}focus(){const e=this.getInputElement();e&&e.focus()}show(){this.picker&&this.easepickLoaded&&this.picker.show()}hide(){this.picker&&this.easepickLoaded&&this.picker.hide()}getFormValue(){return this.getValue()}async setFormValue(e){this.setValue(e)}async onBeforeDestroy(){if(this.picker&&this.easepickLoaded)try{this.picker.destroy()}catch(e){console.warn("Error destroying Easepick instance:",e)}this.picker=null,await super.onBeforeDestroy()}static create(e={}){return new le(e)}}class ce extends v{constructor(e={}){const{name:t,startName:s,endName:i,fieldName:a,startDate:r="",endDate:n="",format:o="YYYY-MM-DD",displayFormat:l="MMM DD, YYYY",outputFormat:c="date",min:d=null,max:h=null,placeholder:f="Select date range...",startPlaceholder:m="Start date...",endPlaceholder:p="End date...",disabled:g=!1,readonly:y=!1,required:b=!1,class:w="",inputClass:C="form-control",autoApply:$=!0,inline:S=!1,separator:M=" - ",...L}=e;super({tagName:"div",className:`date-range-picker-view ${w}`,...L}),this.name=t,this.startName=s,this.endName=i,this.fieldName=a,this.format=o,this.displayFormat=l,this.outputFormat=c,this.min=d,this.max=h,this.placeholder=f,this.startPlaceholder=m,this.endPlaceholder=p,this.disabled=g,this.readonly=y,this.required=b,this.inputClass=C,this.autoApply=$,this.inline=S,this.separator=M,this.currentStartDate=r,this.currentEndDate=n,this.picker=null,this.useNative=!1,this.easepickLoaded=!1,this.checkEasepickAvailability()}async checkEasepickAvailability(){if(typeof window<"u"&&window.easepick)return this.easepickLoaded=!0,!0;try{return await this.loadEasepick(),this.easepickLoaded=!0,!0}catch(e){return console.warn("Easepick failed to load, falling back to native date inputs:",e),this.useNative=!0,!1}}async loadEasepick(){return new Promise((e,t)=>{if(window.easepick){e();return}const s=document.createElement("link");s.rel="stylesheet",s.href="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",document.head.appendChild(s);const i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js",i.onload=()=>{window.easepick?e():t(new Error("Easepick not available after loading"))},i.onerror=()=>t(new Error("Failed to load Easepick script")),document.head.appendChild(i)})}async renderTemplate(){const e=this.getInputId(),t=this.getDisplayValue();if(this.useNative)return this.renderNativeTemplate(e);const s=this.startName||(this.name?`${this.name}_start`:""),i=this.endName||(this.name?`${this.name}_end`:""),a=this.currentStartDate?this.formatForOutput(this.currentStartDate):"",r=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";return`
872
+ `}async onAfterRender(){await super.onAfterRender(),this.easepickLoaded&&!this.useNative?await this.initializeEasepick():this.initializeNativeFallback()}async initializeEasepick(){const e=this.getInputElement();if(!(!e||!window.easepick))try{const t={element:e,css:["https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css"],format:this.displayFormat,lang:"en-US",autoApply:this.autoApply,inline:this.inline,readonly:this.readonly,zIndex:9999};this.min&&(t.minDate=new Date(this.min)),this.max&&(t.maxDate=new Date(this.max)),t.setup=s=>{s.on("select",i=>{const a=i.detail.date;this.handleDateChange(a?this.formatDate(a,this.format):"")}),s.on("clear",()=>{this.handleDateChange("")}),s.on("show",()=>{this.emit("picker:show")}),s.on("hide",()=>{this.emit("picker:hide")})},this.picker=new window.easepick.create(t),this.currentValue&&this.picker.setDate(this.currentValue)}catch(t){console.error("Failed to initialize Easepick:",t),this.useNative=!0,this.initializeNativeFallback()}}initializeNativeFallback(){const e=this.getInputElement();e&&(e.type="date",this.currentValue&&(e.value=this.formatDate(this.currentValue,"YYYY-MM-DD")))}async onChangeDateChanged(e,t,s){const i=s.value;this.handleDateChange(i)}handleDateChange(e){const t=this.currentValue;this.currentValue=e,this.updateHiddenInput(),t!==e&&(this.emit("change",{value:e,formatted:this.formatValueForDisplay(e),oldValue:t}),this.emit("date:changed",{value:e,oldValue:t}))}formatDate(e,t=this.format){if(!e)return"";const s=new Date(e);if(isNaN(s.getTime()))return"";const i=s.getFullYear(),a=String(s.getMonth()+1).padStart(2,"0"),r=String(s.getDate()).padStart(2,"0");switch(t){case"YYYY-MM-DD":return`${i}-${a}-${r}`;case"MM/DD/YYYY":return`${a}/${r}/${i}`;case"DD/MM/YYYY":return`${r}/${a}/${i}`;case"MMM DD, YYYY":return`${["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][s.getMonth()]} ${r}, ${i}`;default:return`${i}-${a}-${r}`}}formatValueForInput(e){return e?this.useNative?this.formatDate(e,"YYYY-MM-DD"):e:""}formatValueForDisplay(e){return e?this.formatDate(e,this.displayFormat):""}getInputId(){return this.name?`datepicker_${this.name}_${Date.now()}`:`datepicker_${Date.now()}`}getInputElement(){return this.element?.querySelector("input")}updateHiddenInput(){}hasError(){return!1}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}setValue(e){if(this.currentValue=e,this.picker&&this.easepickLoaded)this.picker.setDate(e||null);else{const t=this.getInputElement();t&&(t.value=this.formatValueForInput(e))}this.emit("value:set",{value:e})}getValue(){return this.currentValue}getFormattedValue(e=this.displayFormat){return this.formatDate(this.currentValue,e)}clear(){this.setValue("")}setMin(e){if(this.min=e,this.picker&&this.easepickLoaded)this.picker.options.minDate=new Date(e);else{const t=this.getInputElement();t&&(t.min=e)}}setMax(e){if(this.max=e,this.picker&&this.easepickLoaded)this.picker.options.maxDate=new Date(e);else{const t=this.getInputElement();t&&(t.max=e)}}setEnabled(e){this.disabled=!e;const t=this.getInputElement();t&&(t.disabled=this.disabled),this.picker&&this.easepickLoaded&&this.disabled&&this.picker.hide()}setReadonly(e){this.readonly=e;const t=this.getInputElement();t&&(t.readonly=e)}focus(){const e=this.getInputElement();e&&e.focus()}show(){this.picker&&this.easepickLoaded&&this.picker.show()}hide(){this.picker&&this.easepickLoaded&&this.picker.hide()}getFormValue(){return this.getValue()}async setFormValue(e){this.setValue(e)}async onBeforeDestroy(){if(this.picker&&this.easepickLoaded)try{this.picker.destroy()}catch(e){console.warn("Error destroying Easepick instance:",e)}this.picker=null,await super.onBeforeDestroy()}static create(e={}){return new de(e)}}class he extends v{constructor(e={}){const{name:t,startName:s,endName:i,fieldName:a,startDate:r="",endDate:n="",format:o="YYYY-MM-DD",displayFormat:l="MMM DD, YYYY",outputFormat:c="date",min:d=null,max:h=null,placeholder:m="Select date range...",startPlaceholder:p="Start date...",endPlaceholder:f="End date...",disabled:g=!1,readonly:y=!1,required:b=!1,class:w="",inputClass:C="form-control",autoApply:$=!0,inline:S=!1,separator:M=" - ",..._}=e;super({tagName:"div",className:`date-range-picker-view ${w}`,..._}),this.name=t,this.startName=s,this.endName=i,this.fieldName=a,this.format=o,this.displayFormat=l,this.outputFormat=c,this.min=d,this.max=h,this.placeholder=m,this.startPlaceholder=p,this.endPlaceholder=f,this.disabled=g,this.readonly=y,this.required=b,this.inputClass=C,this.autoApply=$,this.inline=S,this.separator=M,this.currentStartDate=r,this.currentEndDate=n,this.picker=null,this.useNative=!1,this.easepickLoaded=!1,this.checkEasepickAvailability()}async checkEasepickAvailability(){if(typeof window<"u"&&window.easepick)return this.easepickLoaded=!0,!0;try{return await this.loadEasepick(),this.easepickLoaded=!0,!0}catch(e){return console.warn("Easepick failed to load, falling back to native date inputs:",e),this.useNative=!0,!1}}async loadEasepick(){return new Promise((e,t)=>{if(window.easepick){e();return}const s=document.createElement("link");s.rel="stylesheet",s.href="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",document.head.appendChild(s);const i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js",i.onload=()=>{window.easepick?e():t(new Error("Easepick not available after loading"))},i.onerror=()=>t(new Error("Failed to load Easepick script")),document.head.appendChild(i)})}async renderTemplate(){const e=this.getInputId(),t=this.getDisplayValue();if(this.useNative)return this.renderNativeTemplate(e);const s=this.startName||(this.name?`${this.name}_start`:""),i=this.endName||(this.name?`${this.name}_end`:""),a=this.currentStartDate?this.formatForOutput(this.currentStartDate):"",r=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";return`
873
873
  <div class="date-range-picker-container">
874
874
  <input
875
875
  type="text"
@@ -939,7 +939,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
939
939
 
940
940
  <div class="date-range-picker-feedback"></div>
941
941
  </div>
942
- `}async onAfterRender(){await super.onAfterRender(),this.easepickLoaded&&!this.useNative?await this.initializeEasepick():this.initializeNativeFallback()}async initializeEasepick(){const e=this.getInputElement();if(!(!e||!window.easepick))try{const t={element:e,css:["https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css"],format:this.displayFormat,lang:"en-US",autoApply:this.autoApply,inline:this.inline,readonly:this.readonly,zIndex:9999,plugins:["RangePlugin"],RangePlugin:{tooltip:!0,locale:{one:"day",other:"days"}}};this.min&&(t.minDate=new Date(this.min)),this.max&&(t.maxDate=new Date(this.max)),t.setup=s=>{s.on("select",i=>{const{start:a,end:r}=i.detail,n=a?this.normalizeDateFromPicker(a):"",o=r?this.normalizeDateFromPicker(r):"";this.handleRangeChange(n,o)}),s.on("clear",()=>{this.handleRangeChange("","")}),s.on("show",()=>{this.emit("picker:show")}),s.on("hide",()=>{this.emit("picker:hide")}),s.on("ready",()=>{this.applyInitialRange(s)})},this.picker=new window.easepick.create(t),this.applyInitialRange(this.picker)}catch(t){console.error("Failed to initialize Easepick range picker:",t),this.useNative=!0,await this.render(),this.initializeNativeFallback()}}initializeNativeFallback(){this.updateConstraints()}async onChangeRangeChanged(e,t,s){}async onChangeStartDateChanged(e,t,s){const i=s.value;this.handleRangeChange(i,this.currentEndDate),this.updateConstraints()}async onChangeEndDateChanged(e,t,s){const i=s.value;this.handleRangeChange(this.currentStartDate,i),this.updateConstraints()}handleRangeChange(e,t){const s=this.currentStartDate,i=this.currentEndDate;this.currentStartDate=e,this.currentEndDate=t,this.updateHiddenInputs(),(s!==e||i!==t)&&(this.emit("change",{startDate:e,endDate:t,combined:this.getCombinedValue(),formatted:this.getDisplayValue(),oldStartDate:s,oldEndDate:i}),this.emit("range:changed",{startDate:e,endDate:t,oldStartDate:s,oldEndDate:i}))}updateConstraints(){if(!this.useNative)return;const e=this.element?.querySelector(`[name="${this.name}_start"]`),t=this.element?.querySelector(`[name="${this.name}_end"]`);e&&t&&(this.currentStartDate&&(t.min=this.currentStartDate),this.currentEndDate&&(e.max=this.currentEndDate))}normalizeDateFromPicker(e){if(!e)return"";if(typeof e.toJSDate=="function"){const t=e.toJSDate();return this.formatDate(t,this.format)}if(typeof e.getFullYear=="function"){const t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),i=String(e.getDate()).padStart(2,"0");switch(this.format){case"YYYY-MM-DD":return`${t}-${s}-${i}`;case"MM/DD/YYYY":return`${s}/${i}/${t}`;case"DD/MM/YYYY":return`${i}/${s}/${t}`;default:return`${t}-${s}-${i}`}}return this.formatDate(e,this.format)}formatDate(e,t=this.format){if(!e)return"";let s,i,a,r;if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e)){const n=e.split("-");s=parseInt(n[0]),i=String(parseInt(n[1])).padStart(2,"0"),a=String(parseInt(n[2])).padStart(2,"0")}else{if(e instanceof Date?r=e:r=new Date(e),isNaN(r.getTime()))return"";s=r.getFullYear(),i=String(r.getMonth()+1).padStart(2,"0"),a=String(r.getDate()).padStart(2,"0")}switch(t){case"YYYY-MM-DD":return`${s}-${i}-${a}`;case"MM/DD/YYYY":return`${i}/${a}/${s}`;case"DD/MM/YYYY":return`${a}/${i}/${s}`;case"MMM DD, YYYY":{const n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=parseInt(i)-1;return`${n[o]} ${a}, ${s}`}default:return`${s}-${i}-${a}`}}formatForOutput(e){if(!e)return"";if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e))switch(this.outputFormat){case"epoch":const s=e.split("-"),i=new Date(parseInt(s[0]),parseInt(s[1])-1,parseInt(s[2]));return Math.floor(i.getTime()/1e3).toString();case"iso":return new Date(e+"T00:00:00").toISOString();default:return e}const t=new Date(e);if(isNaN(t.getTime()))return"";switch(this.outputFormat){case"epoch":return Math.floor(t.getTime()/1e3).toString();case"iso":return t.toISOString();default:return this.formatDate(e,this.format)}}getDisplayValue(){if(!this.currentStartDate&&!this.currentEndDate)return"";const e=this.currentStartDate?this.formatDate(this.currentStartDate,this.displayFormat):"",t=this.currentEndDate?this.formatDate(this.currentEndDate,this.displayFormat):"";return e&&t?`${e}${this.separator}${t}`:e||t||""}getCombinedValue(){return!this.currentStartDate&&!this.currentEndDate?"":JSON.stringify({start:this.currentStartDate,end:this.currentEndDate})}getInputId(){return this.name?`daterange_${this.name}_${Date.now()}`:`daterange_${Date.now()}`}getInputElement(){return this.element?.querySelector('input[type="text"], input[name="'+this.name+'"]')}updateHiddenInputs(){const e=this.startName||(this.name?`${this.name}_start`:""),t=this.endName||(this.name?`${this.name}_end`:""),s=e?this.element?.querySelector(`[name="${e}"]`):null,i=t?this.element?.querySelector(`[name="${t}"]`):null,a=this.name?this.element?.querySelector(`[name="${this.name}"]`):null,r=this.fieldName?this.element?.querySelector(`[name="${this.fieldName}"]`):null,n=this.currentStartDate?this.formatForOutput(this.currentStartDate):"",o=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";s&&(s.value=n),i&&(i.value=o),a&&(a.value=this.getDisplayValue()),r&&(r.value=this.name||"")}hasError(){return this.currentStartDate&&this.currentEndDate?new Date(this.currentEndDate)<new Date(this.currentStartDate):!1}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}setRange(e,t){if(this.currentStartDate=e,this.currentEndDate=t,this.picker&&this.easepickLoaded){const s=this.normalizeDateValue(e),i=this.normalizeDateValue(t);this.picker.setDateRange(s||null,i||null)}else if(this.useNative){const s=this.element?.querySelector(`[name="${this.name}_start"]`),i=this.element?.querySelector(`[name="${this.name}_end"]`);s&&(s.value=this.formatDate(e,"YYYY-MM-DD")),i&&(i.value=this.formatDate(t,"YYYY-MM-DD"))}else{const s=this.getInputElement();s&&(s.value=this.getDisplayValue())}this.updateHiddenInputs(),this.emit("range:set",{startDate:e,endDate:t})}applyInitialRange(e){if(!e||!(this.currentStartDate||this.currentEndDate))return;const t=this.normalizeDateValue(this.currentStartDate),s=this.normalizeDateValue(this.currentEndDate);(t||s)&&e.setDateRange(t||null,s||null)}normalizeDateValue(e){if(!e&&e!==0)return null;if(e instanceof Date)return isNaN(e)?null:e;const t=String(e).trim();if(!t)return null;if(/^\d{4}-\d{2}-\d{2}$/.test(t)){const[i,a,r]=t.split("-").map(Number),n=new Date(i,a-1,r);return isNaN(n)?null:n}if(/^-?\d+$/.test(t)){const i=Number(t),a=t.length<=10?i*1e3:i,r=new Date(a);return isNaN(r)?null:r}const s=new Date(t);return isNaN(s)?null:s}getRange(){return{start:this.currentStartDate,end:this.currentEndDate,combined:this.getCombinedValue()}}clear(){this.setRange("","")}setStartDate(e){this.setRange(e,this.currentEndDate)}setEndDate(e){this.setRange(this.currentStartDate,e)}getStartDate(){return this.currentStartDate}getEndDate(){return this.currentEndDate}setEnabled(e){this.disabled=!e,this.element?.querySelectorAll("input")?.forEach(s=>{s.disabled=this.disabled})}setReadonly(e){this.readonly=e,this.element?.querySelectorAll('input:not([type="hidden"])')?.forEach(s=>{s.readonly=e})}focus(){const e=this.getInputElement();e&&e.focus()}show(){this.picker&&this.easepickLoaded&&this.picker.show()}hide(){this.picker&&this.easepickLoaded&&this.picker.hide()}getFormValue(){return this.getRange()}async setFormValue(e){if(typeof e=="string")try{const t=JSON.parse(e);this.setRange(t.start,t.end)}catch{this.setRange(e,"")}else e&&typeof e=="object"&&this.setRange(e.start||"",e.end||"")}async onBeforeDestroy(){if(this.picker&&this.easepickLoaded)try{this.picker.destroy()}catch(e){console.warn("Error destroying Easepick range picker instance:",e)}this.picker=null,await super.onBeforeDestroy()}static create(e={}){return new ce(e)}}class de extends v{constructor(e={}){const{name:t,value:s="",placeholder:i="Select or type...",options:a=[],allowCustom:r=!0,showDescription:n=!0,minChars:o=0,maxSuggestions:l=10,disabled:c=!1,readonly:d=!1,required:h=!1,class:f="",inputClass:m="form-control",onSelect:p=null,onChange:g=null,...y}=e;super({tagName:"div",className:`combo-input ${f}`,...y}),this.name=t,this.placeholder=i,this.options=this.normalizeOptions(a),this.allowCustom=r,this.showDescription=n,this.minChars=o,this.maxSuggestions=l,this.disabled=c,this.readonly=d,this.required=h,this.inputClass=m,this.onSelectCallback=p,this.onChangeCallback=g,this.currentValue=s,this.inputValue=this.getDisplayValue(s),this.filteredOptions=[],this.highlightedIndex=-1,this.isOpen=!1,this.selectedOption=this.findOptionByValue(s)}normalizeOptions(e){return Array.isArray(e)?e.map(t=>typeof t=="string"?{value:t,label:t}:typeof t=="object"&&t.value!==void 0?{value:t.value,label:t.label||String(t.value),description:t.description||t.label||"",meta:t.meta||{}}:null).filter(t=>t!==null):[]}findOptionByValue(e){return this.options.find(t=>t.value===e)||null}getDisplayValue(e){const t=this.findOptionByValue(e);return t?t.label:e}async renderTemplate(){return`
942
+ `}async onAfterRender(){await super.onAfterRender(),this.easepickLoaded&&!this.useNative?await this.initializeEasepick():this.initializeNativeFallback()}async initializeEasepick(){const e=this.getInputElement();if(!(!e||!window.easepick))try{const t={element:e,css:["https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css"],format:this.displayFormat,lang:"en-US",autoApply:this.autoApply,inline:this.inline,readonly:this.readonly,zIndex:9999,plugins:["RangePlugin"],RangePlugin:{tooltip:!0,locale:{one:"day",other:"days"}}};this.min&&(t.minDate=new Date(this.min)),this.max&&(t.maxDate=new Date(this.max)),t.setup=s=>{s.on("select",i=>{const{start:a,end:r}=i.detail,n=a?this.normalizeDateFromPicker(a):"",o=r?this.normalizeDateFromPicker(r):"";this.handleRangeChange(n,o)}),s.on("clear",()=>{this.handleRangeChange("","")}),s.on("show",()=>{this.emit("picker:show")}),s.on("hide",()=>{this.emit("picker:hide")}),s.on("ready",()=>{this.applyInitialRange(s)})},this.picker=new window.easepick.create(t),this.applyInitialRange(this.picker)}catch(t){console.error("Failed to initialize Easepick range picker:",t),this.useNative=!0,await this.render(),this.initializeNativeFallback()}}initializeNativeFallback(){this.updateConstraints()}async onChangeRangeChanged(e,t,s){}async onChangeStartDateChanged(e,t,s){const i=s.value;this.handleRangeChange(i,this.currentEndDate),this.updateConstraints()}async onChangeEndDateChanged(e,t,s){const i=s.value;this.handleRangeChange(this.currentStartDate,i),this.updateConstraints()}handleRangeChange(e,t){const s=this.currentStartDate,i=this.currentEndDate;this.currentStartDate=e,this.currentEndDate=t,this.updateHiddenInputs(),(s!==e||i!==t)&&(this.emit("change",{startDate:e,endDate:t,combined:this.getCombinedValue(),formatted:this.getDisplayValue(),oldStartDate:s,oldEndDate:i}),this.emit("range:changed",{startDate:e,endDate:t,oldStartDate:s,oldEndDate:i}))}updateConstraints(){if(!this.useNative)return;const e=this.element?.querySelector(`[name="${this.name}_start"]`),t=this.element?.querySelector(`[name="${this.name}_end"]`);e&&t&&(this.currentStartDate&&(t.min=this.currentStartDate),this.currentEndDate&&(e.max=this.currentEndDate))}normalizeDateFromPicker(e){if(!e)return"";if(typeof e.toJSDate=="function"){const t=e.toJSDate();return this.formatDate(t,this.format)}if(typeof e.getFullYear=="function"){const t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),i=String(e.getDate()).padStart(2,"0");switch(this.format){case"YYYY-MM-DD":return`${t}-${s}-${i}`;case"MM/DD/YYYY":return`${s}/${i}/${t}`;case"DD/MM/YYYY":return`${i}/${s}/${t}`;default:return`${t}-${s}-${i}`}}return this.formatDate(e,this.format)}formatDate(e,t=this.format){if(!e)return"";let s,i,a,r;if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e)){const n=e.split("-");s=parseInt(n[0]),i=String(parseInt(n[1])).padStart(2,"0"),a=String(parseInt(n[2])).padStart(2,"0")}else{if(e instanceof Date?r=e:r=new Date(e),isNaN(r.getTime()))return"";s=r.getFullYear(),i=String(r.getMonth()+1).padStart(2,"0"),a=String(r.getDate()).padStart(2,"0")}switch(t){case"YYYY-MM-DD":return`${s}-${i}-${a}`;case"MM/DD/YYYY":return`${i}/${a}/${s}`;case"DD/MM/YYYY":return`${a}/${i}/${s}`;case"MMM DD, YYYY":{const n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=parseInt(i)-1;return`${n[o]} ${a}, ${s}`}default:return`${s}-${i}-${a}`}}formatForOutput(e){if(!e)return"";if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e))switch(this.outputFormat){case"epoch":const s=e.split("-"),i=new Date(parseInt(s[0]),parseInt(s[1])-1,parseInt(s[2]));return Math.floor(i.getTime()/1e3).toString();case"iso":return new Date(e+"T00:00:00").toISOString();default:return e}const t=new Date(e);if(isNaN(t.getTime()))return"";switch(this.outputFormat){case"epoch":return Math.floor(t.getTime()/1e3).toString();case"iso":return t.toISOString();default:return this.formatDate(e,this.format)}}getDisplayValue(){if(!this.currentStartDate&&!this.currentEndDate)return"";const e=this.currentStartDate?this.formatDate(this.currentStartDate,this.displayFormat):"",t=this.currentEndDate?this.formatDate(this.currentEndDate,this.displayFormat):"";return e&&t?`${e}${this.separator}${t}`:e||t||""}getCombinedValue(){return!this.currentStartDate&&!this.currentEndDate?"":JSON.stringify({start:this.currentStartDate,end:this.currentEndDate})}getInputId(){return this.name?`daterange_${this.name}_${Date.now()}`:`daterange_${Date.now()}`}getInputElement(){return this.element?.querySelector('input[type="text"], input[name="'+this.name+'"]')}updateHiddenInputs(){const e=this.startName||(this.name?`${this.name}_start`:""),t=this.endName||(this.name?`${this.name}_end`:""),s=e?this.element?.querySelector(`[name="${e}"]`):null,i=t?this.element?.querySelector(`[name="${t}"]`):null,a=this.name?this.element?.querySelector(`[name="${this.name}"]`):null,r=this.fieldName?this.element?.querySelector(`[name="${this.fieldName}"]`):null,n=this.currentStartDate?this.formatForOutput(this.currentStartDate):"",o=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";s&&(s.value=n),i&&(i.value=o),a&&(a.value=this.getDisplayValue()),r&&(r.value=this.name||"")}hasError(){return this.currentStartDate&&this.currentEndDate?new Date(this.currentEndDate)<new Date(this.currentStartDate):!1}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}setRange(e,t){if(this.currentStartDate=e,this.currentEndDate=t,this.picker&&this.easepickLoaded){const s=this.normalizeDateValue(e),i=this.normalizeDateValue(t);this.picker.setDateRange(s||null,i||null)}else if(this.useNative){const s=this.element?.querySelector(`[name="${this.name}_start"]`),i=this.element?.querySelector(`[name="${this.name}_end"]`);s&&(s.value=this.formatDate(e,"YYYY-MM-DD")),i&&(i.value=this.formatDate(t,"YYYY-MM-DD"))}else{const s=this.getInputElement();s&&(s.value=this.getDisplayValue())}this.updateHiddenInputs(),this.emit("range:set",{startDate:e,endDate:t})}applyInitialRange(e){if(!e||!(this.currentStartDate||this.currentEndDate))return;const t=this.normalizeDateValue(this.currentStartDate),s=this.normalizeDateValue(this.currentEndDate);(t||s)&&e.setDateRange(t||null,s||null)}normalizeDateValue(e){if(!e&&e!==0)return null;if(e instanceof Date)return isNaN(e)?null:e;const t=String(e).trim();if(!t)return null;if(/^\d{4}-\d{2}-\d{2}$/.test(t)){const[i,a,r]=t.split("-").map(Number),n=new Date(i,a-1,r);return isNaN(n)?null:n}if(/^-?\d+$/.test(t)){const i=Number(t),a=t.length<=10?i*1e3:i,r=new Date(a);return isNaN(r)?null:r}const s=new Date(t);return isNaN(s)?null:s}getRange(){return{start:this.currentStartDate,end:this.currentEndDate,combined:this.getCombinedValue()}}clear(){this.setRange("","")}setStartDate(e){this.setRange(e,this.currentEndDate)}setEndDate(e){this.setRange(this.currentStartDate,e)}getStartDate(){return this.currentStartDate}getEndDate(){return this.currentEndDate}setEnabled(e){this.disabled=!e,this.element?.querySelectorAll("input")?.forEach(s=>{s.disabled=this.disabled})}setReadonly(e){this.readonly=e,this.element?.querySelectorAll('input:not([type="hidden"])')?.forEach(s=>{s.readonly=e})}focus(){const e=this.getInputElement();e&&e.focus()}show(){this.picker&&this.easepickLoaded&&this.picker.show()}hide(){this.picker&&this.easepickLoaded&&this.picker.hide()}getFormValue(){return this.getRange()}async setFormValue(e){if(typeof e=="string")try{const t=JSON.parse(e);this.setRange(t.start,t.end)}catch{this.setRange(e,"")}else e&&typeof e=="object"&&this.setRange(e.start||"",e.end||"")}async onBeforeDestroy(){if(this.picker&&this.easepickLoaded)try{this.picker.destroy()}catch(e){console.warn("Error destroying Easepick range picker instance:",e)}this.picker=null,await super.onBeforeDestroy()}static create(e={}){return new he(e)}}class ue extends v{constructor(e={}){const{name:t,value:s="",placeholder:i="Select or type...",options:a=[],allowCustom:r=!0,showDescription:n=!0,minChars:o=0,maxSuggestions:l=10,disabled:c=!1,readonly:d=!1,required:h=!1,class:m="",inputClass:p="form-control",onSelect:f=null,onChange:g=null,...y}=e;super({tagName:"div",className:`combo-input ${m}`,...y}),this.name=t,this.placeholder=i,this.options=this.normalizeOptions(a),this.allowCustom=r,this.showDescription=n,this.minChars=o,this.maxSuggestions=l,this.disabled=c,this.readonly=d,this.required=h,this.inputClass=p,this.onSelectCallback=f,this.onChangeCallback=g,this.currentValue=s,this.inputValue=this.getDisplayValue(s),this.filteredOptions=[],this.highlightedIndex=-1,this.isOpen=!1,this.selectedOption=this.findOptionByValue(s)}normalizeOptions(e){return Array.isArray(e)?e.map(t=>typeof t=="string"?{value:t,label:t}:typeof t=="object"&&t.value!==void 0?{value:t.value,label:t.label||String(t.value),description:t.description||t.label||"",meta:t.meta||{}}:null).filter(t=>t!==null):[]}findOptionByValue(e){return this.options.find(t=>t.value===e)||null}getDisplayValue(e){const t=this.findOptionByValue(e);return t?t.label:e}async renderTemplate(){return`
943
943
  <div class="combo-input-container position-relative">
944
944
  <div class="input-wrapper position-relative">
945
945
  ${this.renderInput()}
@@ -1011,7 +1011,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1011
1011
  <i class="bi bi-search me-1"></i>
1012
1012
  No matching options found.
1013
1013
  </div>
1014
- `}highlightMatch(e){if(!this.inputValue)return this.escapeHtml(e);const t=this.escapeHtml(e),s=new RegExp(`(${this.escapeRegex(this.inputValue)})`,"gi");return t.replace(s,'<mark class="bg-warning bg-opacity-25">$1</mark>')}async onAfterRender(){await super.onAfterRender(),this.updateFilteredOptions(),this.handleOutsideClick=e=>{this.element&&!this.element.contains(e.target)&&this.closeDropdown()},document.addEventListener("click",this.handleOutsideClick)}async onChangeInputChange(e,t){this.inputValue=t.value,this.updateFilteredOptions(),this.inputValue.length>=this.minChars?this.openDropdown():this.closeDropdown(),this.highlightedIndex=-1,await this.updateDropdownDisplay()}async onActionInputKeydown(e,t){switch(e.key){case"ArrowDown":e.preventDefault(),this.isOpen?this.highlightNext():this.openDropdown(),await this.updateDropdownDisplay();break;case"ArrowUp":e.preventDefault(),this.isOpen&&(this.highlightPrevious(),await this.updateDropdownDisplay());break;case"Enter":e.preventDefault(),this.isOpen&&this.highlightedIndex>=0?await this.selectHighlightedOption():this.allowCustom&&this.inputValue&&await this.selectCustomValue(this.inputValue);break;case"Escape":e.preventDefault(),this.closeDropdown();const s=this.element.querySelector(".combo-input-field");s&&(s.value=this.getDisplayValue(this.currentValue),this.inputValue=s.value);break;case"Tab":this.isOpen&&this.closeDropdown();break}}async onActionToggleDropdown(e,t){if(e.preventDefault(),e.stopPropagation(),this.isOpen)this.closeDropdown();else{this.inputValue="";const s=this.element.querySelector(".combo-input-field");s&&(s.value="",s.focus()),this.updateFilteredOptions(),this.openDropdown(),await this.updateDropdownDisplay()}}async onActionSelectOption(e,t){e.preventDefault(),e.stopPropagation();const s=parseInt(t.getAttribute("data-option-index"));s>=0&&s<this.filteredOptions.length&&await this.selectOption(this.filteredOptions[s])}openDropdown(){this.isOpen=!0;const e=this.element?.querySelector(".combo-dropdown");e&&e.classList.add("show");const t=this.element?.querySelector(".combo-input-field");t&&t.setAttribute("aria-expanded","true")}closeDropdown(){this.isOpen=!1,this.highlightedIndex=-1;const e=this.element?.querySelector(".combo-dropdown");e&&e.classList.remove("show");const t=this.element?.querySelector(".combo-input-field");t&&t.setAttribute("aria-expanded","false")}updateFilteredOptions(){const e=this.inputValue.toLowerCase().trim();if(!e){this.filteredOptions=[...this.options];return}this.filteredOptions=this.options.filter(t=>{const s=t.label.toLowerCase().includes(e),i=String(t.value).toLowerCase().includes(e),a=t.description?.toLowerCase().includes(e);return s||i||a}),this.filteredOptions.sort((t,s)=>{const i=t.label.toLowerCase()===e,a=s.label.toLowerCase()===e;if(i&&!a)return-1;if(!i&&a)return 1;const r=t.label.toLowerCase().startsWith(e),n=s.label.toLowerCase().startsWith(e);return r&&!n?-1:!r&&n?1:0})}async updateDropdownDisplay(){const e=this.element?.querySelector(".combo-dropdown");if(e&&(e.innerHTML=this.renderDropdownContent(),this.highlightedIndex>=0)){const t=e.querySelector(".combo-option.active");t&&t.scrollIntoView({block:"nearest"})}}highlightNext(){this.filteredOptions.length!==0&&(this.highlightedIndex=(this.highlightedIndex+1)%Math.min(this.filteredOptions.length,this.maxSuggestions))}highlightPrevious(){this.filteredOptions.length!==0&&(this.highlightedIndex=this.highlightedIndex<=0?Math.min(this.filteredOptions.length,this.maxSuggestions)-1:this.highlightedIndex-1)}async selectHighlightedOption(){this.highlightedIndex>=0&&this.highlightedIndex<this.filteredOptions.length&&await this.selectOption(this.filteredOptions[this.highlightedIndex])}async selectOption(e){this.currentValue=e.value,this.inputValue=e.label,this.selectedOption=e;const t=this.element?.querySelector(".combo-input-field");t&&(t.value=e.label);const s=this.element?.querySelector(".combo-input-hidden");s&&(s.value=e.value),this.closeDropdown(),this.emit("select",{option:e,value:e.value,meta:e.meta}),this.emit("change",{value:e.value,option:e,meta:e.meta}),typeof this.onSelectCallback=="function"&&this.onSelectCallback(e),typeof this.onChangeCallback=="function"&&this.onChangeCallback(e.value)}async selectCustomValue(e){if(!this.allowCustom)return;this.currentValue=e,this.inputValue=e,this.selectedOption=null;const t=this.element?.querySelector(".combo-input-hidden");t&&(t.value=e),this.closeDropdown(),this.emit("custom",{value:e}),this.emit("change",{value:e,custom:!0}),typeof this.onChangeCallback=="function"&&this.onChangeCallback(e)}getValue(){return this.currentValue}async setValue(e){this.currentValue=e,this.selectedOption=this.findOptionByValue(e),this.inputValue=this.getDisplayValue(e);const t=this.element?.querySelector(".combo-input-field");t&&(t.value=this.inputValue);const s=this.element?.querySelector(".combo-input-hidden");s&&(s.value=e),this.updateFilteredOptions()}getSelectedOption(){return this.selectedOption}async setOptions(e){this.options=this.normalizeOptions(e),this.updateFilteredOptions(),this.isOpen&&await this.updateDropdownDisplay()}setEnabled(e){this.disabled=!e;const t=this.element?.querySelector(".combo-input-field");t&&(t.disabled=this.disabled);const s=this.element?.querySelector(".combo-toggle");s&&(s.disabled=this.disabled)}setReadonly(e){this.readonly=e;const t=this.element?.querySelector(".combo-input-field");t&&(e?t.setAttribute("readonly",""):t.removeAttribute("readonly"))}focus(){const e=this.element?.querySelector(".combo-input-field");e&&e.focus()}async clear(){await this.setValue(""),this.inputValue="";const e=this.element?.querySelector(".combo-input-field");e&&(e.value=""),this.emit("clear")}getFormValue(){return this.allowCustom&&this.inputValue&&this.inputValue!==this.getDisplayValue(this.currentValue)?this.inputValue:this.currentValue}async setFormValue(e){await this.setValue(e)}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}escapeRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async onBeforeDestroy(){this.handleOutsideClick&&document.removeEventListener("click",this.handleOutsideClick),await super.onBeforeDestroy()}static create(e={}){return new de(e)}}class ze extends v{constructor(e={}){super(e),this.name=e.name||"combo",this.placeholder=e.placeholder||e.placeHolder||"Type or select...",this.value=e.value||"",this.options=(e.options||[]).map(t=>typeof t=="string"?{label:t,value:t}:typeof t=="object"&&t!==null?t:{label:t,value:t}),this.allowCustom=e.allowCustom!==!1,this.disabled=e.disabled||!1,this.required=e.required||!1,this.maxHeight=e.maxHeight||300,this.filteredOptions=[...this.options],this.highlightedIndex=-1,this.isOpen=!1,this.template=`
1014
+ `}highlightMatch(e){if(!this.inputValue)return this.escapeHtml(e);const t=this.escapeHtml(e),s=new RegExp(`(${this.escapeRegex(this.inputValue)})`,"gi");return t.replace(s,'<mark class="bg-warning bg-opacity-25">$1</mark>')}async onAfterRender(){await super.onAfterRender(),this.updateFilteredOptions(),this.handleOutsideClick=e=>{this.element&&!this.element.contains(e.target)&&this.closeDropdown()},document.addEventListener("click",this.handleOutsideClick)}async onChangeInputChange(e,t){this.inputValue=t.value,this.updateFilteredOptions(),this.inputValue.length>=this.minChars?this.openDropdown():this.closeDropdown(),this.highlightedIndex=-1,await this.updateDropdownDisplay()}async onActionInputKeydown(e,t){switch(e.key){case"ArrowDown":e.preventDefault(),this.isOpen?this.highlightNext():this.openDropdown(),await this.updateDropdownDisplay();break;case"ArrowUp":e.preventDefault(),this.isOpen&&(this.highlightPrevious(),await this.updateDropdownDisplay());break;case"Enter":e.preventDefault(),this.isOpen&&this.highlightedIndex>=0?await this.selectHighlightedOption():this.allowCustom&&this.inputValue&&await this.selectCustomValue(this.inputValue);break;case"Escape":e.preventDefault(),this.closeDropdown();const s=this.element.querySelector(".combo-input-field");s&&(s.value=this.getDisplayValue(this.currentValue),this.inputValue=s.value);break;case"Tab":this.isOpen&&this.closeDropdown();break}}async onActionToggleDropdown(e,t){if(e.preventDefault(),e.stopPropagation(),this.isOpen)this.closeDropdown();else{this.inputValue="";const s=this.element.querySelector(".combo-input-field");s&&(s.value="",s.focus()),this.updateFilteredOptions(),this.openDropdown(),await this.updateDropdownDisplay()}}async onActionSelectOption(e,t){e.preventDefault(),e.stopPropagation();const s=parseInt(t.getAttribute("data-option-index"));s>=0&&s<this.filteredOptions.length&&await this.selectOption(this.filteredOptions[s])}openDropdown(){this.isOpen=!0;const e=this.element?.querySelector(".combo-dropdown");e&&e.classList.add("show");const t=this.element?.querySelector(".combo-input-field");t&&t.setAttribute("aria-expanded","true")}closeDropdown(){this.isOpen=!1,this.highlightedIndex=-1;const e=this.element?.querySelector(".combo-dropdown");e&&e.classList.remove("show");const t=this.element?.querySelector(".combo-input-field");t&&t.setAttribute("aria-expanded","false")}updateFilteredOptions(){const e=this.inputValue.toLowerCase().trim();if(!e){this.filteredOptions=[...this.options];return}this.filteredOptions=this.options.filter(t=>{const s=t.label.toLowerCase().includes(e),i=String(t.value).toLowerCase().includes(e),a=t.description?.toLowerCase().includes(e);return s||i||a}),this.filteredOptions.sort((t,s)=>{const i=t.label.toLowerCase()===e,a=s.label.toLowerCase()===e;if(i&&!a)return-1;if(!i&&a)return 1;const r=t.label.toLowerCase().startsWith(e),n=s.label.toLowerCase().startsWith(e);return r&&!n?-1:!r&&n?1:0})}async updateDropdownDisplay(){const e=this.element?.querySelector(".combo-dropdown");if(e&&(e.innerHTML=this.renderDropdownContent(),this.highlightedIndex>=0)){const t=e.querySelector(".combo-option.active");t&&t.scrollIntoView({block:"nearest"})}}highlightNext(){this.filteredOptions.length!==0&&(this.highlightedIndex=(this.highlightedIndex+1)%Math.min(this.filteredOptions.length,this.maxSuggestions))}highlightPrevious(){this.filteredOptions.length!==0&&(this.highlightedIndex=this.highlightedIndex<=0?Math.min(this.filteredOptions.length,this.maxSuggestions)-1:this.highlightedIndex-1)}async selectHighlightedOption(){this.highlightedIndex>=0&&this.highlightedIndex<this.filteredOptions.length&&await this.selectOption(this.filteredOptions[this.highlightedIndex])}async selectOption(e){this.currentValue=e.value,this.inputValue=e.label,this.selectedOption=e;const t=this.element?.querySelector(".combo-input-field");t&&(t.value=e.label);const s=this.element?.querySelector(".combo-input-hidden");s&&(s.value=e.value),this.closeDropdown(),this.emit("select",{option:e,value:e.value,meta:e.meta}),this.emit("change",{value:e.value,option:e,meta:e.meta}),typeof this.onSelectCallback=="function"&&this.onSelectCallback(e),typeof this.onChangeCallback=="function"&&this.onChangeCallback(e.value)}async selectCustomValue(e){if(!this.allowCustom)return;this.currentValue=e,this.inputValue=e,this.selectedOption=null;const t=this.element?.querySelector(".combo-input-hidden");t&&(t.value=e),this.closeDropdown(),this.emit("custom",{value:e}),this.emit("change",{value:e,custom:!0}),typeof this.onChangeCallback=="function"&&this.onChangeCallback(e)}getValue(){return this.currentValue}async setValue(e){this.currentValue=e,this.selectedOption=this.findOptionByValue(e),this.inputValue=this.getDisplayValue(e);const t=this.element?.querySelector(".combo-input-field");t&&(t.value=this.inputValue);const s=this.element?.querySelector(".combo-input-hidden");s&&(s.value=e),this.updateFilteredOptions()}getSelectedOption(){return this.selectedOption}async setOptions(e){this.options=this.normalizeOptions(e),this.updateFilteredOptions(),this.isOpen&&await this.updateDropdownDisplay()}setEnabled(e){this.disabled=!e;const t=this.element?.querySelector(".combo-input-field");t&&(t.disabled=this.disabled);const s=this.element?.querySelector(".combo-toggle");s&&(s.disabled=this.disabled)}setReadonly(e){this.readonly=e;const t=this.element?.querySelector(".combo-input-field");t&&(e?t.setAttribute("readonly",""):t.removeAttribute("readonly"))}focus(){const e=this.element?.querySelector(".combo-input-field");e&&e.focus()}async clear(){await this.setValue(""),this.inputValue="";const e=this.element?.querySelector(".combo-input-field");e&&(e.value=""),this.emit("clear")}getFormValue(){return this.allowCustom&&this.inputValue&&this.inputValue!==this.getDisplayValue(this.currentValue)?this.inputValue:this.currentValue}async setFormValue(e){await this.setValue(e)}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}escapeRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}async onBeforeDestroy(){this.handleOutsideClick&&document.removeEventListener("click",this.handleOutsideClick),await super.onBeforeDestroy()}static create(e={}){return new ue(e)}}class Ze extends v{constructor(e={}){super(e),this.name=e.name||"combo",this.placeholder=e.placeholder||e.placeHolder||"Type or select...",this.value=e.value||"",this.options=(e.options||[]).map(t=>typeof t=="string"?{label:t,value:t}:typeof t=="object"&&t!==null?t:{label:t,value:t}),this.allowCustom=e.allowCustom!==!1,this.disabled=e.disabled||!1,this.required=e.required||!1,this.maxHeight=e.maxHeight||300,this.filteredOptions=[...this.options],this.highlightedIndex=-1,this.isOpen=!1,this.template=`
1015
1015
  <div class="combobox-container">
1016
1016
  <div class="input-group">
1017
1017
  <input type="text"
@@ -1049,10 +1049,10 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1049
1049
  {{label}}
1050
1050
  </button>
1051
1051
  {{/items}}
1052
- `}async onInit(){await super.onInit()}async onAfterRender(){if(await super.onAfterRender(),this.input=this.element.querySelector(".combobox-input"),this.dropdown=this.element.querySelector(".combobox-dropdown"),this.dropdownItems=this.element.querySelector('[data-region="dropdown-items"]'),this.noMatchDiv=this.element.querySelector(".combobox-no-match"),this.value&&this.input){const e=this.options.find(t=>t.value===this.value);e?this.input.value=e.label||e.value:this.allowCustom&&(this.input.value=this.value)}this.renderItems(),this.setupEventListeners()}setupEventListeners(){this.input.addEventListener("focus",()=>this.openDropdown()),this.input.addEventListener("input",e=>this.handleInput(e)),this.input.addEventListener("keydown",e=>this.handleKeydown(e)),document.addEventListener("click",e=>{this.element.contains(e.target)||this.closeDropdown()})}handleInput(e){const t=e.target.value.toLowerCase();this.filteredOptions=this.options.filter(s=>(s.label||s.value).toLowerCase().includes(t)),this.highlightedIndex=-1,this.renderItems(),this.openDropdown(),!this.allowCustom&&this.noMatchDiv&&(this.noMatchDiv.style.display=this.filteredOptions.length===0?"block":"none"),this.value=e.target.value,this.emit("change",{value:this.value})}handleKeydown(e){if(!this.isOpen&&(e.key==="ArrowDown"||e.key==="ArrowUp")){this.openDropdown(),e.preventDefault();return}if(this.isOpen)switch(e.key){case"ArrowDown":e.preventDefault(),this.highlightedIndex=Math.min(this.highlightedIndex+1,this.filteredOptions.length-1),this.renderItems(),this.scrollToHighlighted();break;case"ArrowUp":e.preventDefault(),this.highlightedIndex=Math.max(this.highlightedIndex-1,-1),this.renderItems(),this.scrollToHighlighted();break;case"Enter":e.preventDefault(),this.highlightedIndex>=0&&this.selectItem(this.filteredOptions[this.highlightedIndex]);break;case"Escape":e.preventDefault(),this.closeDropdown();break;case"Tab":this.closeDropdown();break}}scrollToHighlighted(){if(this.highlightedIndex<0)return;const t=this.dropdownItems.querySelectorAll(".combobox-item")[this.highlightedIndex];t&&t.scrollIntoView({block:"nearest"})}openDropdown(){this.disabled||this.isOpen||(this.isOpen=!0,this.dropdown.classList.add("show"),this.input.value===""&&(this.filteredOptions=[...this.options],this.renderItems()))}closeDropdown(){this.isOpen&&(this.isOpen=!1,this.dropdown.classList.remove("show"),this.highlightedIndex=-1,this.allowCustom||!this.options.find(t=>t.value===this.input.value||t.label===this.input.value)&&this.input.value!==""&&(this.input.value=this.value))}selectItem(e){const t=e.value,s=e.label||e.value;this.input.value=s,this.value=t,this.closeDropdown(),this.filteredOptions=[...this.options],this.highlightedIndex=-1,this.emit("change",{value:this.value,label:s})}renderItems(){const e=this.filteredOptions.map((s,i)=>({value:s.value,label:s.label||s.value,index:i,highlighted:i===this.highlightedIndex})),t=E.render(this.itemTemplate,{items:e});this.dropdownItems.innerHTML=t}async onActionComboboxInput(e,t){}async onActionComboboxToggle(e,t){this.isOpen?this.closeDropdown():(this.input.focus(),this.openDropdown())}async onActionSelectItem(e,t){const s=t.getAttribute("data-value"),i=this.options.find(a=>a.value===s);i&&this.selectItem(i)}getValue(){return this.value}setValue(e){if(this.value=e,!this.input)return;const t=this.options.find(s=>s.value===e);t?this.input.value=t.label||t.value:this.allowCustom&&(this.input.value=e)}setFormValue(e){this.setValue(e)}getTemplateData(){return{placeholder:this.placeholder,value:this.input?this.input.value:this.value,disabled:this.disabled,required:this.required,maxHeight:this.maxHeight,allowCustom:this.allowCustom}}}class q extends v{constructor(e={}){const{formConfig:t=e.config,fields:s,model:i=null,data:a={},defaults:r=null,errors:n={},fileHandling:o="base64",autosaveModelField:l=!1,...c}=e;super({tagName:"div",className:"form-view",...c}),H.onFormViewInit?.(this),this.model=i,this.defaults=r||a,this._originalData=a,this.errors=n,this.loading=!1,this.fileHandling=o,this.autosaveModelField=l,this.customComponents=new Map,this.fieldStatusManagers=new Map,this.saveTimeouts=new Map,this.pendingSaveFields=new Map,this.batchSaveTimeout=null,this.isSaving=!1,this.data=this.prepareFormData(),this.formConfig=t||{fields:s||[]},this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:n})}prepareFormData(){const e={...this.defaults};if(this.model)if(this.model.attributes&&typeof this.model.attributes=="object")Object.assign(e,this.model.attributes);else if(typeof this.model.toJSON=="function"){const t=this.model.toJSON();Object.assign(e,t)}else typeof this.model=="object"&&this.model.constructor===Object&&Object.assign(e,this.model);return this._originalData&&Object.assign(e,this._originalData),e}getFormConfig(){const e={...this.formConfig},t=this.getApp();return this.formConfig.fields&&Array.isArray(this.formConfig.fields)?e.fields=this.formConfig.fields.filter(s=>s.permissions?t.activeUser?.hasPermission(s.permissions):!0):e.fields=[],e}async renderTemplate(){return this.formBuilder.buildFormHTML()}async onAfterRender(){await super.onAfterRender(),this.data=this.prepareFormData(),this.populateFormValues(),this.initializeFormComponents(),this.initializeChangeHandlers();const e=this.getFormElement();e&&e.addEventListener("submit",t=>(t.preventDefault(),!1)),H.onFormViewAfterRender?.(this)}populateFormValues(){if(!(!this.element||!this.formConfig?.fields)){this._isPopulating=!0;try{this.formConfig.fields.forEach(e=>{this.populateFieldRecursive(e)})}finally{this._isPopulating=!1}}}populateFieldRecursive(e){e.type==="group"&&e.fields?e.fields.forEach(t=>{this.populateFieldRecursive(t)}):e.type==="tabset"&&e.tabs?e.tabs.forEach(t=>{t.fields&&Array.isArray(t.fields)&&t.fields.forEach(s=>{this.populateFieldRecursive(s)})}):this.populateFieldValue(e)}populateFieldValue(e){if(!e.name||!this.element)return;const t=this.element.querySelector(`[name="${e.name}"]`);if(!t)return;const s=x.getContextData(this.data,e.name);s!=null&&this.setFieldValue(t,e,s)}initializeFormComponents(){this.initializeImageFields(),this.initializeCustomComponents(),this.initializeTagInputs(),this.initializeMultiSelectDropdowns(),this.initializeComboBoxes(),this.initializeCollectionSelects(),this.initializeCollectionMultiSelects(),this.initializeDatePickers(),this.initializeDateRangePickers(),this.initializePasswordFields()}initializeImageFields(){this.element.querySelectorAll(".image-drop-zone.droppable").length>0&&this.enableFileDrop({acceptedTypes:["image/*"],maxFileSize:10485760,multiple:!1,dropZoneSelector:".image-drop-zone.droppable",visualFeedback:!0,dragOverClass:"drag-over",dragActiveClass:"drag-active"})}initializeCustomComponents(){this.initializeTagInputs(),this.initializeCollectionSelects(),this.initializeCollectionMultiSelects(),this.initializeDatePickers(),this.initializeDateRangePickers(),this.initializeComboInputs();try{const t=(s=[])=>{s.forEach(i=>{if(i&&i.type==="group"&&Array.isArray(i.fields))t(i.fields);else if(i&&i.name){const a=this.element.querySelector(`[name="${i.name}"], #${i.id||i.name}`);a&&H.onFieldInit?.(this,a,i)}})};t(this.formConfig?.fields||[])}catch(t){console.warn("FormPlugins.onFieldInit error:",t)}this.element.querySelectorAll("[data-component]").forEach(t=>{t.getAttribute("data-component"),t.getAttribute("data-field")})}initializeChangeHandlers(){if(!this.element)return;const e=this.element.querySelectorAll("input:not([data-action]), select:not([data-action]), textarea:not([data-action])");console.log("FormView: initializeChangeHandlers - found",e.length,"inputs"),e.forEach(t=>{console.log("FormView: Processing input:",t.type,t.name,t.getAttribute("data-change-action")),!(t.hasAttribute("data-component")||t.classList.contains("form-check-input"))&&(t.addEventListener("change",s=>{if(this._isPopulating)return;const i=t.name;if(i){let a=t.value;if(t.type==="checkbox")a=t.checked;else if(t.type==="radio"){if(!t.checked)return}else if(t.multiple&&t.selectedOptions)a=Array.from(t.selectedOptions).map(r=>r.value);else if(t.type==="file"){const r=t.getAttribute("data-change-action");if(r==="image-selected"){this.onChangeImageSelected(s,t);return}else if(r==="file-selected"){this.onChangeFileSelected(s,t);return}}this.handleFieldChange(i,a)}}),(t.type==="text"||t.type==="email"||t.type==="url"||t.tagName==="TEXTAREA")&&t.addEventListener("input",s=>{if(this._isPopulating)return;const i=t.name;i&&this.handleFieldChange(i,t.value)}))})}initializeTagInputs(){this.element.querySelectorAll('[data-field-type="tag"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new oe({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeMultiSelectDropdowns(){this.element.querySelectorAll('[data-field-type="multiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new Re({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Select...",label:r.label,containerId:null});let o=a.value??x.getContextData(this.data,s);o&&n.setFormValue(o),n.render(!0,t),this.customComponents.set(s,n),n.on("change",l=>{this.handleFieldChange(s,l.value)})}catch(s){console.error("MultiSelectDropdown initialization failed:",s)}})}initializeComboBoxes(){this.element.querySelectorAll('[data-field-type="combobox"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new ze({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Type or select...",containerId:null});let o=a.value??x.getContextData(this.data,s);o&&n.setFormValue(o),n.render(!0,t),this.customComponents.set(s,n),n.on("change",l=>{this.handleFieldChange(s,l.value)})}catch(s){console.error("ComboBox initialization failed:",s)}})}initializeCollectionSelects(){this.element.querySelectorAll('[data-field-type="collection"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r||!r.Collection)return;const n=new r.Collection;r.collectionParams&&(n.params={...n.params,...r.collectionParams});const o=new Ne({...a,collection:n,defaultParams:r.defaultParams||null,containerId:null});let l=x.getContextData(this.data,s);l&&o.setFormValue(l),o.render(!0,t),this.customComponents.set(s,o),o.on("change",c=>{this.handleFieldChange(s,c.value)})}catch{}})}initializeCollectionMultiSelects(){this.element.querySelectorAll('[data-field-type="collectionmultiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r||!r.Collection)return;const n=new r.Collection;r.collectionParams&&(n.params={...n.params,...r.collectionParams});const o=new qe({...a,collection:n,defaultParams:r.defaultParams||null,itemTemplate:r.itemTemplate||null,containerId:null});let l=x.getContextData(this.data,s);l&&o.setFormValue(l),o.render(!0,t),this.customComponents.set(s,o),o.on("change",c=>{this.handleFieldChange(s,c.value)})}catch(s){console.error("CollectionMultiSelect initialization failed:",s)}})}initializeDatePickers(){this.element.querySelectorAll('[data-field-type="datepicker"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new le({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeDateRangePickers(){this.element.querySelectorAll('[data-field-type="daterange"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new ce({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.combined)})}catch{}})}initializeComboInputs(){this.element.querySelectorAll('[data-field-type="combo"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new de({...a,containerId:null});let n=x.getContextData(this.data,s);n&&r.setValue(n),r.render(!0,t),this.customComponents.set(s,r),r.on("change",o=>{this.handleFieldChange(s,o.value)}),r.on("select",o=>{this.emit("field:select",{field:s,value:o.value,option:o.option,meta:o.meta})})}catch(s){console.error("ComboInput initialization failed:",s)}})}handleFieldChange(e,t){this._isPopulating||(this.data[e]=t,this.autosaveModelField&&this.model?this.handleFieldSave(e,t):this.model&&this.options.allowModelChange&&(this._isFormDrivenChange=!0,this.model.set(e,t)),this.emit("field:change",{field:e,value:t}),H.onFieldChange?.(this,e,t))}async handleFieldSave(e,t){if(!this.model)return;this.pendingSaveFields.set(e,t),this.getFieldStatusManager(e).showStatus("saving"),this.batchSaveTimeout&&clearTimeout(this.batchSaveTimeout),this.batchSaveTimeout=setTimeout(async()=>{await this.executeBatchSave()},300)}async executeBatchSave(){if(this.isSaving||this.pendingSaveFields.size===0)return;const e=Object.fromEntries(this.pendingSaveFields),t=Array.from(this.pendingSaveFields.keys());try{if(this.isSaving=!0,this.pendingSaveFields.clear(),this.batchSaveTimeout=null,this._isFormDrivenChange=!0,typeof this.model.save=="function"){const s=await this.model.save(e);if(!s||!s.success||s.data&&!s.data.status){const i=s?.data?.error||s?.error||s?.message||"Save failed";this.getApp()?.toast?.error(i),this.revertFields(t),t.forEach(a=>{this.getFieldStatusManager(a).showStatus("error",{message:i})});return}}else Object.entries(e).forEach(([s,i])=>{this.model.set(s,i)});t.forEach(s=>{this.getFieldStatusManager(s).showStatus("saved")})}catch(s){console.error("Batch save error:",s),this.getApp()?.toast?.error(s.message||"An error occurred while saving"),this.revertFields(t),t.forEach(i=>{this.getFieldStatusManager(i).showStatus("error",{message:s.message})})}finally{this.isSaving=!1}}revertFields(e){if(!this.model)return;const t=this._isPopulating;this._isPopulating=!0;try{e.forEach(s=>{const i=this.model.get(s);this.data[s]=i;const a=this.element?.querySelector(`[name="${s}"]`);if(a){const r=this.getFormFieldConfig(s);r?this.setFieldValue(a,r,i):a.type==="checkbox"?a.checked=!!i:a.value=i??""}})}finally{this._isPopulating=t}}getFieldStatusManager(e){if(!this.fieldStatusManagers.has(e)){const t=this.element.querySelector(`[name="${e}"]`);if(t){const s=new Be(t);this.fieldStatusManagers.set(e,s)}}return this.fieldStatusManagers.get(e)}refreshForm(){this.data=this.prepareFormData(),this.element&&this.populateFormValues()}getChangeReason(e,t){if(e instanceof File)return e.size===0||e.name===""||e.name==="blob"?"empty file, no change":`file upload: ${e.name}, ${e.size} bytes`;if(typeof e=="string"&&e.startsWith("data:image/"))return"base64 image upload";if(typeof e=="boolean"||typeof t=="boolean"){const s=!!e;return`boolean: ${t==null?!1:!!t} → ${s}`}return e==null||String(e).trim(),t==null||String(t).trim(),t==null?"was null/undefined, now has value":e==null?"was value, now null/undefined":"text content changed"}setFormData(e){this._originalData={...this._originalData,...e},this.refreshForm()}async onActionSubmitForm(e,t){e.preventDefault();const s=await this.handleSubmit();s.success?(this.data=s.data,this.emit("submit",{data:s.data,result:s.result,form:this,event:e}),!this.model&&this.formConfig.onSubmit&&typeof this.formConfig.onSubmit=="function"&&await this.formConfig.onSubmit(s.data,this)):this.emit("error",{error:s.error,result:s,form:this})}async onActionResetForm(e,t){const s=this.getFormElement();s&&(s.reset(),this.data={},this.clearAllErrors(),this.emit("reset",{form:this,event:e}))}async onActionClickImageUpload(e,t){console.log("FormView: onActionClickImageUpload called"),console.log("FormView: element:",t);const s=t.getAttribute("data-field-id");if(console.log("FormView: fieldId:",s),!s){console.error("FormView: No fieldId attribute found");return}const i=this.element.querySelector(`#${s}`);console.log("FormView: fileInput:",i),i&&!i.disabled?(i.click(),console.log("FormView: fileInput.click() called")):i?console.log("FormView: fileInput is disabled"):console.error("FormView: fileInput not found for fieldId:",s)}async onActionRemoveImage(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",i.dispatchEvent(new Event("change",{bubbles:!0}))),delete this.data[s],this.emit("change",{field:s,value:null,form:this}),await this.updateField(s)}async onActionClearColor(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",this.handleFieldChange(s,""),await this.updateField(s))}async onActionPreviewHtml(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector(`#${s}`);if(!i)return;const a=i.value||"";(await Promise.resolve().then(()=>_)).default.showHtmlPreview({html:a,title:"HTML Preview"})}async onActionSelectButtonOption(e,t){const s=t.getAttribute("data-field"),i=t.getAttribute("data-value");if(!s||!i)return;this.data[s]=i;const a=t.closest(".btn-group");a&&(a.querySelectorAll("button").forEach(n=>{n.classList.remove("active"),n.classList.add("btn-outline-primary"),n.classList.remove("btn-primary")}),t.classList.add("active"),t.classList.remove("btn-outline-primary"),t.classList.add("btn-primary")),this.emit("field:changed",{field:s,value:i,form:this}),this.emit("change",{field:s,value:i,form:this}),this.emit("form:changed",await this.getFormData())}async onActionApplyFilter(e,t){const s=t.closest(".dropdown"),i=s?.querySelectorAll('input[type="checkbox"]');if(!i||i.length===0)return;const a=i[0].getAttribute("data-field");if(!a)return;const r=[];i.forEach(o=>{o.checked&&r.push(o.value)}),this.data[a]=r;const n=s.querySelector('[data-bs-toggle="dropdown"]');n&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(n)?.hide(),this.emit("field:changed",{field:a,value:r,form:this}),this.emit("change",{field:a,value:r,form:this}),this.emit("form:changed",await this.getFormData())}async onChangeValidateField(e,t){const s=t.name;if(s){const i=t.value;this.handleFieldChange(s,i),this.validateField(s)}}async onChangeToggleSwitch(e,t){const s=t.getAttribute("data-field");if(s){const i=t.checked;this.handleFieldChange(s,i),this.emit("switch:toggle",{field:s,checked:i,form:this})}}async onChangeImageSelected(e,t){console.log("FormView: onChangeImageSelected called"),console.log("FormView: element:",t),console.log("FormView: element.files:",t.files);const s=t.getAttribute("data-field"),i=t.files[0];if(console.log("FormView: fieldName:",s),console.log("FormView: file:",i),s&&i){console.log("FormView: fieldName and file exist, processing...");const a=this.findFieldConfig(s);console.log("FormView: fieldConfig:",a);const r=URL.createObjectURL(i);if(console.log("FormView: previewUrl created:",r),a&&a.imageSize){console.log("FormView: Image cropping is required, imageSize:",a.imageSize);try{const n=window.MOJO?.plugins?.ImageCropView;if(console.log("FormView: ImageCropView available?",!!n),!n){console.log("FormView: ImageCropView not available, falling back to normal handling"),this.data[s]=i,await this.updateImagePreview(s,r),this.emit("image:selected",{field:s,file:i,form:this});return}const o=await n.showDialog(r,{title:`Crop ${a.label||s}`,cropAndScale:a.imageSize,size:"lg"});if(o.action==="crop"&&o.data){const c=await(await fetch(o.data)).blob(),d=new File([c],i.name,{type:i.type||"image/png"});this.data[s]=d,await this.updateImagePreview(s,o.data),this.emit("image:selected",{field:s,file:d,originalFile:i,cropped:!0,cropData:o.cropData,form:this}),this.emit("change",{field:s,value:d,form:this})}else t.value=""}catch(n){console.error("FormView: Error during image cropping:",n),console.log("FormView: Falling back to normal image handling after error"),this.data[s]=i,await this.updateImagePreview(s,r),this.emit("image:selected",{field:s,file:i,form:this}),this.emit("change",{field:s,value:i,form:this})}}else console.log("FormView: Normal image handling (no cropping)"),this.data[s]=i,console.log("FormView: File stored in this.data["+s+"]"),await this.updateImagePreview(s,r),console.log("FormView: updateImagePreview completed"),this.emit("image:selected",{field:s,file:i,form:this}),console.log("FormView: image:selected event emitted")}else console.log("FormView: Missing fieldName or file - not processing")}async onChangeFileSelected(e,t){const s=Array.from(t.files);t.multiple?this.data[t.name]=t.files:this.data[t.name]=s[0]||null,this.emit("file:selected",{field:t.name,files:s,form:this}),this.emit("change",{field:t.name,value:s,form:this})}async onChangeRangeChanged(e,t){const s=t.name,i=t.value,a=t.getAttribute("data-target");if(a){const r=this.element.querySelector(`#${a}`);r&&(r.textContent=i)}s&&this.handleFieldChange(s,i),this.emit("range:changed",{field:s,value:i,form:this})}async onChangeFilterSearch(e,t){const s=t.value;this.emit("search",{query:s,field:t.name,form:this})}async onChangeFilterSelectOptions(e,t){const s=t.value.toLowerCase(),i=t.getAttribute("data-target"),a=i?this.element.querySelector(`#${i}`):null;a&&a.querySelectorAll("option").forEach(n=>{const o=n.textContent.toLowerCase();n.style.display=o.includes(s)?"":"none"})}async onFileDrop(e,t,s){const i=t.target.closest(".image-drop-zone");if(!i)return;const a=i.getAttribute("data-field");if(!a)return;const r=e[0],n=this.element.querySelector(`input[name="${a}"]`);if(n){const l=new DataTransfer;l.items.add(r),n.files=l.files,n.dispatchEvent(new Event("change",{bubbles:!0}))}this.data[a]=r;const o=URL.createObjectURL(r);await this.updateImagePreview(a,o),this.emit("image:dropped",{field:a,file:r,form:this})}async onFileDropError(e,t,s){this.showError(`File upload error: ${e.message}`),this.emit("file:error",{error:e,files:s,form:this})}getFormElement(){return this.element?this.element.querySelector("form"):null}getFormFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}}return null};return t(this.formConfig.fields||[])}async getFormData(){const e=this.getFormElement();if(!e)return this.fileHandling==="multipart"?new FormData:{};if(this.fileHandling==="multipart"){const t=new FormData(e);for(const[s,i]of Object.entries(this.data))if(i instanceof File)t.set(s,i);else if(i instanceof FileList)for(let a=0;a<i.length;a++)t.append(`${s}[${a}]`,i[a]);return t}else{const t=new FormData(e),s={};for(const[n,o]of t.entries())s[n]?(Array.isArray(s[n])||(s[n]=[s[n]]),s[n].push(o)):s[n]=o;e.querySelectorAll('input[type="checkbox"]').forEach(n=>{s[n.name]=n.checked}),e.querySelectorAll('input[type="number"]').forEach(n=>{if(n.name&&s[n.name]!==void 0&&s[n.name]!==""){const o=Number(s[n.name]);isNaN(o)||(s[n.name]=o)}}),this.formConfig.fields?.forEach(n=>{if(n.type==="select"&&n.name&&s[n.name]!==void 0){const o=this.getFormFieldConfig(n.name);if(o?.options&&Array.isArray(o.options)&&o.options.every(c=>{const d=typeof c=="object"?c.value:c;return d===""||!isNaN(Number(d))})&&s[n.name]!==""){const c=Number(s[n.name]);isNaN(c)||(s[n.name]=c)}}}),e.querySelectorAll('[data-field-type="json"]').forEach(n=>{try{s[n.name]=JSON.parse(n.value)}catch{s[n.name]=n.value}}),this.customComponents.forEach((n,o)=>{n.getFormValue?s[o]=n.getFormValue():n.getValue&&(s[o]=n.getValue())});for(const[n,o]of Object.entries(this.data))if(o instanceof File)try{s[n]=await this.fileToBase64(o)}catch{s[n]=null}else if(o instanceof FileList){const l=[];for(let c=0;c<o.length;c++)try{l.push(await this.fileToBase64(o[c]))}catch{l.push(null)}s[n]=l}return s}}_onModelChange(){this.isSaving||(this.data=this.prepareFormData(),this.isMounted()&&(this._isFormDrivenChange||this.syncFormWithModel(),this._isFormDrivenChange=!1))}syncFormWithModel(){!this.model||!this.element||this.formDataMatchesModelData(this.data)||this.populateFormValues()}formDataMatchesModelData(e){if(!this.formConfig?.fields||!this.element)return!0;for(const t of this.formConfig.fields)if(t.type==="group"&&t.fields){for(const s of t.fields)if(!this.fieldValueMatchesModel(s,e))return!1}else if(!this.fieldValueMatchesModel(t,e))return!1;return!0}fieldValueMatchesModel(e,t){if(!e.name)return!0;const s=this.element.querySelector(`[name="${e.name}"]`);if(!s)return!0;const i=this.getFieldCurrentValue(s,e),a=x.getContextData(t,e.name);return this.valuesAreDifferent(i,a)===!1}getFieldCurrentValue(e,t){switch(t.type){case"checkbox":case"toggle":case"switch":return e.checked;case"radio":const s=this.element.querySelector(`[name="${t.name}"]:checked`);return s?s.value:"";case"select":return e.multiple?Array.from(e.selectedOptions).map(i=>i.value):e.value;case"file":case"image":return null;case"json":try{return e.value?JSON.parse(e.value):null}catch{return e.value}default:return e.value}}setFieldValue(e,t,s){switch(t.type){case"checkbox":case"toggle":case"switch":e.checked=!!s;break;case"radio":const i=this.element.querySelector(`[name="${t.name}"][value="${s}"]`);i&&(i.checked=!0);break;case"select":e.multiple&&Array.isArray(s)?Array.from(e.options).forEach(a=>{a.selected=s.includes(a.value)}):e.value=s??"";break;case"file":case"image":break;case"json":if(typeof s=="object"&&s!==null)try{e.value=JSON.stringify(s,null,2)}catch{e.value="{}"}else typeof s=="string"?e.value=s:e.value=String(s||"");break;default:e.value=s||"";break}e.dispatchEvent(new Event("change",{bubbles:!0}))}setDefaults(e){this.defaults={...this.defaults,...e},this.refreshForm()}async handleSubmit(){try{const e=await this.getFormData();if(this.formConfig.validateOnSubmit!==!1&&!this.validate())return this.focusFirstError(),{success:!1,data:e,error:"Form validation failed"};if(this.model&&typeof this.model.save=="function"){const t=await this.saveModel(e);if(t&&t.success!==!1)return{success:!0,data:e,result:t};{const s=t?.message||t?.error||"Save failed. Please try again.";return{success:!1,data:e,result:t,error:s}}}else return e}catch(e){return{success:!1,error:e.message||"An error occurred while submitting the form"}}}async saveModel(e=null){if(!this.model||typeof this.model.save!="function")throw new Error("No model available for saving");e||(e=await this.getFormData());const t=this.getChangedData(e);if(!t||Object.keys(t).length===0)return{success:!0,message:"No changes to save",data:e};try{return this._isFormDrivenChange=!0,await this.model.save(t)}catch(s){throw s}}getChangedData(e){if(!this.model)return e;const t=this.getOriginalModelData();let s;return e instanceof FormData?s=this.getChangedFormData(e,t):s=this.getChangedObjectData(e,t),s}getOriginalModelData(){return this.model.attributes?this.model.attributes:typeof this.model.toJSON=="function"?this.model.toJSON():{}}getChangedFormData(e,t){const s=new FormData;let i=!1;for(const[a,r]of e.entries())if(r instanceof File)r.size===0||r.name===""||r.name==="blob"||(s.set(a,r),i=!0);else{const n=t[a];r!==n&&r!==String(n)&&(s.set(a,r),i=!0)}return i?s:null}getChangedObjectData(e,t){const s={};let i=!1;const a=new Set([...Object.keys(t),...Object.keys(e)]),r=(n,o)=>o.split(".").reduce((l,c)=>l&&typeof l=="object"?l[c]:void 0,n);for(const n of a){const o=this.findFieldConfig(n);if(!o)continue;const l=e[n],c=r(t,n),d=o.type||"text";this.valuesAreDifferent(l,c,d,o)&&(s[n]=l,i=!0)}return i?s:null}valuesAreDifferent(e,t,s="text",i={}){if(e instanceof File)return e.size>0&&e.name!==""&&e.name!=="blob";if(typeof e=="string"&&e.startsWith("data:image/"))return!0;if(s==="collection"&&typeof t=="object"&&t!==null&&t!==void 0&&typeof e=="string"){if(e==="0")return t!==null;const n=i.valueField||"id";if(t[n]==e)return!1}if(s==="switch"||s==="checkbox"||s==="toggle")return!!e!==!!t;const a=e==null?"":String(e).trim(),r=t==null?"":String(t).trim();return a!==r}validate(){const e=this.getFormElement();if(!e)return!1;const t=e.checkValidity();return t||e.classList.add("was-validated"),t}validateField(e){const t=this.getFormElement();if(!t)return!1;const s=t.elements[e];if(!s)return!1;const i=s.checkValidity();return i?(s.classList.remove("is-invalid"),s.classList.add("is-valid"),delete this.errors[e]):(s.classList.remove("is-valid"),s.classList.add("is-invalid"),this.errors[e]=s.validationMessage),i}focusFirstError(){const e=this.getFormElement();if(!e)return;const t=e.querySelector(":invalid");if(!t)return;const s=t.closest(".tab-pane");if(s&&!s.classList.contains("active")){const i=s.id,a=e.querySelector(`[role="tab"][aria-controls="${i}"], [data-bs-target="#${i}"]`);if(a){const r=window.bootstrap?.Tab?.getOrCreateInstance?window.bootstrap.Tab.getOrCreateInstance(a):null;r&&typeof r.show=="function"?r.show():(e.querySelectorAll('[role="tab"].nav-link').forEach(l=>{const c=l===a;l.classList.toggle("active",c),l.setAttribute("aria-selected",c?"true":"false")}),e.querySelectorAll(".tab-pane").forEach(l=>l.classList.remove("show","active")),s.classList.add("show","active"))}}t.focus(),t.scrollIntoView({behavior:"smooth",block:"center"})}clearAllErrors(){const e=this.getFormElement();if(!e)return;this.errors={},e.classList.remove("was-validated"),e.querySelectorAll(".is-invalid").forEach(i=>i.classList.remove("is-invalid")),e.querySelectorAll(".is-valid").forEach(i=>i.classList.remove("is-valid"))}setLoading(e){this.loading=e;const t=this.getFormElement();if(!t)return;const s=t.querySelectorAll("input, select, textarea, button"),i=t.querySelector('button[type="submit"]');if(e)s.forEach(a=>a.disabled=!0),i&&(i.innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Loading...');else if(s.forEach(a=>a.disabled=!1),i){const a=this.formConfig.options?.submitButton||"Submit";i.innerHTML=typeof a=="string"?a:"Submit"}}showError(e){if(this.emit("error",{message:e,form:this}),this.element){this.element.querySelectorAll(".alert").forEach(i=>i.remove());const s=document.createElement("div");s.className="alert alert-danger alert-dismissible fade show",s.innerHTML=`
1052
+ `}async onInit(){await super.onInit()}async onAfterRender(){if(await super.onAfterRender(),this.input=this.element.querySelector(".combobox-input"),this.dropdown=this.element.querySelector(".combobox-dropdown"),this.dropdownItems=this.element.querySelector('[data-region="dropdown-items"]'),this.noMatchDiv=this.element.querySelector(".combobox-no-match"),this.value&&this.input){const e=this.options.find(t=>t.value===this.value);e?this.input.value=e.label||e.value:this.allowCustom&&(this.input.value=this.value)}this.renderItems(),this.setupEventListeners()}setupEventListeners(){this.input.addEventListener("focus",()=>this.openDropdown()),this.input.addEventListener("input",e=>this.handleInput(e)),this.input.addEventListener("keydown",e=>this.handleKeydown(e)),document.addEventListener("click",e=>{this.element.contains(e.target)||this.closeDropdown()})}handleInput(e){const t=e.target.value.toLowerCase();this.filteredOptions=this.options.filter(s=>(s.label||s.value).toLowerCase().includes(t)),this.highlightedIndex=-1,this.renderItems(),this.openDropdown(),!this.allowCustom&&this.noMatchDiv&&(this.noMatchDiv.style.display=this.filteredOptions.length===0?"block":"none"),this.value=e.target.value,this.emit("change",{value:this.value})}handleKeydown(e){if(!this.isOpen&&(e.key==="ArrowDown"||e.key==="ArrowUp")){this.openDropdown(),e.preventDefault();return}if(this.isOpen)switch(e.key){case"ArrowDown":e.preventDefault(),this.highlightedIndex=Math.min(this.highlightedIndex+1,this.filteredOptions.length-1),this.renderItems(),this.scrollToHighlighted();break;case"ArrowUp":e.preventDefault(),this.highlightedIndex=Math.max(this.highlightedIndex-1,-1),this.renderItems(),this.scrollToHighlighted();break;case"Enter":e.preventDefault(),this.highlightedIndex>=0&&this.selectItem(this.filteredOptions[this.highlightedIndex]);break;case"Escape":e.preventDefault(),this.closeDropdown();break;case"Tab":this.closeDropdown();break}}scrollToHighlighted(){if(this.highlightedIndex<0)return;const t=this.dropdownItems.querySelectorAll(".combobox-item")[this.highlightedIndex];t&&t.scrollIntoView({block:"nearest"})}openDropdown(){this.disabled||this.isOpen||(this.isOpen=!0,this.dropdown.classList.add("show"),this.input.value===""&&(this.filteredOptions=[...this.options],this.renderItems()))}closeDropdown(){this.isOpen&&(this.isOpen=!1,this.dropdown.classList.remove("show"),this.highlightedIndex=-1,this.allowCustom||!this.options.find(t=>t.value===this.input.value||t.label===this.input.value)&&this.input.value!==""&&(this.input.value=this.value))}selectItem(e){const t=e.value,s=e.label||e.value;this.input.value=s,this.value=t,this.closeDropdown(),this.filteredOptions=[...this.options],this.highlightedIndex=-1,this.emit("change",{value:this.value,label:s})}renderItems(){const e=this.filteredOptions.map((s,i)=>({value:s.value,label:s.label||s.value,index:i,highlighted:i===this.highlightedIndex})),t=E.render(this.itemTemplate,{items:e});this.dropdownItems.innerHTML=t}async onActionComboboxInput(e,t){}async onActionComboboxToggle(e,t){this.isOpen?this.closeDropdown():(this.input.focus(),this.openDropdown())}async onActionSelectItem(e,t){const s=t.getAttribute("data-value"),i=this.options.find(a=>a.value===s);i&&this.selectItem(i)}getValue(){return this.value}setValue(e){if(this.value=e,!this.input)return;const t=this.options.find(s=>s.value===e);t?this.input.value=t.label||t.value:this.allowCustom&&(this.input.value=e)}setFormValue(e){this.setValue(e)}getTemplateData(){return{placeholder:this.placeholder,value:this.input?this.input.value:this.value,disabled:this.disabled,required:this.required,maxHeight:this.maxHeight,allowCustom:this.allowCustom}}}class q extends v{constructor(e={}){const{formConfig:t=e.config,fields:s,model:i=null,data:a={},defaults:r=null,errors:n={},fileHandling:o="base64",autosaveModelField:l=!1,...c}=e;super({tagName:"div",className:"form-view",...c}),H.onFormViewInit?.(this),this.model=i,this.defaults=r||a,this._originalData=a,this.errors=n,this.loading=!1,this.fileHandling=o,this.autosaveModelField=l,this.customComponents=new Map,this.fieldStatusManagers=new Map,this.saveTimeouts=new Map,this.pendingSaveFields=new Map,this.batchSaveTimeout=null,this.isSaving=!1,this.data=this.prepareFormData(),this.formConfig=t||{fields:s||[]},this.formBuilder=new Q({...this.getFormConfig(),data:this.data,errors:n})}prepareFormData(){const e={...this.defaults};if(this.model)if(this.model.attributes&&typeof this.model.attributes=="object")Object.assign(e,this.model.attributes);else if(typeof this.model.toJSON=="function"){const t=this.model.toJSON();Object.assign(e,t)}else typeof this.model=="object"&&this.model.constructor===Object&&Object.assign(e,this.model);return this._originalData&&Object.assign(e,this._originalData),e}getFormConfig(){const e={...this.formConfig},t=this.getApp();return this.formConfig.fields&&Array.isArray(this.formConfig.fields)?e.fields=this.formConfig.fields.filter(s=>s.permissions?t.activeUser?.hasPermission(s.permissions):!0):e.fields=[],e}async renderTemplate(){return this.formBuilder.buildFormHTML()}async onAfterRender(){await super.onAfterRender(),this.data=this.prepareFormData(),this.populateFormValues(),this.initializeFormComponents(),this.initializeChangeHandlers();const e=this.getFormElement();e&&e.addEventListener("submit",t=>(t.preventDefault(),!1)),H.onFormViewAfterRender?.(this)}populateFormValues(){if(!(!this.element||!this.formConfig?.fields)){this._isPopulating=!0;try{this.formConfig.fields.forEach(e=>{this.populateFieldRecursive(e)})}finally{this._isPopulating=!1}}}populateFieldRecursive(e){e.type==="group"&&e.fields?e.fields.forEach(t=>{this.populateFieldRecursive(t)}):e.type==="tabset"&&e.tabs?e.tabs.forEach(t=>{t.fields&&Array.isArray(t.fields)&&t.fields.forEach(s=>{this.populateFieldRecursive(s)})}):this.populateFieldValue(e)}populateFieldValue(e){if(!e.name||!this.element)return;const t=this.element.querySelector(`[name="${e.name}"]`);if(!t)return;const s=x.getContextData(this.data,e.name);s!=null&&this.setFieldValue(t,e,s)}initializeFormComponents(){this.initializeImageFields(),this.initializeCustomComponents(),this.initializeTagInputs(),this.initializeMultiSelectDropdowns(),this.initializeComboBoxes(),this.initializeCollectionSelects(),this.initializeCollectionMultiSelects(),this.initializeDatePickers(),this.initializeDateRangePickers(),this.initializePasswordFields()}initializeImageFields(){this.element.querySelectorAll(".image-drop-zone.droppable").length>0&&this.enableFileDrop({acceptedTypes:["image/*"],maxFileSize:10485760,multiple:!1,dropZoneSelector:".image-drop-zone.droppable",visualFeedback:!0,dragOverClass:"drag-over",dragActiveClass:"drag-active"})}initializeCustomComponents(){this.initializeTagInputs(),this.initializeCollectionSelects(),this.initializeCollectionMultiSelects(),this.initializeDatePickers(),this.initializeDateRangePickers(),this.initializeComboInputs();try{const t=(s=[])=>{s.forEach(i=>{if(i&&i.type==="group"&&Array.isArray(i.fields))t(i.fields);else if(i&&i.name){const a=this.element.querySelector(`[name="${i.name}"], #${i.id||i.name}`);a&&H.onFieldInit?.(this,a,i)}})};t(this.formConfig?.fields||[])}catch(t){console.warn("FormPlugins.onFieldInit error:",t)}this.element.querySelectorAll("[data-component]").forEach(t=>{t.getAttribute("data-component"),t.getAttribute("data-field")})}initializeChangeHandlers(){if(!this.element)return;const e=this.element.querySelectorAll("input:not([data-action]), select:not([data-action]), textarea:not([data-action])");console.log("FormView: initializeChangeHandlers - found",e.length,"inputs"),e.forEach(t=>{console.log("FormView: Processing input:",t.type,t.name,t.getAttribute("data-change-action")),!(t.hasAttribute("data-component")||t.hasAttribute("data-change-action")||t.classList.contains("form-check-input"))&&(t.addEventListener("change",s=>{if(this._isPopulating)return;const i=t.name;if(i){let a=t.value;if(t.type==="checkbox")a=t.checked;else if(t.type==="radio"){if(!t.checked)return}else if(t.multiple&&t.selectedOptions)a=Array.from(t.selectedOptions).map(r=>r.value);else if(t.type==="file"){const r=t.getAttribute("data-change-action");if(r==="image-selected"){this.onChangeImageSelected(s,t);return}else if(r==="file-selected"){this.onChangeFileSelected(s,t);return}}this.handleFieldChange(i,a)}}),(t.type==="text"||t.type==="email"||t.type==="url"||t.tagName==="TEXTAREA")&&t.addEventListener("input",s=>{if(this._isPopulating)return;const i=t.name;i&&this.handleFieldChange(i,t.value)}))})}initializeTagInputs(){this.element.querySelectorAll('[data-field-type="tag"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new ce({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeMultiSelectDropdowns(){this.element.querySelectorAll('[data-field-type="multiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new Ge({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Select...",label:r.label,containerId:null});let o=a.value??x.getContextData(this.data,s);o&&n.setFormValue(o),n.render(!0,t),this.customComponents.set(s,n),n.on("change",l=>{this.handleFieldChange(s,l.value)})}catch(s){console.error("MultiSelectDropdown initialization failed:",s)}})}initializeComboBoxes(){this.element.querySelectorAll('[data-field-type="combobox"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new Ze({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Type or select...",containerId:null});let o=a.value??x.getContextData(this.data,s);o&&n.setFormValue(o),n.render(!0,t),this.customComponents.set(s,n),n.on("change",l=>{this.handleFieldChange(s,l.value)})}catch(s){console.error("ComboBox initialization failed:",s)}})}initializeCollectionSelects(){this.element.querySelectorAll('[data-field-type="collection"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r||!r.Collection)return;const n=new r.Collection;r.collectionParams&&(n.params={...n.params,...r.collectionParams});const o=new Be({...a,collection:n,defaultParams:r.defaultParams||null,containerId:null});let l=x.getContextData(this.data,s);l&&o.setFormValue(l),o.render(!0,t),this.customComponents.set(s,o),o.on("change",c=>{this.handleFieldChange(s,c.value)})}catch{}})}initializeCollectionMultiSelects(){this.element.querySelectorAll('[data-field-type="collectionmultiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r||!r.Collection)return;const n=new r.Collection;r.collectionParams&&(n.params={...n.params,...r.collectionParams});const o=new We({...a,collection:n,defaultParams:r.defaultParams||null,itemTemplate:r.itemTemplate||null,containerId:null});let l=x.getContextData(this.data,s);l&&o.setFormValue(l),o.render(!0,t),this.customComponents.set(s,o),o.on("change",c=>{this.handleFieldChange(s,c.value)})}catch(s){console.error("CollectionMultiSelect initialization failed:",s)}})}initializeDatePickers(){this.element.querySelectorAll('[data-field-type="datepicker"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new de({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeDateRangePickers(){this.element.querySelectorAll('[data-field-type="daterange"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new he({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.combined)})}catch{}})}initializeComboInputs(){this.element.querySelectorAll('[data-field-type="combo"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new ue({...a,containerId:null});let n=x.getContextData(this.data,s);n&&r.setValue(n),r.render(!0,t),this.customComponents.set(s,r),r.on("change",o=>{this.handleFieldChange(s,o.value)}),r.on("select",o=>{this.emit("field:select",{field:s,value:o.value,option:o.option,meta:o.meta})})}catch(s){console.error("ComboInput initialization failed:",s)}})}handleFieldChange(e,t){this._isPopulating||(this.data[e]=t,this.autosaveModelField&&this.model?this.handleFieldSave(e,t):this.model&&this.options.allowModelChange&&(this._isFormDrivenChange=!0,this.model.set(e,t)),this.emit("field:change",{field:e,value:t}),H.onFieldChange?.(this,e,t))}async handleFieldSave(e,t){if(!this.model)return;this.pendingSaveFields.set(e,t),this.getFieldStatusManager(e).showStatus("saving"),this.batchSaveTimeout&&clearTimeout(this.batchSaveTimeout),this.batchSaveTimeout=setTimeout(async()=>{await this.executeBatchSave()},300)}async executeBatchSave(){if(this.isSaving||this.pendingSaveFields.size===0)return;const e=Object.fromEntries(this.pendingSaveFields),t=Array.from(this.pendingSaveFields.keys());try{if(this.isSaving=!0,this.pendingSaveFields.clear(),this.batchSaveTimeout=null,this._isFormDrivenChange=!0,typeof this.model.save=="function"){const s=await this.model.save(e);if(!s||!s.success||s.data&&!s.data.status){const i=s?.data?.error||s?.error||s?.message||"Save failed";this.getApp()?.toast?.error(i),this.revertFields(t),t.forEach(a=>{this.getFieldStatusManager(a).showStatus("error",{message:i})});return}}else Object.entries(e).forEach(([s,i])=>{this.model.set(s,i)});t.forEach(s=>{this.getFieldStatusManager(s).showStatus("saved")})}catch(s){console.error("Batch save error:",s),this.getApp()?.toast?.error(s.message||"An error occurred while saving"),this.revertFields(t),t.forEach(i=>{this.getFieldStatusManager(i).showStatus("error",{message:s.message})})}finally{this.isSaving=!1}}revertFields(e){if(!this.model)return;const t=this._isPopulating;this._isPopulating=!0;try{e.forEach(s=>{const i=this.model.get(s);this.data[s]=i;const a=this.element?.querySelector(`[name="${s}"]`);if(a){const r=this.getFormFieldConfig(s);r?this.setFieldValue(a,r,i):a.type==="checkbox"?a.checked=!!i:a.value=i??""}})}finally{this._isPopulating=t}}getFieldStatusManager(e){if(!this.fieldStatusManagers.has(e)){const t=this.element.querySelector(`[name="${e}"]`);if(t){const s=new Ke(t);this.fieldStatusManagers.set(e,s)}}return this.fieldStatusManagers.get(e)}refreshForm(){this.data=this.prepareFormData(),this.element&&this.populateFormValues()}getChangeReason(e,t){if(e instanceof File)return e.size===0||e.name===""||e.name==="blob"?"empty file, no change":`file upload: ${e.name}, ${e.size} bytes`;if(typeof e=="string"&&e.startsWith("data:image/"))return"base64 image upload";if(typeof e=="boolean"||typeof t=="boolean"){const s=!!e;return`boolean: ${t==null?!1:!!t} → ${s}`}return e==null||String(e).trim(),t==null||String(t).trim(),t==null?"was null/undefined, now has value":e==null?"was value, now null/undefined":"text content changed"}setFormData(e){this._originalData={...this._originalData,...e},this.refreshForm()}async onActionSubmitForm(e,t){e.preventDefault();const s=await this.handleSubmit();s.success?(this.data=s.data,this.emit("submit",{data:s.data,result:s.result,form:this,event:e}),!this.model&&this.formConfig.onSubmit&&typeof this.formConfig.onSubmit=="function"&&await this.formConfig.onSubmit(s.data,this)):this.emit("error",{error:s.error,result:s,form:this})}async onActionResetForm(e,t){const s=this.getFormElement();s&&(s.reset(),this.data={},this.clearAllErrors(),this.emit("reset",{form:this,event:e}))}async onActionClickImageUpload(e,t){console.log("FormView: onActionClickImageUpload called"),console.log("FormView: element:",t);const s=t.getAttribute("data-field-id");if(console.log("FormView: fieldId:",s),!s){console.error("FormView: No fieldId attribute found");return}const i=this.element.querySelector(`#${s}`);console.log("FormView: fileInput:",i),i&&!i.disabled?(i.click(),console.log("FormView: fileInput.click() called")):i?console.log("FormView: fileInput is disabled"):console.error("FormView: fileInput not found for fieldId:",s)}async onActionRemoveImage(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",i.dispatchEvent(new Event("change",{bubbles:!0}))),delete this.data[s],this.emit("change",{field:s,value:null,form:this}),await this.updateField(s)}async onActionClearColor(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",this.handleFieldChange(s,""),await this.updateField(s))}async onActionPreviewHtml(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector(`#${s}`);if(!i)return;const a=i.value||"";(await Promise.resolve().then(()=>L)).default.showHtmlPreview({html:a,title:"HTML Preview"})}async onActionSelectButtonOption(e,t){const s=t.getAttribute("data-field"),i=t.getAttribute("data-value");if(!s||!i)return;this.data[s]=i;const a=t.closest(".btn-group");a&&(a.querySelectorAll("button").forEach(n=>{n.classList.remove("active"),n.classList.add("btn-outline-primary"),n.classList.remove("btn-primary")}),t.classList.add("active"),t.classList.remove("btn-outline-primary"),t.classList.add("btn-primary")),this.emit("field:changed",{field:s,value:i,form:this}),this.emit("change",{field:s,value:i,form:this}),this.emit("form:changed",await this.getFormData())}async onActionApplyFilter(e,t){const s=t.closest(".dropdown"),i=s?.querySelectorAll('input[type="checkbox"]');if(!i||i.length===0)return;const a=i[0].getAttribute("data-field");if(!a)return;const r=[];i.forEach(o=>{o.checked&&r.push(o.value)}),this.data[a]=r;const n=s.querySelector('[data-bs-toggle="dropdown"]');n&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(n)?.hide(),this.emit("field:changed",{field:a,value:r,form:this}),this.emit("change",{field:a,value:r,form:this}),this.emit("form:changed",await this.getFormData())}async onChangeValidateField(e,t){const s=t.name;if(s){const i=t.value;this.handleFieldChange(s,i),this.validateField(s)}}async onChangeToggleSwitch(e,t){const s=t.getAttribute("data-field");if(s){const i=t.checked;this.handleFieldChange(s,i),this.emit("switch:toggle",{field:s,checked:i,form:this})}}async onChangeImageSelected(e,t){console.log("FormView: onChangeImageSelected called"),console.log("FormView: element:",t),console.log("FormView: element.files:",t.files);const s=t.getAttribute("data-field"),i=t.files[0];if(console.log("FormView: fieldName:",s),console.log("FormView: file:",i),s&&i){console.log("FormView: fieldName and file exist, processing...");const a=this.findFieldConfig(s);console.log("FormView: fieldConfig:",a);const r=URL.createObjectURL(i);if(console.log("FormView: previewUrl created:",r),a&&a.imageSize){console.log("FormView: Image cropping is required, imageSize:",a.imageSize);try{const n=window.MOJO?.plugins?.ImageCropView;if(console.log("FormView: ImageCropView available?",!!n),!n){console.log("FormView: ImageCropView not available, falling back to normal handling"),this.data[s]=i,await this.updateImagePreview(s,r),this.emit("image:selected",{field:s,file:i,form:this});return}const o=await n.showDialog(r,{title:`Crop ${a.label||s}`,cropAndScale:a.imageSize,size:"lg"});if(o.action==="crop"&&o.data){const c=await(await fetch(o.data)).blob(),d=new File([c],i.name,{type:i.type||"image/png"});this.data[s]=d,await this.updateImagePreview(s,o.data),this.emit("image:selected",{field:s,file:d,originalFile:i,cropped:!0,cropData:o.cropData,form:this}),this.emit("change",{field:s,value:d,form:this})}else t.value=""}catch(n){console.error("FormView: Error during image cropping:",n),console.log("FormView: Falling back to normal image handling after error"),this.data[s]=i,await this.updateImagePreview(s,r),this.emit("image:selected",{field:s,file:i,form:this}),this.emit("change",{field:s,value:i,form:this})}}else console.log("FormView: Normal image handling (no cropping)"),this.data[s]=i,console.log("FormView: File stored in this.data["+s+"]"),await this.updateImagePreview(s,r),console.log("FormView: updateImagePreview completed"),this.emit("image:selected",{field:s,file:i,form:this}),console.log("FormView: image:selected event emitted")}else console.log("FormView: Missing fieldName or file - not processing")}async onChangeFileSelected(e,t){const s=Array.from(t.files);t.multiple?this.data[t.name]=t.files:this.data[t.name]=s[0]||null,this.emit("file:selected",{field:t.name,files:s,form:this}),this.emit("change",{field:t.name,value:s,form:this})}async onChangeRangeChanged(e,t){const s=t.name,i=t.value,a=t.getAttribute("data-target");if(a){const r=this.element.querySelector(`#${a}`);r&&(r.textContent=i)}s&&this.handleFieldChange(s,i),this.emit("range:changed",{field:s,value:i,form:this})}async onChangeFilterSearch(e,t){const s=t.value;this.emit("search",{query:s,field:t.name,form:this})}async onChangeFilterSelectOptions(e,t){const s=t.value.toLowerCase(),i=t.getAttribute("data-target"),a=i?this.element.querySelector(`#${i}`):null;a&&a.querySelectorAll("option").forEach(n=>{const o=n.textContent.toLowerCase();n.style.display=o.includes(s)?"":"none"})}async onFileDrop(e,t,s){const i=t.target.closest(".image-drop-zone");if(!i)return;const a=i.getAttribute("data-field");if(!a)return;const r=e[0],n=this.element.querySelector(`input[name="${a}"]`);if(n){const l=new DataTransfer;l.items.add(r),n.files=l.files,n.dispatchEvent(new Event("change",{bubbles:!0}))}this.data[a]=r;const o=URL.createObjectURL(r);await this.updateImagePreview(a,o),this.emit("image:dropped",{field:a,file:r,form:this})}async onFileDropError(e,t,s){this.showError(`File upload error: ${e.message}`),this.emit("file:error",{error:e,files:s,form:this})}getFormElement(){return this.element?this.element.querySelector("form"):null}getFormFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}}return null};return t(this.formConfig.fields||[])}async getFormData(){const e=this.getFormElement();if(!e)return this.fileHandling==="multipart"?new FormData:{};if(this.fileHandling==="multipart"){const t=new FormData(e);for(const[s,i]of Object.entries(this.data))if(i instanceof File)t.set(s,i);else if(i instanceof FileList)for(let a=0;a<i.length;a++)t.append(`${s}[${a}]`,i[a]);return t}else{const t=new FormData(e),s={};for(const[n,o]of t.entries())s[n]?(Array.isArray(s[n])||(s[n]=[s[n]]),s[n].push(o)):s[n]=o;e.querySelectorAll('input[type="checkbox"]').forEach(n=>{s[n.name]=n.checked}),e.querySelectorAll('input[type="number"]').forEach(n=>{if(n.name&&s[n.name]!==void 0&&s[n.name]!==""){const o=Number(s[n.name]);isNaN(o)||(s[n.name]=o)}}),this.formConfig.fields?.forEach(n=>{if(n.type==="select"&&n.name&&s[n.name]!==void 0){const o=this.getFormFieldConfig(n.name);if(o?.options&&Array.isArray(o.options)&&o.options.every(c=>{const d=typeof c=="object"?c.value:c;return d===""||!isNaN(Number(d))})&&s[n.name]!==""){const c=Number(s[n.name]);isNaN(c)||(s[n.name]=c)}}}),e.querySelectorAll('[data-field-type="json"]').forEach(n=>{try{s[n.name]=JSON.parse(n.value)}catch{s[n.name]=n.value}}),this.customComponents.forEach((n,o)=>{n.getFormValue?s[o]=n.getFormValue():n.getValue&&(s[o]=n.getValue())});for(const[n,o]of Object.entries(this.data))if(o instanceof File)try{s[n]=await this.fileToBase64(o)}catch{s[n]=null}else if(o instanceof FileList){const l=[];for(let c=0;c<o.length;c++)try{l.push(await this.fileToBase64(o[c]))}catch{l.push(null)}s[n]=l}return s}}_onModelChange(){this.isSaving||(this.data=this.prepareFormData(),this.isMounted()&&(this._isFormDrivenChange||this.syncFormWithModel(),this._isFormDrivenChange=!1))}syncFormWithModel(){!this.model||!this.element||this.formDataMatchesModelData(this.data)||this.populateFormValues()}formDataMatchesModelData(e){if(!this.formConfig?.fields||!this.element)return!0;for(const t of this.formConfig.fields)if(t.type==="group"&&t.fields){for(const s of t.fields)if(!this.fieldValueMatchesModel(s,e))return!1}else if(!this.fieldValueMatchesModel(t,e))return!1;return!0}fieldValueMatchesModel(e,t){if(!e.name)return!0;const s=this.element.querySelector(`[name="${e.name}"]`);if(!s)return!0;const i=this.getFieldCurrentValue(s,e),a=x.getContextData(t,e.name);return this.valuesAreDifferent(i,a)===!1}getFieldCurrentValue(e,t){switch(t.type){case"checkbox":case"toggle":case"switch":return e.checked;case"radio":const s=this.element.querySelector(`[name="${t.name}"]:checked`);return s?s.value:"";case"select":return e.multiple?Array.from(e.selectedOptions).map(i=>i.value):e.value;case"file":case"image":return null;case"json":try{return e.value?JSON.parse(e.value):null}catch{return e.value}default:return e.value}}setFieldValue(e,t,s){switch(t.type){case"checkbox":case"toggle":case"switch":e.checked=!!s;break;case"radio":const i=this.element.querySelector(`[name="${t.name}"][value="${s}"]`);i&&(i.checked=!0);break;case"select":e.multiple&&Array.isArray(s)?Array.from(e.options).forEach(a=>{a.selected=s.includes(a.value)}):e.value=s??"";break;case"file":case"image":break;case"json":if(typeof s=="object"&&s!==null)try{e.value=JSON.stringify(s,null,2)}catch{e.value="{}"}else typeof s=="string"?e.value=s:e.value=String(s||"");break;default:e.value=s||"";break}e.dispatchEvent(new Event("change",{bubbles:!0}))}setDefaults(e){this.defaults={...this.defaults,...e},this.refreshForm()}async handleSubmit(){try{const e=await this.getFormData();if(this.formConfig.validateOnSubmit!==!1&&!this.validate())return this.focusFirstError(),{success:!1,data:e,error:"Form validation failed"};if(this.model&&typeof this.model.save=="function"){const t=await this.saveModel(e);if(t&&t.success!==!1)return{success:!0,data:e,result:t};{const s=t?.message||t?.error||"Save failed. Please try again.";return{success:!1,data:e,result:t,error:s}}}else return e}catch(e){return{success:!1,error:e.message||"An error occurred while submitting the form"}}}async saveModel(e=null){if(!this.model||typeof this.model.save!="function")throw new Error("No model available for saving");e||(e=await this.getFormData());const t=this.getChangedData(e);if(!t||Object.keys(t).length===0)return{success:!0,message:"No changes to save",data:e};try{return this._isFormDrivenChange=!0,await this.model.save(t)}catch(s){throw s}}getChangedData(e){if(!this.model)return e;const t=this.getOriginalModelData();let s;return e instanceof FormData?s=this.getChangedFormData(e,t):s=this.getChangedObjectData(e,t),s}getOriginalModelData(){return this.model.attributes?this.model.attributes:typeof this.model.toJSON=="function"?this.model.toJSON():{}}getChangedFormData(e,t){const s=new FormData;let i=!1;for(const[a,r]of e.entries())if(r instanceof File)r.size===0||r.name===""||r.name==="blob"||(s.set(a,r),i=!0);else{const n=t[a];r!==n&&r!==String(n)&&(s.set(a,r),i=!0)}return i?s:null}getChangedObjectData(e,t){const s={};let i=!1;const a=new Set([...Object.keys(t),...Object.keys(e)]),r=(n,o)=>o.split(".").reduce((l,c)=>l&&typeof l=="object"?l[c]:void 0,n);for(const n of a){const o=this.findFieldConfig(n);if(!o)continue;const l=e[n],c=r(t,n),d=o.type||"text";this.valuesAreDifferent(l,c,d,o)&&(s[n]=l,i=!0)}return i?s:null}valuesAreDifferent(e,t,s="text",i={}){if(e instanceof File)return e.size>0&&e.name!==""&&e.name!=="blob";if(typeof e=="string"&&e.startsWith("data:image/"))return!0;if(s==="collection"&&typeof t=="object"&&t!==null&&t!==void 0&&typeof e=="string"){if(e==="0")return t!==null;const n=i.valueField||"id";if(t[n]==e)return!1}if(s==="switch"||s==="checkbox"||s==="toggle")return!!e!==!!t;const a=e==null?"":String(e).trim(),r=t==null?"":String(t).trim();return a!==r}validate(){const e=this.getFormElement();if(!e)return!1;const t=e.checkValidity();return t||e.classList.add("was-validated"),t}validateField(e){const t=this.getFormElement();if(!t)return!1;const s=t.elements[e];if(!s)return!1;const i=s.checkValidity();return i?(s.classList.remove("is-invalid"),s.classList.add("is-valid"),delete this.errors[e]):(s.classList.remove("is-valid"),s.classList.add("is-invalid"),this.errors[e]=s.validationMessage),i}focusFirstError(){const e=this.getFormElement();if(!e)return;const t=e.querySelector(":invalid");if(!t)return;const s=t.closest(".tab-pane");if(s&&!s.classList.contains("active")){const i=s.id,a=e.querySelector(`[role="tab"][aria-controls="${i}"], [data-bs-target="#${i}"]`);if(a){const r=window.bootstrap?.Tab?.getOrCreateInstance?window.bootstrap.Tab.getOrCreateInstance(a):null;r&&typeof r.show=="function"?r.show():(e.querySelectorAll('[role="tab"].nav-link').forEach(l=>{const c=l===a;l.classList.toggle("active",c),l.setAttribute("aria-selected",c?"true":"false")}),e.querySelectorAll(".tab-pane").forEach(l=>l.classList.remove("show","active")),s.classList.add("show","active"))}}t.focus(),t.scrollIntoView({behavior:"smooth",block:"center"})}clearAllErrors(){const e=this.getFormElement();if(!e)return;this.errors={},e.classList.remove("was-validated"),e.querySelectorAll(".is-invalid").forEach(i=>i.classList.remove("is-invalid")),e.querySelectorAll(".is-valid").forEach(i=>i.classList.remove("is-valid"))}setLoading(e){this.loading=e;const t=this.getFormElement();if(!t)return;const s=t.querySelectorAll("input, select, textarea, button"),i=t.querySelector('button[type="submit"]');if(e)s.forEach(a=>a.disabled=!0),i&&(i.innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Loading...');else if(s.forEach(a=>a.disabled=!1),i){const a=this.formConfig.options?.submitButton||"Submit";i.innerHTML=typeof a=="string"?a:"Submit"}}showError(e){if(this.emit("error",{message:e,form:this}),this.element){this.element.querySelectorAll(".alert").forEach(i=>i.remove());const s=document.createElement("div");s.className="alert alert-danger alert-dismissible fade show",s.innerHTML=`
1053
1053
  ${e}
1054
1054
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
1055
- `,this.element.insertBefore(s,this.element.firstChild),setTimeout(()=>{s.parentNode&&s.remove()},5e3)}}async updateField(e){this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async updateImagePreview(e,t){const s=this.element.querySelector(`[data-field="${e}"].image-drop-zone`);if(!s)return;let i=s.querySelector("img");const a=s.querySelector(".bi-image")?.parentElement,r=s.getAttribute("data-field-id");if(t){if(i)i.src=t;else{const n=`${r}_preview`;s.innerHTML=`
1055
+ `,this.element.insertBefore(s,this.element.firstChild),setTimeout(()=>{s.parentNode&&s.remove()},5e3)}}async updateField(e){this.formBuilder=new Q({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async updateImagePreview(e,t){const s=this.element.querySelector(`[data-field="${e}"].image-drop-zone`);if(!s)return;let i=s.querySelector("img");const a=s.querySelector(".bi-image")?.parentElement,r=s.getAttribute("data-field-id");if(t){if(i)i.src=t;else{const n=`${r}_preview`;s.innerHTML=`
1056
1056
  <img id="${n}"
1057
1057
  src="${t}"
1058
1058
  alt="Preview"
@@ -1066,7 +1066,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1066
1066
  style="opacity: 0.8;">
1067
1067
  <i class="bi bi-x"></i>
1068
1068
  </button>
1069
- `}a&&(a.style.display="none")}}findFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}if(i.tabs&&Array.isArray(i.tabs)){for(const a of i.tabs)if(a.fields&&Array.isArray(a.fields)){const r=t(a.fields);if(r)return r}}}return null};return t(this.formConfig.fields||[])}async fileToBase64(e){return new Promise((t,s)=>{const i=new FileReader;i.onload=()=>t(i.result),i.onerror=s,i.readAsDataURL(e)})}hasFiles(e){if(e instanceof FormData){for(const[t,s]of e.entries())if(s instanceof File)return!0;return!1}else{for(const t of Object.values(e))if(t instanceof File||Array.isArray(t)&&t.some(s=>s instanceof File))return!0;return!1}}reset(){const e=this.getFormElement();e&&e.reset(),this.data={},this.errors={},this.clearAllErrors(),this.emit("reset",{form:this})}async updateConfig(e){this.formConfig={...this.formConfig,...e},this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async onBeforeDestroy(){const e=[];for(const t of this.customComponents.values())t.destroy&&e.push(t.destroy());await Promise.all(e),this.customComponents.clear(),Object.values(this.data).forEach(t=>{typeof t=="string"&&t.startsWith("blob:")&&URL.revokeObjectURL(t)}),await super.onBeforeDestroy()}initializePasswordFields(){if(!this.element)return;this.element.querySelectorAll('input[data-field-type="password"], input[type="password"]').forEach(t=>{this.updatePasswordStrengthUI(t);const s=a=>{this.updatePasswordStrengthUI(a.target)};t.addEventListener("input",s);const i=a=>{if(typeof a.getModifierState=="function"){const r=a.getModifierState("CapsLock");this.updateCapsLockWarning(t,!!r)}};t.addEventListener("keydown",i),t.addEventListener("keyup",i),this.updateCapsLockWarning(t,!1)})}async onActionTogglePassword(e,t){e.preventDefault();const s=t.getAttribute("data-target");let i=null;if(s&&(i=this.element.querySelector("#"+s)),!i){const n=t.closest(".input-group");n&&(i=n.querySelector('input[type="password"], input[data-field-type="password"], input[type="text"]'))}if(!i)return;const a=i.type==="password";i.type=a?"text":"password",t.setAttribute("aria-pressed",a?"true":"false"),t.setAttribute("aria-label",a?"Hide password":"Show password");const r=t.querySelector("i");r&&(r.classList.toggle("bi-eye",!a),r.classList.toggle("bi-eye-slash",a)),i.focus();try{const n=i.value?.length??0;i.setSelectionRange(n,n)}catch{}}async onActionCopyToClipboard(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector("#"+s);if(i)try{await navigator.clipboard.writeText(i.value);const a=t.querySelector("i");if(a){const r=a.className;a.className="bi bi-check2",t.classList.add("btn-success"),t.classList.remove("btn-outline-secondary"),setTimeout(()=>{a.className=r,t.classList.remove("btn-success"),t.classList.add("btn-outline-secondary")},1500)}this.app&&this.app.toast&&this.app.toast.success("Copied to clipboard!")}catch(a){console.error("Failed to copy to clipboard:",a),this.app&&this.app.toast&&this.app.toast.error("Failed to copy to clipboard")}}computePasswordStrength(e=""){const t=e.length;let s=0;t>=6&&s++,t>=8&&s++,t>=12&&s++;const i=/[a-z]/.test(e),a=/[A-Z]/.test(e),r=/\d/.test(e),n=/[^A-Za-z0-9]/.test(e),o=[i,a,r,n].filter(Boolean).length;return o>=2&&s++,o>=3&&s++,s=Math.max(0,Math.min(4,s)),[{percent:0,label:"Too short",barClass:"bg-secondary"},{percent:25,label:"Weak",barClass:"bg-danger"},{percent:50,label:"Fair",barClass:"bg-warning"},{percent:75,label:"Good",barClass:"bg-info"},{percent:100,label:"Strong",barClass:"bg-success"}][s]}updatePasswordStrengthUI(e){if(!e||!e.id)return;const t=this.element.querySelector(`#${e.id}_strength_bar`),s=this.element.querySelector(`#${e.id}_strength_text`);if(!t&&!s)return;const{percent:i,label:a,barClass:r}=this.computePasswordStrength(e.value||"");t&&(t.className=`progress-bar ${r}`,t.style.width=`${i}%`,t.setAttribute("aria-valuenow",String(i))),s&&(s.textContent=a)}updateCapsLockWarning(e,t){if(!e||!e.id)return;const s=this.element.querySelector(`#${e.id}_caps_warning`);s&&(t?s.classList.remove("d-none"):s.classList.add("d-none"))}}class Be{constructor(e){this.fieldElement=e,this.statusContainer=this.findOrCreateStatusContainer(),this.timeouts=new Map}findOrCreateStatusContainer(){let e=this.fieldElement.parentElement.querySelector(".field-status-label-inline");if(!e){const t=this.findFieldLabel();t&&(e=t.querySelector(".field-status-label-inline"))}return e||(e=this.createStatusContainer()),e}createStatusContainer(){const e=this.getFieldType();this.getPlacementStrategy(e);const t=document.createElement("div");return this.createLabelInlineContainer(t)}getFieldType(){const e=this.fieldElement.tagName.toLowerCase(),t=this.fieldElement.type?.toLowerCase(),s=this.fieldElement.className;return t==="checkbox"||s.includes("form-check-input")||s.includes("form-switch")?"toggle":e==="select"?"select":e==="textarea"?"textarea":"input"}getPlacementStrategy(e){return"label-inline"}createLabelInlineContainer(e){e.className="field-status-label-inline",e.innerHTML=this.getStatusHTML();const t=this.findFieldLabel();return t?t.appendChild(e):this.fieldElement.parentElement.appendChild(e),e}findFieldLabel(){if(this.fieldElement.id){const s=document.querySelector(`label[for="${this.fieldElement.id}"]`);if(s)return s}const e=this.fieldElement.parentElement.querySelector("label");if(e)return e;const t=this.fieldElement.closest("label");return t||null}createInputOverlayContainer(e){e.className="field-status-overlay",e.innerHTML=this.getStatusHTML();const t=this.fieldElement.parentElement;return getComputedStyle(t).position==="static"&&(t.style.position="relative"),t.appendChild(e),e}createFullOverlayContainer(e){e.className="field-status-full-overlay d-none",e.innerHTML=`
1069
+ `}a&&(a.style.display="none")}}findFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}if(i.tabs&&Array.isArray(i.tabs)){for(const a of i.tabs)if(a.fields&&Array.isArray(a.fields)){const r=t(a.fields);if(r)return r}}}return null};return t(this.formConfig.fields||[])}async fileToBase64(e){return new Promise((t,s)=>{const i=new FileReader;i.onload=()=>t(i.result),i.onerror=s,i.readAsDataURL(e)})}hasFiles(e){if(e instanceof FormData){for(const[t,s]of e.entries())if(s instanceof File)return!0;return!1}else{for(const t of Object.values(e))if(t instanceof File||Array.isArray(t)&&t.some(s=>s instanceof File))return!0;return!1}}reset(){const e=this.getFormElement();e&&e.reset(),this.data={},this.errors={},this.clearAllErrors(),this.emit("reset",{form:this})}async updateConfig(e){this.formConfig={...this.formConfig,...e},this.formBuilder=new Q({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async onBeforeDestroy(){const e=[];for(const t of this.customComponents.values())t.destroy&&e.push(t.destroy());await Promise.all(e),this.customComponents.clear(),Object.values(this.data).forEach(t=>{typeof t=="string"&&t.startsWith("blob:")&&URL.revokeObjectURL(t)}),await super.onBeforeDestroy()}initializePasswordFields(){if(!this.element)return;this.element.querySelectorAll('input[data-field-type="password"], input[type="password"]').forEach(t=>{this.updatePasswordStrengthUI(t);const s=a=>{this.updatePasswordStrengthUI(a.target)};t.addEventListener("input",s);const i=a=>{if(typeof a.getModifierState=="function"){const r=a.getModifierState("CapsLock");this.updateCapsLockWarning(t,!!r)}};t.addEventListener("keydown",i),t.addEventListener("keyup",i),this.updateCapsLockWarning(t,!1)})}async onActionTogglePassword(e,t){e.preventDefault();const s=t.getAttribute("data-target");let i=null;if(s&&(i=this.element.querySelector("#"+s)),!i){const n=t.closest(".input-group");n&&(i=n.querySelector('input[type="password"], input[data-field-type="password"], input[type="text"]'))}if(!i)return;const a=i.type==="password";i.type=a?"text":"password",t.setAttribute("aria-pressed",a?"true":"false"),t.setAttribute("aria-label",a?"Hide password":"Show password");const r=t.querySelector("i");r&&(r.classList.toggle("bi-eye",!a),r.classList.toggle("bi-eye-slash",a)),i.focus();try{const n=i.value?.length??0;i.setSelectionRange(n,n)}catch{}}async onActionCopyToClipboard(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector("#"+s);if(i)try{await navigator.clipboard.writeText(i.value);const a=t.querySelector("i");if(a){const r=a.className;a.className="bi bi-check2",t.classList.add("btn-success"),t.classList.remove("btn-outline-secondary"),setTimeout(()=>{a.className=r,t.classList.remove("btn-success"),t.classList.add("btn-outline-secondary")},1500)}this.app&&this.app.toast&&this.app.toast.success("Copied to clipboard!")}catch(a){console.error("Failed to copy to clipboard:",a),this.app&&this.app.toast&&this.app.toast.error("Failed to copy to clipboard")}}computePasswordStrength(e=""){const t=e.length;let s=0;t>=6&&s++,t>=8&&s++,t>=12&&s++;const i=/[a-z]/.test(e),a=/[A-Z]/.test(e),r=/\d/.test(e),n=/[^A-Za-z0-9]/.test(e),o=[i,a,r,n].filter(Boolean).length;return o>=2&&s++,o>=3&&s++,s=Math.max(0,Math.min(4,s)),[{percent:0,label:"Too short",barClass:"bg-secondary"},{percent:25,label:"Weak",barClass:"bg-danger"},{percent:50,label:"Fair",barClass:"bg-warning"},{percent:75,label:"Good",barClass:"bg-info"},{percent:100,label:"Strong",barClass:"bg-success"}][s]}updatePasswordStrengthUI(e){if(!e||!e.id)return;const t=this.element.querySelector(`#${e.id}_strength_bar`),s=this.element.querySelector(`#${e.id}_strength_text`);if(!t&&!s)return;const{percent:i,label:a,barClass:r}=this.computePasswordStrength(e.value||"");t&&(t.className=`progress-bar ${r}`,t.style.width=`${i}%`,t.setAttribute("aria-valuenow",String(i))),s&&(s.textContent=a)}updateCapsLockWarning(e,t){if(!e||!e.id)return;const s=this.element.querySelector(`#${e.id}_caps_warning`);s&&(t?s.classList.remove("d-none"):s.classList.add("d-none"))}}class Ke{constructor(e){this.fieldElement=e,this.statusContainer=this.findOrCreateStatusContainer(),this.timeouts=new Map}findOrCreateStatusContainer(){let e=this.fieldElement.parentElement.querySelector(".field-status-label-inline");if(!e){const t=this.findFieldLabel();t&&(e=t.querySelector(".field-status-label-inline"))}return e||(e=this.createStatusContainer()),e}createStatusContainer(){const e=this.getFieldType();this.getPlacementStrategy(e);const t=document.createElement("div");return this.createLabelInlineContainer(t)}getFieldType(){const e=this.fieldElement.tagName.toLowerCase(),t=this.fieldElement.type?.toLowerCase(),s=this.fieldElement.className;return t==="checkbox"||s.includes("form-check-input")||s.includes("form-switch")?"toggle":e==="select"?"select":e==="textarea"?"textarea":"input"}getPlacementStrategy(e){return"label-inline"}createLabelInlineContainer(e){e.className="field-status-label-inline",e.innerHTML=this.getStatusHTML();const t=this.findFieldLabel();return t?t.appendChild(e):this.fieldElement.parentElement.appendChild(e),e}findFieldLabel(){if(this.fieldElement.id){const s=document.querySelector(`label[for="${this.fieldElement.id}"]`);if(s)return s}const e=this.fieldElement.parentElement.querySelector("label");if(e)return e;const t=this.fieldElement.closest("label");return t||null}createInputOverlayContainer(e){e.className="field-status-overlay",e.innerHTML=this.getStatusHTML();const t=this.fieldElement.parentElement;return getComputedStyle(t).position==="static"&&(t.style.position="relative"),t.appendChild(e),e}createFullOverlayContainer(e){e.className="field-status-full-overlay d-none",e.innerHTML=`
1070
1070
  <div class="saving-indicator">
1071
1071
  <div class="spinner-border spinner-border-sm text-primary" role="status">
1072
1072
  <span class="visually-hidden">Saving...</span>
@@ -1087,7 +1087,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1087
1087
  </div>
1088
1088
  <i class="bi bi-check-circle text-success d-none" data-status="saved"></i>
1089
1089
  <i class="bi bi-exclamation-circle text-danger d-none" data-status="error"></i>
1090
- `}showStatus(e,t={}){this.clearTimeout(e),this.showStandardStatus(e,t)}showStandardStatus(e,t={}){this.hideAllStatuses();const s=this.statusContainer.querySelector(`[data-status="${e}"]`);s&&(s.classList.remove("d-none"),s.classList.add("d-inline-block","show"),e==="saved"?this.setTimeout(e,()=>this.hideStatus(e),2500):e==="error"&&(t.message&&(s.title=t.message),this.setTimeout(e,()=>this.hideStatus(e),6e3)))}showFullOverlayStatus(e,t={}){this.statusContainer.querySelectorAll(".saving-indicator, .success-indicator, .error-indicator").forEach(a=>a.classList.add("d-none")),this.statusContainer.classList.remove("d-none");let i;switch(e){case"saving":i=".saving-indicator";break;case"saved":i=".success-indicator",this.setTimeout(e,()=>this.hideStatus(e),2500);break;case"error":if(i=".error-indicator",t.message){const a=this.statusContainer.querySelector(".error-indicator span");a&&(a.textContent=t.message)}this.setTimeout(e,()=>this.hideStatus(e),6e3);break}if(i){const a=this.statusContainer.querySelector(i);a&&a.classList.remove("d-none")}}hideStatus(e){const t=this.statusContainer.querySelector(`[data-status="${e}"]`);t&&(t.classList.remove("show"),t.classList.add("hide"),setTimeout(()=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","hide"),t.title=""},300))}hideAllStatuses(){this.statusContainer.querySelectorAll("[data-status]").forEach(t=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","show","hide"),t.title=""})}setTimeout(e,t,s){const i=setTimeout(t,s);this.timeouts.set(e,i)}clearTimeout(e){this.timeouts.has(e)&&(clearTimeout(this.timeouts.get(e)),this.timeouts.delete(e))}destroy(){this.timeouts.forEach(e=>clearTimeout(e)),this.timeouts.clear()}}_e(q);const xe=Object.freeze(Object.defineProperty({__proto__:null,FormView:q,default:q},Symbol.toStringTag,{value:"Module"}));class Ue extends W{constructor(e={}){super({title:"Form Page",description:"A page for submitting forms",icon:"form",fields:[],template:'<div data-container="form-view-container"></div>',className:"form-page container-sm",...e})}async onInit(){await super.onInit(),await this.recreateFormView()}async onEnter(){await super.onEnter(),this.formView&&await this.recreateFormView()}async onGroupChange(e){this.formView&&await this.recreateFormView()}async getModel(){return this.model?this.model:this.getApp().activeGroup?this.getApp().activeGroup:null}async recreateFormView(){this.formView&&(await this.formView.destroy(),this.removeChild(this.formView)),this.formView=new q({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView);const e=await this.getModel();e&&this.formView.setModel(e)}}let k=class F extends v{static _openDialogs=[];static _baseZIndex={backdrop:1050,modal:1055};static getFullscreenAwareZIndex(){return document.querySelector(".table-fullscreen")?{backdrop:10040,modal:10050}:this._baseZIndex}static _busyIndicator=null;static _busyCounter=0;static _busyTimeout=null;static fixAllBackdropStacking(){const e=document.querySelectorAll(".modal-backdrop"),t=F._openDialogs;if(e.length===0||t.length===0)return;const s=[...t].sort((i,a)=>(i._dialogZIndex||0)-(a._dialogZIndex||0));e.forEach((i,a)=>{if(a<s.length){const n=s[a]._dialogZIndex-5;i.style.zIndex=n;const l=document.querySelector(".table-fullscreen")||document.body;i.parentNode!==l&&l.appendChild(i)}})}static updateAllBackdropStacking(){F.fixAllBackdropStacking()}static showBusy(e={}){const{timeout:t=3e4,message:s="Loading..."}=e;if(this._busyCounter++,this._busyCounter===1){if(this._busyTimeout&&clearTimeout(this._busyTimeout),!this._busyIndicator){const r=this.getFullscreenAwareZIndex().modal+1e3;this._busyIndicator=document.createElement("div"),this._busyIndicator.className="mojo-busy-indicator",this._busyIndicator.innerHTML=`
1090
+ `}showStatus(e,t={}){this.clearTimeout(e),this.showStandardStatus(e,t)}showStandardStatus(e,t={}){this.hideAllStatuses();const s=this.statusContainer.querySelector(`[data-status="${e}"]`);s&&(s.classList.remove("d-none"),s.classList.add("d-inline-block","show"),e==="saved"?this.setTimeout(e,()=>this.hideStatus(e),2500):e==="error"&&(t.message&&(s.title=t.message),this.setTimeout(e,()=>this.hideStatus(e),6e3)))}showFullOverlayStatus(e,t={}){this.statusContainer.querySelectorAll(".saving-indicator, .success-indicator, .error-indicator").forEach(a=>a.classList.add("d-none")),this.statusContainer.classList.remove("d-none");let i;switch(e){case"saving":i=".saving-indicator";break;case"saved":i=".success-indicator",this.setTimeout(e,()=>this.hideStatus(e),2500);break;case"error":if(i=".error-indicator",t.message){const a=this.statusContainer.querySelector(".error-indicator span");a&&(a.textContent=t.message)}this.setTimeout(e,()=>this.hideStatus(e),6e3);break}if(i){const a=this.statusContainer.querySelector(i);a&&a.classList.remove("d-none")}}hideStatus(e){const t=this.statusContainer.querySelector(`[data-status="${e}"]`);t&&(t.classList.remove("show"),t.classList.add("hide"),setTimeout(()=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","hide"),t.title=""},300))}hideAllStatuses(){this.statusContainer.querySelectorAll("[data-status]").forEach(t=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","show","hide"),t.title=""})}setTimeout(e,t,s){const i=setTimeout(t,s);this.timeouts.set(e,i)}clearTimeout(e){this.timeouts.has(e)&&(clearTimeout(this.timeouts.get(e)),this.timeouts.delete(e))}destroy(){this.timeouts.forEach(e=>clearTimeout(e)),this.timeouts.clear()}}Re(q);const Te=Object.freeze(Object.defineProperty({__proto__:null,FormView:q,default:q},Symbol.toStringTag,{value:"Module"}));class Qe extends K{constructor(e={}){super({title:"Form Page",description:"A page for submitting forms",icon:"form",fields:[],template:'<div data-container="form-view-container"></div>',className:"form-page container-sm",...e})}async onInit(){await super.onInit(),await this.recreateFormView()}async onEnter(){await super.onEnter(),this.formView&&await this.recreateFormView()}async onGroupChange(e){this.formView&&await this.recreateFormView()}async getModel(){return this.model?this.model:this.getApp().activeGroup?this.getApp().activeGroup:null}async recreateFormView(){this.formView&&(await this.formView.destroy(),this.removeChild(this.formView)),this.formView=new q({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView);const e=await this.getModel();e&&this.formView.setModel(e)}}let T=class F extends v{static _openDialogs=[];static _baseZIndex={backdrop:1050,modal:1055};static getFullscreenAwareZIndex(){return document.querySelector(".table-fullscreen")?{backdrop:10040,modal:10050}:this._baseZIndex}static _busyIndicator=null;static _busyCounter=0;static _busyTimeout=null;static fixAllBackdropStacking(){const e=document.querySelectorAll(".modal-backdrop"),t=F._openDialogs;if(e.length===0||t.length===0)return;const s=[...t].sort((i,a)=>(i._dialogZIndex||0)-(a._dialogZIndex||0));e.forEach((i,a)=>{if(a<s.length){const n=s[a]._dialogZIndex-5;i.style.zIndex=n;const l=document.querySelector(".table-fullscreen")||document.body;i.parentNode!==l&&l.appendChild(i)}})}static updateAllBackdropStacking(){F.fixAllBackdropStacking()}static showBusy(e={}){const{timeout:t=3e4,message:s="Loading..."}=e;if(this._busyCounter++,this._busyCounter===1){if(this._busyTimeout&&clearTimeout(this._busyTimeout),!this._busyIndicator){const r=this.getFullscreenAwareZIndex().modal+1e3;this._busyIndicator=document.createElement("div"),this._busyIndicator.className="mojo-busy-indicator",this._busyIndicator.innerHTML=`
1091
1091
  <div class="mojo-busy-spinner">
1092
1092
  <div class="spinner-border text-light" role="status">
1093
1093
  <span class="visually-hidden">Loading...</span>
@@ -1203,14 +1203,14 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1203
1203
  frameborder="0"
1204
1204
  ></iframe>
1205
1205
  </div>
1206
- `,n=new F({title:s,size:i,scrollable:!1,body:r,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}]});n.on("action:refresh-preview",async d=>{const h=n.element.querySelector("#html-preview-frame");if(!h)return;const f=h.contentDocument||h.contentWindow.document;f.open(),f.write(t),f.close()});const l=document.querySelector(".table-fullscreen")||document.body;await n.render(!0,l);const c=n.element.querySelector("#html-preview-frame");if(c){const d=c.contentDocument||c.contentWindow.document;d.open(),d.write(t),d.close()}return n.show(),n.on("hidden",()=>{n.destroy(),n.element.remove()}),n}static async showDialog(e={}){if(typeof e=="string"){const g=arguments[0],y=arguments[1]||"Alert";e={...arguments[2]||{},body:g,title:y}}const{title:t="Dialog",content:s,body:i,view:a,message:r,size:n="md",centered:o=!0,buttons:l=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:c=!1,...d}=e,h=i??a??r??s??"",f=new F({title:t,body:h,size:n,centered:o,buttons:l,...d}),p=document.querySelector(".table-fullscreen")||document.body;return await f.render(!0,p),new Promise((g,y)=>{let b=!1;f.element.querySelectorAll(".modal-footer button").forEach((C,$)=>{const S=l[$];S&&C.addEventListener("click",async M=>{if(b)return;const L=S.value!==void 0?S.value:S.action??$;if(typeof S.handler=="function")try{const A=await S.handler({dialog:f,button:S,index:$,event:M});if(A===null||A===!1)return;const U=A===!0||A===void 0?L:A;b=!0,S.dismiss||f.hide(),g(U)}catch(A){console.error("Dialog button handler error:",A);return}else b=!0,S.dismiss||f.hide(),g(L)})}),f.on("hidden",()=>{b||(b=!0,c?y(new Error("Dialog dismissed")):g(null)),setTimeout(()=>{f.destroy(),f.element.remove()},100)}),f.show()})}static async alert(e={}){typeof e=="string"&&(e={message:e,title:"Alert"});const{message:t="",title:s="Alert",type:i="info",...a}=e;let r="",n="";switch(i){case"success":r='<i class="bi bi-check-circle-fill text-success me-2"></i>',n="text-success";break;case"warning":r='<i class="bi bi-exclamation-triangle-fill text-warning me-2"></i>',n="text-warning";break;case"danger":case"error":r='<i class="bi bi-x-circle-fill text-danger me-2"></i>',n="text-danger";break;default:r='<i class="bi bi-info-circle-fill text-info me-2"></i>',n="text-info"}return F.showDialog({title:`<span class="${n}">${r}${s}</span>`,body:`<p>${t}</p>`,size:"sm",centered:!0,buttons:[{text:"OK",class:"btn-primary",value:!0}],...a})}static async confirm(e,t="Confirm",s={}){typeof e=="object"&&(s=e,e=s.message,t=s.title||t);const i=new F({title:t,body:`<p>${e}</p>`,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:s.cancelText||"Cancel",class:"btn-secondary",dismiss:!0,action:"cancel"},{text:s.confirmText||"Confirm",class:s.confirmClass||"btn-primary",action:"confirm"}],...s}),r=document.querySelector(".table-fullscreen")||document.body;return await i.render(!0,r),i.show(),new Promise(n=>{let o=!1;i.on("action:confirm",()=>{o=!0,i.hide()}),i.on("hidden",()=>{i.destroy(),i.element.remove(),n(o)})})}static async prompt(e,t="Input",s={}){const i=`prompt-input-${Date.now()}`,a=s.defaultValue||"",r=s.inputType||"text",n=s.placeholder||"",o=new F({title:t,body:`
1206
+ `,n=new F({title:s,size:i,scrollable:!1,body:r,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}]});n.on("action:refresh-preview",async d=>{const h=n.element.querySelector("#html-preview-frame");if(!h)return;const m=h.contentDocument||h.contentWindow.document;m.open(),m.write(t),m.close()});const l=document.querySelector(".table-fullscreen")||document.body;await n.render(!0,l);const c=n.element.querySelector("#html-preview-frame");if(c){const d=c.contentDocument||c.contentWindow.document;d.open(),d.write(t),d.close()}return n.show(),n.on("hidden",()=>{n.destroy(),n.element.remove()}),n}static async showDialog(e={}){if(typeof e=="string"){const g=arguments[0],y=arguments[1]||"Alert";e={...arguments[2]||{},body:g,title:y}}const{title:t="Dialog",content:s,body:i,view:a,message:r,size:n="md",centered:o=!0,buttons:l=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:c=!1,...d}=e,h=i??a??r??s??"",m=new F({title:t,body:h,size:n,centered:o,buttons:l,...d}),f=document.querySelector(".table-fullscreen")||document.body;return await m.render(!0,f),new Promise((g,y)=>{let b=!1;m.element.querySelectorAll(".modal-footer button").forEach((C,$)=>{const S=l[$];S&&C.addEventListener("click",async M=>{if(b)return;const _=S.value!==void 0?S.value:S.action??$;if(typeof S.handler=="function")try{const A=await S.handler({dialog:m,button:S,index:$,event:M});if(A===null||A===!1)return;const G=A===!0||A===void 0?_:A;b=!0,S.dismiss||m.hide(),g(G)}catch(A){console.error("Dialog button handler error:",A);return}else b=!0,S.dismiss||m.hide(),g(_)})}),m.on("hidden",()=>{b||(b=!0,c?y(new Error("Dialog dismissed")):g(null)),setTimeout(()=>{m.destroy(),m.element.remove()},100)}),m.show()})}static async alert(e={}){typeof e=="string"&&(e={message:e,title:"Alert"});const{message:t="",title:s="Alert",type:i="info",...a}=e;let r="",n="";switch(i){case"success":r='<i class="bi bi-check-circle-fill text-success me-2"></i>',n="text-success";break;case"warning":r='<i class="bi bi-exclamation-triangle-fill text-warning me-2"></i>',n="text-warning";break;case"danger":case"error":r='<i class="bi bi-x-circle-fill text-danger me-2"></i>',n="text-danger";break;default:r='<i class="bi bi-info-circle-fill text-info me-2"></i>',n="text-info"}return F.showDialog({title:`<span class="${n}">${r}${s}</span>`,body:`<p>${t}</p>`,size:"sm",centered:!0,buttons:[{text:"OK",class:"btn-primary",value:!0}],...a})}static async confirm(e,t="Confirm",s={}){typeof e=="object"&&(s=e,e=s.message,t=s.title||t);const i=new F({title:t,body:`<p>${e}</p>`,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:s.cancelText||"Cancel",class:"btn-secondary",dismiss:!0,action:"cancel"},{text:s.confirmText||"Confirm",class:s.confirmClass||"btn-primary",action:"confirm"}],...s}),r=document.querySelector(".table-fullscreen")||document.body;return await i.render(!0,r),i.show(),new Promise(n=>{let o=!1;i.on("action:confirm",()=>{o=!0,i.hide()}),i.on("hidden",()=>{i.destroy(),i.element.remove(),n(o)})})}static async prompt(e,t="Input",s={}){const i=`prompt-input-${Date.now()}`,a=s.defaultValue||"",r=s.inputType||"text",n=s.placeholder||"",o=new F({title:t,body:`
1207
1207
  <p>${e}</p>
1208
1208
  <input type="${r}"
1209
1209
  class="form-control"
1210
1210
  id="${i}"
1211
1211
  value="${a}"
1212
1212
  placeholder="${n}">
1213
- `,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0},{text:"OK",class:"btn-primary",action:"ok"}],...s}),c=document.querySelector(".table-fullscreen")||document.body;return await o.render(!0,c),o.show(),o.on("shown",()=>{const d=o.element.querySelector(`#${i}`);d&&(d.focus(),d.select())}),new Promise(d=>{let h=null;o.on("action:ok",()=>{const f=o.element.querySelector(`#${i}`);h=f?f.value:null,o.hide()}),o.on("hidden",()=>{o.destroy(),o.element.remove(),d(h)})})}getModal(){return this.modal}isShown(){return this.element?.classList.contains("show")||!1}static async showForm(e={}){const{title:t="Form",formConfig:s={},size:i="md",centered:a=!0,submitText:r="Submit",cancelText:n="Cancel",...o}=e,l=(await Promise.resolve().then(()=>xe)).default,c=new l({fileHandling:e.fileHandling||"base64",data:e.data,defaults:e.defaults,model:e.model,formConfig:{fields:s.fields||e.fields,...s,submitButton:!1,resetButton:!1}}),d=new F({title:t,body:c,size:i,centered:a,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:r,class:"btn-primary",action:"submit"}],...o}),f=document.querySelector(".table-fullscreen")||document.body;return await d.render(!0,f),d.show(),new Promise(m=>{let p=!1;d.on("action:submit",async()=>{if(!p){if(!c.validate()){c.focusFirstError();return}if(e.autoSave&&e.model){d.setLoading(!0);const g=await c.saveModel();if(!g.success){d.setLoading(!1),d.render(),d.getApp().toast.error(g.message);return}p=!0,d.hide(),m(g)}try{const g=await c.getFormData();p=!0,d.hide(),m(g)}catch(g){console.error("Error collecting form data:",g),c.showError("Error collecting form data")}}}),d.on("action:cancel",()=>{p||(p=!0,d.hide(),m(null))}),d.on("hidden",()=>{p||(p=!0,m(null)),setTimeout(()=>{c.destroy(),d.destroy()},100)})})}static async showModelView(e,t){const i=e.constructor.VIEW_CLASS,a=new i({model:e});return t=t||{},await F.showDialog({header:!1,body:a,size:"lg",centered:!1,...t})}static async showModelForm(e={}){const{title:t="Edit",formConfig:s={},size:i="md",centered:a=!0,submitText:r="Save",cancelText:n="Cancel",model:o,fields:l,...c}=e;if(!o)throw new Error("showModelForm requires a model");const d=(await Promise.resolve().then(()=>xe)).default,h=new d({fileHandling:e.fileHandling||"base64",model:o,data:e.data,defaults:e.defaults,formConfig:{fields:l||s.fields||[],...s,submitButton:!1,resetButton:!1}}),f=new F({title:t,body:h,size:i,centered:a,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:r,class:"btn-primary",action:"submit"}],...c}),p=document.querySelector(".table-fullscreen")||document.body;return await f.render(!0,p),f.show(),new Promise(g=>{let y=!1;f.on("action:submit",async()=>{if(!y){f.setLoading(!0,"Saving...");try{const b=await h.handleSubmit();if(b.success)y=!0,f.hide(),g(b);else{f.setLoading(!1);let w=b.error;b.data&&b.data.error&&(w=b.data.error),f.getApp().toast.error(w)}}catch(b){console.error("Error saving form:",b),await f.setContent(h),h.showError(b.message||"An error occurred while saving")}}}),f.on("action:cancel",()=>{y||(y=!0,f.hide(),g(null))}),f.on("hidden",()=>{y||(y=!0,g(null)),setTimeout(()=>{h.destroy(),f.destroy()},100)})})}static async showData(e={}){const{title:t="Data View",data:s={},model:i=null,fields:a=[],columns:r=2,responsive:n=!0,showEmptyValues:o=!1,emptyValueText:l="—",size:c="lg",centered:d=!0,closeText:h="Close",...f}=e,m=(await Promise.resolve().then(()=>Ze)).default,p=new m({data:s,model:i,fields:a,columns:r,responsive:n,showEmptyValues:o,emptyValueText:l}),g=new F({title:t,body:p,size:c,centered:d,buttons:[{text:h,class:"btn-secondary",value:"close"}],...f}),b=document.querySelector(".table-fullscreen")||document.body;return await g.render(!0,b),g.show(),new Promise(w=>{let C=!1;const $=g.element.querySelector(".modal-footer button"),S=()=>{C||(C=!0,g.hide(),w(!0))};$?.addEventListener("click",S),g.on("hidden",()=>{C||(C=!0,w(!0)),setTimeout(()=>{p.destroy(),g.destroy(),g.element.remove()},100)}),p.on("field:click",M=>{g.emit("dataview:field:click",M)}),p.on("error",M=>{g.emit("dataview:error",M)})})}};k.showConfirm=k.confirm,k.showError=k.alert;const _=Object.freeze(Object.defineProperty({__proto__:null,default:k},Symbol.toStringTag,{value:"Module"}));class $e extends v{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=V.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=e.showCancel!==!1,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return`
1213
+ `,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0},{text:"OK",class:"btn-primary",action:"ok"}],...s}),c=document.querySelector(".table-fullscreen")||document.body;return await o.render(!0,c),o.show(),o.on("shown",()=>{const d=o.element.querySelector(`#${i}`);d&&(d.focus(),d.select())}),new Promise(d=>{let h=null;o.on("action:ok",()=>{const m=o.element.querySelector(`#${i}`);h=m?m.value:null,o.hide()}),o.on("hidden",()=>{o.destroy(),o.element.remove(),d(h)})})}getModal(){return this.modal}isShown(){return this.element?.classList.contains("show")||!1}static async showForm(e={}){const{title:t="Form",formConfig:s={},size:i="md",centered:a=!0,submitText:r="Submit",cancelText:n="Cancel",...o}=e,l=(await Promise.resolve().then(()=>Te)).default,c=new l({fileHandling:e.fileHandling||"base64",data:e.data,defaults:e.defaults,model:e.model,formConfig:{fields:s.fields||e.fields,...s,submitButton:!1,resetButton:!1}}),d=new F({title:t,body:c,size:i,centered:a,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:r,class:"btn-primary",action:"submit"}],...o}),m=document.querySelector(".table-fullscreen")||document.body;return await d.render(!0,m),d.show(),new Promise(p=>{let f=!1;d.on("action:submit",async()=>{if(!f){if(!c.validate()){c.focusFirstError();return}if(e.autoSave&&e.model){d.setLoading(!0);const g=await c.saveModel();if(!g.success){d.setLoading(!1),d.render(),d.getApp().toast.error(g.message);return}f=!0,d.hide(),p(g)}try{const g=await c.getFormData();f=!0,d.hide(),p(g)}catch(g){console.error("Error collecting form data:",g),c.showError("Error collecting form data")}}}),d.on("action:cancel",()=>{f||(f=!0,d.hide(),p(null))}),d.on("hidden",()=>{f||(f=!0,p(null)),setTimeout(()=>{c.destroy(),d.destroy()},100)})})}static async updateModelImage(e={},t={}){const s=e.upload||!1,i=t.name||e.field||"image",a={title:"Upload Your Avatar",model:null,autoSave:!s,size:"sm",fields:[{type:"image",name:i,size:"lg",imageSize:{width:200,height:200},placeholder:"Upload your image",...t}],...e},r=await F.showForm(a);if(!s||!r||!e.model)return r;const n=r[i];if(!n||!n.startsWith("data:"))return r;const o=n.split(","),l=o[0].match(/:(.*?);/)[1],c=atob(o[1]);let d=c.length;const h=new Uint8Array(d);for(;d--;)h[d]=c.charCodeAt(d);const m=l.split("/")[1]||"png",p=new window.File([h],`${i}.${m}`,{type:l}),{File:f}=await Promise.resolve().then(()=>rt),g=new f;return await g.upload({file:p,name:`${i}.${m}`,description:e.uploadDescription||`${i} upload`,showToast:!0}),await e.model.save({[i]:g.id})}static async showModelView(e,t){const i=e.constructor.VIEW_CLASS,a=new i({model:e});return t=t||{},await F.showDialog({header:!1,body:a,size:"lg",centered:!1,...t})}static async showModelForm(e={}){const{title:t="Edit",formConfig:s={},size:i="md",centered:a=!0,submitText:r="Save",cancelText:n="Cancel",model:o,fields:l,...c}=e;if(!o)throw new Error("showModelForm requires a model");const d=(await Promise.resolve().then(()=>Te)).default,h=new d({fileHandling:e.fileHandling||"base64",model:o,data:e.data,defaults:e.defaults,formConfig:{fields:l||s.fields||[],...s,submitButton:!1,resetButton:!1}}),m=new F({title:t,body:h,size:i,centered:a,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:r,class:"btn-primary",action:"submit"}],...c}),f=document.querySelector(".table-fullscreen")||document.body;return await m.render(!0,f),m.show(),new Promise(g=>{let y=!1;m.on("action:submit",async()=>{if(!y){m.setLoading(!0,"Saving...");try{const b=await h.handleSubmit();if(b.success)y=!0,m.hide(),g(b);else{m.setLoading(!1);let w=b.error;b.data&&b.data.error&&(w=b.data.error),m.getApp().toast.error(w)}}catch(b){console.error("Error saving form:",b),await m.setContent(h),h.showError(b.message||"An error occurred while saving")}}}),m.on("action:cancel",()=>{y||(y=!0,m.hide(),g(null))}),m.on("hidden",()=>{y||(y=!0,g(null)),setTimeout(()=>{h.destroy(),m.destroy()},100)})})}static async showData(e={}){const{title:t="Data View",data:s={},model:i=null,fields:a=[],columns:r=2,responsive:n=!0,showEmptyValues:o=!1,emptyValueText:l="—",size:c="lg",centered:d=!0,closeText:h="Close",...m}=e,p=(await Promise.resolve().then(()=>nt)).default,f=new p({data:s,model:i,fields:a,columns:r,responsive:n,showEmptyValues:o,emptyValueText:l}),g=new F({title:t,body:f,size:c,centered:d,buttons:[{text:h,class:"btn-secondary",value:"close"}],...m}),b=document.querySelector(".table-fullscreen")||document.body;return await g.render(!0,b),g.show(),new Promise(w=>{let C=!1;const $=g.element.querySelector(".modal-footer button"),S=()=>{C||(C=!0,g.hide(),w(!0))};$?.addEventListener("click",S),g.on("hidden",()=>{C||(C=!0,w(!0)),setTimeout(()=>{f.destroy(),g.destroy(),g.element.remove()},100)}),f.on("field:click",M=>{g.emit("dataview:field:click",M)}),f.on("error",M=>{g.emit("dataview:error",M)})})}};T.showConfirm=T.confirm,T.showError=T.alert;const L=Object.freeze(Object.defineProperty({__proto__:null,default:T},Symbol.toStringTag,{value:"Module"}));class me extends v{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=V.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=e.showCancel!==!1,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return`
1214
1214
  <div class="progress-view">
1215
1215
  <div class="d-flex justify-content-between align-items-start mb-2">
1216
1216
  <div class="flex-grow-1 min-width-0">
@@ -1249,7 +1249,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1249
1249
  </small>
1250
1250
  </div>
1251
1251
  </div>
1252
- `}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=V.pipe(this.loaded,"filesize"),this.totalFormatted=V.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,s){if(!(this.cancelled||this.completed)&&(s.disabled=!0,this.markCancelled(),this.emit("cancel"),typeof this.onCancel=="function"))try{await this.onCancel()}catch(i){console.error("Error in cancel callback:",i)}}setFilename(e){this.filename=e,this.render()}setFilesize(e){this.filesize=e,this.filesizeFormatted=V.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 ee extends v{constructor(e={}){super({className:"list-view-item",...e}),this.selected=!1,this.index=e.index??0,this.listView=e.listView??null,this.template||(this.template=`
1252
+ `}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=V.pipe(this.loaded,"filesize"),this.totalFormatted=V.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,s){if(!(this.cancelled||this.completed)&&(s.disabled=!0,this.markCancelled(),this.emit("cancel"),typeof this.onCancel=="function"))try{await this.onCancel()}catch(i){console.error("Error in cancel callback:",i)}}setFilename(e){this.filename=e,this.render()}setFilesize(e){this.filesize=e,this.filesizeFormatted=V.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 se extends v{constructor(e={}){super({className:"list-view-item",...e}),this.selected=!1,this.index=e.index??0,this.listView=e.listView??null,this.template||(this.template=`
1253
1253
  <div class="list-item-content" data-action="select">
1254
1254
  {{#model}}
1255
1255
  {{#id}}<span class="item-id">{{id}}</span>{{/id}}
@@ -1262,7 +1262,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1262
1262
  <span class="item-empty">No data</span>
1263
1263
  {{/model}}
1264
1264
  </div>
1265
- `)}async onActionSelect(e,t){e.stopPropagation(),this.selected?this.deselect():this.select()}select(){this.selected||(this.selected=!0,this.addClass("selected"),this.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}deselect(){this.selected&&(this.selected=!1,this.removeClass("selected"),this.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}async onActionDefault(e,t,s){this.emit("item:click",{item:this,model:this.model,index:this.index,action:e,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,action:e,data:this.model?.toJSON?this.model.toJSON():this.model})}setIndex(e){return this.index=e,this.element.setAttribute("data-index",e),this}setSelected(e){return e?this.select():this.deselect(),this}async destroy(){this.listView=null,await super.destroy()}}class he extends v{constructor(e={}){super({className:"list-view",template:`
1265
+ `)}async onActionSelect(e,t){e.stopPropagation(),this.selected?this.deselect():this.select()}select(){this.selected||(this.selected=!0,this.addClass("selected"),this.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:select",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}deselect(){this.selected&&(this.selected=!1,this.removeClass("selected"),this.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:deselect",{item:this,model:this.model,index:this.index,data:this.model?.toJSON?this.model.toJSON():this.model}))}async onActionDefault(e,t,s){this.emit("item:click",{item:this,model:this.model,index:this.index,action:e,data:this.model?.toJSON?this.model.toJSON():this.model}),this.listView&&this.listView.emit("item:click",{item:this,model:this.model,index:this.index,action:e,data:this.model?.toJSON?this.model.toJSON():this.model})}setIndex(e){return this.index=e,this.element.setAttribute("data-index",e),this}setSelected(e){return e?this.select():this.deselect(),this}async destroy(){this.listView=null,await super.destroy()}}class pe extends v{constructor(e={}){super({className:"list-view",template:`
1266
1266
  <div class="list-view-container">
1267
1267
  {{#loading}}
1268
1268
  <div class="list-loading">
@@ -1283,7 +1283,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1283
1283
  {{/isEmpty}}
1284
1284
  {{/loading}}
1285
1285
  </div>
1286
- `,...e}),this.collection=null,this.itemViews=new Map,this.selectedItems=new Set,this.itemTemplate=e.itemTemplate||null,this.itemClass=e.itemClass||ee,this.selectionMode=e.selectionMode||"none",this.emptyMessage=e.emptyMessage||"No items to display",this.loading=!1,this.isEmpty=!0}async onInit(){this._initCollection(this.options.collection||this.options.Collection)}_initCollection(e){if(!e){console.log("Collection not provided");return}if(e instanceof J)this.setCollection(e);else if(typeof e=="function"){const t=new e;this.setCollection(t)}else if(Array.isArray(e)){const t=new J(null,{},e);this.setCollection(t)}}setCollection(e){return this.collection===e?this:(this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this.collection=e,this.options.defaultQuery&&!this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.defaultQuery}),this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.collectionParams}),this.collection&&(this.collection.on("add",this._onModelsAdded,this),this.collection.on("remove",this._onModelsRemoved,this),this.collection.on("reset",this._onCollectionReset,this),this.collection.on("fetch:start",this._onFetchStart,this),this.collection.on("fetch:end",this._onFetchEnd,this),this._buildItems()),this)}async _renderChildren(){await super._renderChildren();const e=this.getChildElement("items");e&&this.forEachItem((t,s)=>{e.appendChild(t.element),t.render(!1)})}_buildItems(){if(this._clearItems(),!this.collection||this.collection.isEmpty()){this.isEmpty=!0,this.emit("list:empty");return}this.isEmpty=!1,this.collection.forEach((e,t)=>{this._createItemView(e,t)}),this.emit("list:loaded",{count:this.collection.length()}),this.isMounted()&&this.render()}_createItemView(e,t){if(this.itemViews.has(e.id))return;const s=new this.itemClass({model:e,index:t,listView:this,template:this.itemTemplate});return this.itemViews.set(e.id,s),s.on("item:select",this._onItemSelect.bind(this)),s.on("item:deselect",this._onItemDeselect.bind(this)),s}_clearItems(){this.forEachItem(e=>{this.removeChild(e.id)}),this.itemViews.clear(),this.selectedItems.clear()}_onModelsAdded(e){const{models:t}=e;t.forEach(s=>{const i=this.collection.models.indexOf(s);this._createItemView(s,i)}),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render()}_onModelsRemoved(e){const{models:t}=e;t.forEach(s=>{const i=this.itemViews.get(s.id);i&&(this.removeChild(i.id),this.itemViews.delete(s.id),this.selectedItems.delete(s.id))}),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render(),this.isEmpty&&this.emit("list:empty")}_onCollectionReset(e){this._buildItems()}_onFetchStart(){this.loading=!0,this.isMounted()&&this.render()}_onFetchEnd(){this.loading=!1,this.isMounted()&&this.render()}_onItemSelect(e){const{model:t,item:s}=e;if(this.selectionMode==="none"){s.deselect();return}this.selectionMode==="single"&&(this.itemViews.forEach((i,a)=>{a!==t.id&&i.selected&&i.deselect()}),this.selectedItems.clear()),this.selectedItems.add(t.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:s,model:t})}_onItemDeselect(e){const{model:t}=e;this.selectedItems.delete(t.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:e.item,model:t})}getSelectedItems(){const e=[];return this.selectedItems.forEach(t=>{const s=this.itemViews.get(t);s&&e.push({view:s,model:s.model,data:s.model?.toJSON?s.model.toJSON():s.model})}),e}forEachItem(e,t){if(typeof e!="function")throw new TypeError("Callback must be a function");let s=0;return this.itemViews.forEach((i,a)=>{e.call(t,i,i.model,s++)}),this}clearSelection(){this.forEachItem(e=>{e.selected&&e.deselect()}),this.selectedItems.clear(),this.emit("selection:change",{selected:[]})}selectItem(e){const t=this.itemViews.get(e);return t&&t.select(),this}deselectItem(e){const t=this.itemViews.get(e);return t&&t.deselect(),this}setItemTemplate(e,t=!1){return this.itemTemplate=e,t&&this.itemViews.size>0&&this.forEachItem(s=>{s.setTemplate(e),s.isMounted()&&s.render()}),this}async onAfterMount(){await super.onAfterMount(),this.collection&&(this.options.fetchOnMount||!this.collection.lastFetchTime)&&this.collection.fetch()}async refresh(){if(this.collection&&this.collection.restEnabled)return await this.collection.fetch();this._buildItems()}async destroy(){this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this._clearItems(),await super.destroy()}}class ue extends ee{constructor(e={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...e}),this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.tableView=e.tableView||e.listView||null,this.editingCells=new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if(typeof e=="string")return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if(typeof e=="object"){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}buildRowTemplate(){let e="";return this.tableView&&this.tableView.isSelectable()&&(e+=`
1286
+ `,...e}),this.collection=null,this.itemViews=new Map,this.selectedItems=new Set,this.itemTemplate=e.itemTemplate||null,this.itemClass=e.itemClass||se,this.selectionMode=e.selectionMode||"none",this.emptyMessage=e.emptyMessage||"No items to display",this.loading=!1,this.isEmpty=!0}async onInit(){this._initCollection(this.options.collection||this.options.Collection)}_initCollection(e){if(!e){console.log("Collection not provided");return}if(e instanceof W)this.setCollection(e);else if(typeof e=="function"){const t=new e;this.setCollection(t)}else if(Array.isArray(e)){const t=new W(null,{},e);this.setCollection(t)}}setCollection(e){return this.collection===e?this:(this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this.collection=e,this.options.defaultQuery&&!this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.defaultQuery}),this.options.collectionParams&&(this.collection.params={...this.collection.params,...this.options.collectionParams}),this.collection&&(this.collection.on("add",this._onModelsAdded,this),this.collection.on("remove",this._onModelsRemoved,this),this.collection.on("reset",this._onCollectionReset,this),this.collection.on("fetch:start",this._onFetchStart,this),this.collection.on("fetch:end",this._onFetchEnd,this),this._buildItems()),this)}async _renderChildren(){await super._renderChildren();const e=this.getChildElement("items");e&&this.forEachItem((t,s)=>{e.appendChild(t.element),t.render(!1)})}_buildItems(){if(this._clearItems(),!this.collection||this.collection.isEmpty()){this.isEmpty=!0,this.emit("list:empty");return}this.isEmpty=!1,this.collection.forEach((e,t)=>{this._createItemView(e,t)}),this.emit("list:loaded",{count:this.collection.length()}),this.isMounted()&&this.render()}_createItemView(e,t){if(this.itemViews.has(e.id))return;const s=new this.itemClass({model:e,index:t,listView:this,template:this.itemTemplate});return this.itemViews.set(e.id,s),s.on("item:select",this._onItemSelect.bind(this)),s.on("item:deselect",this._onItemDeselect.bind(this)),s}_clearItems(){this.forEachItem(e=>{this.removeChild(e.id)}),this.itemViews.clear(),this.selectedItems.clear()}_onModelsAdded(e){const{models:t}=e;t.forEach(s=>{const i=this.collection.models.indexOf(s);this._createItemView(s,i)}),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render()}_onModelsRemoved(e){const{models:t}=e;t.forEach(s=>{const i=this.itemViews.get(s.id);i&&(this.removeChild(i.id),this.itemViews.delete(s.id),this.selectedItems.delete(s.id))}),this.isEmpty=this.collection.isEmpty(),!this.loading&&this.isMounted()&&this.render(),this.isEmpty&&this.emit("list:empty")}_onCollectionReset(e){this._buildItems()}_onFetchStart(){this.loading=!0,this.isMounted()&&this.render()}_onFetchEnd(){this.loading=!1,this.isMounted()&&this.render()}_onItemSelect(e){const{model:t,item:s}=e;if(this.selectionMode==="none"){s.deselect();return}this.selectionMode==="single"&&(this.itemViews.forEach((i,a)=>{a!==t.id&&i.selected&&i.deselect()}),this.selectedItems.clear()),this.selectedItems.add(t.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:s,model:t})}_onItemDeselect(e){const{model:t}=e;this.selectedItems.delete(t.id),this.emit("selection:change",{selected:Array.from(this.selectedItems),item:e.item,model:t})}getSelectedItems(){const e=[];return this.selectedItems.forEach(t=>{const s=this.itemViews.get(t);s&&e.push({view:s,model:s.model,data:s.model?.toJSON?s.model.toJSON():s.model})}),e}forEachItem(e,t){if(typeof e!="function")throw new TypeError("Callback must be a function");let s=0;return this.itemViews.forEach((i,a)=>{e.call(t,i,i.model,s++)}),this}clearSelection(){this.forEachItem(e=>{e.selected&&e.deselect()}),this.selectedItems.clear(),this.emit("selection:change",{selected:[]})}selectItem(e){const t=this.itemViews.get(e);return t&&t.select(),this}deselectItem(e){const t=this.itemViews.get(e);return t&&t.deselect(),this}setItemTemplate(e,t=!1){return this.itemTemplate=e,t&&this.itemViews.size>0&&this.forEachItem(s=>{s.setTemplate(e),s.isMounted()&&s.render()}),this}async onAfterMount(){await super.onAfterMount(),this.collection&&(this.options.fetchOnMount||!this.collection.lastFetchTime)&&this.collection.fetch()}async refresh(){if(this.collection&&this.collection.restEnabled)return await this.collection.fetch();this._buildItems()}async destroy(){this.collection&&(this.collection.off("add",this._onModelsAdded,this),this.collection.off("remove",this._onModelsRemoved,this),this.collection.off("reset",this._onCollectionReset,this),this.collection.off("fetch:start",this._onFetchStart,this),this.collection.off("fetch:end",this._onFetchEnd,this)),this._clearItems(),await super.destroy()}}class fe extends se{constructor(e={}){super({tagName:"tr",className:"table-row",enableTooltips:!0,...e}),this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.tableView=e.tableView||e.listView||null,this.editingCells=new Set,this.template=this.buildRowTemplate()}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if(typeof e=="string")return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if(typeof e=="object"){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}buildRowTemplate(){let e="";return this.tableView&&this.tableView.isSelectable()&&(e+=`
1287
1287
  <td style="padding: 0;">
1288
1288
  <div class="mojo-select-cell {{#selected}}selected{{/selected}}"
1289
1289
  data-action="select">
@@ -1396,7 +1396,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1396
1396
  </button>
1397
1397
  </div>
1398
1398
  </div>
1399
- `}setupEditorEvents(e,t,s){const i=e.querySelector(".cell-input"),a=e.querySelector(".cell-save"),r=e.querySelector(".cell-cancel");i&&(i.type==="text"||i.type==="email"||i.type==="number")&&i.addEventListener("keydown",n=>{n.key==="Enter"?(n.preventDefault(),this.saveCellEdit(e,t,s)):n.key==="Escape"&&(n.preventDefault(),this.cancelCellEdit(e,t))}),i&&(i.type==="checkbox"||i.tagName==="SELECT")&&s.autoSave!==!1&&i.addEventListener("change",()=>{this.saveCellEdit(e,t,s)}),a?.addEventListener("click",()=>{this.saveCellEdit(e,t,s)}),r?.addEventListener("click",()=>{this.cancelCellEdit(e,t)})}async saveCellEdit(e,t,s){const i=e.querySelector(".cell-input");if(!i)return;let a;i.type==="checkbox"?a=i.checked:(i.tagName,a=i.value);const r=this.model.get?this.model.get(t):this.model[t];try{this.model.save?await this.model.save({[t]:a}):this.model[t]=a,this.exitEditMode(e,t,a),this.emit("cell:save",{row:this,model:this.model,column:t,oldValue:r,newValue:a})}catch(n){console.error("Failed to save cell edit:",n),this.emit("cell:save:error",{row:this,model:this.model,column:t,oldValue:r,newValue:a,error:n}),e.classList.add("saving-error"),setTimeout(()=>e.classList.remove("saving-error"),3e3)}}cancelCellEdit(e,t){const s=e.dataset.originalContent;this.exitEditMode(e,t,null,s),this.emit("cell:cancel",{row:this,model:this.model,column:t})}exitEditMode(e,t,s=null,i=null){const r=e.closest("td").querySelector(".cell-content");if(r){if(s!==null){const n=this.columns.find(l=>l.key===t);let o=s;n&&n.formatter&&typeof n.formatter=="string"&&(o=V.pipe(s,n.formatter)),r.innerHTML=this.escapeHtml(o)}else i&&(r.innerHTML=i);r.style.display=""}e.remove(),this.editingCells.delete(t)}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}select(){super.select(),this.addClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.remove("selected")}}const Se={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:u=>u==="true"||u===!0?"is null":"is not null",description:"Check if value is null or not"},range:{display:"between",description:"Between two values (comma-separated)"}};function G(u){if(!u||typeof u!="string")return{field:u,lookup:null};const e=u.split("__");if(e.length===1)return{field:u,lookup:null};const t=e[e.length-1];return Se[t]?{field:e.slice(0,-1).join("__"),lookup:t}:{field:u,lookup:null}}function Ye(u,e,t){if(!u||e===null||e===void 0)return"";const{field:s,lookup:i}=G(u),a=Se[i];if(e&&typeof e=="object"&&!Array.isArray(e)){const n=e.start!==void 0&&e.start!==null&&e.start!=="",o=e.end!==void 0&&e.end!==null&&e.end!=="";return n||o?n&&o?`${t} between '${e.start}' and '${e.end}'`:n?`${t} from '${e.start}'`:`${t} until '${e.end}'`:`${t} is '${JSON.stringify(e)}'`}const r=Array.isArray(e)?e.join(","):String(e);if(!i||i==="exact")return`${t} is '${r}'`;if(i==="in"||i==="not_in"){const n=r.split(",").map(l=>l.trim()).filter(l=>l);if(n.length===0)return`${t} ${a.display}`;const o=n.map(l=>`'${l}'`).join(", ");return`${t} ${a.display} ${o}`}if(i==="range"){const n=r.split(",").map(o=>o.trim()).filter(o=>o);return n.length===2?`${t} between '${n[0]}' and '${n[1]}'`:`${t} ${a.display} '${r}'`}if(i==="isnull"){const n=typeof a.display=="function"?a.display(r):a.display;return`${t} ${n}`}return a?`${t} ${a.display} '${r}'`:`${t} is '${r}'`}class Fe extends he{constructor(e={}){const t={className:"table-view-component",itemClass:e.itemClass||ue,selectionMode:e.selectable?"multiple":"none",emptyMessage:e.emptyMessage||"No data available",addButtonIcon:e.addButtonIcon||"bi bi-plus-circle",...e};super(t),this.isFullscreen=!1,this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.searchable=e.searchable!==!1,this.sortable=e.sortable!==!1,this.filterable=e.filterable!==!1,this.paginated=e.paginated!==!1,this.clickAction=e.clickAction||"view",this.itemView=e.itemView,this.addForm=e.addForm,this.editForm=e.editForm,this.deleteTemplate=e.deleteTemplate,this.formDialogConfig=e.formDialogConfig||{},this.viewDialogOptions=e.viewDialogOptions||{},this.exportOptions=e.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=e.exportSource||"remote",this.filters={},this.additionalFilters=e.filters||[],this.hideActivePills=e.hideActivePills||!1,this.hideActivePillNames=e.hideActivePillNames||[],this.rowAction=e.rowAction||"row-click",this.batchBarLocation=e.batchBarLocation||"bottom",this.options.addButtonLabel=e.addButtonLabel||"Add",this.toolbarButtons=e.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...e.tableOptions},this.searchPlacement=e.searchPlacement||"toolbar",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(s=>s.footer_total===!0),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(e=>{!e.key&&e.name&&(e.key=e.name),!e.label&&!e.title&&(e.label=e.key.charAt(0).toUpperCase()+e.key.slice(1))})}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if(typeof e=="string")return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if(typeof e=="object"){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}parseColumnKey(e){const t=e.split("|");return{fieldKey:t[0],formatter:t[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const e=this.calculateFooterTotals();console.log("Updating footer totals in DOM:",e);let t=0;this.columns.forEach(s=>{if(s.footer_total){const i=`col_${t}`,a=this.element.querySelector(`[data-total-column="${i}"]`);if(a&&e[i]){const r=this.parseColumnKey(s.key).formatter||s.formatter;let n;r&&typeof r=="string"?n=this.formatValue(e[i].value,r):n=e[i].value,a.textContent=n}t++}})}formatValue(e,t){try{return V.pipe(e,t)}catch(s){return console.warn("Error formatting value:",s),e}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||this.collection.length===0)return{};const e={};return this.footerTotalColumns.forEach((t,s)=>{const{fieldKey:i,formatter:a}=this.parseColumnKey(t.key);let r=0;this.collection.forEach(o=>{const l=o.get?o.get(i):o[i],c=parseFloat(l)||0;r+=c}),console.log(`Footer total for ${t.key}: ${r} (from ${this.collection.length} items)`);const n=`col_${s}`;e[n]={value:r,formatter:a||t.formatter,fieldKey:i,originalKey:t.key}}),e}extractColumnFilters(){this.filters={},this.columns.forEach(e=>{if(e.filter){const{fieldKey:t}=this.parseColumnKey(e.key);this.filters[t]=e.filter}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&this.selectionMode=="multiple"}buildTableTemplate(){const e=this.batchBarLocation==="top"?this.buildBatchActionsPanel():"",t=this.batchBarLocation==="bottom"?this.buildBatchActionsPanel():"";return`
1399
+ `}setupEditorEvents(e,t,s){const i=e.querySelector(".cell-input"),a=e.querySelector(".cell-save"),r=e.querySelector(".cell-cancel");i&&(i.type==="text"||i.type==="email"||i.type==="number")&&i.addEventListener("keydown",n=>{n.key==="Enter"?(n.preventDefault(),this.saveCellEdit(e,t,s)):n.key==="Escape"&&(n.preventDefault(),this.cancelCellEdit(e,t))}),i&&(i.type==="checkbox"||i.tagName==="SELECT")&&s.autoSave!==!1&&i.addEventListener("change",()=>{this.saveCellEdit(e,t,s)}),a?.addEventListener("click",()=>{this.saveCellEdit(e,t,s)}),r?.addEventListener("click",()=>{this.cancelCellEdit(e,t)})}async saveCellEdit(e,t,s){const i=e.querySelector(".cell-input");if(!i)return;let a;i.type==="checkbox"?a=i.checked:(i.tagName,a=i.value);const r=this.model.get?this.model.get(t):this.model[t];try{this.model.save?await this.model.save({[t]:a}):this.model[t]=a,this.exitEditMode(e,t,a),this.emit("cell:save",{row:this,model:this.model,column:t,oldValue:r,newValue:a})}catch(n){console.error("Failed to save cell edit:",n),this.emit("cell:save:error",{row:this,model:this.model,column:t,oldValue:r,newValue:a,error:n}),e.classList.add("saving-error"),setTimeout(()=>e.classList.remove("saving-error"),3e3)}}cancelCellEdit(e,t){const s=e.dataset.originalContent;this.exitEditMode(e,t,null,s),this.emit("cell:cancel",{row:this,model:this.model,column:t})}exitEditMode(e,t,s=null,i=null){const r=e.closest("td").querySelector(".cell-content");if(r){if(s!==null){const n=this.columns.find(l=>l.key===t);let o=s;n&&n.formatter&&typeof n.formatter=="string"&&(o=V.pipe(s,n.formatter)),r.innerHTML=this.escapeHtml(o)}else i&&(r.innerHTML=i);r.style.display=""}e.remove(),this.editingCells.delete(t)}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}select(){super.select(),this.addClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.remove("selected")}}const Ae={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:u=>u==="true"||u===!0?"is null":"is not null",description:"Check if value is null or not"},range:{display:"between",description:"Between two values (comma-separated)"}};function X(u){if(!u||typeof u!="string")return{field:u,lookup:null};const e=u.split("__");if(e.length===1)return{field:u,lookup:null};const t=e[e.length-1];return Ae[t]?{field:e.slice(0,-1).join("__"),lookup:t}:{field:u,lookup:null}}function Xe(u,e,t){if(!u||e===null||e===void 0)return"";const{field:s,lookup:i}=X(u),a=Ae[i];if(e&&typeof e=="object"&&!Array.isArray(e)){const n=e.start!==void 0&&e.start!==null&&e.start!=="",o=e.end!==void 0&&e.end!==null&&e.end!=="";return n||o?n&&o?`${t} between '${e.start}' and '${e.end}'`:n?`${t} from '${e.start}'`:`${t} until '${e.end}'`:`${t} is '${JSON.stringify(e)}'`}const r=Array.isArray(e)?e.join(","):String(e);if(!i||i==="exact")return`${t} is '${r}'`;if(i==="in"||i==="not_in"){const n=r.split(",").map(l=>l.trim()).filter(l=>l);if(n.length===0)return`${t} ${a.display}`;const o=n.map(l=>`'${l}'`).join(", ");return`${t} ${a.display} ${o}`}if(i==="range"){const n=r.split(",").map(o=>o.trim()).filter(o=>o);return n.length===2?`${t} between '${n[0]}' and '${n[1]}'`:`${t} ${a.display} '${r}'`}if(i==="isnull"){const n=typeof a.display=="function"?a.display(r):a.display;return`${t} ${n}`}return a?`${t} ${a.display} '${r}'`:`${t} is '${r}'`}class ke extends pe{constructor(e={}){const t={className:"table-view-component",itemClass:e.itemClass||fe,selectionMode:e.selectable?"multiple":"none",emptyMessage:e.emptyMessage||"No data available",addButtonIcon:e.addButtonIcon||"bi bi-plus-circle",...e};super(t),this.isFullscreen=!1,this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.searchable=e.searchable!==!1,this.sortable=e.sortable!==!1,this.filterable=e.filterable!==!1,this.paginated=e.paginated!==!1,this.clickAction=e.clickAction||"view",this.itemView=e.itemView,this.addForm=e.addForm,this.editForm=e.editForm,this.deleteTemplate=e.deleteTemplate,this.formDialogConfig=e.formDialogConfig||{},this.viewDialogOptions=e.viewDialogOptions||{},this.exportOptions=e.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=e.exportSource||"remote",this.filters={},this.additionalFilters=e.filters||[],this.hideActivePills=e.hideActivePills||!1,this.hideActivePillNames=e.hideActivePillNames||[],this.rowAction=e.rowAction||"row-click",this.batchBarLocation=e.batchBarLocation||"bottom",this.options.addButtonLabel=e.addButtonLabel||"Add",this.toolbarButtons=e.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...e.tableOptions},this.searchPlacement=e.searchPlacement||"toolbar",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(s=>s.footer_total===!0),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(e=>{!e.key&&e.name&&(e.key=e.name),!e.label&&!e.title&&(e.label=e.key.charAt(0).toUpperCase()+e.key.slice(1))})}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if(typeof e=="string")return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if(typeof e=="object"){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}parseColumnKey(e){const t=e.split("|");return{fieldKey:t[0],formatter:t[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const e=this.calculateFooterTotals();console.log("Updating footer totals in DOM:",e);let t=0;this.columns.forEach(s=>{if(s.footer_total){const i=`col_${t}`,a=this.element.querySelector(`[data-total-column="${i}"]`);if(a&&e[i]){const r=this.parseColumnKey(s.key).formatter||s.formatter;let n;r&&typeof r=="string"?n=this.formatValue(e[i].value,r):n=e[i].value,a.textContent=n}t++}})}formatValue(e,t){try{return V.pipe(e,t)}catch(s){return console.warn("Error formatting value:",s),e}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||this.collection.length===0)return{};const e={};return this.footerTotalColumns.forEach((t,s)=>{const{fieldKey:i,formatter:a}=this.parseColumnKey(t.key);let r=0;this.collection.forEach(o=>{const l=o.get?o.get(i):o[i],c=parseFloat(l)||0;r+=c}),console.log(`Footer total for ${t.key}: ${r} (from ${this.collection.length} items)`);const n=`col_${s}`;e[n]={value:r,formatter:a||t.formatter,fieldKey:i,originalKey:t.key}}),e}extractColumnFilters(){this.filters={},this.columns.forEach(e=>{if(e.filter){const{fieldKey:t}=this.parseColumnKey(e.key);this.filters[t]=e.filter}})}isSelectable(){return this.batchActions&&this.batchActions.length>0&&this.selectionMode=="multiple"}buildTableTemplate(){const e=this.batchBarLocation==="top"?this.buildBatchActionsPanel():"",t=this.batchBarLocation==="bottom"?this.buildBatchActionsPanel():"";return`
1400
1400
  <div class="mojo-table-wrapper">
1401
1401
  ${this.buildToolbarTemplate()}
1402
1402
  ${e}
@@ -1481,11 +1481,11 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1481
1481
  <i class="bi bi-download me-1"></i>
1482
1482
  <span class="d-none d-lg-inline">Export</span>
1483
1483
  </button>
1484
- `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((t,s)=>{const{label:i="Button",icon:a="",action:r="",handler:n=null,variant:o="outline-secondary",title:l=i,className:c="",permissions:d=null}=t;if(d&&!this.checkPermissions(d))return;const h=a?`<i class="${a} me-1"></i>`:"",f=`<span class="d-none d-lg-inline">${i}</span>`;let m="";n?m=`data-action="custom-toolbar-button" data-button-index="${s}"`:r&&(m=`data-action="${r}"`);const p=`btn btn-sm btn-${o} ${c}`.trim();e.push(`
1485
- <button class="${p}"
1486
- ${m}
1484
+ `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((t,s)=>{const{label:i="Button",icon:a="",action:r="",handler:n=null,variant:o="outline-secondary",title:l=i,className:c="",permissions:d=null}=t;if(d&&!this.checkPermissions(d))return;const h=a?`<i class="${a} me-1"></i>`:"",m=`<span class="d-none d-lg-inline">${i}</span>`;let p="";n?p=`data-action="custom-toolbar-button" data-button-index="${s}"`:r&&(p=`data-action="${r}"`);const f=`btn btn-sm btn-${o} ${c}`.trim();e.push(`
1485
+ <button class="${f}"
1486
+ ${p}
1487
1487
  title="${l}">
1488
- ${h}${f}
1488
+ ${h}${m}
1489
1489
  </button>
1490
1490
  `)}),e.join("")}buildSearchTemplate(){return`
1491
1491
  <div class="flex-grow-1" style="max-width: 400px;">
@@ -1536,7 +1536,7 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1536
1536
  <i class="bi bi-x-circle me-2"></i>Clear All Filters
1537
1537
  </button>
1538
1538
  `:""}
1539
- `}updateFilterPills(){const e=this.element?.querySelector('[data-container="filter-pills"]');if(!e)return;this.getActiveFilters();const t=this.buildActivePills();e.innerHTML=t}updateSearchInputs(e){const t=this.element?.querySelectorAll('[data-filter="search"]');t&&t.forEach(s=>{s.value=e||""})}buildActivePills(){if(this.hideActivePills)return"";const e=this.getActiveFilters(),t=e.search&&e.search.toString().trim()!=="";let s=Object.entries(e).filter(([n,o])=>o&&o.toString().trim()!==""&&n!=="search");if(this.hideActivePillNames&&this.hideActivePillNames.length>0&&(s=s.filter(([n])=>!this.hideActivePillNames.includes(n))),s.length===0&&!t)return"";const i=s.map(([n,o])=>{const{field:l}=G(n),c=this.getFilterLabel(l),d=Ye(n,o,c);return`
1539
+ `}updateFilterPills(){const e=this.element?.querySelector('[data-container="filter-pills"]');if(!e)return;this.getActiveFilters();const t=this.buildActivePills();e.innerHTML=t}updateSearchInputs(e){const t=this.element?.querySelectorAll('[data-filter="search"]');t&&t.forEach(s=>{s.value=e||""})}buildActivePills(){if(this.hideActivePills)return"";const e=this.getActiveFilters(),t=e.search&&e.search.toString().trim()!=="";let s=Object.entries(e).filter(([n,o])=>o&&o.toString().trim()!==""&&n!=="search");if(this.hideActivePillNames&&this.hideActivePillNames.length>0&&(s=s.filter(([n])=>!this.hideActivePillNames.includes(n))),s.length===0&&!t)return"";const i=s.map(([n,o])=>{const{field:l}=X(n),c=this.getFilterLabel(l),d=Xe(n,o,c);return`
1540
1540
  <span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">
1541
1541
  <i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>
1542
1542
 
@@ -1694,25 +1694,46 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1694
1694
  </nav>
1695
1695
  </div>
1696
1696
  </div>
1697
- `:""}_createItemView(e,t){const s=new this.itemClass({model:e,index:t,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(e.id,s),s.on("item:select",i=>{this._onItemSelect(i),this.updateBatchActionsPanel()}),s.on("item:deselect",i=>{this._onItemDeselect(i),this.updateBatchActionsPanel()}),s.on("row:click",this._onRowClick.bind(this)),s.on("row:view",this._onRowView.bind(this)),s.on("row:edit",this._onRowEdit.bind(this)),s.on("row:delete",this._onRowDelete.bind(this)),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onMounted(){await super.onMounted();const e=this.getActiveFilters();this.collection&&Object.keys(e).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){if(!this.element)return;this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",s=>{s.target.value===""&&this.getActiveFilters().search&&this.onActionClearSearch(s,s.target)})})}_onRowClick(e){if(this.emit("row:click",e),this.options.onRowClick)return this.options.onRowClick(e.model,e.event);this.clickAction==="view"?this._onRowView(e):this.clickAction==="edit"&&this._onRowEdit(e)}getModelClass(e){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:e?.constructor?e.constructor:null}getModelName(e){const t=this.getModelClass(e);return t&&(t.MODEL_NAME||t.name.replace(/Model$/,""))||"Item"}getItemViewClass(e){if(this.itemView)return this.itemView;const t=this.getModelClass(e);return t?.VIEW_CLASS?t.VIEW_CLASS:null}getAddFormConfig(e){return this.addForm||e?.ADD_FORM||this.editForm||e?.EDIT_FORM}getEditFormConfig(e){return this.editForm||e?.EDIT_FORM||this.addForm||e?.ADD_FORM}getFormDialogConfig(e){return{...e?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(e,t){return e?E.render(e,t):""}async _onRowView(e){if(this.emit("row:view",e),this.options.onItemView){await this.options.onItemView(e.model,e.event);return}const t=this.getItemViewClass(e.model);if(t){const s=new t({model:e.model,collection:this.collection});await k.showDialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await k.showData({title:`View ${this.getModelName(e.model)} #${e.model.id}`,model:e.model})}async _onRowEdit(e){if(this.emit("row:edit",e),this.options.onItemEdit){await this.options.onItemEdit(e.model,e.event);return}const t=this.getModelClass(e.model);let s=this.getEditFormConfig(t);if(s){s.fields||(s={title:`Edit ${this.getModelName(e.model)}`,fields:s});const i=await k.showModelForm({model:e.model,...s,...this.getFormDialogConfig(t)});if(!i)return;if(!i.success||!i?.result?.data.status){k.showError(i?.result?.data?.error||i?.result?.message||"An error occurred");return}}else{const i=await k.showDialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new q({model:e.model,fields:this.options.formFields||[]})});if(i){const a=await e.model.save(i);if(!a.data?.status){k.showError(a.data.error||"An error occurred");return}await this.refresh()}}}async _onRowDelete(e){if(this.emit("row:delete",e),this.options.onItemDelete){await this.options.onItemDelete(e.model,e.event);return}const t=this.getModelClass(e.model),s=this.deleteTemplate||t?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',i=this.renderTemplateString(s,e.model);await k.confirm({message:i||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await e.model.destroy(),this.collection.fetch())}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){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(e){console.warn("Could not enter fullscreen:",e)}}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(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!(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",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}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(e,t){await this.refresh()}async onActionAdd(e,t){if(this.options.onAdd){this.emit("table:add",{event:e}),await this.options.onAdd(e);return}this.emit("table:add",{event:e});const s=this.getModelClass();if(!s){console.warn("Cannot determine Model class for add operation");return}let i=this.getAddFormConfig(s);if(i){const a=new s;i.fields||(i={title:`Add ${this.getModelName()}`,fields:i});const r=await k.showForm({model:a,...i,...this.getFormDialogConfig(s)});if(r){this.options.addRequiresActiveGroup&&(r.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(r.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(r,this.options.addFormDefaults);const n=await a.save(r);if(!n?.data.status){k.showError(n?.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}else{const a=new s,r=await k.showDialog({title:`Add ${this.getModelName()}`,body:new q({model:a,fields:this.options.formFields||[]})});if(r){const n=await a.save(r);if(!n?.data.status){k.showError(n.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";this.emit("table:export",{format:s,source:this.exportSource,event:e}),this.exportSource==="remote"?this.collection?await this.collection.download(s):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],s):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(e,t){const s=t.value.trim();this.collection&&(this.setFilter("search",s),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:s,event:e}),this.emit("params-changed")}async onActionClearSearch(e,t){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:e}),this.emit("params-changed")}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return e==="asc"?'<i class="bi bi-sort-alpha-down text-primary"></i>':e==="desc"?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),i=t.getAttribute("data-direction");if(this.collection){let a;if(i==="none"?a=void 0:i==="desc"?a=`-${s}`:a=s,this.collection.setParams({...this.collection.params,sort:a,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(a){const r=a.startsWith("-"),n=r?a.slice(1):a;this.collection.sort((o,l)=>{const c=o.get(n),d=l.get(n);return c<d?r?1:-1:c>d?r?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:s,event:e}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const e=this.getSortBy(),t=this.getSortDirection();this.columns.forEach(s=>{if(this.sortable&&s.sortable!==!1){const{fieldKey:i}=this.parseColumnKey(s.key),a=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${i}"]`);if(a){const r=e===i,n=this.getSortIcon(r?t:null);a.innerHTML=n;const o=a.nextElementSibling;if(o){const l=o.querySelector(`[data-field="${i}"][data-direction="asc"]`),c=o.querySelector(`[data-field="${i}"][data-direction="desc"]`),d=o.querySelector(`[data-field="${i}"][data-direction="none"]`);l&&l.classList.toggle("active",r&&t==="asc"),c&&c.classList.toggle("active",r&&t==="desc"),d&&d.classList.toggle("active",!r||e!==i)}}}})}async onActionSelectAll(e,t){e.stopPropagation();const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(a=>a.selected);s?this.clearSelection():this.forEachItem(a=>{a.selected||a.select()});const i=this.element?.querySelector(".mojo-select-all-cell");i&&i.classList.toggle("selected",!s),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 e=this.collection.meta?.count||this.collection.length(),t=this.collection.params?.start||0,s=this.collection.params?.size||10,i=Math.min(t+s,e),a=this.element.querySelector('[data-value="start"]'),r=this.element.querySelector('[data-value="end"]'),n=this.element.querySelector('[data-value="total"]');a&&(a.textContent=t+1),r&&(r.textContent=i),n&&(n.textContent=e);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=s),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const e=this.element.querySelector('[data-container="pagination"]');if(!e||!this.collection)return;const t=this.collection.meta?.count||this.collection.length(),s=this.collection.params?.size||10,i=this.collection.params?.start||0,a=Math.floor(i/s)+1,r=Math.ceil(t/s);if(r<=1){e.innerHTML="";return}const n=a>1?a-1:r,o=a<r?a+1:1,l=[];l.push(`
1697
+ `:""}_createItemView(e,t){const s=new this.itemClass({model:e,index:t,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(e.id,s),s.on("item:select",i=>{this._onItemSelect(i),this.updateBatchActionsPanel()}),s.on("item:deselect",i=>{this._onItemDeselect(i),this.updateBatchActionsPanel()}),s.on("row:click",this._onRowClick.bind(this)),s.on("row:view",this._onRowView.bind(this)),s.on("row:edit",this._onRowEdit.bind(this)),s.on("row:delete",this._onRowDelete.bind(this)),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onMounted(){await super.onMounted();const e=this.getActiveFilters();this.collection&&Object.keys(e).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){if(!this.element)return;this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",s=>{s.target.value===""&&this.getActiveFilters().search&&this.onActionClearSearch(s,s.target)})})}_onRowClick(e){if(this.emit("row:click",e),this.options.onRowClick)return this.options.onRowClick(e.model,e.event);this.clickAction==="view"?this._onRowView(e):this.clickAction==="edit"&&this._onRowEdit(e)}getModelClass(e){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:e?.constructor?e.constructor:null}getModelName(e){const t=this.getModelClass(e);return t&&(t.MODEL_NAME||t.name.replace(/Model$/,""))||"Item"}getItemViewClass(e){if(this.itemView)return this.itemView;const t=this.getModelClass(e);return t?.VIEW_CLASS?t.VIEW_CLASS:null}getAddFormConfig(e){return this.addForm||e?.ADD_FORM||this.editForm||e?.EDIT_FORM}getEditFormConfig(e){return this.editForm||e?.EDIT_FORM||this.addForm||e?.ADD_FORM}getFormDialogConfig(e){return{...e?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(e,t){return e?E.render(e,t):""}async _onRowView(e){if(this.emit("row:view",e),this.options.onItemView){await this.options.onItemView(e.model,e.event);return}const t=this.getItemViewClass(e.model);if(t){const s=new t({model:e.model,collection:this.collection});await T.showDialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await T.showData({title:`View ${this.getModelName(e.model)} #${e.model.id}`,model:e.model})}async _onRowEdit(e){if(this.emit("row:edit",e),this.options.onItemEdit){await this.options.onItemEdit(e.model,e.event);return}const t=this.getModelClass(e.model);let s=this.getEditFormConfig(t);if(s){s.fields||(s={title:`Edit ${this.getModelName(e.model)}`,fields:s});const i=await T.showModelForm({model:e.model,...s,...this.getFormDialogConfig(t)});if(!i)return;if(!i.success||!i?.result?.data.status){T.showError(i?.result?.data?.error||i?.result?.message||"An error occurred");return}}else{const i=await T.showDialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new q({model:e.model,fields:this.options.formFields||[]})});if(i){const a=await e.model.save(i);if(!a.data?.status){T.showError(a.data.error||"An error occurred");return}await this.refresh()}}}async _onRowDelete(e){if(this.emit("row:delete",e),this.options.onItemDelete){await this.options.onItemDelete(e.model,e.event);return}const t=this.getModelClass(e.model),s=this.deleteTemplate||t?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',i=this.renderTemplateString(s,e.model);await T.confirm({message:i||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await e.model.destroy(),this.collection.fetch())}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){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(e){console.warn("Could not enter fullscreen:",e)}}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(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!(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",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}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(e,t){await this.refresh()}async onActionAdd(e,t){if(this.options.onAdd){this.emit("table:add",{event:e}),await this.options.onAdd(e);return}this.emit("table:add",{event:e});const s=this.getModelClass();if(!s){console.warn("Cannot determine Model class for add operation");return}let i=this.getAddFormConfig(s);if(i){const a=new s;i.fields||(i={title:`Add ${this.getModelName()}`,fields:i});const r=await T.showForm({model:a,...i,...this.getFormDialogConfig(s)});if(r){this.options.addRequiresActiveGroup&&(r.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(r.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(r,this.options.addFormDefaults);const n=await a.save(r);if(!n?.data.status){T.showError(n?.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}else{const a=new s,r=await T.showDialog({title:`Add ${this.getModelName()}`,body:new q({model:a,fields:this.options.formFields||[]})});if(r){const n=await a.save(r);if(!n?.data.status){T.showError(n.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";this.emit("table:export",{format:s,source:this.exportSource,event:e}),this.exportSource==="remote"?this.collection?await this.collection.download(s):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],s):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(e,t){const s=t.value.trim();this.collection&&(this.setFilter("search",s),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:s,event:e}),this.emit("params-changed")}async onActionClearSearch(e,t){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:e}),this.emit("params-changed")}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return e==="asc"?'<i class="bi bi-sort-alpha-down text-primary"></i>':e==="desc"?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),i=t.getAttribute("data-direction");if(this.collection){let a;if(i==="none"?a=void 0:i==="desc"?a=`-${s}`:a=s,this.collection.setParams({...this.collection.params,sort:a,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(a){const r=a.startsWith("-"),n=r?a.slice(1):a;this.collection.sort((o,l)=>{const c=o.get(n),d=l.get(n);return c<d?r?1:-1:c>d?r?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:s,event:e}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const e=this.getSortBy(),t=this.getSortDirection();this.columns.forEach(s=>{if(this.sortable&&s.sortable!==!1){const{fieldKey:i}=this.parseColumnKey(s.key),a=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${i}"]`);if(a){const r=e===i,n=this.getSortIcon(r?t:null);a.innerHTML=n;const o=a.nextElementSibling;if(o){const l=o.querySelector(`[data-field="${i}"][data-direction="asc"]`),c=o.querySelector(`[data-field="${i}"][data-direction="desc"]`),d=o.querySelector(`[data-field="${i}"][data-direction="none"]`);l&&l.classList.toggle("active",r&&t==="asc"),c&&c.classList.toggle("active",r&&t==="desc"),d&&d.classList.toggle("active",!r||e!==i)}}}})}async onActionSelectAll(e,t){e.stopPropagation();const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(a=>a.selected);s?this.clearSelection():this.forEachItem(a=>{a.selected||a.select()});const i=this.element?.querySelector(".mojo-select-all-cell");i&&i.classList.toggle("selected",!s),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 e=this.collection.meta?.count||this.collection.length(),t=this.collection.params?.start||0,s=this.collection.params?.size||10,i=Math.min(t+s,e),a=this.element.querySelector('[data-value="start"]'),r=this.element.querySelector('[data-value="end"]'),n=this.element.querySelector('[data-value="total"]');a&&(a.textContent=t+1),r&&(r.textContent=i),n&&(n.textContent=e);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=s),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const e=this.element.querySelector('[data-container="pagination"]');if(!e||!this.collection)return;const t=this.collection.meta?.count||this.collection.length(),s=this.collection.params?.size||10,i=this.collection.params?.start||0,a=Math.floor(i/s)+1,r=Math.ceil(t/s);if(r<=1){e.innerHTML="";return}const n=a>1?a-1:r,o=a<r?a+1:1,l=[];l.push(`
1698
1698
  <li class="page-item">
1699
1699
  <a class="page-link" href="#" data-action="page" data-page="${n}">
1700
1700
  <i class="bi bi-chevron-left"></i>
1701
1701
  </a>
1702
1702
  </li>
1703
- `);const c=1,d=new Set([1,r]);for(let m=a-c;m<=a+c;m++)m>=1&&m<=r&&d.add(m);const h=Array.from(d).sort((m,p)=>m-p);let f=0;for(const m of h)f&&m-f>1&&l.push(`
1703
+ `);const c=1,d=new Set([1,r]);for(let p=a-c;p<=a+c;p++)p>=1&&p<=r&&d.add(p);const h=Array.from(d).sort((p,f)=>p-f);let m=0;for(const p of h)m&&p-m>1&&l.push(`
1704
1704
  <li class="page-item disabled"><span class="page-link">…</span></li>
1705
1705
  `),l.push(`
1706
- <li class="page-item ${m===a?"active":""}">
1707
- <a class="page-link" href="#" data-action="page" data-page="${m}">${m}</a>
1706
+ <li class="page-item ${p===a?"active":""}">
1707
+ <a class="page-link" href="#" data-action="page" data-page="${p}">${p}</a>
1708
1708
  </li>
1709
- `),f=m;l.push(`
1709
+ `),m=p;l.push(`
1710
1710
  <li class="page-item">
1711
1711
  <a class="page-link" href="#" data-action="page" data-page="${o}">
1712
1712
  <i class="bi bi-chevron-right"></i>
1713
1713
  </a>
1714
1714
  </li>
1715
- `),e.innerHTML=l.join("")}async onActionPage(e,t){e.preventDefault();const s=parseInt(t.getAttribute("data-page"),10),i=this.collection.params?.size||10,a=this.collection.meta?.count||this.collection.length(),r=Math.max(1,Math.ceil(a/i));let n=isNaN(s)?1:s;n<1&&(n=r),n>r&&(n=1),this.collection.setParams({...this.collection.params,start:(n-1)*i}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:n,event:e}),this.emit("params-changed")}async onChangePageSize(e,t){const s=parseInt(t.value);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:s}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("table:pagesize",{size:s,event:e}),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:e,size:t,sort:s,...i}=this.collection.params,a={},r=new Set;return this.getAllAvailableFilters().forEach(o=>{if(o.config.type==="daterange"){const l=o.key,c=o.config.startName||"dr_start",d=o.config.endName||"dr_end",h=o.config.fieldName||"dr_field";i[h]===l&&(i[c]||i[d])&&(a[l]={start:i[c]||"",end:i[d]||""},r.add(c),r.add(d),r.add(h))}}),Object.keys(i).forEach(o=>{r.has(o)||(a[o]=i[o])}),Object.keys(a).forEach(o=>{if(a.hasOwnProperty(o)){const l=`${o}__in`;a.hasOwnProperty(l)&&(delete a[o],a[l]=a[l])}}),a}setFilter(e,t){if(!this.collection)return;const s=this.getFilterConfig(e);if(s&&s.type==="daterange"){const i=s.startName||"dr_start",a=s.endName||"dr_end",r=s.fieldName||"dr_field";delete this.collection.params[i],delete this.collection.params[a],delete this.collection.params[r],t&&typeof t=="object"&&(t.start||t.end)&&(t.start&&(this.collection.params[i]=t.start),t.end&&(this.collection.params[a]=t.end),this.collection.params[r]=e)}else{const{field:i,lookup:a}=G(e);if(delete this.collection.params[e],delete this.collection.params[i],delete this.collection.params[`${i}__in`],!t||Array.isArray(t)&&t.length===0)return;Array.isArray(t)?t.length===1?this.collection.params[i]=t[0]:this.collection.params[`${i}__in`]=t.join(","):this.collection.params[e]=t}}getAllAvailableFilters(){const e=[];return this.columns.forEach(t=>{if(t.filter){const{fieldKey:s}=this.parseColumnKey(t.key);e.push({key:s,label:t.filter.label||t.label||s,type:t.filter.type,config:t.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(t=>{e.push({key:t.name||t.key,label:t.label,type:t.type,config:t})}),e}getFilterConfig(e){const t=this.columns.find(s=>{const{fieldKey:i}=this.parseColumnKey(s.key);return i===e});if(t&&t.filter)return t.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const s=this.additionalFilters.find(i=>(i.name||i.key)===e);if(s)return s}return null}getFilterLabel(e){if(e==="search")return"Search";const t=this.filters[e];if(t&&t.label)return t.label;const s=this.additionalFilters.find(i=>(i.name||i.key)===e);return s&&s.label?s.label:e.charAt(0).toUpperCase()+e.slice(1)}getFilterDisplayValue(e,t){if(e==="search")return`"${t}"`;const s=this.filters[e]||this.additionalFilters.find(i=>(i.name||i.key)===e);if(s&&s.type==="daterange"&&typeof t=="object"){const i=t.start||"",a=t.end||"";return`${i} to ${a}`}if(s&&s.type==="select"&&s.options){if(typeof s.options[0]=="object"){const i=s.options.find(a=>a.value===t);return i?i.label:t}return t}return t}getFilterIcon(e){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[e]||"filter"}async onActionAddFilter(e,t){const s=t.getAttribute("data-filter-key"),i=this.getFilterConfig(s),a=this.getActiveFilters()[s];if(!i){console.warn("No filter config found for key:",s);return}const r=await k.showForm({title:`${a!==void 0&&a!==""?"Edit":"Add"} ${this.getFilterLabel(s)} Filter`,size:"md",fields:[this.buildFilterDialogField(i,a,s)]});if(r){const n=this.extractFilterValue(i,r);this.setFilter(s,n),await this.applyFilters()}}buildFilterDialogField(e,t,s){const i={name:"filter_value",label:e.label,value:t,...e,placeholder:e.placeholder||e.placeHolder};if(e.type==="daterange"){if(i.startName=i.startName||"dr_start",i.endName=i.endName||"dr_end",i.fieldName=i.fieldName||"dr_field",i.format=i.format||"YYYY-MM-DD",i.displayFormat=i.displayFormat||"MMM DD, YYYY",i.separator=i.separator||" to ",i.label=i.label||"Date Range",t&&typeof t=="object"){const a=r=>{if(!r&&r!==0)return"";if(r instanceof Date&&!isNaN(r))return r.toISOString().slice(0,10);const n=String(r).trim();if(!n)return"";if(/^-?\d+$/.test(n)){const l=Number(n),c=n.length<=10?l*1e3:l,d=new Date(c);if(!isNaN(d))return d.toISOString().slice(0,10)}const o=new Date(n);return isNaN(o)?n:o.toISOString().slice(0,10)};i.startDate=a(t.start||t.from||t.begin||""),i.endDate=a(t.end||t.to||t.finish||"")}}else if(e.type==="multiselect"){let a=[];t&&(Array.isArray(t)?a=t:typeof t=="string"&&(a=t.split(",").map(r=>r.trim()).filter(r=>r))),i.value=a,!i.placeholder&&!i.placeHolder&&(e.placeholder||e.placeHolder?i.placeholder=e.placeholder||e.placeHolder:e.label&&(i.placeholder=`Select ${e.label}...`))}return i}extractFilterValue(e,t){if(e.type==="daterange"){const s=e.startName||"dr_start",i=e.endName||"dr_end";return{start:t[s],end:t[i]}}return e.type==="multiselect",t.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),this.render()}catch(e){console.error("Failed to fetch filtered data:",e),this.render()}else this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=G(s);let a=this.getFilterConfig(i)||this.getFilterConfig(s);const r=this.getActiveFilters(),n=r[s]||r[i];if(!a){console.warn("No filter config found for key:",s,"or field:",i);return}const o={filter_value:n};if(a.type==="daterange"&&n&&typeof n=="object"){const c=a.startName||"dr_start",d=a.endName||"dr_end";o[c]=n.start||"",o[d]=n.end||""}const l=await k.showForm({title:`Edit ${this.getFilterLabel(i)} Filter`,size:"md",data:o,fields:[this.buildFilterDialogField(a,n,i)]});if(l){const c=this.extractFilterValue(a,l);this.setFilter(s,c),await this.applyFilters()}}async onActionRemoveFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=G(s);this.setFilter(s,null),s==="search"&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:s,field:i}),this.emit("params-changed")}async onActionClearAllFilters(e,t){if(!this.collection)return;const{start:s,size:i,sort:a}=this.collection.params;this.collection.params={start:s,size:i},a&&(this.collection.params.sort=a),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||this.batchActions.length===0)return;const e=this.getSelectedItems().length;if(this.batchBarLocation==="top"){const s=this.element?.querySelector(".batch-actions-panel-top"),i=this.element?.querySelector(".batch-select-count");s&&i&&(i.textContent=e,e>0?s.classList.remove("d-none"):s.classList.add("d-none"))}else{const s=this.element?.querySelector(".batch-actions-panel"),i=this.element?.querySelector(".batch-select-count");s&&i&&(i.textContent=e,s.style.display=e>0?"block":"none")}const t=this.element?.querySelector(".mojo-select-all-cell");if(t){const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(r=>r.selected),i=Array.from(this.itemViews.values()).some(r=>r.selected);t.classList.toggle("selected",s),t.classList.toggle("indeterminate",!s&&i);const a=t.querySelector("i");a&&(a.className=!s&&i?"bi bi-dash":"bi bi-check")}}async onActionBatch(e,t){const s=t.getAttribute("data-action").replace("batch-",""),i=this.getSelectedItems();this.emit("batch:action",{action:s,items:i,event:e})}async onActionClearSelection(e,t){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(e,t){const s=parseInt(t.getAttribute("data-button-index"),10),i=this.toolbarButtons[s];i&&typeof i.handler=="function"&&await i.handler.call(this,e,t)}}function We(){return typeof window>"u"?null:((!window.MOJO||typeof window.MOJO!="object")&&(window.MOJO={}),window.MOJO)}function Je(u){return u.__lite&&u.__lite.version||(u.WebApp=K,u.View=v,u.Page=W,u.Router=te,u.Model=X,u.Collection=J,u.Rest=z,u.FormBuilder=Z,u.FormView=q,u.Dialog=k,u.ProgressView=$e,u.ListView=he,u.ListViewItem=ee,u.TableView=Fe,u.TableRow=ue,u.DataFormatter=V,u.MOJOUtils=x,u.__lite={version:"dev",build:"web-mojo.lite"},u.mount=async function(t,s){if(!t)throw new Error("MOJO.mount(view, container) requires a view");const i=typeof s=="string"?document.querySelector(s):s;if(!i)throw new Error("MOJO.mount(view, container) container not found");if(typeof t.render!="function"||typeof t.mount!="function")throw new Error("MOJO.mount expects a View instance with render() and mount() methods");return await t.render(),await t.mount(i),t}),u}const fe=We();fe&&Je(fe);class me extends v{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:r,showEmptyValues:n,emptyValueText:o,...l}=e;super({tagName:"div",className:"data-view",...l}),this.data=t||{},this.fields=i||[],this.model=s||null,this.model&&(this.data=this.model),this.dataViewOptions={columns:a||2,responsive:r!==!1,showEmptyValues:n||!1,emptyValueText:o||"—",rowClass:"row g-3",itemClass:"data-view-item",labelClass:"data-view-label fw-semibold text-muted small text-uppercase",valueClass:"data-view-value"}}async onBeforeRender(){this.fields.length===0&&this.getData()&&this.generateFieldsFromData()}async renderTemplate(){const e=this.buildItemsHTML();return`
1715
+ `),e.innerHTML=l.join("")}async onActionPage(e,t){e.preventDefault();const s=parseInt(t.getAttribute("data-page"),10),i=this.collection.params?.size||10,a=this.collection.meta?.count||this.collection.length(),r=Math.max(1,Math.ceil(a/i));let n=isNaN(s)?1:s;n<1&&(n=r),n>r&&(n=1),this.collection.setParams({...this.collection.params,start:(n-1)*i}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:n,event:e}),this.emit("params-changed")}async onChangePageSize(e,t){const s=parseInt(t.value);this.collection&&(this.collection.setParams({...this.collection.params,start:0,size:s}),this.collection.restEnabled&&await this.collection.fetch(),this.render()),this.emit("table:pagesize",{size:s,event:e}),this.emit("params-changed")}getActiveFilters(){if(!this.collection?.params)return{};const{start:e,size:t,sort:s,...i}=this.collection.params,a={},r=new Set;return this.getAllAvailableFilters().forEach(o=>{if(o.config.type==="daterange"){const l=o.key,c=o.config.startName||"dr_start",d=o.config.endName||"dr_end",h=o.config.fieldName||"dr_field";i[h]===l&&(i[c]||i[d])&&(a[l]={start:i[c]||"",end:i[d]||""},r.add(c),r.add(d),r.add(h))}}),Object.keys(i).forEach(o=>{r.has(o)||(a[o]=i[o])}),Object.keys(a).forEach(o=>{if(a.hasOwnProperty(o)){const l=`${o}__in`;a.hasOwnProperty(l)&&(delete a[o],a[l]=a[l])}}),a}setFilter(e,t){if(!this.collection)return;const s=this.getFilterConfig(e);if(s&&s.type==="daterange"){const i=s.startName||"dr_start",a=s.endName||"dr_end",r=s.fieldName||"dr_field";delete this.collection.params[i],delete this.collection.params[a],delete this.collection.params[r],t&&typeof t=="object"&&(t.start||t.end)&&(t.start&&(this.collection.params[i]=t.start),t.end&&(this.collection.params[a]=t.end),this.collection.params[r]=e)}else{const{field:i,lookup:a}=X(e);if(delete this.collection.params[e],delete this.collection.params[i],delete this.collection.params[`${i}__in`],!t||Array.isArray(t)&&t.length===0)return;Array.isArray(t)?t.length===1?this.collection.params[i]=t[0]:this.collection.params[`${i}__in`]=t.join(","):this.collection.params[e]=t}}getAllAvailableFilters(){const e=[];return this.columns.forEach(t=>{if(t.filter){const{fieldKey:s}=this.parseColumnKey(t.key);e.push({key:s,label:t.filter.label||t.label||s,type:t.filter.type,config:t.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(t=>{e.push({key:t.name||t.key,label:t.label,type:t.type,config:t})}),e}getFilterConfig(e){const t=this.columns.find(s=>{const{fieldKey:i}=this.parseColumnKey(s.key);return i===e});if(t&&t.filter)return t.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const s=this.additionalFilters.find(i=>(i.name||i.key)===e);if(s)return s}return null}getFilterLabel(e){if(e==="search")return"Search";const t=this.filters[e];if(t&&t.label)return t.label;const s=this.additionalFilters.find(i=>(i.name||i.key)===e);return s&&s.label?s.label:e.charAt(0).toUpperCase()+e.slice(1)}getFilterDisplayValue(e,t){if(e==="search")return`"${t}"`;const s=this.filters[e]||this.additionalFilters.find(i=>(i.name||i.key)===e);if(s&&s.type==="daterange"&&typeof t=="object"){const i=t.start||"",a=t.end||"";return`${i} to ${a}`}if(s&&s.type==="select"&&s.options){if(typeof s.options[0]=="object"){const i=s.options.find(a=>a.value===t);return i?i.label:t}return t}return t}getFilterIcon(e){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[e]||"filter"}async onActionAddFilter(e,t){const s=t.getAttribute("data-filter-key"),i=this.getFilterConfig(s),a=this.getActiveFilters()[s];if(!i){console.warn("No filter config found for key:",s);return}const r=await T.showForm({title:`${a!==void 0&&a!==""?"Edit":"Add"} ${this.getFilterLabel(s)} Filter`,size:"md",fields:[this.buildFilterDialogField(i,a,s)]});if(r){const n=this.extractFilterValue(i,r);this.setFilter(s,n),await this.applyFilters()}}buildFilterDialogField(e,t,s){const i={name:"filter_value",label:e.label,value:t,...e,placeholder:e.placeholder||e.placeHolder};if(e.type==="daterange"){if(i.startName=i.startName||"dr_start",i.endName=i.endName||"dr_end",i.fieldName=i.fieldName||"dr_field",i.format=i.format||"YYYY-MM-DD",i.displayFormat=i.displayFormat||"MMM DD, YYYY",i.separator=i.separator||" to ",i.label=i.label||"Date Range",t&&typeof t=="object"){const a=r=>{if(!r&&r!==0)return"";if(r instanceof Date&&!isNaN(r))return r.toISOString().slice(0,10);const n=String(r).trim();if(!n)return"";if(/^-?\d+$/.test(n)){const l=Number(n),c=n.length<=10?l*1e3:l,d=new Date(c);if(!isNaN(d))return d.toISOString().slice(0,10)}const o=new Date(n);return isNaN(o)?n:o.toISOString().slice(0,10)};i.startDate=a(t.start||t.from||t.begin||""),i.endDate=a(t.end||t.to||t.finish||"")}}else if(e.type==="multiselect"){let a=[];t&&(Array.isArray(t)?a=t:typeof t=="string"&&(a=t.split(",").map(r=>r.trim()).filter(r=>r))),i.value=a,!i.placeholder&&!i.placeHolder&&(e.placeholder||e.placeHolder?i.placeholder=e.placeholder||e.placeHolder:e.label&&(i.placeholder=`Select ${e.label}...`))}return i}extractFilterValue(e,t){if(e.type==="daterange"){const s=e.startName||"dr_start",i=e.endName||"dr_end";return{start:t[s],end:t[i]}}return e.type==="multiselect",t.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),this.render()}catch(e){console.error("Failed to fetch filtered data:",e),this.render()}else this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=X(s);let a=this.getFilterConfig(i)||this.getFilterConfig(s);const r=this.getActiveFilters(),n=r[s]||r[i];if(!a){console.warn("No filter config found for key:",s,"or field:",i);return}const o={filter_value:n};if(a.type==="daterange"&&n&&typeof n=="object"){const c=a.startName||"dr_start",d=a.endName||"dr_end";o[c]=n.start||"",o[d]=n.end||""}const l=await T.showForm({title:`Edit ${this.getFilterLabel(i)} Filter`,size:"md",data:o,fields:[this.buildFilterDialogField(a,n,i)]});if(l){const c=this.extractFilterValue(a,l);this.setFilter(s,c),await this.applyFilters()}}async onActionRemoveFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=X(s);this.setFilter(s,null),s==="search"&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:s,field:i}),this.emit("params-changed")}async onActionClearAllFilters(e,t){if(!this.collection)return;const{start:s,size:i,sort:a}=this.collection.params;this.collection.params={start:s,size:i},a&&(this.collection.params.sort=a),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||this.batchActions.length===0)return;const e=this.getSelectedItems().length;if(this.batchBarLocation==="top"){const s=this.element?.querySelector(".batch-actions-panel-top"),i=this.element?.querySelector(".batch-select-count");s&&i&&(i.textContent=e,e>0?s.classList.remove("d-none"):s.classList.add("d-none"))}else{const s=this.element?.querySelector(".batch-actions-panel"),i=this.element?.querySelector(".batch-select-count");s&&i&&(i.textContent=e,s.style.display=e>0?"block":"none")}const t=this.element?.querySelector(".mojo-select-all-cell");if(t){const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(r=>r.selected),i=Array.from(this.itemViews.values()).some(r=>r.selected);t.classList.toggle("selected",s),t.classList.toggle("indeterminate",!s&&i);const a=t.querySelector("i");a&&(a.className=!s&&i?"bi bi-dash":"bi bi-check")}}async onActionBatch(e,t){const s=t.getAttribute("data-action").replace("batch-",""),i=this.getSelectedItems();this.emit("batch:action",{action:s,items:i,event:e})}async onActionClearSelection(e,t){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(e,t){const s=parseInt(t.getAttribute("data-button-index"),10),i=this.toolbarButtons[s];i&&typeof i.handler=="function"&&await i.handler.call(this,e,t)}}function et(){return typeof window>"u"?null:((!window.MOJO||typeof window.MOJO!="object")&&(window.MOJO={}),window.MOJO)}function tt(u){return u.__lite&&u.__lite.version||(u.WebApp=ee,u.View=v,u.Page=K,u.Router=ie,u.Model=B,u.Collection=W,u.Rest=z,u.FormBuilder=Q,u.FormView=q,u.Dialog=T,u.ProgressView=me,u.ListView=pe,u.ListViewItem=se,u.TableView=ke,u.TableRow=fe,u.DataFormatter=V,u.MOJOUtils=x,u.__lite={version:"dev",build:"web-mojo.lite"},u.mount=async function(t,s){if(!t)throw new Error("MOJO.mount(view, container) requires a view");const i=typeof s=="string"?document.querySelector(s):s;if(!i)throw new Error("MOJO.mount(view, container) container not found");if(typeof t.render!="function"||typeof t.mount!="function")throw new Error("MOJO.mount expects a View instance with render() and mount() methods");return await t.render(),await t.mount(i),t}),u}const ge=et();ge&&tt(ge);class st{constructor(e={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:5e3,maxToasts:5,...e},this.toasts=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",s={}){this.enforceMaxToasts();const i=`toast-${++this.toastCounter}`,a={title:this.getDefaultTitle(t),icon:this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},r=this.createToastElement(i,e,t,a);if(this.container.appendChild(r),typeof bootstrap>"u")throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(r,{autohide:a.autohide,delay:a.delay});return this.toasts.set(i,{element:r,bootstrap:n,type:t,message:e}),r.addEventListener("hidden.bs.toast",()=>{this.cleanup(i)}),n.show(),{id:i,hide:()=>{try{n.hide()}catch(o){console.warn("Error hiding toast:",o)}},dispose:()=>this.cleanup(i),updateProgress:s.updateProgress||null}}showView(e,t="info",s={}){this.enforceMaxToasts();const i=`toast-${++this.toastCounter}`,a={title:s.title||this.getDefaultTitle(t),icon:s.icon||this.getDefaultIcon(t),autohide:this.options.autohide,delay:this.options.defaultDelay,dismissible:!0,...s},r=this.createViewToastElement(i,e,t,a);if(this.container.appendChild(r),typeof bootstrap>"u")throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const n=new bootstrap.Toast(r,{autohide:a.autohide,delay:a.delay});this.toasts.set(i,{element:r,bootstrap:n,type:t,view:e,message:"View toast"}),r.addEventListener("hidden.bs.toast",()=>{this.cleanupView(i)});const o=r.querySelector(".toast-view-body");return o&&e&&e.render(!0,o),n.show(),{id:i,view:e,hide:()=>{try{n.hide()}catch(l){console.warn("Error hiding view toast:",l)}},dispose:()=>this.cleanupView(i),updateProgress:l=>{e&&typeof e.updateProgress=="function"&&e.updateProgress(l)}}}createToastElement(e,t,s,i){const a=document.createElement("div");a.id=e,a.className=`toast toast-service-${s}`,a.setAttribute("role","alert"),a.setAttribute("aria-live","assertive"),a.setAttribute("aria-atomic","true");const r=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createToastBody(t,i.icon&&!i.title);return a.innerHTML=`
1716
+ ${r}
1717
+ ${n}
1718
+ `,a}createViewToastElement(e,t,s,i){const a=document.createElement("div");a.id=e,a.className=`toast toast-service-${s}`,a.setAttribute("role","alert"),a.setAttribute("aria-live","assertive"),a.setAttribute("aria-atomic","true");const r=i.title||i.icon?this.createToastHeader(i,s):"",n=this.createViewToastBody();return a.innerHTML=`
1719
+ ${r}
1720
+ ${n}
1721
+ `,a}createViewToastBody(){return`
1722
+ <div class="toast-body p-0">
1723
+ <div class="toast-view-body p-3"></div>
1724
+ </div>
1725
+ `}createToastHeader(e,t){const s=e.icon?`<i class="${e.icon} toast-service-icon me-2"></i>`:"",i=e.title?`<strong class="me-auto">${s}${this.escapeHtml(e.title)}</strong>`:"",a=e.showTime?`<small class="text-muted">${this.getTimeString()}</small>`:"",r=e.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return!i&&!a&&!r?"":`
1726
+ <div class="toast-header">
1727
+ ${i}
1728
+ ${a}
1729
+ ${r}
1730
+ </div>
1731
+ `}createToastBody(e,t=!1){return`
1732
+ <div class="toast-body d-flex align-items-center">
1733
+ ${t?`<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>`:""}
1734
+ <span>${this.escapeHtml(e)}</span>
1735
+ </div>
1736
+ `}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 t=this.toasts.keys().next().value,s=this.toasts.get(t);s&&s.bootstrap.hide()}}cleanup(e){const t=this.toasts.get(e);if(t){try{t.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}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&&typeof t.view.dispose=="function")try{t.view.dispose()}catch(s){console.warn("Error disposing view in toast:",s)}try{t.bootstrap.dispose()}catch(s){console.warn("Error disposing toast:",s)}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(){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 it{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 st),this.promise=this._startUpload()}async _startUpload(){try{this.options.showToast&&this._showProgressToast();let e;try{e=await this._initiateUpload()}catch(i){throw new Error(`Failed to initiate upload: ${i.message}`)}if(this.cancelled)throw new Error("Upload cancelled");if(!e||!e.upload_url)throw new Error("Invalid upload response: missing upload URL");let t;if(typeof e.upload_url=="string")t={url:e.upload_url,method:"PUT",fields:null,headers:{}};else if(e.upload_url&&typeof e.upload_url=="object"&&e.upload_url.upload_url)t={url:e.upload_url.upload_url,method:e.upload_url.method||"POST",fields:e.upload_url.fields||null,headers:e.upload_url.headers||{}};else throw new Error(`Invalid upload response: unrecognised upload_url format. Server returned: ${JSON.stringify(e.upload_url)}`);let s;try{s=await this._performUpload(t)}catch(i){throw new Error(`File upload failed: ${i.message}`)}if(this.cancelled)throw new Error("Upload cancelled");try{await this._completeUpload()}catch(i){console.warn("Failed to mark upload as completed:",i)}return this._onComplete(this.fileModel),this.fileModel}catch(e){throw e.message!=="Upload cancelled"&&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 s=t.data.error||"Upload initiation failed";throw new Error(s)}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){throw e.message==="Network Error"||e.name==="TypeError"?new Error("Network error during upload initiation. Please check your connection."):e}}async _performUpload(e){return new Promise((t,s)=>{if(!(this.options.file instanceof File)){s(new Error("Only single File objects are supported"));return}const{url:i,method:a,fields:r,headers:n}=e,o=a==="POST"&&r!==null,l=new XMLHttpRequest;this.uploadRequest=l,l.upload.onprogress=h=>{this.cancelled||this._onProgress({progress:h.loaded/h.total,loaded:h.loaded,total:h.total,percentage:Math.round(h.loaded/h.total*100)})},l.onload=()=>{l.status>=200&&l.status<300?t({data:l.response,status:l.status,statusText:l.statusText,xhr:l}):s(new Error(`Upload failed: ${l.status} ${l.statusText}`))},l.onerror=()=>s(new Error("Upload failed: Network error")),l.ontimeout=()=>s(new Error("Upload timed out — file may be too large or connection too slow")),l.onabort=()=>s(new Error("Upload cancelled"));let c=i;i.startsWith("/")&&!i.startsWith("/api/")&&(c="/api"+i);const d=this.fileModel.rest.buildUrl(c);if(l.open(a,d),l.timeout=3e4,o){for(const[m,p]of Object.entries(n||{}))m.toLowerCase()!=="content-type"&&l.setRequestHeader(m,p);const h=new FormData;for(const[m,p]of Object.entries(r))h.append(m,p);h.append("file",this.options.file),l.send(h)}else{l.setRequestHeader("Content-Type",this.options.file.type);for(const[h,m]of Object.entries(n||{}))h.toLowerCase()!=="content-type"&&l.setRequestHeader(h,m);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){throw e.message==="Network Error"||e.name==="TypeError"?new Error("Network error during upload completion. The file may have uploaded successfully."):e}}_onProgress(e){this.progressToast&&this.progressToast.updateProgress&&this.progressToast.updateProgress(e),typeof this.options.onProgress=="function"&&this.options.onProgress(e)}_onComplete(e){this.progressView&&this.progressView.markCompleted("Upload completed successfully!"),this.progressToast&&setTimeout(()=>{try{this.progressToast&&typeof this.progressToast.hide=="function"&&this.progressToast.hide()}catch(t){console.warn("Error hiding progress toast:",t)}},2e3),typeof this.options.onComplete=="function"&&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}`),typeof this.options.onError=="function"&&this.options.onError(e)}_showProgressToast(){this.progressView=new me({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?!1:(this.cancelled=!0,this.uploadRequest&&typeof this.uploadRequest.abort=="function"&&this.uploadRequest.abort(),this.progressView&&this.progressView.markCancelled(),this.progressToast&&setTimeout(()=>{try{this.progressToast&&typeof this.progressToast.hide=="function"&&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 J extends B{constructor(e={}){super(e,{endpoint:"/api/group"})}}class be extends W{constructor(e={}){super({ModelClass:J,endpoint:"/api/group",size:10,...e})}}const Ie={org:"Organization",division:"Division",department:"Department",team:"Team",merchant:"Merchant",partner:"Partner",client:"Client",iso:"ISO",sales:"Sales",reseller:"Reseller",location:"Location",region:"Region",route:"Route",project:"Project",inventory:"Inventory",test:"Testing",misc:"Miscellaneous",qa:"Quality Assurance"},ye=Object.entries(Ie).map(([u,e])=>({value:u,label:e})),we={create:{title:"Create Group",fields:[{name:"name",type:"text",label:"Group Name",required:!0,placeholder:"Enter group name"},{name:"kind",type:"select",label:"Group Kind",required:!0,options:ye},{type:"collection",name:"parent",label:"Parent Group",Collection:be,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300}]},edit:{title:"Edit Group",fields:[{name:"name",type:"text",label:"Group Name",required:!0,placeholder:"Enter group name"},{name:"kind",type:"select",label:"Group Kind",required:!0,options:ye},{type:"collection",name:"parent",label:"Parent Group",Collection:be,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search groups...",emptyFetch:!1,debounceMs:300},{name:"metadata.domain",type:"text",label:"Default Domain",placeholder:"Enter Domain"},{name:"metadata.portal",type:"text",label:"Default Portal",placeholder:"Enter Portal URL"},{name:"is_active",type:"switch",label:"Is Active",cols:4}]}};J.EDIT_FORM=we.edit,J.ADD_FORM=we.create,J.CREATE_FORM=we.create,J.GroupKindOptions=ye,J.GroupKinds=Ie;class U extends B{constructor(e={}){super(e,{endpoint:"/api/user"})}hasPermission(e){if(this.get("is_superuser"))return!0;if(Array.isArray(e))return e.some(i=>this.hasPermission(i));const t=e.startsWith("sys."),s=t?e.substring(4):e;return!!(this._hasPermission(s)||!t&&this.member&&this.member.hasPermission(e))}_hasPermission(e){const t=this.get("permissions");return t?t[e]==!0:!1}hasPerm(e){return this.hasPermission(e)}}U.PERMISSIONS=[{name:"manage_users",label:"Manage Users"},{name:"view_users",label:"View Users"},{name:"view_groups",label:"View Groups"},{name:"manage_groups",label:"Manage Groups"},{name:"view_metrics",label:"View System Metrics"},{name:"manage_metrics",label:"Manage System Metrics"},{name:"view_logs",label:"View Logs"},{name:"view_incidents",label:"View Incidents"},{name:"manage_incidents",label:"Manage Incidents"},{name:"view_tickets",label:"View Tickets"},{name:"manage_tickets",label:"Manage Tickets"},{name:"view_admin",label:"View Admin"},{name:"view_jobs",label:"View Jobs"},{name:"manage_jobs",label:"Manage Jobs"},{name:"view_global",label:"View Global"},{name:"manage_notifications",label:"Manage Notifications"},{name:"manage_files",label:"Manage Files"},{name:"force_single_session",label:"Force Single Session"},{name:"file_vault",label:"Access File Vault"},{name:"manage_aws",label:"Manage AWS"},{name:"manage_docit",label:"Manage DocIt"}],U.PERMISSION_FIELDS=[...U.PERMISSIONS.map(u=>({name:`permissions.${u.name}`,type:"switch",label:u.label,columns:4}))];const Me={create:{title:"Create User",fields:[{name:"email",type:"text",label:"Email",required:!0},{name:"phone_number",type:"text",label:"Phone number",columns:12},{name:"display_name",type:"text",label:"Display Name"}]},edit:{title:"Edit User",fields:[{name:"email",type:"email",label:"Email",columns:12},{name:"display_name",type:"text",label:"Display Name",columns:12},{name:"phone_number",type:"text",label:"Phone number",columns:12},{type:"collection",name:"org",label:"Organization",Collection:be,labelField:"name",valueField:"id",columns:12}]},permissions:{fields:U.PERMISSION_FIELDS}},at={detailed:{title:"Detailed User Information",columns:2,showEmptyValues:!0,emptyValueText:"Not set",fields:[{name:"id",label:"User ID",type:"number",colSize:3},{name:"display_name",label:"Display Name",type:"text",format:'capitalize|default("Unnamed User")',colSize:9},{name:"username",label:"Username",type:"text",format:"lowercase",colSize:6},{name:"email",label:"Email Address",type:"email",colSize:6},{name:"phone_number",label:"Phone Number",type:"phone",format:'phone|default("Not provided")',colSize:6},{name:"is_active",label:"Account Status",type:"boolean",colSize:6},{name:"last_login",label:"Last Login",type:"datetime",format:"relative",colSize:6},{name:"last_activity",label:"Last Activity",type:"datetime",format:"relative",colSize:6},{name:"avatar.url",label:"Avatar",type:"url",colSize:12},{name:"permissions",label:"User Permissions",type:"dataview",dataViewColumns:2,showEmptyValues:!1},{name:"metadata",label:"User Metadata",type:"dataview",dataViewColumns:1},{name:"avatar",label:"Avatar Details",type:"dataview",dataViewColumns:1}]}};U.DATA_VIEW=at.detailed,U.EDIT_FORM=Me.edit,U.ADD_FORM=Me.create;const rt=Object.freeze(Object.defineProperty({__proto__:null,File:class extends B{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return this.get("category")==="image"}upload(e={}){return new it(this,e)}}},Symbol.toStringTag,{value:"Module"}));class ve extends v{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:r,showEmptyValues:n,emptyValueText:o,...l}=e;super({tagName:"div",className:"data-view",...l}),this.data=t||{},this.fields=i||[],this.model=s||null,this.model&&(this.data=this.model),this.dataViewOptions={columns:a||2,responsive:r!==!1,showEmptyValues:n||!1,emptyValueText:o||"—",rowClass:"row g-3",itemClass:"data-view-item",labelClass:"data-view-label fw-semibold text-muted small text-uppercase",valueClass:"data-view-value"}}async onBeforeRender(){this.fields.length===0&&this.getData()&&this.generateFieldsFromData()}async renderTemplate(){const e=this.buildItemsHTML();return`
1716
1737
  <div class="${this.dataViewOptions.rowClass}">
1717
1738
  ${e}
1718
1739
  </div>
@@ -1760,5 +1781,5 @@ var MOJO=(function(D){"use strict";class te{constructor(e={}){this.defaultRoute=
1760
1781
  ${i}
1761
1782
  </div>
1762
1783
  </div>
1763
- `}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&typeof this.model.set=="function"&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&typeof this.model.fetch=="function")try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?(s.format=t,s.formatter=t):this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t,formatter:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?(s.format+=`|${t}`,s.formatter=s.format):(s.format=t,s.formatter=t)),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData(),i=this.inferFormatter(s[e],e,t.type);t.format=i,t.formatter=i}return this}getFormattedValue(e,t=null){const s=this.getField(e);if(!s)return null;const i=t!==null?t:this.getData()[e],a=s.format||s.formatter;return a&&i!=null?V.pipe(i,a):i}setFieldFormats(e){return Object.entries(e).forEach(([t,s])=>{this.setFieldFormat(t,s)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{const s=t.format||t.formatter;s&&(e[t.name]=s)}),e}onInit(){super.onInit(),this.model&&typeof this.model.on=="function"&&this.model.on("change",()=>{this.render()})}static create(e={}){return new me(e)}}const Ze=Object.freeze(Object.defineProperty({__proto__:null,default:me},Symbol.toStringTag,{value:"Module"}));return D.Collection=J,D.DataFormatter=V,D.Dialog=k,D.FormBuilder=Z,D.FormPage=Ue,D.FormView=q,D.ListView=he,D.ListViewItem=ee,D.MOJOUtils=x,D.Model=X,D.Page=W,D.ProgressView=$e,D.Rest=z,D.Router=te,D.TableRow=ue,D.TableView=Fe,D.View=v,D.WebApp=K,D.default=fe,Object.defineProperties(D,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),D})({});
1784
+ `}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&typeof this.model.set=="function"&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&typeof this.model.fetch=="function")try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?(s.format=t,s.formatter=t):this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t,formatter:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?(s.format+=`|${t}`,s.formatter=s.format):(s.format=t,s.formatter=t)),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData(),i=this.inferFormatter(s[e],e,t.type);t.format=i,t.formatter=i}return this}getFormattedValue(e,t=null){const s=this.getField(e);if(!s)return null;const i=t!==null?t:this.getData()[e],a=s.format||s.formatter;return a&&i!=null?V.pipe(i,a):i}setFieldFormats(e){return Object.entries(e).forEach(([t,s])=>{this.setFieldFormat(t,s)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{const s=t.format||t.formatter;s&&(e[t.name]=s)}),e}onInit(){super.onInit(),this.model&&typeof this.model.on=="function"&&this.model.on("change",()=>{this.render()})}static create(e={}){return new ve(e)}}const nt=Object.freeze(Object.defineProperty({__proto__:null,default:ve},Symbol.toStringTag,{value:"Module"}));return D.Collection=W,D.DataFormatter=V,D.Dialog=T,D.FormBuilder=Q,D.FormPage=Qe,D.FormView=q,D.ListView=pe,D.ListViewItem=se,D.MOJOUtils=x,D.Model=B,D.Page=K,D.ProgressView=me,D.Rest=z,D.Router=ie,D.TableRow=fe,D.TableView=ke,D.View=v,D.WebApp=ee,D.default=ge,Object.defineProperties(D,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),D})({});
1764
1785
  //# sourceMappingURL=web-mojo.lite.iife.min.js.map