web-mojo 2.2.98 → 2.2.100

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 (97) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/admin.cjs.js +1 -1
  3. package/dist/admin.es.js +1 -1
  4. package/dist/auth.cjs.js +1 -1
  5. package/dist/auth.es.js +1 -1
  6. package/dist/charts.cjs.js +1 -1
  7. package/dist/charts.es.js +1 -1
  8. package/dist/chunks/{AssistantPanelView-ti3iZFZJ.js → AssistantPanelView-CZhZmVSG.js} +2 -2
  9. package/dist/chunks/{AssistantPanelView-ti3iZFZJ.js.map → AssistantPanelView-CZhZmVSG.js.map} +1 -1
  10. package/dist/chunks/{AssistantPanelView-BGuG2-e7.js → AssistantPanelView-WFHYQjq7.js} +2 -2
  11. package/dist/chunks/{AssistantPanelView-BGuG2-e7.js.map → AssistantPanelView-WFHYQjq7.js.map} +1 -1
  12. package/dist/chunks/ChatView-Bj3Tvx7a.js +2 -0
  13. package/dist/chunks/ChatView-Bj3Tvx7a.js.map +1 -0
  14. package/dist/chunks/ChatView-Uoqp80PU.js +2 -0
  15. package/dist/chunks/ChatView-Uoqp80PU.js.map +1 -0
  16. package/dist/chunks/{Dialog-99_2o2h6.js → Dialog-B0r9puaZ.js} +2 -2
  17. package/dist/chunks/Dialog-B0r9puaZ.js.map +1 -0
  18. package/dist/chunks/{Dialog-C1p6V5qV.js → Dialog-GzCkpMad.js} +2 -2
  19. package/dist/chunks/Dialog-GzCkpMad.js.map +1 -0
  20. package/dist/chunks/{FormView-56cBWH2j.js → FormView-DEROOklJ.js} +2 -2
  21. package/dist/chunks/{FormView-56cBWH2j.js.map → FormView-DEROOklJ.js.map} +1 -1
  22. package/dist/chunks/{FormView-Ej9wQv4A.js → FormView-UmvPskWT.js} +2 -2
  23. package/dist/chunks/{FormView-Ej9wQv4A.js.map → FormView-UmvPskWT.js.map} +1 -1
  24. package/dist/chunks/{MetricsMiniChartWidget-VIBJZ4wn.js → MetricsMiniChartWidget-CV-McxOl.js} +2 -2
  25. package/dist/chunks/{MetricsMiniChartWidget-VIBJZ4wn.js.map → MetricsMiniChartWidget-CV-McxOl.js.map} +1 -1
  26. package/dist/chunks/{MetricsMiniChartWidget-C7u5jcb0.js → MetricsMiniChartWidget-DG0DeDuR.js} +2 -2
  27. package/dist/chunks/{MetricsMiniChartWidget-C7u5jcb0.js.map → MetricsMiniChartWidget-DG0DeDuR.js.map} +1 -1
  28. package/dist/chunks/{Modal-IG_UGRMf.js → Modal-jWZGSSOo.js} +2 -2
  29. package/dist/chunks/{Modal-IG_UGRMf.js.map → Modal-jWZGSSOo.js.map} +1 -1
  30. package/dist/chunks/{Modal-BLaJTwNQ.js → Modal-xSaurlfZ.js} +2 -2
  31. package/dist/chunks/{Modal-BLaJTwNQ.js.map → Modal-xSaurlfZ.js.map} +1 -1
  32. package/dist/chunks/{Passkeys-BIV_WQTC.js → Passkeys-BPV1gNkR.js} +2 -2
  33. package/dist/chunks/{Passkeys-BIV_WQTC.js.map → Passkeys-BPV1gNkR.js.map} +1 -1
  34. package/dist/chunks/{Passkeys-CbIVCcrV.js → Passkeys-CIRjqi7n.js} +2 -2
  35. package/dist/chunks/{Passkeys-CbIVCcrV.js.map → Passkeys-CIRjqi7n.js.map} +1 -1
  36. package/dist/chunks/{TokenManager-CHlIEAGa.js → TokenManager-CKgK0MIz.js} +2 -2
  37. package/dist/chunks/{TokenManager-CHlIEAGa.js.map → TokenManager-CKgK0MIz.js.map} +1 -1
  38. package/dist/chunks/{TokenManager-DrIJVcOt.js → TokenManager-Cu0YmxAx.js} +2 -2
  39. package/dist/chunks/{TokenManager-DrIJVcOt.js.map → TokenManager-Cu0YmxAx.js.map} +1 -1
  40. package/dist/chunks/{UserProfileView-6oMkhsyq.js → UserProfileView-BXT8COCF.js} +2 -2
  41. package/dist/chunks/{UserProfileView-6oMkhsyq.js.map → UserProfileView-BXT8COCF.js.map} +1 -1
  42. package/dist/chunks/{UserProfileView-BhaajMo4.js → UserProfileView-Slj-9C__.js} +2 -2
  43. package/dist/chunks/{UserProfileView-BhaajMo4.js.map → UserProfileView-Slj-9C__.js.map} +1 -1
  44. package/dist/chunks/{WebApp-Bb7bEVO-.js → WebApp-CXkNqtZX.js} +2 -2
  45. package/dist/chunks/{WebApp-Bb7bEVO-.js.map → WebApp-CXkNqtZX.js.map} +1 -1
  46. package/dist/chunks/{WebApp-CXZl59le.js → WebApp-m6YeJFWY.js} +2 -2
  47. package/dist/chunks/{WebApp-CXZl59le.js.map → WebApp-m6YeJFWY.js.map} +1 -1
  48. package/dist/chunks/admin-CpLR06pX.js +2 -0
  49. package/dist/chunks/admin-CpLR06pX.js.map +1 -0
  50. package/dist/chunks/admin-JtLeoEHZ.js +2 -0
  51. package/dist/chunks/admin-JtLeoEHZ.js.map +1 -0
  52. package/dist/chunks/{index-5KtcUYiX.js → index-CSmG_P2r.js} +2 -2
  53. package/dist/chunks/{index-5KtcUYiX.js.map → index-CSmG_P2r.js.map} +1 -1
  54. package/dist/chunks/{index-BHEAlNEb.js → index-uo77m1wt.js} +2 -2
  55. package/dist/chunks/{index-BHEAlNEb.js.map → index-uo77m1wt.js.map} +1 -1
  56. package/dist/chunks/version-CRfGMZSI.js +2 -0
  57. package/dist/chunks/version-CRfGMZSI.js.map +1 -0
  58. package/dist/chunks/version-D-5X69D6.js +2 -0
  59. package/dist/chunks/version-D-5X69D6.js.map +1 -0
  60. package/dist/css/web-mojo.css +1 -1
  61. package/dist/docit.cjs.js +1 -1
  62. package/dist/docit.es.js +1 -1
  63. package/dist/index.cjs.js +1 -1
  64. package/dist/index.cjs.js.map +1 -1
  65. package/dist/index.es.js +1 -1
  66. package/dist/index.es.js.map +1 -1
  67. package/dist/lightbox.cjs.js +1 -1
  68. package/dist/lightbox.cjs.js.map +1 -1
  69. package/dist/lightbox.es.js +1 -1
  70. package/dist/lightbox.es.js.map +1 -1
  71. package/dist/map.cjs.js +1 -1
  72. package/dist/map.es.js +1 -1
  73. package/dist/user-profile.cjs.js +1 -1
  74. package/dist/user-profile.es.js +1 -1
  75. package/dist/web-mojo.lite.iife.js +101 -0
  76. package/dist/web-mojo.lite.iife.js.map +1 -1
  77. package/dist/web-mojo.lite.iife.min.js +98 -98
  78. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  79. package/package.json +1 -1
  80. package/dist/chunks/ChatView-DCFwLZjg.js +0 -2
  81. package/dist/chunks/ChatView-DCFwLZjg.js.map +0 -1
  82. package/dist/chunks/ChatView-DQljthdV.js +0 -2
  83. package/dist/chunks/ChatView-DQljthdV.js.map +0 -1
  84. package/dist/chunks/Dialog-99_2o2h6.js.map +0 -1
  85. package/dist/chunks/Dialog-C1p6V5qV.js.map +0 -1
  86. package/dist/chunks/PDFViewer-D5QdfNWT.js +0 -2
  87. package/dist/chunks/PDFViewer-D5QdfNWT.js.map +0 -1
  88. package/dist/chunks/PDFViewer-Erds3gKx.js +0 -2
  89. package/dist/chunks/PDFViewer-Erds3gKx.js.map +0 -1
  90. package/dist/chunks/admin-BHppr_L3.js +0 -2
  91. package/dist/chunks/admin-BHppr_L3.js.map +0 -1
  92. package/dist/chunks/admin-DkDOQQa1.js +0 -2
  93. package/dist/chunks/admin-DkDOQQa1.js.map +0 -1
  94. package/dist/chunks/version-C8bxzJU0.js +0 -2
  95. package/dist/chunks/version-C8bxzJU0.js.map +0 -1
  96. package/dist/chunks/version-nBNoiDmZ.js +0 -2
  97. package/dist/chunks/version-nBNoiDmZ.js.map +0 -1
@@ -1,4 +1,4 @@
1
- var MOJO=(function(A){"use strict";class re{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 Pe{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 Ne{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}};try{r=await this.processRequestInterceptors(r)}catch(l){if(l.name==="AuthRequiredError")return{success:!1,status:401,statusText:"Unauthorized",headers:{},data:null,errors:{auth:l.message},message:"Authentication required",reason:"unauthorized"};throw l}if(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),c=await this.processResponseInterceptors(l,r);return a.dataOnly&&c.data&&typeof c.data=="object"&&"data"in c.data&&(c.message=c.message||c.data.message,c.data=c.data.data),c}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)}get(...e){return this.GET(...e)}post(...e){return this.POST(...e)}put(...e){return this.PUT(...e)}patch(...e){return this.PATCH(...e)}delete(...e){return this.DELETE(...e)}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 U=new Ne,Oe="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==";class qe{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(A){"use strict";class ne{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:n,queryParams:r}=this.parseInput(e);a&&await this.handleRouteChange(n,r)}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,n)=>{i[a]=s[n+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),n=new URLSearchParams(a);if(n.has("page")){t=n.get("page")||this.defaultRoute;for(const[r,o]of n)r!=="page"&&(s[r]=o)}else{i.startsWith("/")?t=i.substring(1)||this.defaultRoute:t=i||this.defaultRoute;for(const[r,o]of n)s[r]=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(([r,o])=>{o!=null&&o!==""&&a.searchParams.set(r,String(o))});const n=a.toString();s?window.history.replaceState(i,"",n):window.history.pushState(i,"",n)}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 Pe{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(n=>{try{const r=a(t,e);n(r)}catch(r){console.error(`Error in async event listener for '${e}':`,r),this.emitError(r,e,a),n()}}));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 n=a!==void 0?a:i;return t.call(this,s,n)}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 n=()=>{a&&clearTimeout(a)},r=o=>{n(),s(o)};this.once(e,r),t&&(a=setTimeout(()=>{this.off(e,r),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 Ne{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(n=>t.append(`${i}[]`,n)):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 n=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=a.errors||{},s.message=a.message||n.message,s.reason=n.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 n={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(i),headers:{...this.config.headers,...a.headers},data:s,options:{timeout:this.config.timeout,...a}};try{n=await this.processRequestInterceptors(n)}catch(l){if(l.name==="AuthRequiredError")return{success:!1,status:401,statusText:"Unauthorized",headers:{},data:null,errors:{auth:l.message},message:"Authentication required",reason:"unauthorized"};throw l}if(this.config.trackDevice&&this.duid)if(this.config.duidTransport==="header")n.headers[this.config.duidHeader]=this.duid;else if(n.method==="GET"){const l=new URL(n.url);l.searchParams.append("duid",this.duid),n.url=l.toString()}else n.data&&typeof n.data=="object"&&!(n.data instanceof FormData)&&(n.data.duid=this.duid);const r={method:n.method,headers:n.headers},o=[];n.options.timeout&&o.push(AbortSignal.timeout(n.options.timeout)),n.options.signal&&o.push(n.options.signal),o.length>1?r.signal=AbortSignal.any?AbortSignal.any(o):o[0]:o.length===1&&(r.signal=o[0]),n.data&&["POST","PUT","PATCH"].includes(n.method)&&(n.data instanceof FormData?(r.body=n.data,delete r.headers["Content-Type"]):typeof n.data=="object"?r.body=JSON.stringify(n.data):r.body=n.data);try{const l=await fetch(n.url,r),c=await this.processResponseInterceptors(l,n);return a.dataOnly&&c.data&&typeof c.data=="object"&&"data"in c.data&&(c.message=c.message||c.data.message,c.data=c.data.data),c}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,n),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)}get(...e){return this.GET(...e)}post(...e){return this.POST(...e)}put(...e){return this.PUT(...e)}patch(...e){return this.PATCH(...e)}delete(...e){return this.DELETE(...e)}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 n=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!n.ok)throw new Error(`Download failed: ${n.status} ${n.statusText}`);const r=n.headers.get("content-disposition");let o=s.filename||"download";if(r){const p=r.match(/filename="?(.+)"?/);p&&p.length>1&&(o=p[1])}const l=n.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(n){return console.error("Download error:",n),{success:!1,message:n.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 n=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!n.ok)throw new Error(`Download failed: ${n.status} ${n.statusText}`);const r=await n.blob(),o=n.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(r),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(n){return console.error("Download error:",n),{success:!1,message:n.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 n=new XMLHttpRequest;s.onProgress&&typeof s.onProgress=="function"&&(n.upload.onprogress=s.onProgress),n.onload=function(){n.status>=200&&n.status<300?i({data:n.response,status:n.status,statusText:n.statusText,xhr:n}):a(new Error(`Upload failed: ${n.status} ${n.statusText}`))},n.onerror=function(){a(new Error("Upload failed: Network error"))},n.ontimeout=function(){a(new Error("Upload failed: Timeout"))},n.open("PUT",e),n.setRequestHeader("Content-Type",t.type),s.timeout&&(n.timeout=s.timeout),n.send(t)})}async uploadMultipart(e,t,s={},i={}){const a=new FormData;if(t instanceof FileList)Array.from(t).forEach((n,r)=>{a.append(`file_${r}`,n)});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(([n,r])=>{a.append(n,r)}),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 U=new Ne,Oe="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==";class qe{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"},n=t&&typeof t=="object"?{...a,...t}:a;let r=i;if(n.urls!==!1){const o=/(^|\s)((?:https?:\/\/|www\.)[^\s<]+)/gi;r=r.replace(o,(l,c,d)=>{const h=d.startsWith("www.")?`https://${d}`:d;return`${c}<a href="${h}" target="${n.target}" rel="${n.rel}">${d}</a>`})}if(n.emails!==!1){const o=/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;r=r.replace(o,l=>`<a href="mailto:${l}">${l}</a>`)}return r}clipboard(e,t="text"){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a=t!=="icon-only",n=`
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"
@@ -9,24 +9,24 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
9
9
  </button>`.trim();return`
10
10
  <span class="mojo-clipboard d-inline-flex align-items-center">
11
11
  ${a?`<span class="font-monospace">${i}</span>`:""}
12
- ${r}
12
+ ${n}
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 M="";try{const z=new Intl.DateTimeFormat(r,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(O=>O.type==="timeZoneName");if(M=z?z.value:"",M&&/^GMT[+-]/i.test(M))try{const B=new Intl.DateTimeFormat(r,{timeStyle:"short",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(dt=>dt.type==="timeZoneName");B&&B.value&&!/^GMT[+-]/i.test(B.value)&&(M=B.value)}catch{}if(M&&/\s/.test(M)){const O=M.split(/\s+/).map(B=>B[0]).join("").toUpperCase();O.length>=2&&O.length<=4&&(M=O)}}catch{M=""}return M};if(!n){const M=this.date(a,t),H=this.time(a,s),z=o();return M&&H?`${M} ${H} ${z}`.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=M=>{const H=l.find(z=>z.type===M);return H?H.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)):"",v=p?String(parseInt(p,10)):"",S=p?parseInt(p,10)%12||12:"",F=p?parseInt(p,10)>=12?"PM":"AM":"",D=F?F.toLowerCase():"",V=new Intl.DateTimeFormat(r,{timeZone:n,month:"long"}).format(a),P=new Intl.DateTimeFormat(r,{timeZone:n,month:"short"}).format(a),I=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"long"}).format(a),Z=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"short"}).format(a),xe={YYYY:d,YY:d?d.slice(-2):"",MMMM:V,MMM:P,MM:h,M:y,dddd:I,ddd:Z,DD:m,D:b},$e={HH:p,H:v,hh:S!==""?String(S).padStart(2,"0"):"",h:S!==""?String(S):"",mm:f,m:f?String(parseInt(f,10)):"",ss:g,s:g?String(parseInt(g,10)):"",A:F,a:D},k=(M,H)=>{if(!M)return"";const z=new RegExp(`(${Object.keys(H).sort((O,B)=>B.length-O.length).join("|")})`,"g");return M.replace(z,O=>H[O]??O)},q=k(t,xe),Le=k(s,$e),ct=o();return q&&Le?`${q} ${Le} ${ct}`.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")||Oe,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 L=new qe;window.dataFormatter=L;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?L.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 ne(i,t):i):new ne(e,t)}}class ne{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?L.pipe(r,s,this._rootContext||this._data):r}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}x.DataWrapper=ne,typeof window<"u"&&(window.utils=x);const He=Object.prototype.toString,W=Array.isArray||function(u){return He.call(u)==="[object Array]"},K=function(u){return typeof u=="function"},Fe=function(u){return u!==null&&typeof u=="object"};function Re(){return typeof window>"u"?null:window.MOJO?.dataFormatter?window.MOJO.dataFormatter:window.dataFormatter?window.dataFormatter:null}const De=function(u){const e={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(u).replace(/[&<>"'`=\/]/g,function(t){return e[t]})};class Ee{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&&K(o)&&(o=o.call(this.view))}catch{o=void 0}if(o===void 0&&i in this.view&&(o=this.view[i],K(o)&&(o=o.call(this.view))),r&&o!==void 0)try{const l=Re();l&&typeof l.pipe=="function"&&(o=l.pipe(o,r,this))}catch{}return W(o)?a?o:o.length>0:Fe(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=oe(a,r[n])||Ae(a,r[n])),a=a[r[n++]]}else n===r.length-1&&(o=oe(a,r[n])||Ae(a,r[n])),a=a[r[n++]];else a=i.view[e],o=oe(i.view,e);if(o){s=a;break}i=i.parent}t[e]=s}if(K(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 oe(u,e){return u!=null&&typeof u=="object"&&e in u}function Ae(u,e){return u!=null&&typeof u!="object"&&u.hasOwnProperty&&u.hasOwnProperty(e)}class Te{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 Ee(e),r=[];let n,o,l,c,d;const h=new RegExp(le(s)+"\\s*"),m=new RegExp("\\s*"+le(i)),p=new RegExp("\\s*"+le("}"+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||!W(d)){console.warn(`MUSTACHE WARNING - Section ${l[1]} has no child tokens:`,l);continue}if(W(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(K(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||W(c)&&c.length===0){const h=l[4];h&&W(h)&&(n+=this.renderTokens(h,t,s,i,a,r))}break;case">":if(!s)continue;c=K(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+=De(c));break;case"text":n+=l[1];break}}return n}getConfigTags(e){return Fe(e)&&W(e.tags)?e.tags:null}}function le(u){return u.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const ce=new Te,T={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:Ee,Context:te,Writer:Te,escape:De,clearCache(){return ce.clearCache()},parse(u,e){return ce.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)),ce.render(u,e,t,s)}};class je{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(o.tagName==="A"&&n.preventDefault(),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 de={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=T);class C{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??C._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 je(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){C._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=>C._warn("removeChild destroy error",i)),delete this.children[t])}catch(t){C._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 C._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){C._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=>C._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=>C._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){C._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){C._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||C._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){C._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 T.render(e,this,t)}renderTemplateString(e,t,s){return T.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){C._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(C.prototype,de);class Y{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=U,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(){for(const e of Object.keys(this.attributes))e in this.originalAttributes||delete this[e];for(const[e,t]of Object.entries(this.originalAttributes))this[e]=t;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(Y.prototype,de);class J{constructor(e={},t=null){if(Array.isArray(e)?(t=e,e=t||{}):t=t||e.data||[],this.ModelClass=e.ModelClass||Y,this.models=[],this.loading=!1,this.errors={},this.meta={},this.rest=U,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.emit("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({ModelClass:e,...s});return i.add(t,{silent:!0}),i}}Object.assign(J.prototype,de);class ze{constructor(e={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:3e3,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,size:"md",...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}${i.size?` toast-${i.size}`:""}`,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=`
16
- ${r}
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,n)=>this.apply(n.name,a,...n.args),e):e}parsePipeString(e,t=null){const s=[],i=e.split("|").map(a=>a.trim());for(const a of i){const n=this.parseFormatter(a,t);n&&s.push(n)}return s}parseFormatter(e,t=null){const s=e.match(/^([a-zA-Z_]\w*)\s*\((.*)\)$/);if(s){const[,a,n]=s,r=n?this.parseArguments(n,t):[];return{name:a,args:r}}const i=e.match(/^([a-zA-Z_]\w*)(?::(.+))?$/);if(i){const[,a,n]=i,r=n?this.parseColonArguments(n,t):[];return{name:a,args:r}}return null}parseArguments(e,t=null){const s=[];let i="",a=!1,n=null,r=0;for(let o=0;o<e.length;o++){const l=e[o];!a&&(l==='"'||l==="'")?(a=!0,n=l,i+=l):a&&l===n&&e[o-1]!=="\\"?(a=!1,n=null,i+=l):!a&&l==="{"?(r++,i+=l):!a&&l==="}"?(r--,i+=l):!a&&r===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,n=null;for(let r=0;r<e.length;r++){const o=e[r];!a&&(o==='"'||o==="'")?(a=!0,n=o,i+=o):a&&o===n&&e[r-1]!=="\\"?(a=!1,n=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[r,o,l]=e.split("-").map(Number);s=new Date(r,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 n=new RegExp(`(${Object.keys(i).join("|")})`,"g");return a=a.replace(n,r=>i[r]||r),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 n=t;const r=Object.keys(a).sort((o,l)=>l.length-o.length);for(const o of r)n=n.replace(new RegExp(o,"g"),a[o]);return n}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 n=i&&i.locale||"en-US",r=i&&i.timeZone?i.timeZone:void 0,o=()=>{let _="";try{const z=new Intl.DateTimeFormat(n,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...r?{timeZone:r}:{}}).formatToParts(a).find(O=>O.type==="timeZoneName");if(_=z?z.value:"",_&&/^GMT[+-]/i.test(_))try{const B=new Intl.DateTimeFormat(n,{timeStyle:"short",timeZoneName:"short",...r?{timeZone:r}:{}}).formatToParts(a).find(dt=>dt.type==="timeZoneName");B&&B.value&&!/^GMT[+-]/i.test(B.value)&&(_=B.value)}catch{}if(_&&/\s/.test(_)){const O=_.split(/\s+/).map(B=>B[0]).join("").toUpperCase();O.length>=2&&O.length<=4&&(_=O)}}catch{_=""}return _};if(!r){const _=this.date(a,t),H=this.time(a,s),z=o();return _&&H?`${_} ${H} ${z}`.trim():""}const l=new Intl.DateTimeFormat(n,{timeZone:r,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(a),c=_=>{const H=l.find(z=>z.type===_);return H?H.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)):"",v=p?String(parseInt(p,10)):"",S=p?parseInt(p,10)%12||12:"",F=p?parseInt(p,10)>=12?"PM":"AM":"",D=F?F.toLowerCase():"",V=new Intl.DateTimeFormat(n,{timeZone:r,month:"long"}).format(a),P=new Intl.DateTimeFormat(n,{timeZone:r,month:"short"}).format(a),I=new Intl.DateTimeFormat(n,{timeZone:r,weekday:"long"}).format(a),Z=new Intl.DateTimeFormat(n,{timeZone:r,weekday:"short"}).format(a),xe={YYYY:d,YY:d?d.slice(-2):"",MMMM:V,MMM:P,MM:h,M:y,dddd:I,ddd:Z,DD:m,D:b},$e={HH:p,H:v,hh:S!==""?String(S).padStart(2,"0"):"",h:S!==""?String(S):"",mm:f,m:f?String(parseInt(f,10)):"",ss:g,s:g?String(parseInt(g,10)):"",A:F,a:D},k=(_,H)=>{if(!_)return"";const z=new RegExp(`(${Object.keys(H).sort((O,B)=>B.length-O.length).join("|")})`,"g");return _.replace(z,O=>H[O]??O)},q=k(t,xe),Le=k(s,$e),ct=o();return q&&Le?`${q} ${Le} ${ct}`.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),n=this.date(i,s);return!a||!n?"":`${a} - ${n}`}datetime_range(e,t=null,s="MM/DD/YYYY",i="HH:mm"){if(!e)return"";const a=t||new Date,n=this.datetime(e,s,i),r=this.datetime(a,s,i);return!n||!r?"":`${n} - ${r}`}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,n=Math.abs(a),r=Math.floor(n/1e3),o=Math.floor(r/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(r>30){const h=d?"in ":"",m=d?"":" ago";return h+r+" 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(),n=i<0?"-":"";let r,o;a.length<=2?(r="0",o=a.padStart(2,"0")):(r=a.slice(0,-2),o=a.slice(-2)),r=r.replace(/\B(?=(\d{3})+(?!\d))/g,",");let l;if(s===0)parseInt(o)>=50&&(r=(parseInt(r.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),l=r;else if(s===2)l=`${r}.${o}`;else{const c=o.slice(0,s).padEnd(s,"0");l=`${r}.${c}`}return n+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"],n=t?1024:1e3;let r=i,o=0;for(;r>=n&&o<a.length-1;)r/=n,o++;const l=o===0?0:s;return`${r.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 n="th";return i===1&&a!==11?n="st":i===2&&a!==12?n="nd":i===3&&a!==13&&(n="rd"),t?n:s+n}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 n=String(t),r=n.match(/^\/(.+)\/([a-z]*)$/i);if(r){const[,o,l]=r;try{return a.replace(new RegExp(o,l),String(s))}catch{}}return String(i).includes("g")?a.split(n).join(String(s)):a.replace(n,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),n=i.substring(0,a),r=i.substring(i.length-a);return`${n}${s}${r}`}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)),n=i.slice(-s);return a+n}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)}`:"",n=t.class?` class="${t.class}"`:"";return`<a href="mailto:${s}${i}${a}"${n}>${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(n=>this.badge(n,t)).join(" ");const s=String(e);if(typeof t=="string"&&t.includes("=")){const n=Object.fromEntries(t.split(",").map(l=>l.split("=").map(c=>c.trim()))),r=n[s]||n[s.toLowerCase()]||this.inferBadgeType(s);return`<span class="badge ${r?`bg-${r}`:"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 n=String(e).toLowerCase(),r={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[n]||r[n]||"",c=s[n]||o[n]||"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")||Oe,n={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;"},r=n[t]||n.md,l=`object-fit-cover ${s}`.trim();return`<img src="${a}" class="${l}" style="${r}" alt="${i}" />`}tooltip(e,t="",s="top",i=""){if(e==null)return"";const a=String(e),n=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="${n}">${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 n=Math.abs(a)===1?t:s||t+"s";return i?`${a} ${n}`:n}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 n=e.slice(),r=!1;if(i&&e.length>i&&(n=e.slice(0,i),r=!0),r){const o=e.length-i;return`${n.join(", ")}, ${s} ${o} ${a}`}return n.length===2?`${n[0]} ${s} ${n[1]}`:`${n.slice(0,-1).join(", ")}, ${s} ${n[n.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 n;switch(t){case"s":case"sec":case"seconds":n=a*1e3;break;case"m":case"min":case"minutes":n=a*6e4;break;case"h":case"hr":case"hours":n=a*36e5;break;case"d":case"day":case"days":n=a*864e5;break;default:n=a}const r=[{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(n===0)return s?"0s":"0 seconds";const o=Math.abs(n),l=n<0?"-":"",c=[];let d=o;for(const h of r)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=n=>Array.from(n).map(r=>r.toString(16).padStart(2,"0")).join("");if(typeof e=="number"){let n=Math.abs(Math.trunc(e)).toString(16);n.length%2&&(n="0"+n),i=n}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(n=>typeof n=="number"))i=a(Uint8Array.from(e.map(n=>n&255)));else{const r=new TextEncoder().encode(String(e));i=a(r)}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 n of s)a+=String.fromCharCode(n);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 L=new qe;window.dataFormatter=L;class x{static getContextData(e,t){if(!t||e==null)return;let s=t,i="",a=0,n=-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){n=o;break}}n>-1&&(s=t.substring(0,n).trim(),i=t.substring(n+1).trim());const r=this.getNestedValue(e,s);return i?L.pipe(r,i,e):r}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 n=s[a];if(i==null)return;if(a===0)if(i.hasOwnProperty(n)){const r=i[n];typeof r=="function"?i=r.call(e):i=r}else return;else{if(i&&typeof i.getContextValue=="function"){const r=s.slice(a).join(".");return i.getContextValue(r)}if(Array.isArray(i)&&!isNaN(n))i=i[parseInt(n)];else if(i.hasOwnProperty(n))i=i[n];else if(typeof i[n]=="function")i=i[n].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 n=()=>{clearTimeout(s),e(...a)};clearTimeout(s),s=setTimeout(n,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 r;return i<2?r="very-weak":i<4?r="weak":i<6?r="fair":i<8?r="good":r="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:r,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",n="0123456789",r="!@#$%^&*()_+-=[]{}|;:,.<>?";s.excludeAmbiguous&&(i=i.replace(/[il]/g,""),a=a.replace(/[IOL]/g,""),n=n.replace(/[01]/g,""),r=r.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+=n,l.push(n[Math.floor(Math.random()*n.length)])),s.includeSpecialChars&&(o+=r,l.push(r[Math.floor(Math.random()*r.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 re(i,t):i):new re(e,t)}}class re{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 r=0;r<e.length;r++){const o=e[r];if(o==="(")i++;else if(o===")")i--;else if(o==="|"&&i===0){a=r;break}}a>-1&&(t=e.substring(0,a).trim(),s=e.substring(a+1).trim());let n;return t in this&&t!=="_data"&&t!=="_rootContext"?n=this[t]:n=x.getNestedValue(this._data,t),s&&n!==void 0?L.pipe(n,s,this._rootContext||this._data):n}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}x.DataWrapper=re,typeof window<"u"&&(window.utils=x);const He=Object.prototype.toString,W=Array.isArray||function(u){return He.call(u)==="[object Array]"},K=function(u){return typeof u=="function"},Fe=function(u){return u!==null&&typeof u=="object"};function Re(){return typeof window>"u"?null:window.MOJO?.dataFormatter?window.MOJO.dataFormatter:window.dataFormatter?window.dataFormatter:null}const De=function(u){const e={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(u).replace(/[&<>"'`=\/]/g,function(t){return e[t]})};class Ee{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,n=null;const r=i.indexOf("|");if(r!==-1&&(n=i.substring(r+1).trim(),i=i.substring(0,r).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&&K(o)&&(o=o.call(this.view))}catch{o=void 0}if(o===void 0&&i in this.view&&(o=this.view[i],K(o)&&(o=o.call(this.view))),n&&o!==void 0)try{const l=Re();l&&typeof l.pipe=="function"&&(o=l.pipe(o,n,this))}catch{}return W(o)?a?o:o.length>0:Fe(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,n,r,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,n=e.split("."),r=0;a!=null&&r<n.length;)if(a&&typeof a.getContextValue=="function"&&r<n.length)try{const l=n.slice(r).join(".");a=a.getContextValue(l),r=n.length,a!==void 0&&(o=!0)}catch{r===n.length-1&&(o=oe(a,n[r])||Ae(a,n[r])),a=a[n[r++]]}else r===n.length-1&&(o=oe(a,n[r])||Ae(a,n[r])),a=a[n[r++]];else a=i.view[e],o=oe(i.view,e);if(o){s=a;break}i=i.parent}t[e]=s}if(K(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 oe(u,e){return u!=null&&typeof u=="object"&&e in u}function Ae(u,e){return u!=null&&typeof u!="object"&&u.hasOwnProperty&&u.hasOwnProperty(e)}class Te{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 Ee(e),n=[];let r,o,l,c,d;const h=new RegExp(le(s)+"\\s*"),m=new RegExp("\\s*"+le(i)),p=new RegExp("\\s*"+le("}"+i));for(;!a.eos();){if(r=a.pos,l=a.scanUntil(h),l)for(let f=0;f<l.length;++f)c=l.charAt(f),c===`
15
+ `?n.push(["text",c]):n.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,r,a.pos],n.push(d);else if(o==="/"){let f;for(let g=n.length-1;g>=0;--g)if((n[g][0]==="#"||n[g][0]==="^")&&n[g][1]===l){f=n[g];break}f&&f.length===4&&f.push(a.pos),n.push([o,l,r,a.pos])}else n.push([o,l,r,a.pos])}return this.nestSections(this.squashTokens(n))}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 n=e[a];switch(n[0]){case"#":case"^":const r=[n[0],n[1],n[2],n[3],[],n[4]||null];s.push(r),i.push(r),s=r[4];break;case"/":const o=i.pop();o&&(o[5]=n[2],s=i.length>0?i[i.length-1][4]:t);break;default:s.push(n)}}return t}render(e,t,s,i){const a=this.getConfigTags(i)||["{{","}}"],n=this.parse(e,a),r=new Map;return this.renderTokens(n,new te(t),s,e,i,r)}renderTokens(e,t,s,i,a,n){n&&!t.renderCache&&(t.renderCache=n);let r="";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||!W(d)){console.warn(`MUSTACHE WARNING - Section ${l[1]} has no child tokens:`,l);continue}if(W(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,n);r+=p}else if(typeof c=="object"||typeof c=="string"||typeof c=="number"){const h=t.push(c);t.renderCache&&(h.renderCache=t.renderCache),r+=this.renderTokens(d,h,s,i,a,n)}else if(K(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&&(r+=c)}else c&&(r+=this.renderTokens(d,t,s,i,a,n));break;case"^":if(c=t.lookup(l[1]),!c||W(c)&&c.length===0){const h=l[4];h&&W(h)&&(r+=this.renderTokens(h,t,s,i,a,n))}break;case">":if(!s)continue;c=K(s)?s(l[1]):s[l[1]],c!=null&&(r+=this.render(c,t.view,s,a));break;case"&":c=t.lookup(l[1]),c!=null&&(r+=c);break;case"name":c=t.lookup(l[1]),c!=null&&(r+=De(c));break;case"text":r+=l[1];break}}return r}getConfigTags(e){return Fe(e)&&W(e.tags)?e.tags:null}}function le(u){return u.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const ce=new Te,T={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:Ee,Context:te,Writer:Te,escape:De,clearCache(){return ce.clearCache()},parse(u,e){return ce.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)),ce.render(u,e,t,s)}};class je{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=new Map}bind(e){if(this.unbind(),!e)return;const t=async r=>{const o=r.target.closest("[data-action]");if(o&&this.shouldHandle(o,r)){const c=o.getAttribute("data-action");if(o.tagName==="A"&&r.preventDefault(),this.hideTooltip(o),await this.dispatch(c,r,o)){r.preventDefault(),r.stopPropagation(),r.handledByChild=!0;return}}const l=r.target.closest("a[href], [data-page]");if(l&&!l.hasAttribute("data-action")&&this.shouldHandle(l,r)){if(r.ctrlKey||r.metaKey||r.shiftKey||r.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),r.preventDefault(),r.stopPropagation(),r.handledByChild=!0,l.hasAttribute("data-page")?await this.view.handlePageNavigation(l):await this.view.handleHrefNavigation(l)}},s=r=>{const o=r.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,r))return;const l=o.getAttribute("data-change-action");this.dispatchChange(l,r,o).then(c=>{c&&(r.stopPropagation(),r.handledByChild=!0)})},i=r=>{const o=r.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,r)||!r.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,r,o).then(p=>{p&&(r.stopPropagation(),r.handledByChild=!0)})},d);this.debounceTimers.set(h,m)},a=r=>{if(r.target.matches('[data-filter="search"]'))return;const o=r.target.closest("[data-keydown-action]")||r.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,r))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(r.key)){const c=o.getAttribute("data-keydown-action")||o.getAttribute("data-change-action");this.dispatch(c,r,o).then(d=>{d&&(r.preventDefault(),r.stopPropagation(),r.handledByChild=!0)})}},n=r=>{const o=r.target.closest("form[data-action]");if(!o||!this.shouldHandle(o,r))return;r.preventDefault();const l=o.getAttribute("data-action");this.dispatch(l,r,o)};e.addEventListener("click",t),e.addEventListener("change",s),e.addEventListener("input",i),e.addEventListener("keydown",a),e.addEventListener("submit",n),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:n})}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),n=`handleAction${a(e)}`;if(typeof i[n]=="function")try{return t.preventDefault(),await i[n](t,s),!0}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const r=`onAction${a(e)}`;if(typeof i[r]=="function")try{return await i[r](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,n=`onChange${(r=>r.includes("-")?r.split("-").map(o=>o[0].toUpperCase()+o.slice(1)).join(""):r[0].toUpperCase()+r.slice(1))(e)}`;if(typeof i[n]=="function")try{return await i[n](t,s),!0}catch(r){return console.error(`Error in onChange ${e}:`,r),i.handleActionError?.(e,r,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 de={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=T);class C{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??C._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 je(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){C._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=>C._warn("removeChild destroy error",i)),delete this.children[t])}catch(t){C._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 C._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){C._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=>C._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=>C._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){C._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){C._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||C._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){C._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 T.render(e,this,t)}renderTemplateString(e,t,s){return T.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){C._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 n=this.findRouter();n&&typeof n.navigateToPage=="function"?await n.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 n={},r=a.trim();return r&&(n.customClass=r),new window.bootstrap.Tooltip(t,n)})}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 r=document.createElement("textarea");r.value=i,document.body.appendChild(r),r.select(),document.execCommand("copy"),document.body.removeChild(r)}const a=t.querySelector("i"),n=a&&a.className;return a&&(a.className="bi bi-check",setTimeout(()=>{a.className=n},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(C.prototype,de);class Y{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=U,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[n,r]of Object.entries(e))a=this._setNestedAttribute(n,r)||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[n,r]of Object.entries(e)){const o=this._getNestedValue(n);JSON.stringify(this._getNestedValue(n,i))!==JSON.stringify(o)&&this.emit(`change:${n}`,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 n=this.attributes[i],r=this[i];for(let l=1;l<s.length-1;l++){const c=s[l];(!n[c]||typeof n[c]!="object")&&(n[c]={}),(!r[c]||typeof r[c]!="object")&&(r[c]={}),n=n[c],r=r[c]}const o=s[s.length-1];return n[o]=t,r[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 n=e.id||this.getId();if(!n&&this.options.requiresId!==!1)throw new Error("Model: ID is required for fetching");t=this.buildUrl(n)}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(n){if(n.name==="AbortError")return console.info("Model: Request was cancelled"),this;throw n}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 n=await this.rest[i](a,e,t.params);return n.success?n.data.status?(this.originalAttributes={...this.attributes},this.set(n.data.data),this.errors={}):this.errors=n.data:this.errors=n.errors||{},n}catch(n){return{success:!1,error:n.message,status:n.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(){for(const e of Object.keys(this.attributes))e in this.originalAttributes||delete this[e];for(const[e,t]of Object.entries(this.originalAttributes))this[e]=t;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 n=a(s,this);if(n!==!0){this.errors[e]=n||`${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(Y.prototype,de);class J{constructor(e={},t=null){if(Array.isArray(e)?(t=e,e=t||{}):t=t||e.data||[],this.ModelClass=e.ModelClass||Y,this.models=[],this.loading=!1,this.errors={},this.meta={},this.rest=U,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(n){return n.name==="AbortError"?(console.info("Collection: Request was cancelled"),{success:!1,error:"Request cancelled",status:0}):{success:!1,error:n.message,status:n.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 n=this.options.parse?this.parse(a):a.data;(this.options.reset||t.reset!==!1)&&this.reset(),this.add(n,{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 n=await this.fetch();i(n)}catch(n){a(n)}},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()}`,n=this._buildDateRangeSuffix(i),r=`${a}${n}.${e}`,l={json:"application/json",csv:"text/csv"}[e]||"*/*";return i.filename=r,this.rest.download(s,i,{...t,filename:r,headers:{Accept:l}})}_buildDateRangeSuffix(e={}){const t=e.dr_start,s=e.dr_end;if(!t&&!s)return"";const i=r=>r?String(r).replace(/[^\dA-Za-z_-]/g,"-"):"",a=[],n=e.dr_field||"daterange";return a.push(i(n)),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 n;a instanceof this.ModelClass?n=a:n=new this.ModelClass(a,{endpoint:this.endpoint,collection:this});const r=this.models.findIndex(o=>o.id===n.id);r!==-1?t.merge!==!1&&this.models[r].set(n.attributes):(this.models.push(n),i.push(n))}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 n=-1;if(typeof a=="string"||typeof a=="number"?n=this.models.findIndex(r=>r.id==a):n=this.models.indexOf(a),n!==-1){const r=this.models.splice(n,1)[0];i.push(r)}}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 n=i.get(s),r=a.get(s);return n<r?-1:n>r?1:0}}return this.models.sort(e),t.silent||this.emit("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({ModelClass:e,...s});return i.add(t,{silent:!0}),i}}Object.assign(J.prototype,de);class ze{constructor(e={}){this.options={containerId:"toast-container",position:"top-end",autohide:!0,defaultDelay:3e3,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},n=this.createToastElement(i,e,t,a);if(this.container.appendChild(n),typeof bootstrap>"u")throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const r=new bootstrap.Toast(n,{autohide:a.autohide,delay:a.delay});return this.toasts.set(i,{element:n,bootstrap:r,type:t,message:e}),n.addEventListener("hidden.bs.toast",()=>{this.cleanup(i)}),r.show(),{id:i,hide:()=>{try{r.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,size:"md",...s},n=this.createViewToastElement(i,e,t,a);if(this.container.appendChild(n),typeof bootstrap>"u")throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");const r=new bootstrap.Toast(n,{autohide:a.autohide,delay:a.delay});this.toasts.set(i,{element:n,bootstrap:r,type:t,view:e,message:"View toast"}),n.addEventListener("hidden.bs.toast",()=>{this.cleanupView(i)});const o=n.querySelector(".toast-view-body");return o&&e&&e.render(!0,o),r.show(),{id:i,view:e,hide:()=>{try{r.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}${i.size?` toast-${i.size}`:""}`,a.setAttribute("role","alert"),a.setAttribute("aria-live","assertive"),a.setAttribute("aria-atomic","true");const n=i.title||i.icon?this.createToastHeader(i,s):"",r=this.createToastBody(t,i.icon&&!i.title);return a.innerHTML=`
17
16
  ${n}
18
- `,a}createViewToastElement(e,t,s,i){const a=document.createElement("div");a.id=e,a.className=`toast toast-service-${s}${i.size?` toast-${i.size}`:""}`,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=`
19
17
  ${r}
18
+ `,a}createViewToastElement(e,t,s,i){const a=document.createElement("div");a.id=e,a.className=`toast toast-service-${s}${i.size?` toast-${i.size}`:""}`,a.setAttribute("role","alert"),a.setAttribute("aria-live","assertive"),a.setAttribute("aria-atomic","true");const n=i.title||i.icon?this.createToastHeader(i,s):"",r=this.createViewToastBody();return a.innerHTML=`
20
19
  ${n}
20
+ ${r}
21
21
  `,a}createViewToastBody(){return`
22
22
  <div class="toast-body p-0">
23
23
  <div class="toast-view-body p-3"></div>
24
24
  </div>
25
- `}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?"":`
25
+ `}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>`:"",n=e.dismissible?'<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>':"";return!i&&!a&&!n?"":`
26
26
  <div class="toast-header">
27
27
  ${i}
28
28
  ${a}
29
- ${r}
29
+ ${n}
30
30
  </div>
31
31
  `}createToastBody(e,t=!1){return`
32
32
  <div class="toast-body d-flex align-items-center">
@@ -72,7 +72,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
72
72
  </small>
73
73
  </div>
74
74
  </div>
75
- `}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=L.pipe(this.loaded,"filesize"),this.totalFormatted=L.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=L.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 Be{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 ze),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 he({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 G extends Y{constructor(e={}){super(e,{endpoint:"/api/group"})}}class ue extends J{constructor(e={}){super({ModelClass:G,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"},me=Object.entries(Ie).map(([u,e])=>({value:u,label:e})),pe={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:me},{type:"collection",name:"parent",label:"Parent Group",Collection:ue,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:me},{type:"collection",name:"parent",label:"Parent Group",Collection:ue,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}]}};G.EDIT_FORM=pe.edit,G.ADD_FORM=pe.create,G.CREATE_FORM=pe.create,G.GroupKindOptions=me,G.GroupKinds=Ie;class $ extends Y{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");if(!t)return!1;if(t[e]==!0)return!0;const s=$.GRANULAR_TO_CATEGORY[e];return!!(s&&t[s]==!0)}hasPerm(e){return this.hasPermission(e)}}$.CATEGORY_PERMISSIONS=[{name:"view_admin",label:"Admin Panel",tooltip:"Access the admin panel, assistant, and system tools"},{name:"security",label:"Security",tooltip:"Incidents, events, rules, tickets, firewall, bouncer, GeoIP, system logs"},{name:"users",label:"Users",tooltip:"User records, passkeys, TOTP, API keys, OAuth, devices, locations"},{name:"groups",label:"Groups",tooltip:"Groups, members, group API keys, settings"},{name:"comms",label:"Communications",tooltip:"Email, phone, SMS, push notifications, chat, notifications"},{name:"jobs",label:"Jobs",tooltip:"Jobs, job events, job logs, runners, queue control, system stats"},{name:"metrics",label:"Metrics",tooltip:"Metrics recording, fetching, categories, values, permissions"},{name:"files",label:"Files",tooltip:"File managers, files, renditions, vault files, vault data, S3 buckets"},{name:"assistant",label:"AI Assistant",tooltip:"Access to the AI Assistant"},{name:"comms",label:"Communication",tooltip:"Ability to notify users"}],$.GRANULAR_PERMISSION_TABS=[{label:"Account",permissions:[{name:"view_users",label:"View Users"},{name:"manage_users",label:"Manage Users"},{name:"view_groups",label:"View Groups"},{name:"manage_groups",label:"Manage Groups"},{name:"manage_group",label:"Manage Own Group"},{name:"view_members",label:"View Members"},{name:"manage_settings",label:"Manage Settings"}]},{label:"Communication",permissions:[{name:"manage_chat",label:"Manage Chat"},{name:"manage_aws",label:"Manage Email (AWS)"},{name:"view_notifications",label:"View Notifications"},{name:"manage_notifications",label:"Manage Notifications"},{name:"send_notifications",label:"Send Notifications"},{name:"view_devices",label:"View Push Devices"},{name:"manage_devices",label:"Manage Push Devices"},{name:"manage_push_config",label:"Push Config"},{name:"view_phone_numbers",label:"View Phone Numbers"},{name:"manage_phone_numbers",label:"Manage Phone Numbers"},{name:"manage_phone_config",label:"Phone Config"},{name:"view_sms",label:"View SMS"},{name:"manage_sms",label:"Manage SMS"},{name:"send_sms",label:"Send SMS"}]},{label:"Platform",permissions:[{name:"view_security",label:"View Security"},{name:"manage_security",label:"Manage Security"},{name:"admin",label:"Log Admin"},{name:"view_logs",label:"View Logs"},{name:"manage_logs",label:"Manage Logs"},{name:"view_jobs",label:"View Jobs"},{name:"manage_jobs",label:"Manage Jobs"},{name:"view_metrics",label:"View Metrics"},{name:"manage_metrics",label:"Manage Metrics"},{name:"write_metrics",label:"Write Metrics"},{name:"view_fileman",label:"View File Managers"},{name:"manage_files",label:"Manage Files"},{name:"view_vault",label:"View Vault"},{name:"manage_vault",label:"Manage Vault"},{name:"manage_docit",label:"Manage Docs"},{name:"manage_shortlinks",label:"Manage Shortlinks"}]}],$.CATEGORY_GRANULAR_MAP={security:["view_security","manage_security"],users:["view_users","manage_users","view_members"],groups:["view_groups","manage_groups","manage_group"],comms:["manage_chat","manage_aws","view_notifications","manage_notifications","send_notifications","view_devices","manage_devices","manage_push_config","view_phone_numbers","manage_phone_numbers","manage_phone_config","view_sms","manage_sms","send_sms"],jobs:["view_jobs","manage_jobs"],metrics:["view_metrics","manage_metrics","write_metrics"],files:["view_fileman","manage_files","view_vault","manage_vault"]},$.GRANULAR_TO_CATEGORY={};for(const[u,e]of Object.entries($.CATEGORY_GRANULAR_MAP))for(const t of e)$.GRANULAR_TO_CATEGORY[t]=u;$.APP_CATEGORY_PERMISSIONS=[],$.APP_GRANULAR_PERMISSIONS=[],$.PERMISSIONS=[...$.CATEGORY_PERMISSIONS,...$.GRANULAR_PERMISSION_TABS.flatMap(u=>u.permissions),...$.APP_CATEGORY_PERMISSIONS,...$.APP_GRANULAR_PERMISSIONS],$.PERMISSION_FIELDS=[...$.PERMISSIONS.map(u=>({name:`permissions.${u.name}`,type:"switch",label:u.label,columns:6}))];const se=u=>({name:`permissions.${u.name}`,type:"switch",label:u.label,columns:6,...u.tooltip?{tooltip:u.tooltip}:{}});$.CATEGORY_PERMISSION_FIELDS=(()=>{const u=[{label:"System",fields:$.CATEGORY_PERMISSIONS.map(se)}];return $.APP_CATEGORY_PERMISSIONS.length>0&&u.push({label:"App",fields:$.APP_CATEGORY_PERMISSIONS.map(se)}),[{type:"tabset",tabs:u}]})(),$.GRANULAR_PERMISSION_FIELDS=(()=>{const u=$.GRANULAR_PERMISSION_TABS.map(e=>({label:e.label,fields:e.permissions.map(se)}));return $.APP_GRANULAR_PERMISSIONS.length>0&&u.push({label:"App",fields:$.APP_GRANULAR_PERMISSIONS.map(se)}),[{type:"tabset",tabs:u}]})();const ke={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:ue,labelField:"name",valueField:"id",columns:12}]},permissions:{fields:$.PERMISSION_FIELDS}},Ue={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}]}};$.DATA_VIEW=Ue.detailed,$.EDIT_FORM=ke.edit,$.ADD_FORM=ke.create;let Ye=class extends Y{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return this.get("category")==="image"}upload(e={}){return new Be(this,e)}},_=class E extends C{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=E._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(){E.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=`
75
+ `}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=L.pipe(this.loaded,"filesize"),this.totalFormatted=L.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=L.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 Be{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 ze),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:n,headers:r}=e,o=a==="POST"&&n!==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(r||{}))m.toLowerCase()!=="content-type"&&l.setRequestHeader(m,p);const h=new FormData;for(const[m,p]of Object.entries(n))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(r||{}))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 he({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 G extends Y{constructor(e={}){super(e,{endpoint:"/api/group"})}}class ue extends J{constructor(e={}){super({ModelClass:G,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"},me=Object.entries(Ie).map(([u,e])=>({value:u,label:e})),pe={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:me},{type:"collection",name:"parent",label:"Parent Group",Collection:ue,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:me},{type:"collection",name:"parent",label:"Parent Group",Collection:ue,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}]}};G.EDIT_FORM=pe.edit,G.ADD_FORM=pe.create,G.CREATE_FORM=pe.create,G.GroupKindOptions=me,G.GroupKinds=Ie;class $ extends Y{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");if(!t)return!1;if(t[e]==!0)return!0;const s=$.GRANULAR_TO_CATEGORY[e];return!!(s&&t[s]==!0)}hasPerm(e){return this.hasPermission(e)}}$.CATEGORY_PERMISSIONS=[{name:"view_admin",label:"Admin Panel",tooltip:"Access the admin panel, assistant, and system tools"},{name:"security",label:"Security",tooltip:"Incidents, events, rules, tickets, firewall, bouncer, GeoIP, system logs"},{name:"users",label:"Users",tooltip:"User records, passkeys, TOTP, API keys, OAuth, devices, locations"},{name:"groups",label:"Groups",tooltip:"Groups, members, group API keys, settings"},{name:"comms",label:"Communications",tooltip:"Email, phone, SMS, push notifications, chat, notifications"},{name:"jobs",label:"Jobs",tooltip:"Jobs, job events, job logs, runners, queue control, system stats"},{name:"metrics",label:"Metrics",tooltip:"Metrics recording, fetching, categories, values, permissions"},{name:"files",label:"Files",tooltip:"File managers, files, renditions, vault files, vault data, S3 buckets"},{name:"assistant",label:"AI Assistant",tooltip:"Access to the AI Assistant"},{name:"comms",label:"Communication",tooltip:"Ability to notify users"}],$.GRANULAR_PERMISSION_TABS=[{label:"Account",permissions:[{name:"view_users",label:"View Users"},{name:"manage_users",label:"Manage Users"},{name:"view_groups",label:"View Groups"},{name:"manage_groups",label:"Manage Groups"},{name:"manage_group",label:"Manage Own Group"},{name:"view_members",label:"View Members"},{name:"manage_settings",label:"Manage Settings"}]},{label:"Communication",permissions:[{name:"manage_chat",label:"Manage Chat"},{name:"manage_aws",label:"Manage Email (AWS)"},{name:"view_notifications",label:"View Notifications"},{name:"manage_notifications",label:"Manage Notifications"},{name:"send_notifications",label:"Send Notifications"},{name:"view_devices",label:"View Push Devices"},{name:"manage_devices",label:"Manage Push Devices"},{name:"manage_push_config",label:"Push Config"},{name:"view_phone_numbers",label:"View Phone Numbers"},{name:"manage_phone_numbers",label:"Manage Phone Numbers"},{name:"manage_phone_config",label:"Phone Config"},{name:"view_sms",label:"View SMS"},{name:"manage_sms",label:"Manage SMS"},{name:"send_sms",label:"Send SMS"}]},{label:"Platform",permissions:[{name:"view_security",label:"View Security"},{name:"manage_security",label:"Manage Security"},{name:"admin",label:"Log Admin"},{name:"view_logs",label:"View Logs"},{name:"manage_logs",label:"Manage Logs"},{name:"view_jobs",label:"View Jobs"},{name:"manage_jobs",label:"Manage Jobs"},{name:"view_metrics",label:"View Metrics"},{name:"manage_metrics",label:"Manage Metrics"},{name:"write_metrics",label:"Write Metrics"},{name:"view_fileman",label:"View File Managers"},{name:"manage_files",label:"Manage Files"},{name:"view_vault",label:"View Vault"},{name:"manage_vault",label:"Manage Vault"},{name:"manage_docit",label:"Manage Docs"},{name:"manage_shortlinks",label:"Manage Shortlinks"}]}],$.CATEGORY_GRANULAR_MAP={security:["view_security","manage_security"],users:["view_users","manage_users","view_members"],groups:["view_groups","manage_groups","manage_group"],comms:["manage_chat","manage_aws","view_notifications","manage_notifications","send_notifications","view_devices","manage_devices","manage_push_config","view_phone_numbers","manage_phone_numbers","manage_phone_config","view_sms","manage_sms","send_sms"],jobs:["view_jobs","manage_jobs"],metrics:["view_metrics","manage_metrics","write_metrics"],files:["view_fileman","manage_files","view_vault","manage_vault"]},$.GRANULAR_TO_CATEGORY={};for(const[u,e]of Object.entries($.CATEGORY_GRANULAR_MAP))for(const t of e)$.GRANULAR_TO_CATEGORY[t]=u;$.APP_CATEGORY_PERMISSIONS=[],$.APP_GRANULAR_PERMISSIONS=[],$.PERMISSIONS=[...$.CATEGORY_PERMISSIONS,...$.GRANULAR_PERMISSION_TABS.flatMap(u=>u.permissions),...$.APP_CATEGORY_PERMISSIONS,...$.APP_GRANULAR_PERMISSIONS],$.PERMISSION_FIELDS=[...$.PERMISSIONS.map(u=>({name:`permissions.${u.name}`,type:"switch",label:u.label,columns:6}))];const se=u=>({name:`permissions.${u.name}`,type:"switch",label:u.label,columns:6,...u.tooltip?{tooltip:u.tooltip}:{}});$.CATEGORY_PERMISSION_FIELDS=(()=>{const u=[{label:"System",fields:$.CATEGORY_PERMISSIONS.map(se)}];return $.APP_CATEGORY_PERMISSIONS.length>0&&u.push({label:"App",fields:$.APP_CATEGORY_PERMISSIONS.map(se)}),[{type:"tabset",tabs:u}]})(),$.GRANULAR_PERMISSION_FIELDS=(()=>{const u=$.GRANULAR_PERMISSION_TABS.map(e=>({label:e.label,fields:e.permissions.map(se)}));return $.APP_GRANULAR_PERMISSIONS.length>0&&u.push({label:"App",fields:$.APP_GRANULAR_PERMISSIONS.map(se)}),[{type:"tabset",tabs:u}]})();const ke={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:ue,labelField:"name",valueField:"id",columns:12}]},permissions:{fields:$.PERMISSION_FIELDS}},Ue={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}]}};$.DATA_VIEW=Ue.detailed,$.EDIT_FORM=ke.edit,$.ADD_FORM=ke.create;let Ye=class extends Y{constructor(e={}){super(e,{endpoint:"/api/fileman/file"})}isImage(){return this.get("category")==="image"}getCategory(){return this.get("category")||this._inferCategoryFromContentType()}_inferCategoryFromContentType(){const e=(this.get("content_type")||"").toLowerCase();return e?e.startsWith("image/")?"image":e.startsWith("video/")?"video":e.startsWith("audio/")?"audio":e==="application/pdf"?"pdf":e.startsWith("text/")||e==="application/msword"||e.startsWith("application/vnd.openxmlformats-officedocument.wordprocessingml")||e==="application/vnd.oasis.opendocument.text"?"document":e==="application/vnd.ms-excel"||e.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml")||e==="application/vnd.oasis.opendocument.spreadsheet"?"spreadsheet":e==="application/vnd.ms-powerpoint"||e.startsWith("application/vnd.openxmlformats-officedocument.presentationml")||e==="application/vnd.oasis.opendocument.presentation"?"presentation":e==="application/zip"||e==="application/x-rar-compressed"||e==="application/x-7z-compressed"||e==="application/x-tar"||e==="application/gzip"?"archive":"other":"other"}hasRenditions(){const e=this.get("renditions");return!!(e&&Object.keys(e).length)}isRenditionsProcessing(){return this.get("upload_status")==="completed"&&!this.hasRenditions()}regenerateRenditions(e){const t={action:"regenerate_renditions"};return Array.isArray(e)&&e.length&&(t.roles=e),this.save(t)}getRenditions(){const e=this.get("renditions");return e?Object.values(e):[]}getBestImageRendition(){const e=this.getRenditions().filter(t=>t&&typeof t.content_type=="string"&&t.content_type.startsWith("image/"));return e.length?e.reduce((t,s)=>{const i=(parseInt(t.width)||0)*(parseInt(t.height)||0);return(parseInt(s.width)||0)*(parseInt(s.height)||0)>i?s:t}):null}getThumbnailUrl(){const e=this.get("renditions")||{};if(e.thumbnail&&e.thumbnail.url)return e.thumbnail.url;const t=this.getBestImageRendition();return t?t.url:null}upload(e={}){return new Be(this,e)}},M=class E extends C{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=E._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 r=s[a]._dialogZIndex-5;i.style.zIndex=r;const l=document.querySelector(".table-fullscreen")||document.body;i.parentNode!==l&&l.appendChild(i)}})}static updateAllBackdropStacking(){E.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 n=this.getFullscreenAwareZIndex().modal+1e3;this._busyIndicator=document.createElement("div"),this._busyIndicator.className="mojo-busy-indicator",this._busyIndicator.innerHTML=`
76
76
  <div class="mojo-busy-spinner">
77
77
  <div class="spinner-border text-light" role="status">
78
78
  <span class="visually-hidden">Loading...</span>
@@ -82,7 +82,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
82
82
  <style>
83
83
  .mojo-busy-indicator {
84
84
  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
85
- background-color: rgba(0, 0, 0, 0.5); z-index: ${r};
85
+ background-color: rgba(0, 0, 0, 0.5); z-index: ${n};
86
86
  display: flex; align-items: center; justify-content: center;
87
87
  opacity: 0; transition: opacity 0.15s linear;
88
88
  }
@@ -105,7 +105,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
105
105
  ${this.title?`<h5 class="modal-title" id="${this.titleId}">${this.title}</h5>`:""}
106
106
  ${e}
107
107
  </div>
108
- `}async buildContextMenu(){const e=await this.filterContextMenuItems();if(e.length===0)return this.closeButton?'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>':"";const t=this.contextMenu.icon||"bi-three-dots-vertical",s=this.contextMenu.buttonClass||"btn btn-link p-1 mojo-modal-context-menu-btn",i=e.map(a=>{if(a.type==="divider")return'<li><hr class="dropdown-divider"></li>';const r=a.icon?`<i class="${a.icon} me-2"></i>`:"",n=a.label||"";if(a.href)return`<li><a class="dropdown-item" href="${a.href}"${a.target?` target="${a.target}"`:""}>${r}${n}</a></li>`;if(a.action){const o=Object.keys(a).filter(l=>l.startsWith("data-")).map(l=>`${l}="${a[l]}"`).join(" ");return`<li><a class="dropdown-item" data-action="${a.action}" ${o}>${r}${n}</a></li>`}return""}).join("");return`
108
+ `}async buildContextMenu(){const e=await this.filterContextMenuItems();if(e.length===0)return this.closeButton?'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>':"";const t=this.contextMenu.icon||"bi-three-dots-vertical",s=this.contextMenu.buttonClass||"btn btn-link p-1 mojo-modal-context-menu-btn",i=e.map(a=>{if(a.type==="divider")return'<li><hr class="dropdown-divider"></li>';const n=a.icon?`<i class="${a.icon} me-2"></i>`:"",r=a.label||"";if(a.href)return`<li><a class="dropdown-item" href="${a.href}"${a.target?` target="${a.target}"`:""}>${n}${r}</a></li>`;if(a.action){const o=Object.keys(a).filter(l=>l.startsWith("data-")).map(l=>`${l}="${a[l]}"`).join(" ");return`<li><a class="dropdown-item" data-action="${a.action}" ${o}>${n}${r}</a></li>`}return""}).join("");return`
109
109
  <div class="dropdown">
110
110
  <button class="${s}" type="button" data-bs-toggle="dropdown" aria-expanded="false">
111
111
  <i class="${t}"></i>
@@ -121,17 +121,17 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
121
121
  <div class="${this.noBodyPadding?`modal-body p-0 ${this.bodyClass}`:`modal-body ${this.bodyClass}`}">
122
122
  ${this.body}
123
123
  </div>
124
- `}async buildFooter(){if(this.footerView)return`<div class="modal-footer ${this.footerClass}" data-view-container="footer"></div>`;if(this.footer!==null&&typeof this.footer=="string")return`<div class="modal-footer ${this.footerClass}">${this.footer}</div>`;if(this.buttons&&this.buttons.length>0){const e=this.buttons.map(t=>{const s=t.dismiss?'data-bs-dismiss="modal"':"",i=t.action?`data-action="${t.action}"`:"",a=t.id?`id="${t.id}"`:"",r=t.disabled?"disabled":"";return`
124
+ `}async buildFooter(){if(this.footerView)return`<div class="modal-footer ${this.footerClass}" data-view-container="footer"></div>`;if(this.footer!==null&&typeof this.footer=="string")return`<div class="modal-footer ${this.footerClass}">${this.footer}</div>`;if(this.buttons&&this.buttons.length>0){const e=this.buttons.map(t=>{const s=t.dismiss?'data-bs-dismiss="modal"':"",i=t.action?`data-action="${t.action}"`:"",a=t.id?`id="${t.id}"`:"",n=t.disabled?"disabled":"";return`
125
125
  <button type="${t.type||"button"}"
126
126
  class="btn ${t.class||"btn-secondary"}"
127
127
  ${a}
128
128
  ${s}
129
129
  ${i}
130
- ${r}>
130
+ ${n}>
131
131
  ${t.icon?`<i class="bi ${t.icon} me-1"></i>`:""}
132
132
  ${t.text||"Button"}
133
133
  </button>
134
- `}).join("");return`<div class="modal-footer ${this.footerClass}">${e}</div>`}return""}async mount(e=null){if(this.mounted||this.destroyed)return;if(!this.element)throw new Error("Cannot mount dialog without element");return await this.onBeforeMount(),(document.querySelector(".table-fullscreen")||document.body).appendChild(this.element),this.bindEvents(),this.mounted=!0,await this.onAfterMount(),this.emit("mounted",{view:this}),this}async onAfterRender(){if(await super.onAfterRender(),window.Prism&&this.element&&this.element.querySelectorAll("pre code").length>0&&window.Prism.highlightAllUnder(this.element),this.autoSize)this.setupAutoSizing();else if(this.maxHeight){const e=this.element.querySelector(".modal-body");e&&(e.style.maxHeight=`${this.maxHeight}px`)}}async onAfterMount(){await super.onAfterMount(),typeof window<"u"&&window.bootstrap&&window.bootstrap.Modal&&(this.backdrop==="static"&&this.element.setAttribute("data-bs-backdrop","static"),this.keyboard||this.element.setAttribute("data-bs-keyboard","false"),this.modal=new window.bootstrap.Modal(this.element,{backdrop:this.backdrop,keyboard:this.keyboard,focus:this.focus}),this.bindBootstrapEvents(),this.autoShow&&this.show(this.relatedTarget))}setupAutoSizing(){this.element&&(this.element.addEventListener("shown.bs.modal",()=>{this.applyAutoSizing()},{once:!0}),setTimeout(()=>{this.isShown()&&this.applyAutoSizing()},100))}applyAutoSizing(){if(this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),s=this.element.querySelector(".modal-body");if(!e||!t||!s){console.warn("Dialog auto-sizing: Required elements not found");return}if(this.bodyView&&!this.bodyView.element){setTimeout(()=>this.applyAutoSizing(),50);return}const i={dialogMaxWidth:e.style.maxWidth,dialogWidth:e.style.width,contentWidth:t.style.width,contentMaxHeight:t.style.maxHeight,hadScrollableClass:e.classList.contains("modal-dialog-scrollable")};e.style.maxWidth="none",e.style.width="auto",t.style.width="auto",t.style.maxHeight="none",t.offsetHeight;const a=t.getBoundingClientRect(),r=40,n=Math.min(window.innerWidth*this.maxWidthPercent,window.innerWidth-r);let o=Math.min(window.innerHeight*this.maxHeightPercent,window.innerHeight-r),l=Math.max(this.minWidth,Math.ceil(a.width+20)),c=Math.max(this.minHeight,Math.ceil(a.height));this.maxHeight&&(o=Math.min(this.maxHeight,o),e.style.maxHeight=`${o}px`),l=Math.min(l,n);const d=a.height>o;e.style.maxWidth=`${l}px`,e.style.width=`${l}px`,d&&(e.classList.contains("modal-dialog-scrollable")||e.classList.add("modal-dialog-scrollable"),t.style.maxHeight=`${o}px`,c=o),this.autoSizedWidth=l,this.autoSizedHeight=c,this._originalStyles=i}catch(e){console.error("Error in dialog auto-sizing:",e),this.element.querySelector(".modal-dialog").style.maxWidth=""}}resetAutoSizing(){if(!(!this.autoSize||!this._originalStyles||!this.element))try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),s=this.element.querySelector(".modal-body");e&&t&&s&&(e.style.maxWidth=this._originalStyles.dialogMaxWidth||"",e.style.width=this._originalStyles.dialogWidth||"",t.style.width=this._originalStyles.contentWidth||"",t.style.maxHeight=this._originalStyles.contentMaxHeight||"",!this._originalStyles.hadScrollableClass&&e.classList.contains("modal-dialog-scrollable")&&e.classList.remove("modal-dialog-scrollable"),delete this.autoSizedWidth,delete this.autoSizedHeight,delete this._originalStyles)}catch(e){console.error("Error resetting dialog auto-sizing:",e)}}bindBootstrapEvents(){this.element.addEventListener("show.bs.modal",e=>{const t=E._openDialogs.length,i=E.getFullscreenAwareZIndex().modal+t*20;this.element.style.zIndex=i,this._dialogZIndex=i,this._backdropZIndex=i-10,E._openDialogs.push(this),this.onShow&&this.onShow(e),this.emit("show",{dialog:this,relatedTarget:e.relatedTarget})}),this.element.addEventListener("shown.bs.modal",e=>{if(setTimeout(()=>{E.fixAllBackdropStacking()},50),this.onShown&&this.onShown(e),this.emit("shown",{dialog:this,relatedTarget:e.relatedTarget}),this.focus){const t=this.element.querySelector('input:not([type="hidden"]), textarea, select');t&&t.focus()}}),this.element.addEventListener("hide.bs.modal",e=>{const t=this.element.querySelector(":focus");if(t&&t.blur(),this.onHide&&this.onHide(e)===!1){e.preventDefault();return}this.emit("hide",{dialog:this})}),this.element.addEventListener("hidden.bs.modal",e=>{const t=E._openDialogs.indexOf(this);t>-1&&E._openDialogs.splice(t,1),E._openDialogs.length>0&&(document.body.classList.add("modal-open"),setTimeout(()=>{E.fixAllBackdropStacking()},50)),this.previousFocus&&document.body.contains(this.previousFocus)&&this.previousFocus.focus(),this.onHidden&&this.onHidden(e),this.emit("hidden",{dialog:this})}),this.element.addEventListener("hidePrevented.bs.modal",e=>{this.onHidePrevented&&this.onHidePrevented(e),this.emit("hidePrevented",{dialog:this})})}show(e=null){this.previousFocus=document.activeElement,window.lastDialog=this,this.modal&&this.modal.show(e)}hide(){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal&&this.modal.hide()}toggle(e=null){this.modal&&this.modal.toggle(e)}async destroy(){if(this.modal){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal.dispose(),this.modal=null}this.previousFocus&&document.body.contains(this.previousFocus)&&(this.previousFocus.focus(),this.previousFocus=null),this.autoSize&&this.resetAutoSizing(),await super.destroy()}handleUpdate(){this.modal&&this.modal.handleUpdate()}async setContent(e){if(e instanceof C){this.bodyView&&(await this.bodyView.destroy(),this.removeChild(this.bodyView)),this.bodyView=e,this.body="",this.addChild(this.bodyView);const t=this.element?.querySelector(".modal-body");t&&(t.innerHTML="",await this.bodyView.render(t))}else{this.body=e;const t=this.element?.querySelector(".modal-body");t&&(t.innerHTML=e)}this.handleUpdate()}setTitle(e){this.title=e;const t=this.element?.querySelector(".modal-title");t&&(t.textContent=e)}setLoading(e=!0,t="Loading..."){const s=this.element?.querySelector(".modal-body");s&&(e?s.innerHTML=`
134
+ `}).join("");return`<div class="modal-footer ${this.footerClass}">${e}</div>`}return""}async mount(e=null){if(this.mounted||this.destroyed)return;if(!this.element)throw new Error("Cannot mount dialog without element");return await this.onBeforeMount(),(document.querySelector(".table-fullscreen")||document.body).appendChild(this.element),this.bindEvents(),this.mounted=!0,await this.onAfterMount(),this.emit("mounted",{view:this}),this}async onAfterRender(){if(await super.onAfterRender(),window.Prism&&this.element&&this.element.querySelectorAll("pre code").length>0&&window.Prism.highlightAllUnder(this.element),this.autoSize)this.setupAutoSizing();else if(this.maxHeight){const e=this.element.querySelector(".modal-body");e&&(e.style.maxHeight=`${this.maxHeight}px`)}}async onAfterMount(){await super.onAfterMount(),typeof window<"u"&&window.bootstrap&&window.bootstrap.Modal&&(this.backdrop==="static"&&this.element.setAttribute("data-bs-backdrop","static"),this.keyboard||this.element.setAttribute("data-bs-keyboard","false"),this.modal=new window.bootstrap.Modal(this.element,{backdrop:this.backdrop,keyboard:this.keyboard,focus:this.focus}),this.bindBootstrapEvents(),this.autoShow&&this.show(this.relatedTarget))}setupAutoSizing(){this.element&&(this.element.addEventListener("shown.bs.modal",()=>{this.applyAutoSizing()},{once:!0}),setTimeout(()=>{this.isShown()&&this.applyAutoSizing()},100))}applyAutoSizing(){if(this.element)try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),s=this.element.querySelector(".modal-body");if(!e||!t||!s){console.warn("Dialog auto-sizing: Required elements not found");return}if(this.bodyView&&!this.bodyView.element){setTimeout(()=>this.applyAutoSizing(),50);return}const i={dialogMaxWidth:e.style.maxWidth,dialogWidth:e.style.width,contentWidth:t.style.width,contentMaxHeight:t.style.maxHeight,hadScrollableClass:e.classList.contains("modal-dialog-scrollable")};e.style.maxWidth="none",e.style.width="auto",t.style.width="auto",t.style.maxHeight="none",t.offsetHeight;const a=t.getBoundingClientRect(),n=40,r=Math.min(window.innerWidth*this.maxWidthPercent,window.innerWidth-n);let o=Math.min(window.innerHeight*this.maxHeightPercent,window.innerHeight-n),l=Math.max(this.minWidth,Math.ceil(a.width+20)),c=Math.max(this.minHeight,Math.ceil(a.height));this.maxHeight&&(o=Math.min(this.maxHeight,o),e.style.maxHeight=`${o}px`),l=Math.min(l,r);const d=a.height>o;e.style.maxWidth=`${l}px`,e.style.width=`${l}px`,d&&(e.classList.contains("modal-dialog-scrollable")||e.classList.add("modal-dialog-scrollable"),t.style.maxHeight=`${o}px`,c=o),this.autoSizedWidth=l,this.autoSizedHeight=c,this._originalStyles=i}catch(e){console.error("Error in dialog auto-sizing:",e),this.element.querySelector(".modal-dialog").style.maxWidth=""}}resetAutoSizing(){if(!(!this.autoSize||!this._originalStyles||!this.element))try{const e=this.element.querySelector(".modal-dialog"),t=this.element.querySelector(".modal-content"),s=this.element.querySelector(".modal-body");e&&t&&s&&(e.style.maxWidth=this._originalStyles.dialogMaxWidth||"",e.style.width=this._originalStyles.dialogWidth||"",t.style.width=this._originalStyles.contentWidth||"",t.style.maxHeight=this._originalStyles.contentMaxHeight||"",!this._originalStyles.hadScrollableClass&&e.classList.contains("modal-dialog-scrollable")&&e.classList.remove("modal-dialog-scrollable"),delete this.autoSizedWidth,delete this.autoSizedHeight,delete this._originalStyles)}catch(e){console.error("Error resetting dialog auto-sizing:",e)}}bindBootstrapEvents(){this.element.addEventListener("show.bs.modal",e=>{const t=E._openDialogs.length,i=E.getFullscreenAwareZIndex().modal+t*20;this.element.style.zIndex=i,this._dialogZIndex=i,this._backdropZIndex=i-10,E._openDialogs.push(this),this.onShow&&this.onShow(e),this.emit("show",{dialog:this,relatedTarget:e.relatedTarget})}),this.element.addEventListener("shown.bs.modal",e=>{if(setTimeout(()=>{E.fixAllBackdropStacking()},50),this.onShown&&this.onShown(e),this.emit("shown",{dialog:this,relatedTarget:e.relatedTarget}),this.focus){const t=this.element.querySelector('input:not([type="hidden"]), textarea, select');t&&t.focus()}}),this.element.addEventListener("hide.bs.modal",e=>{const t=this.element.querySelector(":focus");if(t&&t.blur(),this.onHide&&this.onHide(e)===!1){e.preventDefault();return}this.emit("hide",{dialog:this})}),this.element.addEventListener("hidden.bs.modal",e=>{const t=E._openDialogs.indexOf(this);t>-1&&E._openDialogs.splice(t,1),E._openDialogs.length>0&&(document.body.classList.add("modal-open"),setTimeout(()=>{E.fixAllBackdropStacking()},50)),this.previousFocus&&document.body.contains(this.previousFocus)&&this.previousFocus.focus(),this.onHidden&&this.onHidden(e),this.emit("hidden",{dialog:this})}),this.element.addEventListener("hidePrevented.bs.modal",e=>{this.onHidePrevented&&this.onHidePrevented(e),this.emit("hidePrevented",{dialog:this})})}show(e=null){this.previousFocus=document.activeElement,window.lastDialog=this,this.modal&&this.modal.show(e)}hide(){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal&&this.modal.hide()}toggle(e=null){this.modal&&this.modal.toggle(e)}async destroy(){if(this.modal){const e=this.element?.querySelector(":focus");e&&e.blur(),this.modal.dispose(),this.modal=null}this.previousFocus&&document.body.contains(this.previousFocus)&&(this.previousFocus.focus(),this.previousFocus=null),this.autoSize&&this.resetAutoSizing(),await super.destroy()}handleUpdate(){this.modal&&this.modal.handleUpdate()}async setContent(e){if(e instanceof C){this.bodyView&&(await this.bodyView.destroy(),this.removeChild(this.bodyView)),this.bodyView=e,this.body="",this.addChild(this.bodyView);const t=this.element?.querySelector(".modal-body");t&&(t.innerHTML="",await this.bodyView.render(t))}else{this.body=e;const t=this.element?.querySelector(".modal-body");t&&(t.innerHTML=e)}this.handleUpdate()}setTitle(e){this.title=e;const t=this.element?.querySelector(".modal-title");t&&(t.textContent=e)}setLoading(e=!0,t="Loading..."){const s=this.element?.querySelector(".modal-body");s&&(e?s.innerHTML=`
135
135
  <div class="text-center py-4">
136
136
  <div class="spinner-border text-primary mb-3" role="status">
137
137
  <span class="visually-hidden">Loading...</span>
@@ -172,7 +172,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
172
172
  <pre class="${i} dialog-code-block" style="${a}">
173
173
  <code class="${i}" style="color: inherit; background: transparent; text-shadow: none;">${s}</code>
174
174
  </pre>
175
- `}static highlightCodeBlocks(e=document){window.Prism&&window.Prism.highlightAllUnder&&window.Prism.highlightAllUnder(e)}showCopySuccess(){const e=this.element.querySelector('[data-action="copy"]');if(e){const t=e.innerHTML;e.innerHTML='<i class="bi bi-check me-1"></i>Copied!',e.classList.remove("btn-primary"),e.classList.add("btn-success"),e.disabled=!0,setTimeout(()=>{e.innerHTML=t,e.classList.remove("btn-success"),e.classList.add("btn-primary"),e.disabled=!1},2e3)}}static async showHtmlPreview(e={}){const t=e.html||e.content||"",s=e.title||"HTML Preview",i=e.size||"lg",r=`
175
+ `}static highlightCodeBlocks(e=document){window.Prism&&window.Prism.highlightAllUnder&&window.Prism.highlightAllUnder(e)}showCopySuccess(){const e=this.element.querySelector('[data-action="copy"]');if(e){const t=e.innerHTML;e.innerHTML='<i class="bi bi-check me-1"></i>Copied!',e.classList.remove("btn-primary"),e.classList.add("btn-success"),e.disabled=!0,setTimeout(()=>{e.innerHTML=t,e.classList.remove("btn-success"),e.classList.add("btn-primary"),e.disabled=!1},2e3)}}static async showHtmlPreview(e={}){const t=e.html||e.content||"",s=e.title||"HTML Preview",i=e.size||"lg",n=`
176
176
  <div class="html-preview-container">
177
177
  <div class="d-flex justify-content-between align-items-center mb-2">
178
178
  <small class="text-muted">Preview (sandboxed)</small>
@@ -188,14 +188,14 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
188
188
  frameborder="0"
189
189
  ></iframe>
190
190
  </div>
191
- `,n=new E({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 E({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((S,F)=>{const D=l[F];D&&S.addEventListener("click",async V=>{if(b)return;const P=D.value!==void 0?D.value:D.action??F;if(typeof D.handler=="function")try{const I=await D.handler({dialog:m,button:D,index:F,event:V});if(I===null||I===!1)return;const Z=I===!0||I===void 0?P:I;b=!0,D.dismiss||m.hide(),g(Z)}catch(I){console.error("Dialog button handler error:",I);return}else b=!0,D.dismiss||m.hide(),g(P)})}),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 E.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 E({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 E({title:t,body:`
191
+ `,r=new E({title:s,size:i,scrollable:!1,body:n,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}]});r.on("action:refresh-preview",async d=>{const h=r.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 r.render(!0,l);const c=r.element.querySelector("#html-preview-frame");if(c){const d=c.contentDocument||c.contentWindow.document;d.open(),d.write(t),d.close()}return r.show(),r.on("hidden",()=>{r.destroy(),r.element.remove()}),r}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:n,size:r="md",centered:o=!0,buttons:l=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:c=!1,...d}=e,h=i??a??n??s??"",m=new E({title:t,body:h,size:r,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((S,F)=>{const D=l[F];D&&S.addEventListener("click",async V=>{if(b)return;const P=D.value!==void 0?D.value:D.action??F;if(typeof D.handler=="function")try{const I=await D.handler({dialog:m,button:D,index:F,event:V});if(I===null||I===!1)return;const Z=I===!0||I===void 0?P:I;b=!0,D.dismiss||m.hide(),g(Z)}catch(I){console.error("Dialog button handler error:",I);return}else b=!0,D.dismiss||m.hide(),g(P)})}),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 n="",r="";switch(i){case"success":n='<i class="bi bi-check-circle-fill text-success me-2"></i>',r="text-success";break;case"warning":n='<i class="bi bi-exclamation-triangle-fill text-warning me-2"></i>',r="text-warning";break;case"danger":case"error":n='<i class="bi bi-x-circle-fill text-danger me-2"></i>',r="text-danger";break;default:n='<i class="bi bi-info-circle-fill text-info me-2"></i>',r="text-info"}return E.showDialog({title:`<span class="${r}">${n}${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 E({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}),n=document.querySelector(".table-fullscreen")||document.body;return await i.render(!0,n),i.show(),new Promise(r=>{let o=!1;i.on("action:confirm",()=>{o=!0,i.hide()}),i.on("hidden",()=>{i.destroy(),i.element.remove(),r(o)})})}static async prompt(e,t="Input",s={}){const i=`prompt-input-${Date.now()}`,a=s.defaultValue||"",n=s.inputType||"text",r=s.placeholder||"",o=new E({title:t,body:`
192
192
  <p>${e}</p>
193
- <input type="${r}"
193
+ <input type="${n}"
194
194
  class="form-control"
195
195
  id="${i}"
196
196
  value="${a}"
197
- placeholder="${n}">
198
- `,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(()=>Me)).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 E({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 E.showForm(a);if(!s||!r||!e.model)return r;const n=r[i];if(!n||!n.startsWith("data:"))return r;const o=n.split(","),c=o[0]?.match(/:(.*?);/)?.[1]||"image/png",d=atob(o[1]);let h=d.length;const m=new Uint8Array(h);for(;h--;)m[h]=d.charCodeAt(h);const p=c.split("/")[1]||"png",f=typeof window<"u"&&window.File||globalThis.File;if(!f)throw new Error("File API is not available in this environment");const g=new f([m],`${i}.${p}`,{type:c}),y=new Ye;return await y.upload({file:g,name:`${i}.${p}`,description:e.uploadDescription||`${i} upload`,showToast:!0}),await e.model.save({[i]:y.id})}static async showModelView(e,t){const i=e.constructor.VIEW_CLASS,a=new i({model:e});return t=t||{},await E.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(()=>Me)).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 E({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 v=b.error;b.data&&b.data.error&&(v=b.data.error),m.getApp().toast.error(v)}}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(()=>lt)).default,f=new p({data:s,model:i,fields:a,columns:r,responsive:n,showEmptyValues:o,emptyValueText:l}),g=new E({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(v=>{let S=!1;const F=g.element.querySelector(".modal-footer button"),D=()=>{S||(S=!0,g.hide(),v(!0))};F?.addEventListener("click",D),g.on("hidden",()=>{S||(S=!0,v(!0)),setTimeout(()=>{f.destroy(),g.destroy(),g.element.remove()},100)}),f.on("field:click",V=>{g.emit("dataview:field:click",V)}),f.on("error",V=>{g.emit("dataview:error",V)})})}};_.showConfirm=_.confirm,_.showError=_.alert;const N=Object.freeze(Object.defineProperty({__proto__:null,default:_},Symbol.toStringTag,{value:"Module"}));class w{static async show(e,t={}){return _.showDialog({header:t.title!==void 0?!!t.title:!1,title:t.title||void 0,body:e,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...t})}static async showModel(e,t={}){const s=e.constructor,i=s?.VIEW_CLASS;if(!i)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${s?.name||"model"}. Set ${s?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const a=new i({model:e});return w.show(a,t)}static async showModelById(e,t,s={}){const i=new e({id:t});return await i.fetch(),i.id?w.showModel(i,s):(_.alert({message:`Could not find ${e.name||"record"} with ID: ${t}`,type:"warning"}),null)}static confirm(e,t,s){return _.confirm(e,t,s)}static alert(e,t,s){return _.alert(e,t,s)}static prompt(e,t,s){return _.prompt(e,t,s)}static form(e){return _.showForm(e)}static modelForm(e){return _.showModelForm(e)}static data(e){return _.showData(e)}static dialog(e){return _.showDialog(e)}static showError(e){return _.alert(e,"Error",{type:"danger"})}static _loadingEl=null;static _loadingCounter=0;static _loadingTimeout=null;static loading(e){typeof e=="string"&&(e={message:e});const{message:t="Loading...",timeout:s=3e4}=e||{};if(w._loadingCounter++,w._loadingCounter===1){w._loadingTimeout&&clearTimeout(w._loadingTimeout),w._loadingEl||(w._loadingEl=document.createElement("div"),w._loadingEl.className="mojo-loading-overlay",w._loadingEl.innerHTML=`
197
+ placeholder="${r}">
198
+ `,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:n="Submit",cancelText:r="Cancel",...o}=e,l=(await Promise.resolve().then(()=>_e)).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 E({title:t,body:c,size:i,centered:a,buttons:[{text:r,class:"btn-secondary",action:"cancel"},{text:n,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},n=await E.showForm(a);if(!s||!n||!e.model)return n;const r=n[i];if(!r||!r.startsWith("data:"))return n;const o=r.split(","),c=o[0]?.match(/:(.*?);/)?.[1]||"image/png",d=atob(o[1]);let h=d.length;const m=new Uint8Array(h);for(;h--;)m[h]=d.charCodeAt(h);const p=c.split("/")[1]||"png",f=typeof window<"u"&&window.File||globalThis.File;if(!f)throw new Error("File API is not available in this environment");const g=new f([m],`${i}.${p}`,{type:c}),y=new Ye;return await y.upload({file:g,name:`${i}.${p}`,description:e.uploadDescription||`${i} upload`,showToast:!0}),await e.model.save({[i]:y.id})}static async showModelView(e,t){const i=e.constructor.VIEW_CLASS,a=new i({model:e});return t=t||{},await E.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:n="Save",cancelText:r="Cancel",model:o,fields:l,...c}=e;if(!o)throw new Error("showModelForm requires a model");const d=(await Promise.resolve().then(()=>_e)).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 E({title:t,body:h,size:i,centered:a,buttons:[{text:r,class:"btn-secondary",action:"cancel"},{text:n,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 v=b.error;b.data&&b.data.error&&(v=b.data.error),m.getApp().toast.error(v)}}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:n=2,responsive:r=!0,showEmptyValues:o=!1,emptyValueText:l="—",size:c="lg",centered:d=!0,closeText:h="Close",...m}=e,p=(await Promise.resolve().then(()=>lt)).default,f=new p({data:s,model:i,fields:a,columns:n,responsive:r,showEmptyValues:o,emptyValueText:l}),g=new E({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(v=>{let S=!1;const F=g.element.querySelector(".modal-footer button"),D=()=>{S||(S=!0,g.hide(),v(!0))};F?.addEventListener("click",D),g.on("hidden",()=>{S||(S=!0,v(!0)),setTimeout(()=>{f.destroy(),g.destroy(),g.element.remove()},100)}),f.on("field:click",V=>{g.emit("dataview:field:click",V)}),f.on("error",V=>{g.emit("dataview:error",V)})})}};M.showConfirm=M.confirm,M.showError=M.alert;const N=Object.freeze(Object.defineProperty({__proto__:null,default:M},Symbol.toStringTag,{value:"Module"}));class w{static async show(e,t={}){return M.showDialog({header:t.title!==void 0?!!t.title:!1,title:t.title||void 0,body:e,size:"lg",centered:!1,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}],...t})}static async showModel(e,t={}){const s=e.constructor,i=s?.VIEW_CLASS;if(!i)throw new Error(`Modal.showModel: No VIEW_CLASS defined on ${s?.name||"model"}. Set ${s?.name||"Model"}.VIEW_CLASS = YourView to use this method.`);const a=new i({model:e});return w.show(a,t)}static async showModelById(e,t,s={}){const i=new e({id:t});return await i.fetch(),i.id?w.showModel(i,s):(M.alert({message:`Could not find ${e.name||"record"} with ID: ${t}`,type:"warning"}),null)}static confirm(e,t,s){return M.confirm(e,t,s)}static alert(e,t,s){return M.alert(e,t,s)}static prompt(e,t,s){return M.prompt(e,t,s)}static form(e){return M.showForm(e)}static modelForm(e){return M.showModelForm(e)}static data(e){return M.showData(e)}static dialog(e){return M.showDialog(e)}static showError(e){return M.alert(e,"Error",{type:"danger"})}static _loadingEl=null;static _loadingCounter=0;static _loadingTimeout=null;static loading(e){typeof e=="string"&&(e={message:e});const{message:t="Loading...",timeout:s=3e4}=e||{};if(w._loadingCounter++,w._loadingCounter===1){w._loadingTimeout&&clearTimeout(w._loadingTimeout),w._loadingEl||(w._loadingEl=document.createElement("div"),w._loadingEl.className="mojo-loading-overlay",w._loadingEl.innerHTML=`
199
199
  <div class="mojo-loading-card">
200
200
  <div class="mojo-loading-spinner"></div>
201
201
  <div class="mojo-loading-message">${t}</div>
@@ -237,7 +237,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
237
237
  to { transform: rotate(360deg); }
238
238
  }
239
239
  </style>
240
- `,document.body.appendChild(w._loadingEl));const i=w._loadingEl.querySelector(".mojo-loading-message");i&&(i.textContent=t),requestAnimationFrame(()=>{w._loadingEl&&w._loadingEl.classList.add("show")}),s>0&&(w._loadingTimeout=setTimeout(()=>{console.error("Modal.loading timed out."),w.hideLoading(!0)},s))}else if(w._loadingEl){const i=w._loadingEl.querySelector(".mojo-loading-message");i&&(i.textContent=t)}}static hideLoading(e){e?w._loadingCounter=0:w._loadingCounter--,w._loadingCounter<=0&&(w._loadingCounter=0,w._loadingTimeout&&(clearTimeout(w._loadingTimeout),w._loadingTimeout=null),w._loadingEl&&(w._loadingEl.classList.remove("show"),setTimeout(()=>{w._loadingEl&&w._loadingCounter===0&&(w._loadingEl.remove(),w._loadingEl=null)},200)))}static showBusy(e){return w.loading(e)}static hideBusy(e){return w.hideLoading(e)}}class ie{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 Pe,this.rest=U,this.modal=w,e.api&&this.rest.configure(e.api),this.router=new re({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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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=U,U.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 ie(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}`))}}class Q extends C{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=`
240
+ `,document.body.appendChild(w._loadingEl));const i=w._loadingEl.querySelector(".mojo-loading-message");i&&(i.textContent=t),requestAnimationFrame(()=>{w._loadingEl&&w._loadingEl.classList.add("show")}),s>0&&(w._loadingTimeout=setTimeout(()=>{console.error("Modal.loading timed out."),w.hideLoading(!0)},s))}else if(w._loadingEl){const i=w._loadingEl.querySelector(".mojo-loading-message");i&&(i.textContent=t)}}static hideLoading(e){e?w._loadingCounter=0:w._loadingCounter--,w._loadingCounter<=0&&(w._loadingCounter=0,w._loadingTimeout&&(clearTimeout(w._loadingTimeout),w._loadingTimeout=null),w._loadingEl&&(w._loadingEl.classList.remove("show"),setTimeout(()=>{w._loadingEl&&w._loadingCounter===0&&(w._loadingEl.remove(),w._loadingEl=null)},200)))}static showBusy(e){return w.loading(e)}static hideBusy(e){return w.hideLoading(e)}}class ie{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 Pe,this.rest=U,this.modal=w,e.api&&this.rest.configure(e.api),this.router=new ne({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},n=new s(a);return i.route&&(n.route=i.route),this.pageCache.set(e,n),console.log(`Created page: ${e} with route: ${n.route}`),n}catch(a){return console.error(`Failed to create page ${e}:`,a),null}}async showPage(e,t={},s={},i={}){const{fromRouter:a=!1,replace:n=!1,force:r=!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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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(()=>N)).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=U,U.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 ie(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}`))}}class Q extends C{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=`
241
241
  ${e}
242
242
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
243
243
  `,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=`
@@ -582,38 +582,38 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
582
582
  ${e}
583
583
  ${t}
584
584
  </form>
585
- `}isAutoSizingField(e){return!e.columns||e.columns==="auto"||e.columns===""}buildFieldsHTML(){const e=[];let t=0;for(;t<this.fields.length;){const s=this.fields[t];if(s.columns=s.columns||s.cols,s.type==="group"){const i=[s];let a=s.columns||12;typeof a=="object"&&a!==null&&(a=a.md||a.sm||a.xs||12);let r=t+1;for(;r<this.fields.length&&this.fields[r].type==="group"&&a<12;){const o=this.fields[r];let l=o.columns||12;if(typeof l=="object"&&l!==null&&(l=l.md||l.sm||l.xs||12),a+l<=12)i.push(o),a+=l,r++;else break}let n=i.length>1;if(i.length===1&&s.columns){let o=s.columns;typeof o=="object"&&o!==null&&(o=o.md||o.sm||o.xs||12),n=n||o<12}if(n){const o=i.map(l=>this.buildGroupHTML(l)).join("");e.push(`<div class="row">${o}</div>`)}else e.push(this.buildGroupHTML(s));t=r}else if(s.columns&&s.columns<12){const i=[s];let a=s.columns||12;typeof a=="object"&&a!==null&&(a=a.md||a.sm||a.xs||12);let r=t+1;for(;r<this.fields.length&&this.fields[r].columns&&a<12;){const o=this.fields[r];let l=o.columns||12;if(typeof l=="object"&&l!==null&&(l=l.md||l.sm||l.xs||12),a+l<=12)i.push(o),a+=l,r++;else break}const n=i.map(o=>this.buildFieldHTML(o)).join("");e.push(`<div class="row">${n}</div>`),t=r}else if(this.isAutoSizingField(s)){const i=[s];let a=t+1;for(;a<this.fields.length;){const r=this.fields[a];if(this.isAutoSizingField(r))i.push(r),a++;else break}if(i.length>1){const r=i.map(n=>this.buildFieldHTML(n)).join("");e.push(`<div class="row">${r}</div>`)}else e.push(`<div class="row">${this.buildFieldHTML(s)}</div>`);t=a}else e.push(this.buildFieldHTML(s)),t++}return e.join("")}buildGroupHTML(e){const{columns:t=12,title:s,fields:i=[],class:a="",titleClass:r="h6 mb-3",responsive:n={}}=e;let o=[];if(typeof t=="object"&&t!==null){if(t.xs&&o.push(`col-${t.xs}`),t.sm&&o.push(`col-sm-${t.sm}`),t.md&&o.push(`col-md-${t.md}`),t.lg&&o.push(`col-lg-${t.lg}`),t.xl&&o.push(`col-xl-${t.xl}`),t.xxl&&o.push(`col-xxl-${t.xxl}`),!t.md&&(t.xs||t.sm)){const d=t.sm||t.xs;o.push(`col-md-${d}`)}o.length===0&&o.push("col-md-12")}else o.push(`col-md-${t}`);n.xs&&o.push(`col-${n.xs}`),n.sm&&o.push(`col-sm-${n.sm}`),n.lg&&o.push(`col-lg-${n.lg}`),n.xl&&o.push(`col-xl-${n.xl}`);const l=o.join(" "),c=i.map(d=>d.type==="group"?this.buildGroupHTML(d):this.buildFieldHTML(d)).join("");return`
585
+ `}isAutoSizingField(e){return!e.columns||e.columns==="auto"||e.columns===""}buildFieldsHTML(){const e=[];let t=0;for(;t<this.fields.length;){const s=this.fields[t];if(s.columns=s.columns||s.cols,s.type==="group"){const i=[s];let a=s.columns||12;typeof a=="object"&&a!==null&&(a=a.md||a.sm||a.xs||12);let n=t+1;for(;n<this.fields.length&&this.fields[n].type==="group"&&a<12;){const o=this.fields[n];let l=o.columns||12;if(typeof l=="object"&&l!==null&&(l=l.md||l.sm||l.xs||12),a+l<=12)i.push(o),a+=l,n++;else break}let r=i.length>1;if(i.length===1&&s.columns){let o=s.columns;typeof o=="object"&&o!==null&&(o=o.md||o.sm||o.xs||12),r=r||o<12}if(r){const o=i.map(l=>this.buildGroupHTML(l)).join("");e.push(`<div class="row">${o}</div>`)}else e.push(this.buildGroupHTML(s));t=n}else if(s.columns&&s.columns<12){const i=[s];let a=s.columns||12;typeof a=="object"&&a!==null&&(a=a.md||a.sm||a.xs||12);let n=t+1;for(;n<this.fields.length&&this.fields[n].columns&&a<12;){const o=this.fields[n];let l=o.columns||12;if(typeof l=="object"&&l!==null&&(l=l.md||l.sm||l.xs||12),a+l<=12)i.push(o),a+=l,n++;else break}const r=i.map(o=>this.buildFieldHTML(o)).join("");e.push(`<div class="row">${r}</div>`),t=n}else if(this.isAutoSizingField(s)){const i=[s];let a=t+1;for(;a<this.fields.length;){const n=this.fields[a];if(this.isAutoSizingField(n))i.push(n),a++;else break}if(i.length>1){const n=i.map(r=>this.buildFieldHTML(r)).join("");e.push(`<div class="row">${n}</div>`)}else e.push(`<div class="row">${this.buildFieldHTML(s)}</div>`);t=a}else e.push(this.buildFieldHTML(s)),t++}return e.join("")}buildGroupHTML(e){const{columns:t=12,title:s,fields:i=[],class:a="",titleClass:n="h6 mb-3",responsive:r={}}=e;let o=[];if(typeof t=="object"&&t!==null){if(t.xs&&o.push(`col-${t.xs}`),t.sm&&o.push(`col-sm-${t.sm}`),t.md&&o.push(`col-md-${t.md}`),t.lg&&o.push(`col-lg-${t.lg}`),t.xl&&o.push(`col-xl-${t.xl}`),t.xxl&&o.push(`col-xxl-${t.xxl}`),!t.md&&(t.xs||t.sm)){const d=t.sm||t.xs;o.push(`col-md-${d}`)}o.length===0&&o.push("col-md-12")}else o.push(`col-md-${t}`);r.xs&&o.push(`col-${r.xs}`),r.sm&&o.push(`col-sm-${r.sm}`),r.lg&&o.push(`col-lg-${r.lg}`),r.xl&&o.push(`col-xl-${r.xl}`);const l=o.join(" "),c=i.map(d=>d.type==="group"?this.buildGroupHTML(d):this.buildFieldHTML(d)).join("");return`
586
586
  <div class="${l}">
587
587
  <div class="mojo-form-group ${a}">
588
- ${s?`<div class="${r}">${this.escapeHtml(s)}</div>`:""}
588
+ ${s?`<div class="${n}">${this.escapeHtml(s)}</div>`:""}
589
589
  <div class="row">
590
590
  ${c}
591
591
  </div>
592
592
  </div>
593
593
  </div>
594
- `}buildFieldHTML(e){const{type:t,columns:s,class:i=""}=e;let a="";const r=R&&typeof R.getRenderer=="function"?R.getRenderer(t):null;if(typeof r=="function")try{const c=r(this,e);c!=null&&(a=String(c))}catch(c){console.error("FormPlugins custom renderer error:",c)}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;this.isAutoSizingField(e)?n=`col ${i}`.trim():n=`col-${s} ${i}`.trim();let o="",l="";if(e.showWhen){const c=e.showWhen,d=Array.isArray(c.value)?c.value:[c.value];o=` data-show-when-field="${c.field}" data-show-when-value="${d.join(",")}"`,c.negate&&(o+=' data-show-when-negate="true"');let h=this.data[c.field];if(h==null){const g=this._findField(c.field,this.fields);g&&(h=g.value)}const m=String(h??""),p=d.map(String).includes(m);(c.negate?!p:p)||(l=' style="display:none"')}return`<div class="${n}"${o}${l}>${a}</div>`}_findField(e,t){for(const s of t){if(s.name===e)return s;if(s.fields){const i=this._findField(e,s.fields);if(i)return i}if(s.tabs){for(const i of s.tabs)if(i.fields){const a=this._findField(e,i.fields);if(a)return a}}}return null}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(([v,S])=>`${v}="${this.escapeHtml(S)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):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 v={...b,showToggle:!!e.showToggle,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return T.render(this.templates.password,v)}if(t==="password"){const v={...b,showToggle:e.showToggle!==!1,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return T.render(this.templates.password,v)}return T.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(([S,F])=>`${S}="${this.escapeHtml(F)}"`).join(" "),b=this.getFieldId(t),v={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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:f?this.escapeHtml(f):null,rows:l||3,required:r,disabled:n,readonly:o,showCopy:!!e.showCopy,attrs:y};return T.render(this.templates.textarea,v)}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(([v,S])=>`${v}="${this.escapeHtml(S)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:p?this.escapeHtml(p):null,rows:l||5,required:r,disabled:n,readonly:o,attrs:g,showCopy:!!e.showCopy};return T.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(([v,S])=>`${v}="${this.escapeHtml(S)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:f?this.escapeHtml(f):null,rows:o||3,required:a,disabled:r,readonly:n,attrs:y};return T.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,v=`form-select ${c}`.trim(),S=this.errors[t],D=this.getFieldValue(t)??a,V=Object.entries(d).map(([k,q])=>`${k}="${this.escapeHtml(q)}"`).join(" "),P=this.getFieldId(t);let I=[...i];if(m!==void 0&&p!==void 0){const k=f!==void 0?f:1,q=this.generateSelectOptions(m,p,k,{format:g,prefix:y,suffix:b});I=[...I,...q]}let Z="";Array.isArray(I)&&(Z=I.map(k=>{if(typeof k=="string"){const q=k===D?"selected":"";return`<option value="${this.escapeHtml(k)}" ${q}>${this.escapeHtml(k)}</option>`}else if(k&&typeof k=="object"){const q=k.value==D?"selected":"";return`<option value="${this.escapeHtml(k.value)}" ${q}>${this.escapeHtml(k.label||k.text||k.value)}</option>`}return""}).join(""));const xe=l?`
594
+ `}buildFieldHTML(e){const{type:t,columns:s,class:i=""}=e;let a="";const n=R&&typeof R.getRenderer=="function"?R.getRenderer(t):null;if(typeof n=="function")try{const c=n(this,e);c!=null&&(a=String(c))}catch(c){console.error("FormPlugins custom renderer error:",c)}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 r;this.isAutoSizingField(e)?r=`col ${i}`.trim():r=`col-${s} ${i}`.trim();let o="",l="";if(e.showWhen){const c=e.showWhen,d=Array.isArray(c.value)?c.value:[c.value];o=` data-show-when-field="${c.field}" data-show-when-value="${d.join(",")}"`,c.negate&&(o+=' data-show-when-negate="true"');let h=this.data[c.field];if(h==null){const g=this._findField(c.field,this.fields);g&&(h=g.value)}const m=String(h??""),p=d.map(String).includes(m);(c.negate?!p:p)||(l=' style="display:none"')}return`<div class="${r}"${o}${l}>${a}</div>`}_findField(e,t){for(const s of t){if(s.name===e)return s;if(s.fields){const i=this._findField(e,s.fields);if(i)return i}if(s.tabs){for(const i of s.tabs)if(i.fields){const a=this._findField(e,i.fields);if(a)return a}}}return null}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,n=[];return t!==void 0&&n.push(`min="${t}"`),s!==void 0&&n.push(`max="${s}"`),i!==void 0&&n.push(`step="${i}"`),this.renderInputField({...a,attributes:{...a.attributes,...n.reduce((r,o)=>{const[l,c]=o.split("=");return r[l]=c.replace(/"/g,""),r},{})}},"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,...n}=e;let r,o,l,c,d;switch(t){case"color":r=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":r=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":r="^[0-9A-Fa-f]+$",o=i||1,l=a||64,c="ABCDEF123456",d=d||"Only hexadecimal characters (0-9, A-F) allowed";break;default:r=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={...n,pattern:r,minLength:o,maxLength:l,placeholder:n.placeholder||c,help:n.help||d,attributes:{"data-hex-type":t,"data-allow-prefix":s,style:"text-transform: uppercase;",...n.attributes}};return this.renderInputField(h,"text")}renderInputField(e,t="text"){const{name:s,label:i,value:a="",placeholder:n="",required:r=!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(([v,S])=>`${v}="${this.escapeHtml(S)}"`).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:n?this.escapeHtml(n):null,help:h?this.escapeHtml(h):null,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:p?this.escapeHtml(p):null,required:r,disabled:o,readonly:l,attrs:g,showCopy:!!e.showCopy};if(t==="password"&&(e.showToggle||e.strengthMeter||e.capsLockWarning)){const v={...b,showToggle:!!e.showToggle,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return T.render(this.templates.password,v)}if(t==="password"){const v={...b,showToggle:e.showToggle!==!1,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return T.render(this.templates.password,v)}return T.render(this.templates.input,b)}renderTextareaField(e){const{name:t,label:s,value:i="",placeholder:a="",required:n=!1,disabled:r=!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(([S,F])=>`${S}="${this.escapeHtml(F)}"`).join(" "),b=this.getFieldId(t),v={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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:f?this.escapeHtml(f):null,rows:l||3,required:n,disabled:r,readonly:o,showCopy:!!e.showCopy,attrs:y};return T.render(this.templates.textarea,v)}renderHtmlPreviewField(e){const{name:t,label:s,value:i="",placeholder:a="",required:n=!1,disabled:r=!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(([v,S])=>`${v}="${this.escapeHtml(S)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:p?this.escapeHtml(p):null,rows:l||5,required:n,disabled:r,readonly:o,attrs:g,showCopy:!!e.showCopy};return T.render(this.templates.htmlpreview,b)}renderJsonField(e){const{name:t,label:s,placeholder:i="",required:a=!1,disabled:n=!1,readonly:r=!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(([v,S])=>`${v}="${this.escapeHtml(S)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:f?this.escapeHtml(f):null,rows:o||3,required:a,disabled:n,readonly:r,attrs:y};return T.render(this.templates.textarea,b)}renderSelectField(e){const{name:t,label:s,options:i=[],value:a="",required:n=!1,disabled:r=!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,v=`form-select ${c}`.trim(),S=this.errors[t],D=this.getFieldValue(t)??a,V=Object.entries(d).map(([k,q])=>`${k}="${this.escapeHtml(q)}"`).join(" "),P=this.getFieldId(t);let I=[...i];if(m!==void 0&&p!==void 0){const k=f!==void 0?f:1,q=this.generateSelectOptions(m,p,k,{format:g,prefix:y,suffix:b});I=[...I,...q]}let Z="";Array.isArray(I)&&(Z=I.map(k=>{if(typeof k=="string"){const q=k===D?"selected":"";return`<option value="${this.escapeHtml(k)}" ${q}>${this.escapeHtml(k)}</option>`}else if(k&&typeof k=="object"){const q=k.value==D?"selected":"";return`<option value="${this.escapeHtml(k.value)}" ${q}>${this.escapeHtml(k.label||k.text||k.value)}</option>`}return""}).join(""));const xe=l?`
595
595
  <input type="text"
596
596
  class="form-control form-control-sm mb-2"
597
597
  placeholder="Search options..."
598
598
  data-filter="live-search"
599
599
  data-change-action="filter-select-options"
600
600
  data-target="${P}">
601
- `:"",$e={labelClass:this.options.labelClass,inputClass:v,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:P,name:t,label:s?this.escapeHtml(s):null,help:h?this.escapeHtml(h):null,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:S?this.escapeHtml(S):null,searchInput:l?xe:null,optionsHTML:Z,required:r,disabled:n,multiple:o,attrs:V};return T.render(this.templates.select,$e)}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`
601
+ `:"",$e={labelClass:this.options.labelClass,inputClass:v,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:P,name:t,label:s?this.escapeHtml(s):null,help:h?this.escapeHtml(h):null,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:S?this.escapeHtml(S):null,searchInput:l?xe:null,optionsHTML:Z,required:n,disabled:r,multiple:o,attrs:V};return T.render(this.templates.select,$e)}renderMultiSelectField(e){const{name:t,label:s,options:i=[],value:a=[],required:n=!1,disabled:r=!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`
602
602
  <div class="multiselect-placeholder"
603
603
  data-field-name="${t}"
604
604
  data-field-type="multiselect"
605
- data-field-config='${JSON.stringify({name:t,value:h,placeholder:c,maxHeight:o,disabled:n,required:r})}'>
605
+ data-field-config='${JSON.stringify({name:t,value:h,placeholder:c,maxHeight:o,disabled:r,required:n})}'>
606
606
  <input type="hidden" name="${t}" value="${this.escapeHtml(JSON.stringify(h))}">
607
607
  <select class="form-select${d?" is-invalid":""}"
608
608
  multiple
609
- ${n?"disabled":""}
610
- ${r?"required":""}>
609
+ ${r?"disabled":""}
610
+ ${n?"required":""}>
611
611
  ${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("")}
612
612
  </select>
613
613
  <small class="form-text text-muted">This will be enhanced with MultiSelectDropdown component</small>
614
614
  </div>
615
- `}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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:c?this.escapeHtml(c):null,value:this.escapeHtml(i),fieldClass:n,checked:h,required:a,disabled:r,attrs:m};return T.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,v])=>`${b}="${this.escapeHtml(v)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:d?this.escapeHtml(d):null,value:this.escapeHtml(i),sizeClass:g,fieldClass:o,checked:m,required:a,disabled:r,attrs:p};return T.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,v=typeof f=="string"?f:f.label||f.text||f.value,S=b===h?"checked":"";return`
616
- <div class="form-check ${n?"form-check-inline":""}">
615
+ `}renderCheckboxField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:n=!1,class:r="",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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:c?this.escapeHtml(c):null,value:this.escapeHtml(i),fieldClass:r,checked:h,required:a,disabled:n,attrs:m};return T.render(this.templates.checkbox,f)}renderSwitchField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:n=!1,size:r="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,v])=>`${b}="${this.escapeHtml(v)}"`).join(" "),f=this.getFieldId(t),g=r!=="md"?`form-switch-${r}`:"",y={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:f,name:t,label:this.escapeHtml(s),help:c?this.escapeHtml(c):null,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:d?this.escapeHtml(d):null,value:this.escapeHtml(i),sizeClass:g,fieldClass:o,checked:m,required:a,disabled:n,attrs:p};return T.render(this.templates.switch,y)}renderRadioField(e){const{name:t,label:s,options:i=[],value:a="",disabled:n=!1,inline:r=!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,v=typeof f=="string"?f:f.label||f.text||f.value,S=b===h?"checked":"";return`
616
+ <div class="form-check ${r?"form-check-inline":""}">
617
617
  <input
618
618
  type="radio"
619
619
  id="${y}"
@@ -621,7 +621,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
621
621
  class="form-check-input ${d?"is-invalid":""}"
622
622
  value="${this.escapeHtml(b)}"
623
623
  ${S}
624
- ${r?"disabled":""}
624
+ ${n?"disabled":""}
625
625
 
626
626
  ${m}
627
627
  >
@@ -640,13 +640,13 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
640
640
  ${c?`<div class="${this.options.helpClass}">${this.escapeHtml(c)}</div>`:""}
641
641
  ${d?`<div class="${this.options.errorClass}">${this.escapeHtml(d)}</div>`:""}
642
642
  </div>
643
- `}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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:h?this.escapeHtml(h):null,accept:n,required:i,disabled:a,multiple:r,attrs:m};return T.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"}},v=b[c]||b.md,S=Object.entries(o).map(([P,I])=>`${P}="${this.escapeHtml(I)}"`).join(" "),F=this.getFieldValue(t),D=this.extractImageUrl(F,c),V={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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:p?this.escapeHtml(p):null,dropZoneId:g,previewId:y,containerClass:v.containerClass,width:v.width,height:v.height,accept:r,imageUrl:D,placeholderText:a?"No image":this.escapeHtml(h),cursor:a?"default":"pointer",allowDrop:d,showRemove:!a,required:i,disabled:a,attrs:S};return T.render(this.templates.image,V)}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,v])=>`${b}="${this.escapeHtml(v)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:m?this.escapeHtml(m):null,required:r,disabled:n,readonly:o,attrs:f};return T.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,v])=>`${b}="${this.escapeHtml(v)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:m?this.escapeHtml(m):null,min:i,max:a,step:r,fieldValue:p,disabled:o,attrs:f};return T.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`
643
+ `}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:n=!1,accept:r="*/*",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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:h?this.escapeHtml(h):null,accept:r,required:i,disabled:a,multiple:n,attrs:m};return T.render(this.templates.file,f)}renderImageField(e){const{name:t,label:s,required:i=!1,disabled:a=!1,accept:n="image/*",class:r="",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} ${r}`.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"}},v=b[c]||b.md,S=Object.entries(o).map(([P,I])=>`${P}="${this.escapeHtml(I)}"`).join(" "),F=this.getFieldValue(t),D=this.extractImageUrl(F,c),V={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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:p?this.escapeHtml(p):null,dropZoneId:g,previewId:y,containerClass:v.containerClass,width:v.width,height:v.height,accept:n,imageUrl:D,placeholderText:a?"No image":this.escapeHtml(h),cursor:a?"default":"pointer",allowDrop:d,showRemove:!a,required:i,disabled:a,attrs:S};return T.render(this.templates.image,V)}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:n=!1,disabled:r=!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,v])=>`${b}="${this.escapeHtml(v)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:m?this.escapeHtml(m):null,required:n,disabled:r,readonly:o,attrs:f};return T.render(this.templates.color,y)}renderRangeField(e){const{name:t,label:s,min:i=0,max:a=100,step:n=1,value:r=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)??r,f=Object.entries(c).map(([b,v])=>`${b}="${this.escapeHtml(v)}"`).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,tooltip:e.tooltip?this.escapeHtml(e.tooltip):null,error:m?this.escapeHtml(m):null,min:i,max:a,step:n,fieldValue:p,disabled:o,attrs:f};return T.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:n="btn-secondary",disabled:r=!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`
644
644
  <button
645
645
  type="button"
646
646
  ${t?`name="${t}"`:""}
647
- class="btn ${r}"
647
+ class="btn ${n}"
648
648
  ${l?`data-action="${l}"`:""}
649
- ${n?"disabled":""}
649
+ ${r?"disabled":""}
650
650
  ${c}
651
651
  >
652
652
  ${this.escapeHtml(s)}
@@ -660,23 +660,23 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
660
660
  <div class="form-html ${s}">
661
661
  ${t}
662
662
  </div>
663
- `}renderHeaderField(e){const{text:t="",level:s=3,class:i="",id:a=""}=e,r=Math.max(1,Math.min(6,parseInt(s))),n=a?` id="${this.escapeHtml(a)}"`:"",o=i?` class="${this.escapeHtml(i)}"`:"";return`<h${r}${n}${o}>${this.escapeHtml(t)}</h${r}>`}buildButtonsHTML(){if(!this.options.submitButton&&!this.options.resetButton&&!this.buttons.length)return"";let e="";if(this.buttons.forEach(t=>{e+=this.renderButton(t)+" "}),this.options.submitButton){let t="Submit";typeof this.options.submitButton=="string"?t=this.options.submitButton:this.options.submitButton===!0&&(t="Submit"),e+=`<button type="submit" class="btn btn-primary me-2" data-action="submit-form">${t}</button>`}if(this.options.resetButton){let t="Reset";typeof this.options.resetButton=="string"?t=this.options.resetButton:this.options.resetButton===!0&&(t="Reset"),e+=`<button type="button" class="btn btn-secondary" data-action="reset-form">${t}</button>`}return e?`
663
+ `}renderHeaderField(e){const{text:t="",level:s=3,class:i="",id:a=""}=e,n=Math.max(1,Math.min(6,parseInt(s))),r=a?` id="${this.escapeHtml(a)}"`:"",o=i?` class="${this.escapeHtml(i)}"`:"";return`<h${n}${r}${o}>${this.escapeHtml(t)}</h${n}>`}buildButtonsHTML(){if(!this.options.submitButton&&!this.options.resetButton&&!this.buttons.length)return"";let e="";if(this.buttons.forEach(t=>{e+=this.renderButton(t)+" "}),this.options.submitButton){let t="Submit";typeof this.options.submitButton=="string"?t=this.options.submitButton:this.options.submitButton===!0&&(t="Submit"),e+=`<button type="submit" class="btn btn-primary me-2" data-action="submit-form">${t}</button>`}if(this.options.resetButton){let t="Reset";typeof this.options.resetButton=="string"?t=this.options.resetButton:this.options.resetButton===!0&&(t="Reset"),e+=`<button type="button" class="btn btn-secondary" data-action="reset-form">${t}</button>`}return e?`
664
664
  <div class="form-actions mt-3">
665
665
  ${e}
666
666
  </div>
667
- `:""}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`
667
+ `:""}getFieldValue(e){return this.structureOnly?"":x.getContextData(this.data,e)}renderTagField(e){const{name:t,label:s,value:i="",placeholder:a="Add tags...",required:n=!1,disabled:r=!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`
668
668
  <div class="mojo-form-control">
669
- ${s?`<label for="${m}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
669
+ ${s?`<label for="${m}" class="${this.options.labelClass}">${this.escapeHtml(s)}${n?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
670
670
  <div class="tag-input-placeholder"
671
671
  data-field-name="${t}"
672
672
  data-field-type="tag"
673
- data-field-config='${JSON.stringify({name:t,value:f,placeholder:a,maxTags:l,allowDuplicates:c,separator:d,disabled:n,readonly:o,required:r})}'>
673
+ data-field-config='${JSON.stringify({name:t,value:f,placeholder:a,maxTags:l,allowDuplicates:c,separator:d,disabled:r,readonly:o,required:n})}'>
674
674
  <input type="text"
675
675
  id="${m}"
676
676
  name="${t}_display"
677
677
  class="${this.options.inputClass}${p?" is-invalid":""}"
678
678
  placeholder="${this.escapeHtml(a)}"
679
- ${n?"disabled":""}
679
+ ${r?"disabled":""}
680
680
  ${o?"readonly":""}
681
681
 
682
682
  <input type="hidden" name="${t}" value="${this.escapeHtml(f)}">
@@ -685,19 +685,19 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
685
685
  ${h?`<div class="${this.options.helpClass}">${this.escapeHtml(h)}</div>`:""}
686
686
  ${p?`<div class="${this.options.errorClass}">${this.escapeHtml(p)}</div>`:""}
687
687
  </div>
688
- `}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],v=this.getFieldValue(t)??i;return`
688
+ `}renderCollectionField(e){const{name:t,label:s,value:i="",placeholder:a="Search...",required:n=!1,disabled:r=!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],v=this.getFieldValue(t)??i;return`
689
689
  <div class="mojo-form-control">
690
- ${s?`<label for="${y}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
690
+ ${s?`<label for="${y}" class="${this.options.labelClass}">${this.escapeHtml(s)}${n?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
691
691
  <div class="collection-select-placeholder"
692
692
  data-field-name="${t}"
693
693
  data-field-type="collection"
694
- data-field-config='${JSON.stringify({name:t,value:v,placeholder:a,labelField:c,valueField:d,maxItems:h,emptyFetch:m,debounceMs:p,disabled:n,readonly:o,required:r,requiresActiveGroup:f})}'>
694
+ data-field-config='${JSON.stringify({name:t,value:v,placeholder:a,labelField:c,valueField:d,maxItems:h,emptyFetch:m,debounceMs:p,disabled:r,readonly:o,required:n,requiresActiveGroup:f})}'>
695
695
  <input type="text"
696
696
  id="${y}"
697
697
  name="${t}_display"
698
698
  class="${this.options.inputClass}${b?" is-invalid":""}"
699
699
  placeholder="${this.escapeHtml(a)}"
700
- ${n?"disabled":""}
700
+ ${r?"disabled":""}
701
701
  ${o?"readonly":""}
702
702
 
703
703
  <input type="hidden" name="${t}" value="${this.escapeHtml(v)}">
@@ -706,26 +706,26 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
706
706
  ${g?`<div class="${this.options.helpClass}">${this.escapeHtml(g)}</div>`:""}
707
707
  ${b?`<div class="${this.options.errorClass}">${this.escapeHtml(b)}</div>`:""}
708
708
  </div>
709
- `}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:v=!1,help:S=e.helpText||e.help||""}=e;this.getFieldId(t);const F=this.errors[t],D=this.getFieldValue(t)??i;return`
709
+ `}renderCollectionMultiSelectField(e){const{name:t,label:s,value:i=[],required:a=!1,disabled:n=!1,Collection:r,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:v=!1,help:S=e.helpText||e.help||""}=e;this.getFieldId(t);const F=this.errors[t],D=this.getFieldValue(t)??i;return`
710
710
  <div class="mojo-form-control">
711
711
  ${s?`<label class="${this.options.labelClass}">${this.escapeHtml(s)}${a?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
712
712
  <div class="collection-multiselect-placeholder"
713
713
  data-field-name="${t}"
714
714
  data-field-type="collectionmultiselect"
715
- data-field-config='${JSON.stringify({name:t,value:D,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:v})}'>
715
+ data-field-config='${JSON.stringify({name:t,value:D,labelField:l,valueField:c,excludeIds:d,ignoreIds:h,size:m,maxHeight:p,showSelectAll:f,enableSearch:g,searchPlaceholder:y,searchDebounce:b,disabled:n,required:a,requiresActiveGroup:v})}'>
716
716
  <input type="hidden" name="${t}" value="${this.escapeHtml(JSON.stringify(D))}">
717
717
  <small class="form-text text-muted">This will be enhanced with CollectionMultiSelect component</small>
718
718
  </div>
719
719
  ${S?`<div class="${this.options.helpClass}">${this.escapeHtml(S)}</div>`:""}
720
720
  ${F?`<div class="${this.options.errorClass}">${this.escapeHtml(F)}</div>`:""}
721
721
  </div>
722
- `}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`
722
+ `}renderDatePickerField(e){const{name:t,label:s,value:i="",placeholder:a="Select date...",required:n=!1,disabled:r=!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`
723
723
  <div class="mojo-form-control">
724
- ${s?`<label for="${p}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
724
+ ${s?`<label for="${p}" class="${this.options.labelClass}">${this.escapeHtml(s)}${n?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
725
725
  <div class="date-picker-placeholder"
726
726
  data-field-name="${t}"
727
727
  data-field-type="datepicker"
728
- 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})}'>
728
+ data-field-config='${JSON.stringify({name:t,value:g,placeholder:a,min:l,max:c,format:d,displayFormat:h,disabled:r,readonly:o,required:n})}'>
729
729
  <input type="date"
730
730
  id="${p}"
731
731
  name="${t}"
@@ -734,18 +734,18 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
734
734
  placeholder="${this.escapeHtml(a)}"
735
735
  ${l?`min="${l}"`:""}
736
736
  ${c?`max="${c}"`:""}
737
- ${n?"disabled":""}
737
+ ${r?"disabled":""}
738
738
  ${o?"readonly":""}
739
- ${r?"required":""}
739
+ ${n?"required":""}
740
740
 
741
741
  <small class="form-text text-muted">This will be enhanced with Easepick DatePicker</small>
742
742
  </div>
743
743
  ${m?`<div class="${this.options.helpClass}">${this.escapeHtml(m)}</div>`:""}
744
744
  ${f?`<div class="${this.options.errorClass}">${this.escapeHtml(f)}</div>`:""}
745
745
  </div>
746
- `}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:v=e.helpText||e.help||""}=e,S=this.getFieldId(t||s||"daterange"),F=this.errors[t],D=s||(t?t+"_start":""),V=i||(t?t+"_end":""),P=this.getFieldValue(D)||n,I=this.getFieldValue(V)||o;return`
746
+ `}renderDateRangeField(e){const{name:t,startName:s,endName:i,fieldName:a,label:n,startDate:r="",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:v=e.helpText||e.help||""}=e,S=this.getFieldId(t||s||"daterange"),F=this.errors[t],D=s||(t?t+"_start":""),V=i||(t?t+"_end":""),P=this.getFieldValue(D)||r,I=this.getFieldValue(V)||o;return`
747
747
  <div class="mojo-form-control">
748
- ${r?`<label for="${S}" class="${this.options.labelClass}">${this.escapeHtml(r)}${c?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
748
+ ${n?`<label for="${S}" class="${this.options.labelClass}">${this.escapeHtml(n)}${c?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
749
749
  <div class="date-range-picker-placeholder"
750
750
  data-field-name="${t||s||"daterange"}"
751
751
  data-field-type="daterange"
@@ -788,25 +788,25 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
788
788
  ${v?`<div class="${this.options.helpClass}">${this.escapeHtml(v)}</div>`:""}
789
789
  ${F?`<div class="${this.options.errorClass}">${this.escapeHtml(F)}</div>`:""}
790
790
  </div>
791
- `}renderChecklistDropdownField(e){const t=this.getFieldId(e.name),s=this.getFieldValue(e.name)??[],i={fieldId:t,fieldName:e.name,buttonText:e.buttonText||"Select Options",buttonIcon:e.buttonIcon||"bi-chevron-down",buttonClass:e.buttonClass||"btn btn-outline-secondary btn-sm dropdown-toggle",dropdownClass:e.dropdownClass||"dropdown-menu p-2",minWidth:e.minWidth||"200px",options:e.options.map(a=>({value:a.value,label:a.label,id:`${e.name}-${a.value}`,checked:s.includes(a.value)}))};return T.render(this.templates.checklistdropdown,i)}renderButtonGroupField(e){const t=this.getFieldId(e.name),s=this.getFieldValue(e.name)??e.value,i={fieldId:t,fieldName:e.name,size:e.size||"sm",variant:e.variant||"outline-primary",options:e.options.map(a=>({value:a.value,label:a.label,action:a.action,active:a.value===s,buttonClass:this.getButtonClass(a.value===s,e.variant)}))};return T.render(this.templates.buttongroup,i)}getButtonClass(e,t="outline-primary"){return e?`btn btn-${t.replace("outline-","")}`:`btn btn-${t}`}renderComboField(e){const{name:t,label:s,value:i="",required:a=!1,disabled:r=!1,maxHeight:n=300,help:o=e.helpText||e.help||""}=e,l=e.placeholder||e.placeHolder||"Type or select...",c=e.allowCustom!==!1;this.getFieldId(t);const d=this.errors[t],h=e.value??this.getFieldValue(t)??i;return`
791
+ `}renderChecklistDropdownField(e){const t=this.getFieldId(e.name),s=this.getFieldValue(e.name)??[],i={fieldId:t,fieldName:e.name,buttonText:e.buttonText||"Select Options",buttonIcon:e.buttonIcon||"bi-chevron-down",buttonClass:e.buttonClass||"btn btn-outline-secondary btn-sm dropdown-toggle",dropdownClass:e.dropdownClass||"dropdown-menu p-2",minWidth:e.minWidth||"200px",options:e.options.map(a=>({value:a.value,label:a.label,id:`${e.name}-${a.value}`,checked:s.includes(a.value)}))};return T.render(this.templates.checklistdropdown,i)}renderButtonGroupField(e){const t=this.getFieldId(e.name),s=this.getFieldValue(e.name)??e.value,i={fieldId:t,fieldName:e.name,size:e.size||"sm",variant:e.variant||"outline-primary",options:e.options.map(a=>({value:a.value,label:a.label,action:a.action,active:a.value===s,buttonClass:this.getButtonClass(a.value===s,e.variant)}))};return T.render(this.templates.buttongroup,i)}getButtonClass(e,t="outline-primary"){return e?`btn btn-${t.replace("outline-","")}`:`btn btn-${t}`}renderComboField(e){const{name:t,label:s,value:i="",required:a=!1,disabled:n=!1,maxHeight:r=300,help:o=e.helpText||e.help||""}=e,l=e.placeholder||e.placeHolder||"Type or select...",c=e.allowCustom!==!1;this.getFieldId(t);const d=this.errors[t],h=e.value??this.getFieldValue(t)??i;return`
792
792
  <div class="mojo-form-control">
793
793
  ${s?`<label class="${this.options.labelClass}">${this.escapeHtml(s)}${a?'<span class="text-danger">*</span>':""}${this.renderTooltipIcon(e)}</label>`:""}
794
794
  <div class="combobox-placeholder"
795
795
  data-field-name="${t}"
796
796
  data-field-type="combobox"
797
- data-field-config='${JSON.stringify({name:t,value:h,placeholder:l,maxHeight:n,allowCustom:c,disabled:r,required:a})}'>
797
+ data-field-config='${JSON.stringify({name:t,value:h,placeholder:l,maxHeight:r,allowCustom:c,disabled:n,required:a})}'>
798
798
  <input type="text"
799
799
  class="form-control${d?" is-invalid":""}"
800
800
  value="${this.escapeHtml(h)}"
801
801
  placeholder="${this.escapeHtml(l)}"
802
- ${r?"disabled":""}
802
+ ${n?"disabled":""}
803
803
  ${a?"required":""}>
804
804
  <small class="form-text text-muted">This will be enhanced with ComboBox component</small>
805
805
  </div>
806
806
  ${o?`<div class="${this.options.helpClass}">${this.escapeHtml(o)}</div>`:""}
807
807
  ${d?`<div class="${this.options.errorClass}">${this.escapeHtml(d)}</div>`:""}
808
808
  </div>
809
- `}renderTabsetField(e){const{tabs:t=[],name:s=`tabset-${Date.now()}`,navClass:i="nav nav-tabs mb-3",contentClass:a="tab-content"}=e,r=String(s).toLowerCase().replace(/[^a-z0-9]/g,"-"),n=t.map((l,c)=>{const d=`${r}-pane-${c}`,h=c===0;return`
809
+ `}renderTabsetField(e){const{tabs:t=[],name:s=`tabset-${Date.now()}`,navClass:i="nav nav-tabs mb-3",contentClass:a="tab-content"}=e,n=String(s).toLowerCase().replace(/[^a-z0-9]/g,"-"),r=t.map((l,c)=>{const d=`${n}-pane-${c}`,h=c===0;return`
810
810
  <li class="nav-item" role="presentation">
811
811
  <button class="nav-link ${h?"active":""}"
812
812
  id="${d}-tab"
@@ -819,7 +819,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
819
819
  ${this.escapeHtml(l.label||`Tab ${c+1}`)}
820
820
  </button>
821
821
  </li>
822
- `}).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`
822
+ `}).join(""),o=t.map((l,c)=>{const d=`${n}-pane-${c}`,h=c===0,m=(l.fields||[]).map(p=>p.type==="group"?this.buildGroupHTML(p):this.buildFieldHTML(p)).join("");return`
823
823
  <div class="tab-pane fade ${h?"show active":""}"
824
824
  id="${d}"
825
825
  role="tabpanel"
@@ -832,13 +832,13 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
832
832
  `}).join("");return`
833
833
  <div class="mojo-form-tabset">
834
834
  <ul class="${i}" role="tablist">
835
- ${n}
835
+ ${r}
836
836
  </ul>
837
837
  <div class="${a}">
838
838
  ${o}
839
839
  </div>
840
840
  </div>
841
- `}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}renderTooltipIcon(e){return!e||!e.tooltip?"":` <i class="bi bi-info-circle text-muted" data-bs-toggle="tooltip" title="${this.escapeHtml(e.tooltip)}" style="font-size: 0.75rem; cursor: help;"></i>`}}const We={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 Je(u){Object.assign(u.prototype,We)}class fe extends C{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`
841
+ `}generateSelectOptions(e,t,s=1,i={}){const{format:a,prefix:n="",suffix:r=""}=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=`${n}${d}${r}`,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}renderTooltipIcon(e){return!e||!e.tooltip?"":` <i class="bi bi-info-circle text-muted" data-bs-toggle="tooltip" title="${this.escapeHtml(e.tooltip)}" style="font-size: 0.75rem; cursor: help;"></i>`}}const We={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 Je(u){Object.assign(u.prototype,We)}class fe extends C{constructor(e={}){const{name:t,value:s="",placeholder:i="Add tags...",maxTags:a=50,allowDuplicates:n=!1,separator:r=",",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=n,this.separator=r,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`
842
842
  <div class="tag-input-container">
843
843
  <div class="tag-input-wrapper border rounded p-2"
844
844
  data-action="focus-input"
@@ -1017,7 +1017,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1017
1017
  <div class="invalid-feedback d-block">{{error}}</div>
1018
1018
  {{/error}}
1019
1019
  </div>
1020
- `,...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 Ke({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 Qe({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 et extends C{constructor(e={}){super({tagName:"div",className:"multiselect-items",template:`
1020
+ `,...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 Ke({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 Qe({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),n={label:this.getFieldValue(t,this.labelField),value:a,index:s,selected:this.selectedValues.some(r=>r==a),disabled:this.disabled,model:i};return this.itemTemplate&&(n.customContent=this.renderItemTemplate(n)),n})}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),n=Math.max(this.listView.lastClickedIndex,t),r=!this.items[t].selected;for(let o=a;o<=n;o++){const l=this.items[o];l.disabled||(r?this.selectedValues.includes(l.value)||this.selectedValues.push(l.value):this.selectedValues=this.selectedValues.filter(c=>c!=l.value),l.selected=r)}this.updateListView()}else{const a=this.items[t];a.selected?(this.selectedValues=this.selectedValues.filter(n=>n!=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 et extends C{constructor(e={}){super({tagName:"div",className:"multiselect-items",template:`
1021
1021
  {{#items.length}}
1022
1022
  <div class="multiselect-list" style="max-height: {{maxHeight}}px; overflow-y: auto;">
1023
1023
  {{#items}}
@@ -1048,7 +1048,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1048
1048
  <small>No options available</small>
1049
1049
  </div>
1050
1050
  {{/items.length}}
1051
- `,...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 tt extends C{constructor(e={}){super({tagName:"div",className:"multiselect-dropdown",template:`
1051
+ `,...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 n=t.querySelector('input[type="checkbox"]');n&&(n.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 tt extends C{constructor(e={}){super({tagName:"div",className:"multiselect-dropdown",template:`
1052
1052
  <div class="mojo-form-control">
1053
1053
  {{#label}}
1054
1054
  <label class="form-label">
@@ -1075,7 +1075,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1075
1075
  <div class="invalid-feedback d-block">{{error}}</div>
1076
1076
  {{/error}}
1077
1077
  </div>
1078
- `,...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 et({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 ge extends C{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`
1078
+ `,...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,n=typeof s=="string"?s:s.label||s.text||s.value,r=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:n,index:i,selected:this.selectedValues.includes(a),disabled:r}});this.listView=new et({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,n=typeof s=="string"?s:s.label||s.text||s.value,r=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:n,index:i,selected:this.selectedValues.includes(a),disabled:r}});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 ge extends C{constructor(e={}){const{name:t,value:s="",format:i="YYYY-MM-DD",displayFormat:a="MMM DD, YYYY",min:n=null,max:r=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=n,this.max=r,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`
1079
1079
  <div class="date-picker-container">
1080
1080
  <input
1081
1081
  type="${t}"
@@ -1094,7 +1094,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1094
1094
  />
1095
1095
  <div class="date-picker-feedback"></div>
1096
1096
  </div>
1097
- `}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 ge(e)}}class be extends C{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:v="",inputClass:S="form-control",autoApply:F=!0,inline:D=!1,separator:V=" - ",...P}=e;super({tagName:"div",className:`date-range-picker-view ${v}`,...P}),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=S,this.autoApply=F,this.inline=D,this.separator=V,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`
1097
+ `}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"),n=String(s.getDate()).padStart(2,"0");switch(t){case"YYYY-MM-DD":return`${i}-${a}-${n}`;case"MM/DD/YYYY":return`${a}/${n}/${i}`;case"DD/MM/YYYY":return`${n}/${a}/${i}`;case"MMM DD, YYYY":return`${["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][s.getMonth()]} ${n}, ${i}`;default:return`${i}-${a}-${n}`}}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 ge(e)}}class be extends C{constructor(e={}){const{name:t,startName:s,endName:i,fieldName:a,startDate:n="",endDate:r="",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:v="",inputClass:S="form-control",autoApply:F=!0,inline:D=!1,separator:V=" - ",...P}=e;super({tagName:"div",className:`date-range-picker-view ${v}`,...P}),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=S,this.autoApply=F,this.inline=D,this.separator=V,this.currentStartDate=n,this.currentEndDate=r,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):"",n=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";return`
1098
1098
  <div class="date-range-picker-container">
1099
1099
  <input
1100
1100
  type="text"
@@ -1113,7 +1113,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1113
1113
 
1114
1114
  <!-- Hidden inputs for form submission -->
1115
1115
  ${s?`<input type="hidden" name="${s}" value="${this.escapeHtml(a)}" />`:""}
1116
- ${i?`<input type="hidden" name="${i}" value="${this.escapeHtml(r)}" />`:""}
1116
+ ${i?`<input type="hidden" name="${i}" value="${this.escapeHtml(n)}" />`:""}
1117
1117
  ${this.fieldName?`<input type="hidden" name="${this.fieldName}" value="${this.escapeHtml(this.name||"")}" />`:""}
1118
1118
 
1119
1119
  <div class="date-range-picker-feedback"></div>
@@ -1164,7 +1164,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1164
1164
 
1165
1165
  <div class="date-range-picker-feedback"></div>
1166
1166
  </div>
1167
- `}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 be(e)}}class ye extends C{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`
1167
+ `}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:n}=i.detail,r=a?this.normalizeDateFromPicker(a):"",o=n?this.normalizeDateFromPicker(n):"";this.handleRangeChange(r,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,n;if(typeof e=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(e)){const r=e.split("-");s=parseInt(r[0]),i=String(parseInt(r[1])).padStart(2,"0"),a=String(parseInt(r[2])).padStart(2,"0")}else{if(e instanceof Date?n=e:n=new Date(e),isNaN(n.getTime()))return"";s=n.getFullYear(),i=String(n.getMonth()+1).padStart(2,"0"),a=String(n.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 r=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],o=parseInt(i)-1;return`${r[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,n=this.fieldName?this.element?.querySelector(`[name="${this.fieldName}"]`):null,r=this.currentStartDate?this.formatForOutput(this.currentStartDate):"",o=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";s&&(s.value=r),i&&(i.value=o),a&&(a.value=this.getDisplayValue()),n&&(n.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,n]=t.split("-").map(Number),r=new Date(i,a-1,n);return isNaN(r)?null:r}if(/^-?\d+$/.test(t)){const i=Number(t),a=t.length<=10?i*1e3:i,n=new Date(a);return isNaN(n)?null:n}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 be(e)}}class ye extends C{constructor(e={}){const{name:t,value:s="",placeholder:i="Select or type...",options:a=[],allowCustom:n=!0,showDescription:r=!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=n,this.showDescription=r,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`
1168
1168
  <div class="combo-input-container position-relative">
1169
1169
  <div class="input-wrapper position-relative">
1170
1170
  ${this.renderInput()}
@@ -1236,7 +1236,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1236
1236
  <i class="bi bi-search me-1"></i>
1237
1237
  No matching options found.
1238
1238
  </div>
1239
- `}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 ye(e)}}class st extends C{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=`
1239
+ `}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 n=t.label.toLowerCase().startsWith(e),r=s.label.toLowerCase().startsWith(e);return n&&!r?-1:!n&&r?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 ye(e)}}class st extends C{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=`
1240
1240
  <div class="combobox-container">
1241
1241
  <div class="input-group">
1242
1242
  <input type="text"
@@ -1274,11 +1274,11 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1274
1274
  {{label}}
1275
1275
  </button>
1276
1276
  {{/items}}
1277
- `}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=T.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 j extends C{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",enableTooltips:!0,...c}),R.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 X({...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)),this._initShowWhen(),R.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&&R.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 fe({...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 tt({...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 st({...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 Ze({...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 Xe({...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 ge({...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 be({...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 ye({...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}),this._updateShowWhen(e,t),R.onFieldChange?.(this,e,t))}_initShowWhen(){if(this._showWhenMap={},!this.element)return;this.element.querySelectorAll("[data-show-when-field]").forEach(t=>{const s=t.getAttribute("data-show-when-field");this._showWhenMap[s]||(this._showWhenMap[s]=[]),this._showWhenMap[s].push(t),t.style.display==="none"?t.querySelectorAll("input, select, textarea").forEach(a=>{a.required&&(a.dataset.wasRequired="true",a.required=!1)}):t.querySelectorAll("input, select, textarea").forEach(a=>{a.required&&(a.dataset.wasRequired="true")})})}_updateShowWhen(e,t){const s=this._showWhenMap?.[e];if(!s)return;const i=String(t??"");s.forEach(a=>{const r=a.getAttribute("data-show-when-value").split(","),n=a.getAttribute("data-show-when-negate")==="true",o=r.includes(i),l=n?!o:o;a.style.display=l?"":"none",a.querySelectorAll("input, select, textarea").forEach(d=>{l?d.dataset.wasRequired==="true"&&(d.required=!0):(d.required&&(d.dataset.wasRequired="true"),d.required=!1)})})}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 it(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(()=>N)).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())}),this.element&&this.element.querySelectorAll("[data-show-when-field]").forEach(o=>{o.style.display==="none"&&o.querySelectorAll("[name]").forEach(c=>{delete s[c.name]})});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=`
1277
+ `}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=T.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 j extends C{constructor(e={}){const{formConfig:t=e.config,fields:s,model:i=null,data:a={},defaults:n=null,errors:r={},fileHandling:o="base64",autosaveModelField:l=!1,...c}=e;super({tagName:"div",className:"form-view",enableTooltips:!0,...c}),R.onFormViewInit?.(this),this.model=i,this.defaults=n||a,this._originalData=a,this.errors=r,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 X({...this.getFormConfig(),data:this.data,errors:r})}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)),this._initShowWhen(),R.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&&R.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(n=>n.value);else if(t.type==="file"){const n=t.getAttribute("data-change-action");if(n==="image-selected"){this.onChangeImageSelected(s,t);return}else if(n==="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),n=new fe({...a,containerId:null});n.render(!0,t),this.customComponents.set(s,n),n.on("change",r=>{this.handleFieldChange(s,r.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),n=this.getFormFieldConfig(s);if(!n)return;const r=new tt({...a,options:n.options||[],placeholder:n.placeholder||a.placeholder||"Select...",label:n.label,containerId:null});let o=a.value??x.getContextData(this.data,s);o&&r.setFormValue(o),r.render(!0,t),this.customComponents.set(s,r),r.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),n=this.getFormFieldConfig(s);if(!n)return;const r=new st({...a,options:n.options||[],placeholder:n.placeholder||a.placeholder||"Type or select...",containerId:null});let o=a.value??x.getContextData(this.data,s);o&&r.setFormValue(o),r.render(!0,t),this.customComponents.set(s,r),r.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),n=this.getFormFieldConfig(s);if(!n||!n.Collection)return;const r=new n.Collection;n.collectionParams&&(r.params={...r.params,...n.collectionParams});const o=new Ze({...a,collection:r,defaultParams:n.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),n=this.getFormFieldConfig(s);if(!n||!n.Collection)return;const r=new n.Collection;n.collectionParams&&(r.params={...r.params,...n.collectionParams});const o=new Xe({...a,collection:r,defaultParams:n.defaultParams||null,itemTemplate:n.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),n=new ge({...a,containerId:null});n.render(!0,t),this.customComponents.set(s,n),n.on("change",r=>{this.handleFieldChange(s,r.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),n=new be({...a,containerId:null});n.render(!0,t),this.customComponents.set(s,n),n.on("change",r=>{this.handleFieldChange(s,r.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),n=new ye({...a,containerId:null});let r=x.getContextData(this.data,s);r&&n.setValue(r),n.render(!0,t),this.customComponents.set(s,n),n.on("change",o=>{this.handleFieldChange(s,o.value)}),n.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}),this._updateShowWhen(e,t),R.onFieldChange?.(this,e,t))}_initShowWhen(){if(this._showWhenMap={},!this.element)return;this.element.querySelectorAll("[data-show-when-field]").forEach(t=>{const s=t.getAttribute("data-show-when-field");this._showWhenMap[s]||(this._showWhenMap[s]=[]),this._showWhenMap[s].push(t),t.style.display==="none"?t.querySelectorAll("input, select, textarea").forEach(a=>{a.required&&(a.dataset.wasRequired="true",a.required=!1)}):t.querySelectorAll("input, select, textarea").forEach(a=>{a.required&&(a.dataset.wasRequired="true")})})}_updateShowWhen(e,t){const s=this._showWhenMap?.[e];if(!s)return;const i=String(t??"");s.forEach(a=>{const n=a.getAttribute("data-show-when-value").split(","),r=a.getAttribute("data-show-when-negate")==="true",o=n.includes(i),l=r?!o:o;a.style.display=l?"":"none",a.querySelectorAll("input, select, textarea").forEach(d=>{l?d.dataset.wasRequired==="true"&&(d.required=!0):(d.required&&(d.dataset.wasRequired="true"),d.required=!1)})})}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 n=this.getFormFieldConfig(s);n?this.setFieldValue(a,n,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 it(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(()=>N)).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(r=>{r.classList.remove("active"),r.classList.add("btn-outline-primary"),r.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 n=[];i.forEach(o=>{o.checked&&n.push(o.value)}),this.data[a]=n;const r=s.querySelector('[data-bs-toggle="dropdown"]');r&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(r)?.hide(),this.emit("field:changed",{field:a,value:n,form:this}),this.emit("change",{field:a,value:n,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 n=URL.createObjectURL(i);if(console.log("FormView: previewUrl created:",n),a&&a.imageSize){console.log("FormView: Image cropping is required, imageSize:",a.imageSize);try{const r=window.MOJO?.plugins?.ImageCropView;if(console.log("FormView: ImageCropView available?",!!r),!r){console.log("FormView: ImageCropView not available, falling back to normal handling"),this.data[s]=i,await this.updateImagePreview(s,n),this.emit("image:selected",{field:s,file:i,form:this});return}const o=await r.showDialog(n,{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(r){console.error("FormView: Error during image cropping:",r),console.log("FormView: Falling back to normal image handling after error"),this.data[s]=i,await this.updateImagePreview(s,n),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,n),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 n=this.element.querySelector(`#${a}`);n&&(n.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(r=>{const o=r.textContent.toLowerCase();r.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 n=e[0],r=this.element.querySelector(`input[name="${a}"]`);if(r){const l=new DataTransfer;l.items.add(n),r.files=l.files,r.dispatchEvent(new Event("change",{bubbles:!0}))}this.data[a]=n;const o=URL.createObjectURL(n);await this.updateImagePreview(a,o),this.emit("image:dropped",{field:a,file:n,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[r,o]of t.entries())s[r]?(Array.isArray(s[r])||(s[r]=[s[r]]),s[r].push(o)):s[r]=o;e.querySelectorAll('input[type="checkbox"]').forEach(r=>{s[r.name]=r.checked}),e.querySelectorAll('input[type="number"]').forEach(r=>{if(r.name&&s[r.name]!==void 0&&s[r.name]!==""){const o=Number(s[r.name]);isNaN(o)||(s[r.name]=o)}}),this.formConfig.fields?.forEach(r=>{if(r.type==="select"&&r.name&&s[r.name]!==void 0){const o=this.getFormFieldConfig(r.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[r.name]!==""){const c=Number(s[r.name]);isNaN(c)||(s[r.name]=c)}}}),e.querySelectorAll('[data-field-type="json"]').forEach(r=>{try{s[r.name]=JSON.parse(r.value)}catch{s[r.name]=r.value}}),this.customComponents.forEach((r,o)=>{r.getFormValue?s[o]=r.getFormValue():r.getValue&&(s[o]=r.getValue())}),this.element&&this.element.querySelectorAll("[data-show-when-field]").forEach(o=>{o.style.display==="none"&&o.querySelectorAll("[name]").forEach(c=>{delete s[c.name]})});for(const[r,o]of Object.entries(this.data))if(o instanceof File)try{s[r]=await this.fileToBase64(o)}catch{s[r]=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[r]=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,n]of e.entries())if(n instanceof File)n.size===0||n.name===""||n.name==="blob"||(s.set(a,n),i=!0);else{const r=t[a];n!==r&&n!==String(r)&&(s.set(a,n),i=!0)}return i?s:null}getChangedObjectData(e,t){const s={};let i=!1;const a=new Set([...Object.keys(t),...Object.keys(e)]),n=(r,o)=>o.split(".").reduce((l,c)=>l&&typeof l=="object"?l[c]:void 0,r);for(const r of a){const o=this.findFieldConfig(r);if(!o)continue;const l=e[r],c=n(t,r),d=o.type||"text";this.valuesAreDifferent(l,c,d,o)&&(s[r]=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 r=i.valueField||"id";if(t[r]==e)return!1}if(s==="switch"||s==="checkbox"||s==="toggle")return!!e!==!!t;const a=e==null?"":String(e).trim(),n=t==null?"":String(t).trim();return a!==n}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 n=window.bootstrap?.Tab?.getOrCreateInstance?window.bootstrap.Tab.getOrCreateInstance(a):null;n&&typeof n.show=="function"?n.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=`
1278
1278
  ${e}
1279
1279
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
1280
- `,this.element.insertBefore(s,this.element.firstChild),setTimeout(()=>{s.parentNode&&s.remove()},5e3)}}async updateField(e){this.formBuilder=new X({...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=`
1281
- <img id="${n}"
1280
+ `,this.element.insertBefore(s,this.element.firstChild),setTimeout(()=>{s.parentNode&&s.remove()},5e3)}}async updateField(e){this.formBuilder=new X({...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,n=s.getAttribute("data-field-id");if(t){if(i)i.src=t;else{const r=`${n}_preview`;s.innerHTML=`
1281
+ <img id="${r}"
1282
1282
  src="${t}"
1283
1283
  alt="Preview"
1284
1284
  class="img-thumbnail w-100 h-100"
@@ -1286,12 +1286,12 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1286
1286
  <button type="button"
1287
1287
  class="btn btn-sm btn-danger position-absolute top-0 end-0 m-1"
1288
1288
  data-action="remove-image"
1289
- data-field-id="${r}"
1289
+ data-field-id="${n}"
1290
1290
  data-field="${e}"
1291
1291
  style="opacity: 0.8;">
1292
1292
  <i class="bi bi-x"></i>
1293
1293
  </button>
1294
- `}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 X({...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 it{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=`
1294
+ `}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 n=t(a.fields);if(n)return n}}}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 X({...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 n=a.getModifierState("CapsLock");this.updateCapsLockWarning(t,!!n)}};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 r=t.closest(".input-group");r&&(i=r.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 n=t.querySelector("i");n&&(n.classList.toggle("bi-eye",!a),n.classList.toggle("bi-eye-slash",a)),i.focus();try{const r=i.value?.length??0;i.setSelectionRange(r,r)}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 n=a.className;a.className="bi bi-check2",t.classList.add("btn-success"),t.classList.remove("btn-outline-secondary"),setTimeout(()=>{a.className=n,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),n=/\d/.test(e),r=/[^A-Za-z0-9]/.test(e),o=[i,a,n,r].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:n}=this.computePasswordStrength(e.value||"");t&&(t.className=`progress-bar ${n}`,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 it{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=`
1295
1295
  <div class="saving-indicator">
1296
1296
  <div class="spinner-border spinner-border-sm text-primary" role="status">
1297
1297
  <span class="visually-hidden">Saving...</span>
@@ -1312,7 +1312,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1312
1312
  </div>
1313
1313
  <i class="bi bi-check-circle text-success d-none" data-status="saved"></i>
1314
1314
  <i class="bi bi-exclamation-circle text-danger d-none" data-status="error"></i>
1315
- `}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()}}Je(j);const Me=Object.freeze(Object.defineProperty({__proto__:null,FormView:j,default:j},Symbol.toStringTag,{value:"Module"}));class at extends Q{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 j({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView);const e=await this.getModel();e&&this.formView.setModel(e)}}class ae extends C{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=`
1315
+ `}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()}}Je(j);const _e=Object.freeze(Object.defineProperty({__proto__:null,FormView:j,default:j},Symbol.toStringTag,{value:"Module"}));class at extends Q{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 j({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView);const e=await this.getModel();e&&this.formView.setModel(e)}}class ae extends C{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=`
1316
1316
  <div class="list-item-content" data-action="select">
1317
1317
  {{#model}}
1318
1318
  {{#id}}<span class="item-id">{{id}}</span>{{/id}}
@@ -1355,7 +1355,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1355
1355
  </div>
1356
1356
  </div>
1357
1357
  </td>
1358
- `),this.columns.forEach((t,s)=>{const i=t.class||t.className||"",a=this.getResponsiveClasses(t.visibility),r=t.editable?"editable-cell":"",n=[i,a,r].filter(c=>c).join(" "),o=this.buildCellTemplate(t,s);let l=t.action;!l&&t.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),l?e+=`<td class="${n}" data-action="${l}" data-column="${t.key}">${o}</td>`:e+=`<td class="${n}" data-column="${t.key}">${o}</td>`}),this.actions?e+=this.buildActionsTemplate():this.contextMenu&&(e+=this.buildContextMenuTemplate()),e}buildCellTemplate(e,t=0){const s=`model.${e.key}`,i=e.formatter||e.format;if(i){if(typeof i=="string")return`{{{${s}|${i}}}}`;if(typeof i=="function")return`<span data-formatter="${e.key}" data-formatter-id="${t}">{{${s}}}</span>`}return e.template?e.template:e.editable?`<span class="cell-content" data-field="${e.key}">{{{${s}}}}</span>`:`{{{${s}}}}`}buildActionsTemplate(){return!this.actions||this.actions.length===0?"":`<td><div class="btn-group btn-group-sm">${this.actions.map(t=>{if(typeof t=="string")switch(t){case"view":return`
1358
+ `),this.columns.forEach((t,s)=>{const i=t.class||t.className||"",a=this.getResponsiveClasses(t.visibility),n=t.editable?"editable-cell":"",r=[i,a,n].filter(c=>c).join(" "),o=this.buildCellTemplate(t,s);let l=t.action;!l&&t.editable?l="edit-cell":!l&&this.tableView.rowAction&&(l=this.tableView.rowAction),l?e+=`<td class="${r}" data-action="${l}" data-column="${t.key}">${o}</td>`:e+=`<td class="${r}" data-column="${t.key}">${o}</td>`}),this.actions?e+=this.buildActionsTemplate():this.contextMenu&&(e+=this.buildContextMenuTemplate()),e}buildCellTemplate(e,t=0){const s=`model.${e.key}`,i=e.formatter||e.format;if(i){if(typeof i=="string")return`{{{${s}|${i}}}}`;if(typeof i=="function")return`<span data-formatter="${e.key}" data-formatter-id="${t}">{{${s}}}</span>`}return e.template?e.template:e.editable?`<span class="cell-content" data-field="${e.key}">{{{${s}}}}</span>`:`{{{${s}}}}`}buildActionsTemplate(){return!this.actions||this.actions.length===0?"":`<td><div class="btn-group btn-group-sm">${this.actions.map(t=>{if(typeof t=="string")switch(t){case"view":return`
1359
1359
  <button class="btn btn-sm btn-outline-primary"
1360
1360
  data-action="view"
1361
1361
  title="View">
@@ -1406,7 +1406,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1406
1406
  ${e.label}
1407
1407
  </a>
1408
1408
  </li>
1409
- `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((t,s)=>{if(t.formatter&&typeof t.formatter=="function"){let i=this.element.querySelector(`[data-formatter-id="${s}"]`);if(i||(i=this.element.querySelector(`[data-formatter="${t.key}"]`)),i){const a=this.model.get?this.model.get(t.key):this.model[t.key],r={value:a,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{i.innerHTML=t.formatter(a,r)}catch(n){console.error(`Error formatting cell for column ${t.key}:`,n)}}}}),this.selected&&this.element.classList.add("selected");const e=this.model.get?this.model.get("id"):this.model.id;e&&this.element.setAttribute("data-id",e)}async onActionEditCell(e,t){e.stopPropagation();const s=t.getAttribute("data-column"),i=this.columns.find(a=>a.key===s);!i||!i.editable||this.editingCells.has(s)||await this.enterEditMode(s,i,t)}async onActionRowClick(e,t){e.target.closest(".btn-group")||e.target.closest(".dropdown")||e.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}))}async onActionView(e,t){e.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:e})}async onActionEdit(e,t){return e.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:e}),!0}async onActionDelete(e,t){e.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:e})}async enterEditMode(e,t,s){const i=s.querySelector(".cell-content");if(!i)return;this.editingCells.add(e);const a=this.model.get?this.model.get(e):this.model[e],r=this.createCellEditor(t,a),n=i.innerHTML;i.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=r,s.appendChild(o);const l=o.querySelector("input, select, .form-check-input");l&&(l.focus(),(l.type==="text"||l.type==="textarea")&&l.select()),o.dataset.originalContent=n,o.dataset.columnKey=e,this.setupEditorEvents(o,e,t),this.emit("cell:edit",{row:this,model:this.model,column:e,originalValue:a})}createCellEditor(e,t){const s=e.editableOptions||{};switch(s.type){case"select":return this.createSelectEditor(s,t);case"switch":case"checkbox":return this.createSwitchEditor(s,t);case"textarea":return this.createTextareaEditor(s,t);default:return this.createTextEditor(s,t)}}createTextEditor(e,t){const s=e.placeholder||"";return`
1409
+ `}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach((t,s)=>{if(t.formatter&&typeof t.formatter=="function"){let i=this.element.querySelector(`[data-formatter-id="${s}"]`);if(i||(i=this.element.querySelector(`[data-formatter="${t.key}"]`)),i){const a=this.model.get?this.model.get(t.key):this.model[t.key],n={value:a,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{i.innerHTML=t.formatter(a,n)}catch(r){console.error(`Error formatting cell for column ${t.key}:`,r)}}}}),this.selected&&this.element.classList.add("selected");const e=this.model.get?this.model.get("id"):this.model.id;e&&this.element.setAttribute("data-id",e)}async onActionEditCell(e,t){e.stopPropagation();const s=t.getAttribute("data-column"),i=this.columns.find(a=>a.key===s);!i||!i.editable||this.editingCells.has(s)||await this.enterEditMode(s,i,t)}async onActionRowClick(e,t){e.target.closest(".btn-group")||e.target.closest(".dropdown")||e.target.closest(".cell-editor")||(this.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}),this.tableView&&this.tableView.emit("row:click",{row:this,model:this.model,column:t.getAttribute("data-column"),event:e}))}async onActionView(e,t){e.stopPropagation(),this.emit("row:view",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:view",{row:this,model:this.model,event:e})}async onActionEdit(e,t){return e.stopPropagation(),this.emit("row:edit",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:edit",{row:this,model:this.model,event:e}),!0}async onActionDelete(e,t){e.stopPropagation(),this.emit("row:delete",{row:this,model:this.model,event:e}),this.tableView&&this.tableView.emit("row:delete",{row:this,model:this.model,event:e})}async enterEditMode(e,t,s){const i=s.querySelector(".cell-content");if(!i)return;this.editingCells.add(e);const a=this.model.get?this.model.get(e):this.model[e],n=this.createCellEditor(t,a),r=i.innerHTML;i.style.display="none";const o=document.createElement("div");o.className="cell-editor",o.innerHTML=n,s.appendChild(o);const l=o.querySelector("input, select, .form-check-input");l&&(l.focus(),(l.type==="text"||l.type==="textarea")&&l.select()),o.dataset.originalContent=r,o.dataset.columnKey=e,this.setupEditorEvents(o,e,t),this.emit("cell:edit",{row:this,model:this.model,column:e,originalValue:a})}createCellEditor(e,t){const s=e.editableOptions||{};switch(s.type){case"select":return this.createSelectEditor(s,t);case"switch":case"checkbox":return this.createSwitchEditor(s,t);case"textarea":return this.createTextareaEditor(s,t);default:return this.createTextEditor(s,t)}}createTextEditor(e,t){const s=e.placeholder||"";return`
1410
1410
  <div class="d-flex gap-1 align-items-center">
1411
1411
  <input type="${e.inputType||"text"}"
1412
1412
  class="form-control form-control-sm cell-input"
@@ -1433,7 +1433,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1433
1433
  </button>
1434
1434
  </div>
1435
1435
  </div>
1436
- `}createSelectEditor(e,t){const s=e.options||[];let i="";return s.forEach(a=>{if(typeof a=="string")i+=`<option value="${a}" ${a===t?"selected":""}>${a}</option>`;else if(typeof a=="object"&&a.value!==void 0){const r=a.value===t?"selected":"";i+=`<option value="${a.value}" ${r}>${a.label||a.value}</option>`}}),`
1436
+ `}createSelectEditor(e,t){const s=e.options||[];let i="";return s.forEach(a=>{if(typeof a=="string")i+=`<option value="${a}" ${a===t?"selected":""}>${a}</option>`;else if(typeof a=="object"&&a.value!==void 0){const n=a.value===t?"selected":"";i+=`<option value="${a.value}" ${n}>${a.label||a.value}</option>`}}),`
1437
1437
  <div class="d-flex gap-1 align-items-center">
1438
1438
  <select class="form-select form-select-sm cell-input">
1439
1439
  ${i}
@@ -1459,7 +1459,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1459
1459
  </button>
1460
1460
  </div>
1461
1461
  </div>
1462
- `}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=L.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 _e={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 ee(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 _e[t]?{field:e.slice(0,-1).join("__"),lookup:t}:{field:u,lookup:null}}function rt(u,e,t){if(!u||e===null||e===void 0)return"";const{field:s,lookup:i}=ee(u),a=_e[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 Ve extends we{constructor(e={}){const t={className:"table-view-component",itemClass:e.itemClass||ve,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===!0,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 L.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`
1462
+ `}setupEditorEvents(e,t,s){const i=e.querySelector(".cell-input"),a=e.querySelector(".cell-save"),n=e.querySelector(".cell-cancel");i&&(i.type==="text"||i.type==="email"||i.type==="number")&&i.addEventListener("keydown",r=>{r.key==="Enter"?(r.preventDefault(),this.saveCellEdit(e,t,s)):r.key==="Escape"&&(r.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)}),n?.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 n=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:n,newValue:a})}catch(r){console.error("Failed to save cell edit:",r),this.emit("cell:save:error",{row:this,model:this.model,column:t,oldValue:n,newValue:a,error:r}),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 n=e.closest("td").querySelector(".cell-content");if(n){if(s!==null){const r=this.columns.find(l=>l.key===t);let o=s;r&&r.formatter&&typeof r.formatter=="string"&&(o=L.pipe(s,r.formatter)),n.innerHTML=this.escapeHtml(o)}else i&&(n.innerHTML=i);n.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 Me={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 ee(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 Me[t]?{field:e.slice(0,-1).join("__"),lookup:t}:{field:u,lookup:null}}function nt(u,e,t){if(!u||e===null||e===void 0)return"";const{field:s,lookup:i}=ee(u),a=Me[i];if(e&&typeof e=="object"&&!Array.isArray(e)){const r=e.start!==void 0&&e.start!==null&&e.start!=="",o=e.end!==void 0&&e.end!==null&&e.end!=="";return r||o?r&&o?`${t} between '${e.start}' and '${e.end}'`:r?`${t} from '${e.start}'`:`${t} until '${e.end}'`:`${t} is '${JSON.stringify(e)}'`}const n=Array.isArray(e)?e.join(","):String(e);if(!i||i==="exact")return`${t} is '${n}'`;if(i==="in"||i==="not_in"){const r=n.split(",").map(l=>l.trim()).filter(l=>l);if(r.length===0)return`${t} ${a.display}`;const o=r.map(l=>`'${l}'`).join(", ");return`${t} ${a.display} ${o}`}if(i==="range"){const r=n.split(",").map(o=>o.trim()).filter(o=>o);return r.length===2?`${t} between '${r[0]}' and '${r[1]}'`:`${t} ${a.display} '${n}'`}if(i==="isnull"){const r=typeof a.display=="function"?a.display(n):a.display;return`${t} ${r}`}return a?`${t} ${a.display} '${n}'`:`${t} is '${n}'`}class Ve extends we{constructor(e={}){const t={className:"table-view-component",itemClass:e.itemClass||ve,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===!0,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 n=this.parseColumnKey(s.key).formatter||s.formatter;let r;n&&typeof n=="string"?r=this.formatValue(e[i].value,n):r=e[i].value,a.textContent=r}t++}})}formatValue(e,t){try{return L.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 n=0;this.collection.forEach(o=>{const l=o.get?o.get(i):o[i],c=parseFloat(l)||0;n+=c}),console.log(`Footer total for ${t.key}: ${n} (from ${this.collection.length} items)`);const r=`col_${s}`;e[r]={value:n,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`
1463
1463
  <div class="mojo-table-wrapper">
1464
1464
  ${this.buildToolbarTemplate()}
1465
1465
  ${e}
@@ -1544,7 +1544,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1544
1544
  <i class="bi bi-download me-1"></i>
1545
1545
  <span class="d-none d-lg-inline">Export</span>
1546
1546
  </button>
1547
- `)}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(`
1547
+ `)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((t,s)=>{const{label:i="Button",icon:a="",action:n="",handler:r=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="";r?p=`data-action="custom-toolbar-button" data-button-index="${s}"`:n&&(p=`data-action="${n}"`);const f=`btn btn-sm btn-${o} ${c}`.trim();e.push(`
1548
1548
  <button class="${f}"
1549
1549
  ${p}
1550
1550
  title="${l}">
@@ -1584,11 +1584,11 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1584
1584
  </div>
1585
1585
  </div>
1586
1586
  `:""}buildFilterList(){const e=this.getAllAvailableFilters(),t=this.getActiveFilters();return e.length===0?'<div class="dropdown-item-text text-muted">No filters available</div>':`
1587
- ${e.map(i=>{const a=t.hasOwnProperty(i.key),r=a?"active":"",n=this.getFilterIcon(i.type||i.config?.type);return`
1588
- <button class="dropdown-item ${r}"
1587
+ ${e.map(i=>{const a=t.hasOwnProperty(i.key),n=a?"active":"",r=this.getFilterIcon(i.type||i.config?.type);return`
1588
+ <button class="dropdown-item ${n}"
1589
1589
  data-action="add-filter"
1590
1590
  data-filter-key="${i.key}">
1591
- <i class="bi bi-${n} me-2"></i>
1591
+ <i class="bi bi-${r} me-2"></i>
1592
1592
  ${i.label}
1593
1593
  ${a?'<i class="bi bi-check-circle ms-auto"></i>':""}
1594
1594
  </button>
@@ -1599,14 +1599,14 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1599
1599
  <i class="bi bi-x-circle me-2"></i>Clear All Filters
1600
1600
  </button>
1601
1601
  `:""}
1602
- `}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}=ee(n),c=this.getFilterLabel(l),d=rt(n,o,c);return`
1602
+ `}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(([r,o])=>o&&o.toString().trim()!==""&&r!=="search");if(this.hideActivePillNames&&this.hideActivePillNames.length>0&&(s=s.filter(([r])=>!this.hideActivePillNames.includes(r))),s.length===0&&!t)return"";const i=s.map(([r,o])=>{const{field:l}=ee(r),c=this.getFilterLabel(l),d=nt(r,o,c);return`
1603
1603
  <span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">
1604
1604
  <i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>
1605
1605
 
1606
1606
  <button type="button" class="btn btn-link text-white p-0 ms-1"
1607
1607
  style="font-size: 0.65rem; line-height: 1;"
1608
1608
  data-action="edit-filter"
1609
- data-filter="${n}"
1609
+ data-filter="${r}"
1610
1610
  title="Edit filter">
1611
1611
  ${d}
1612
1612
  </button>
@@ -1614,11 +1614,11 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1614
1614
  <button type="button" class="btn-close btn-close-white ms-1"
1615
1615
  style="font-size: 0.6rem; width: 0.5rem; height: 0.5rem;"
1616
1616
  data-action="remove-filter"
1617
- data-filter="${n}"
1617
+ data-filter="${r}"
1618
1618
  title="Remove filter">
1619
1619
  </button>
1620
1620
  </span>
1621
- `}).join(""),r=s.length>1||s.length>0&&t||s.length===0&&t?`
1621
+ `}).join(""),n=s.length>1||s.length>0&&t||s.length===0&&t?`
1622
1622
  <button class="btn btn-sm btn-outline-secondary mb-1 py-0 px-2" style="font-size: 0.75rem;" data-action="clear-all-filters">
1623
1623
  <i class="bi bi-x-circle me-1" style="font-size: 0.7rem;"></i>
1624
1624
  <small>Clear All</small>
@@ -1628,7 +1628,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1628
1628
  <div class="col-12">
1629
1629
  <div class="d-flex flex-wrap align-items-center">
1630
1630
  ${i}
1631
- ${r}
1631
+ ${n}
1632
1632
  </div>
1633
1633
  </div>
1634
1634
  </div>
@@ -1640,12 +1640,12 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1640
1640
  </div>
1641
1641
  </div>
1642
1642
  </th>
1643
- `),this.columns.forEach(t=>{const{fieldKey:s}=this.parseColumnKey(t.key),i=this.sortable&&t.sortable!==!1,a=this.getSortBy()===s?this.getSortDirection():null,r=this.getSortIcon(a),n=t.label||t.title||s,o=this.getResponsiveClasses(t.visibility),l=i?`
1643
+ `),this.columns.forEach(t=>{const{fieldKey:s}=this.parseColumnKey(t.key),i=this.sortable&&t.sortable!==!1,a=this.getSortBy()===s?this.getSortDirection():null,n=this.getSortIcon(a),r=t.label||t.title||s,o=this.getResponsiveClasses(t.visibility),l=i?`
1644
1644
  <div class="dropdown d-inline-block ms-2">
1645
1645
  <button class="btn btn-sm btn-link p-0 text-decoration-none" type="button"
1646
1646
  data-bs-toggle="dropdown" aria-expanded="false"
1647
1647
  data-column="${s}">
1648
- ${r}
1648
+ ${n}
1649
1649
  </button>
1650
1650
  <ul class="dropdown-menu dropdown-menu-end">
1651
1651
  <li><a class="dropdown-item ${a==="asc"?"active":""}"
@@ -1665,7 +1665,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1665
1665
  `:"";e+=`
1666
1666
  <th class="${i?"sortable":""} ${o}">
1667
1667
  <div class="d-flex align-items-center">
1668
- <span>${n}</span>
1668
+ <span>${r}</span>
1669
1669
  ${l}
1670
1670
  </div>
1671
1671
  </th>
@@ -1675,7 +1675,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1675
1675
  ${e}
1676
1676
  </tr>
1677
1677
  </thead>
1678
- `}buildTableFooterTemplate(){let e="";this.isSelectable()&&(e+="<td></td>");let t=0;return this.columns.forEach((s,i)=>{const a=this.getResponsiveClasses(s.visibility);if(s.footer_total){const r=`col_${t}`,n=this.parseColumnKey(s.key).formatter||s.formatter;let o;n&&typeof n=="string"?o=`{{{footerTotals.${r}.value|${n}}}}`:o=`{{footerTotals.${r}.value}}`,e+=`<td class="table-footer-total ${a}" data-total-column="${r}">${o}</td>`,t++}else i===0?e+=`<td class="table-footer-label ${a}"><strong>Totals</strong></td>`:e+=`<td class="${a}"></td>`}),(this.actions||this.contextMenu)&&(e+="<td></td>"),`
1678
+ `}buildTableFooterTemplate(){let e="";this.isSelectable()&&(e+="<td></td>");let t=0;return this.columns.forEach((s,i)=>{const a=this.getResponsiveClasses(s.visibility);if(s.footer_total){const n=`col_${t}`,r=this.parseColumnKey(s.key).formatter||s.formatter;let o;r&&typeof r=="string"?o=`{{{footerTotals.${n}.value|${r}}}}`:o=`{{footerTotals.${n}.value}}`,e+=`<td class="table-footer-total ${a}" data-total-column="${n}">${o}</td>`,t++}else i===0?e+=`<td class="table-footer-label ${a}"><strong>Totals</strong></td>`:e+=`<td class="${a}"></td>`}),(this.actions||this.contextMenu)&&(e+="<td></td>"),`
1679
1679
  <tfoot>
1680
1680
  <tr class="table-totals-row">
1681
1681
  ${e}
@@ -1757,13 +1757,13 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1757
1757
  </nav>
1758
1758
  </div>
1759
1759
  </div>
1760
- `:""}_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?T.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 w.dialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await w.data({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 w.modelForm({model:e.model,...s,...this.getFormDialogConfig(t)});if(!i)return;if(!i.success||!i?.result?.data.status){w.showError(i?.result?.data?.error||i?.result?.message||"An error occurred");return}}else{const i=await w.dialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new j({model:e.model,fields:this.options.formFields||[]})});if(i){const a=await e.model.save(i);if(!a.data?.status){w.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 w.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 w.form({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){w.showError(n?.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}else{const a=new s,r=await w.dialog({title:`Add ${this.getModelName()}`,body:new j({model:a,fields:this.options.formFields||[]})});if(r){const n=await a.save(r);if(!n?.data.status){w.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():await 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(`
1760
+ `:""}_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?T.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 w.dialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await w.data({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 w.modelForm({model:e.model,...s,...this.getFormDialogConfig(t)});if(!i)return;if(!i.success||!i?.result?.data.status){w.showError(i?.result?.data?.error||i?.result?.message||"An error occurred");return}}else{const i=await w.dialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new j({model:e.model,fields:this.options.formFields||[]})});if(i){const a=await e.model.save(i);if(!a.data?.status){w.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 w.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 n=await w.form({model:a,...i,...this.getFormDialogConfig(s)});if(n){this.options.addRequiresActiveGroup&&(n.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(n.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(n,this.options.addFormDefaults);const r=await a.save(n);if(!r?.data.status){w.showError(r?.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}else{const a=new s,n=await w.dialog({title:`Add ${this.getModelName()}`,body:new j({model:a,fields:this.options.formFields||[]})});if(n){const r=await a.save(n);if(!r?.data.status){w.showError(r.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():await 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 n=a.startsWith("-"),r=n?a.slice(1):a;this.collection.sort((o,l)=>{const c=o.get(r),d=l.get(r);return c<d?n?1:-1:c>d?n?-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 n=e===i,r=this.getSortIcon(n?t:null);a.innerHTML=r;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",n&&t==="asc"),c&&c.classList.toggle("active",n&&t==="desc"),d&&d.classList.toggle("active",!n||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"]'),n=this.element.querySelector('[data-value="end"]'),r=this.element.querySelector('[data-value="total"]');a&&(a.textContent=t+1),n&&(n.textContent=i),r&&(r.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,n=Math.ceil(t/s);if(n<=1){e.innerHTML="";return}const r=a>1?a-1:n,o=a<n?a+1:1,l=[];l.push(`
1761
1761
  <li class="page-item">
1762
- <a class="page-link" href="#" data-action="page" data-page="${n}">
1762
+ <a class="page-link" href="#" data-action="page" data-page="${r}">
1763
1763
  <i class="bi bi-chevron-left"></i>
1764
1764
  </a>
1765
1765
  </li>
1766
- `);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(`
1766
+ `);const c=1,d=new Set([1,n]);for(let p=a-c;p<=a+c;p++)p>=1&&p<=n&&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(`
1767
1767
  <li class="page-item disabled"><span class="page-link">…</span></li>
1768
1768
  `),l.push(`
1769
1769
  <li class="page-item ${p===a?"active":""}">
@@ -1775,24 +1775,24 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1775
1775
  <i class="bi bi-chevron-right"></i>
1776
1776
  </a>
1777
1777
  </li>
1778
- `),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}=ee(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 w.form({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(),await this.render()}catch(e){console.error("Failed to fetch filtered data:",e),await this.render()}else await this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=ee(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 w.form({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}=ee(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 nt(){return typeof window>"u"?null:((!window.MOJO||typeof window.MOJO!="object")&&(window.MOJO={}),window.MOJO)}function ot(u){return u.__lite&&u.__lite.version||(u.WebApp=ie,u.View=C,u.Page=Q,u.Router=re,u.Model=Y,u.Collection=J,u.Rest=U,u.FormBuilder=X,u.FormView=j,u.Dialog=_,u.ProgressView=he,u.ListView=we,u.ListViewItem=ae,u.TableView=Ve,u.TableRow=ve,u.DataFormatter=L,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 Ce=nt();Ce&&ot(Ce);class Se extends C{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`
1778
+ `),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(),n=Math.max(1,Math.ceil(a/i));let r=isNaN(s)?1:s;r<1&&(r=n),r>n&&(r=1),this.collection.setParams({...this.collection.params,start:(r-1)*i}),this.collection.restEnabled?await this.collection.fetch():this.render(),this.emit("table:page",{page:r,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={},n=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]||""},n.add(c),n.add(d),n.add(h))}}),Object.keys(i).forEach(o=>{n.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",n=s.fieldName||"dr_field";delete this.collection.params[i],delete this.collection.params[a],delete this.collection.params[n],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[n]=e)}else{const{field:i,lookup:a}=ee(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 n=await w.form({title:`${a!==void 0&&a!==""?"Edit":"Add"} ${this.getFilterLabel(s)} Filter`,size:"md",fields:[this.buildFilterDialogField(i,a,s)]});if(n){const r=this.extractFilterValue(i,n);this.setFilter(s,r),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=n=>{if(!n&&n!==0)return"";if(n instanceof Date&&!isNaN(n))return n.toISOString().slice(0,10);const r=String(n).trim();if(!r)return"";if(/^-?\d+$/.test(r)){const l=Number(r),c=r.length<=10?l*1e3:l,d=new Date(c);if(!isNaN(d))return d.toISOString().slice(0,10)}const o=new Date(r);return isNaN(o)?r: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(n=>n.trim()).filter(n=>n))),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(),await this.render()}catch(e){console.error("Failed to fetch filtered data:",e),await this.render()}else await this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=ee(s);let a=this.getFilterConfig(i)||this.getFilterConfig(s);const n=this.getActiveFilters(),r=n[s]||n[i];if(!a){console.warn("No filter config found for key:",s,"or field:",i);return}const o={filter_value:r};if(a.type==="daterange"&&r&&typeof r=="object"){const c=a.startName||"dr_start",d=a.endName||"dr_end";o[c]=r.start||"",o[d]=r.end||""}const l=await w.form({title:`Edit ${this.getFilterLabel(i)} Filter`,size:"md",data:o,fields:[this.buildFilterDialogField(a,r,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}=ee(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(n=>n.selected),i=Array.from(this.itemViews.values()).some(n=>n.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 rt(){return typeof window>"u"?null:((!window.MOJO||typeof window.MOJO!="object")&&(window.MOJO={}),window.MOJO)}function ot(u){return u.__lite&&u.__lite.version||(u.WebApp=ie,u.View=C,u.Page=Q,u.Router=ne,u.Model=Y,u.Collection=J,u.Rest=U,u.FormBuilder=X,u.FormView=j,u.Dialog=M,u.ProgressView=he,u.ListView=we,u.ListViewItem=ae,u.TableView=Ve,u.TableRow=ve,u.DataFormatter=L,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 Ce=rt();Ce&&ot(Ce);class Se extends C{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:n,showEmptyValues:r,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:n!==!1,showEmptyValues:r||!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`
1779
1779
  <div class="${this.dataViewOptions.rowClass}">
1780
1780
  ${e}
1781
1781
  </div>
1782
- `}generateFieldsFromData(){const e=this.getData();e&&typeof e=="object"&&(this.fields=Object.keys(e).map(t=>{const s=e[t],i=this.inferFieldType(s,t),a=this.inferFormatter(s,t,i);return{name:t,label:this.formatLabel(t),type:i,format:a,formatter:a}}))}formatLabel(e){return e.replace(/([A-Z])/g," $1").replace(/[_-]/g," ").replace(/\b\w/g,t=>t.toUpperCase()).trim()}inferFieldType(e,t=""){if(e==null)return"text";const s=t.toLowerCase(),i=typeof e;if(s.includes("date")||s.includes("time")||s.includes("created")||s.includes("updated")||s.includes("modified")||s.includes("last_login")||s.includes("expires")||s.includes("last_activity"))return"datetime";if(s.includes("email")||s.includes("mail"))return"email";if(s.includes("url")||s.includes("link")||s.includes("website")||s.includes("homepage"))return"url";if(s.includes("phone")||s.includes("tel")||s.includes("mobile")||s.includes("cell"))return"phone";if(s.includes("price")||s.includes("cost")||s.includes("amount")||s.includes("fee")||s.includes("salary")||s.includes("revenue"))return"currency";if(s.includes("size")||s.includes("bytes"))return"filesize";if(s.includes("percent")||s.includes("rate")||s.includes("ratio")&&i==="number")return"percent";if(i==="boolean")return"boolean";if(i==="number")return"number";if(i==="object")return Array.isArray(e)?"array":e&&e.renditions?"file":this.shouldUseDataView(e,s)?"dataview":"object";if(i==="string"){if(e.includes("@")&&e.includes("."))return"email";if(e.match(/^\d{4}-\d{2}-\d{2}/))return"date";if(e.match(/^https?:\/\//))return"url";if(e.match(/^\+?[\d\s\-\(\)]+$/))return"phone"}return"text"}inferFormatter(e,t,s){const i=t.toLowerCase(),a=[];switch(s){case"datetime":i.includes("time")&&!i.includes("date")?a.push("time"):i.includes("relative")||i.includes("ago")||i.includes("last_")?a.push("relative"):i.includes("created")||i.includes("updated")||i.includes("modified")?a.push('date("MMM D, YYYY")'):a.push('date("MMMM D, YYYY")');break;case"date":i.includes("birth")||i.includes("dob")?a.push('date("MMMM D, YYYY")'):a.push('date("MMM D, YYYY")');break;case"email":break;case"url":break;case"phone":a.push("phone");break;case"currency":a.push("currency"),i.includes("eur")||i.includes("euro")?a[a.length-1]='currency("EUR")':(i.includes("gbp")||i.includes("pound"))&&(a[a.length-1]='currency("GBP")');break;case"filesize":a.push("filesize");break;case"percent":a.push("percent");break;case"number":if(typeof e=="number")if(i.includes("count")||i.includes("total")||i.includes("followers")||i.includes("views"))e>=1e3?a.push("compact"):a.push("number");else if(i.includes("score")||i.includes("rating"))a.push("number"),e%1!==0&&(a[a.length-1]="number(1)");else{if(i.includes("version")||i.includes("id"))return null;a.push("number")}break;case"boolean":break;case"text":typeof e=="string"&&(i.includes("description")||i.includes("content")||i.includes("body")?e.length>200?a.push("truncate(200)"):e.length>100&&a.push("truncate(100)"):i.includes("summary")||i.includes("excerpt")?e.length>150&&a.push("truncate(150)"):i.includes("name")||i.includes("title")||i.includes("label")?(a.push("capitalize"),e.length>50&&a.unshift("truncate(50)")):i.includes("slug")||i.includes("handle")||i.includes("username")?a.push("slug"):i.includes("code")||i.includes("token")||i.includes("key")?e.length>20&&a.push("mask"):e.length>100&&a.push("truncate(100)"));break;case"array":case"object":break;case"dataview":break;default:typeof e=="string"&&e.length>100&&a.push("truncate(100)");break}return a.length>0?a.join("|"):null}shouldUseDataView(e,t){if(!e||typeof e!="object"||Array.isArray(e))return!1;const s=["permissions","perms","access","rights","settings","config","configuration","options","profile","info","details","data","metadata","meta","attributes","props","preferences","prefs","user_data","contact","address","location","stats","statistics","metrics","counts"];if(window.utils&&window.utils.isObject(e)&&e.id)return!0;if(s.some(a=>t.includes(a))){const a=Object.keys(e);if(a.length>=2&&a.length<=20&&!a.some(n=>typeof e[n]=="object"&&e[n]!==null&&!Array.isArray(e[n])&&Object.keys(e[n]).length>3))return!0}return!1}getData(){return this.model&&this.model.attributes?{...this.model.attributes}:this.data||{}}getFieldValue(e){let t,s=e.name||e.key,i=e.format||e.formatter;if(!s)return null;if(s&&s.includes("|")){const a=s.split("|");s=a[0].trim(),i||(i=a.slice(1).join("|").trim())}if(this.model&&typeof this.model.get=="function"?t=this.model.get(s):t=this.getData()[s],i&&(t=L.pipe(t,i)),t==null||t==="")return this.dataViewOptions.showEmptyValues?this.dataViewOptions.emptyValueText:null;if(e.template){const a=this.model?this.model:this.data;return this.renderTemplateString(e.template,a)}return t}renderTemplateString(e,t){return!e||!t?"":e.replace(/\{\{([^}]+)\}\}/g,(s,i)=>{const a=i.trim();let r;const n=a.split("|"),o=n[0],l=n.slice(1).join("|");return this.model&&typeof this.model.get=="function"?r=this.model.get(o):r=o.split(".").reduce((c,d)=>c?c[d]:void 0,t),l&&(r=L.pipe(r,l)),r??""})}getColumnClasses(e){let t=this.getColumnSizeClasses(e);return e.justify=="right"?t+=" d-flex justify-content-end":e.justify=="center"&&(t+=" d-flex justify-content-center"),t}getColumnSizeClasses(e){if(e.type==="array"||e.type==="object"||e.type==="dataview")return"col-12";const t=e.columns||e.colSize||e.cols||Math.floor(12/this.dataViewOptions.columns);return this.dataViewOptions.responsive?`col-12 col-md-${t}`:`col-${t}`}buildItemsHTML(){return this.fields.map(e=>this.buildItemHTML(e)).filter(Boolean).join("")}buildItemHTML(e){const t=this.getFieldValue(e);if(t===null&&!this.dataViewOptions.showEmptyValues)return"";const s=e.label||this.formatLabel(e.name);return`
1782
+ `}generateFieldsFromData(){const e=this.getData();e&&typeof e=="object"&&(this.fields=Object.keys(e).map(t=>{const s=e[t],i=this.inferFieldType(s,t),a=this.inferFormatter(s,t,i);return{name:t,label:this.formatLabel(t),type:i,format:a,formatter:a}}))}formatLabel(e){return e.replace(/([A-Z])/g," $1").replace(/[_-]/g," ").replace(/\b\w/g,t=>t.toUpperCase()).trim()}inferFieldType(e,t=""){if(e==null)return"text";const s=t.toLowerCase(),i=typeof e;if(s.includes("date")||s.includes("time")||s.includes("created")||s.includes("updated")||s.includes("modified")||s.includes("last_login")||s.includes("expires")||s.includes("last_activity"))return"datetime";if(s.includes("email")||s.includes("mail"))return"email";if(s.includes("url")||s.includes("link")||s.includes("website")||s.includes("homepage"))return"url";if(s.includes("phone")||s.includes("tel")||s.includes("mobile")||s.includes("cell"))return"phone";if(s.includes("price")||s.includes("cost")||s.includes("amount")||s.includes("fee")||s.includes("salary")||s.includes("revenue"))return"currency";if(s.includes("size")||s.includes("bytes"))return"filesize";if(s.includes("percent")||s.includes("rate")||s.includes("ratio")&&i==="number")return"percent";if(i==="boolean")return"boolean";if(i==="number")return"number";if(i==="object")return Array.isArray(e)?"array":e&&e.renditions?"file":this.shouldUseDataView(e,s)?"dataview":"object";if(i==="string"){if(e.includes("@")&&e.includes("."))return"email";if(e.match(/^\d{4}-\d{2}-\d{2}/))return"date";if(e.match(/^https?:\/\//))return"url";if(e.match(/^\+?[\d\s\-\(\)]+$/))return"phone"}return"text"}inferFormatter(e,t,s){const i=t.toLowerCase(),a=[];switch(s){case"datetime":i.includes("time")&&!i.includes("date")?a.push("time"):i.includes("relative")||i.includes("ago")||i.includes("last_")?a.push("relative"):i.includes("created")||i.includes("updated")||i.includes("modified")?a.push('date("MMM D, YYYY")'):a.push('date("MMMM D, YYYY")');break;case"date":i.includes("birth")||i.includes("dob")?a.push('date("MMMM D, YYYY")'):a.push('date("MMM D, YYYY")');break;case"email":break;case"url":break;case"phone":a.push("phone");break;case"currency":a.push("currency"),i.includes("eur")||i.includes("euro")?a[a.length-1]='currency("EUR")':(i.includes("gbp")||i.includes("pound"))&&(a[a.length-1]='currency("GBP")');break;case"filesize":a.push("filesize");break;case"percent":a.push("percent");break;case"number":if(typeof e=="number")if(i.includes("count")||i.includes("total")||i.includes("followers")||i.includes("views"))e>=1e3?a.push("compact"):a.push("number");else if(i.includes("score")||i.includes("rating"))a.push("number"),e%1!==0&&(a[a.length-1]="number(1)");else{if(i.includes("version")||i.includes("id"))return null;a.push("number")}break;case"boolean":break;case"text":typeof e=="string"&&(i.includes("description")||i.includes("content")||i.includes("body")?e.length>200?a.push("truncate(200)"):e.length>100&&a.push("truncate(100)"):i.includes("summary")||i.includes("excerpt")?e.length>150&&a.push("truncate(150)"):i.includes("name")||i.includes("title")||i.includes("label")?(a.push("capitalize"),e.length>50&&a.unshift("truncate(50)")):i.includes("slug")||i.includes("handle")||i.includes("username")?a.push("slug"):i.includes("code")||i.includes("token")||i.includes("key")?e.length>20&&a.push("mask"):e.length>100&&a.push("truncate(100)"));break;case"array":case"object":break;case"dataview":break;default:typeof e=="string"&&e.length>100&&a.push("truncate(100)");break}return a.length>0?a.join("|"):null}shouldUseDataView(e,t){if(!e||typeof e!="object"||Array.isArray(e))return!1;const s=["permissions","perms","access","rights","settings","config","configuration","options","profile","info","details","data","metadata","meta","attributes","props","preferences","prefs","user_data","contact","address","location","stats","statistics","metrics","counts"];if(window.utils&&window.utils.isObject(e)&&e.id)return!0;if(s.some(a=>t.includes(a))){const a=Object.keys(e);if(a.length>=2&&a.length<=20&&!a.some(r=>typeof e[r]=="object"&&e[r]!==null&&!Array.isArray(e[r])&&Object.keys(e[r]).length>3))return!0}return!1}getData(){return this.model&&this.model.attributes?{...this.model.attributes}:this.data||{}}getFieldValue(e){let t,s=e.name||e.key,i=e.format||e.formatter;if(!s)return null;if(s&&s.includes("|")){const a=s.split("|");s=a[0].trim(),i||(i=a.slice(1).join("|").trim())}if(this.model&&typeof this.model.get=="function"?t=this.model.get(s):t=this.getData()[s],i&&(t=L.pipe(t,i)),t==null||t==="")return this.dataViewOptions.showEmptyValues?this.dataViewOptions.emptyValueText:null;if(e.template){const a=this.model?this.model:this.data;return this.renderTemplateString(e.template,a)}return t}renderTemplateString(e,t){return!e||!t?"":e.replace(/\{\{([^}]+)\}\}/g,(s,i)=>{const a=i.trim();let n;const r=a.split("|"),o=r[0],l=r.slice(1).join("|");return this.model&&typeof this.model.get=="function"?n=this.model.get(o):n=o.split(".").reduce((c,d)=>c?c[d]:void 0,t),l&&(n=L.pipe(n,l)),n??""})}getColumnClasses(e){let t=this.getColumnSizeClasses(e);return e.justify=="right"?t+=" d-flex justify-content-end":e.justify=="center"&&(t+=" d-flex justify-content-center"),t}getColumnSizeClasses(e){if(e.type==="array"||e.type==="object"||e.type==="dataview")return"col-12";const t=e.columns||e.colSize||e.cols||Math.floor(12/this.dataViewOptions.columns);return this.dataViewOptions.responsive?`col-12 col-md-${t}`:`col-${t}`}buildItemsHTML(){return this.fields.map(e=>this.buildItemHTML(e)).filter(Boolean).join("")}buildItemHTML(e){const t=this.getFieldValue(e);if(t===null&&!this.dataViewOptions.showEmptyValues)return"";const s=e.label||this.formatLabel(e.name);return`
1783
1783
  <div class="${this.getColumnClasses(e)}">
1784
1784
  <div class="${this.dataViewOptions.itemClass} ${e.className}" data-field="${e.name}">
1785
1785
  ${this.buildLabelHTML(s,e)}
1786
1786
  ${this.buildValueHTML(t,e)}
1787
1787
  </div>
1788
1788
  </div>
1789
- `}buildLabelHTML(e,t){return`<div class="${t.labelClass||this.dataViewOptions.labelClass}">${this.escapeHtml(e)}:</div>`}buildValueHTML(e,t){const s=t.valueClass||this.dataViewOptions.valueClass,i=this.formatDisplayValue(e,t);return`<div class="${s}">${i}</div>`}formatDisplayValue(e,t){if(e==null)return this.dataViewOptions.emptyValueText;if(t.template||t.format||t.formatter)return String(e);const i=this.getData()[t.name];switch(t.type){case"boolean":return i?'<span class="badge bg-success">Yes</span>':'<span class="badge bg-secondary">No</span>';case"email":const a=String(e);return`<a href="mailto:${this.escapeHtml(a)}" class="text-decoration-none">${this.escapeHtml(a)}</a>`;case"url":const r=String(e);return`<a href="${this.escapeHtml(r)}" target="_blank" rel="noopener" class="text-decoration-none">${this.escapeHtml(r)} <i class="bi bi-box-arrow-up-right"></i></a>`;case"array":case"object":return this.formatAsJson(i);case"dataview":return this.formatAsDataView(i,t);case"phone":const n=String(e);return`<a href="tel:${n.replace(/[^\d\+]/g,"")}" class="text-decoration-none">${this.escapeHtml(n)}</a>`;default:return this.escapeHtml(String(e))}}formatAsJson(e){try{const t=JSON.stringify(e,null,2),s=this.escapeHtml(t),i=t.split(`
1790
- `).length,a=i>10||t.length>500,r=`json-${Math.random().toString(36).substr(2,9)}`;if(a){const n=JSON.stringify(e).substring(0,100)+(JSON.stringify(e).length>100?"...":""),o=this.escapeHtml(n);return`
1789
+ `}buildLabelHTML(e,t){return`<div class="${t.labelClass||this.dataViewOptions.labelClass}">${this.escapeHtml(e)}:</div>`}buildValueHTML(e,t){const s=t.valueClass||this.dataViewOptions.valueClass,i=this.formatDisplayValue(e,t);return`<div class="${s}">${i}</div>`}formatDisplayValue(e,t){if(e==null)return this.dataViewOptions.emptyValueText;if(t.template||t.format||t.formatter)return String(e);const i=this.getData()[t.name];switch(t.type){case"boolean":return i?'<span class="badge bg-success">Yes</span>':'<span class="badge bg-secondary">No</span>';case"email":const a=String(e);return`<a href="mailto:${this.escapeHtml(a)}" class="text-decoration-none">${this.escapeHtml(a)}</a>`;case"url":const n=String(e);return`<a href="${this.escapeHtml(n)}" target="_blank" rel="noopener" class="text-decoration-none">${this.escapeHtml(n)} <i class="bi bi-box-arrow-up-right"></i></a>`;case"array":case"object":return this.formatAsJson(i);case"dataview":return this.formatAsDataView(i,t);case"phone":const r=String(e);return`<a href="tel:${r.replace(/[^\d\+]/g,"")}" class="text-decoration-none">${this.escapeHtml(r)}</a>`;default:return this.escapeHtml(String(e))}}formatAsJson(e){try{const t=JSON.stringify(e,null,2),s=this.escapeHtml(t),i=t.split(`
1790
+ `).length,a=i>10||t.length>500,n=`json-${Math.random().toString(36).substr(2,9)}`;if(a){const r=JSON.stringify(e).substring(0,100)+(JSON.stringify(e).length>100?"...":""),o=this.escapeHtml(r);return`
1791
1791
  <div class="json-container">
1792
1792
  <div class="d-flex align-items-center justify-content-between mb-1">
1793
1793
  <small class="text-muted">${Array.isArray(e)?"Array":"Object"} (${i} lines)</small>
1794
1794
  <div class="btn-group btn-group-sm" role="group">
1795
- <button type="button" class="btn btn-outline-secondary btn-sm json-toggle" data-bs-toggle="collapse" data-bs-target="#${r}" aria-expanded="false">
1795
+ <button type="button" class="btn btn-outline-secondary btn-sm json-toggle" data-bs-toggle="collapse" data-bs-target="#${n}" aria-expanded="false">
1796
1796
  <i class="bi bi-eye"></i> Show
1797
1797
  </button>
1798
1798
  <button type="button" class="btn btn-outline-secondary btn-sm json-copy" data-json='${this.escapeHtml(t)}' title="Copy JSON">
@@ -1803,7 +1803,7 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1803
1803
  <div class="json-preview bg-light p-2 rounded small border" style="font-family: 'Courier New', monospace;">
1804
1804
  <code class="text-muted">${o}</code>
1805
1805
  </div>
1806
- <div class="collapse mt-2" id="${r}">
1806
+ <div class="collapse mt-2" id="${n}">
1807
1807
  <pre class="json-display p-3 rounded small mb-0" style="max-height: 400px; overflow-y: auto; white-space: pre-wrap; font-family: 'Courier New', monospace;"><code>${this.syntaxHighlightJson(s)}</code></pre>
1808
1808
  </div>
1809
1809
  </div>
@@ -1823,5 +1823,5 @@ var MOJO=(function(A){"use strict";class re{constructor(e={}){this.defaultRoute=
1823
1823
  ${i}
1824
1824
  </div>
1825
1825
  </div>
1826
- `}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?L.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 Se(e)}}const lt=Object.freeze(Object.defineProperty({__proto__:null,default:Se},Symbol.toStringTag,{value:"Module"}));return A.Collection=J,A.DataFormatter=L,A.Dialog=_,A.FormBuilder=X,A.FormPage=at,A.FormView=j,A.ListView=we,A.ListViewItem=ae,A.MOJOUtils=x,A.Model=Y,A.Page=Q,A.ProgressView=he,A.Rest=U,A.Router=re,A.TableRow=ve,A.TableView=Ve,A.View=C,A.WebApp=ie,A.default=Ce,Object.defineProperties(A,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),A})({});
1826
+ `}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?L.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 Se(e)}}const lt=Object.freeze(Object.defineProperty({__proto__:null,default:Se},Symbol.toStringTag,{value:"Module"}));return A.Collection=J,A.DataFormatter=L,A.Dialog=M,A.FormBuilder=X,A.FormPage=at,A.FormView=j,A.ListView=we,A.ListViewItem=ae,A.MOJOUtils=x,A.Model=Y,A.Page=Q,A.ProgressView=he,A.Rest=U,A.Router=ne,A.TableRow=ve,A.TableView=Ve,A.View=C,A.WebApp=ie,A.default=Ce,Object.defineProperties(A,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),A})({});
1827
1827
  //# sourceMappingURL=web-mojo.lite.iife.min.js.map