web-mojo 2.2.52 → 2.2.53
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.
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.es.js +9 -9
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +3 -3
- package/dist/chunks/{ChatView-DLE0vLnI.js → ChatView-BMdq23sR.js} +2 -2
- package/dist/chunks/ChatView-BMdq23sR.js.map +1 -0
- package/dist/chunks/{ChatView-B-l018ne.js → ChatView-_GYT1IcQ.js} +7 -7
- package/dist/chunks/ChatView-_GYT1IcQ.js.map +1 -0
- package/dist/chunks/{Collection-DaiL0uGl.js → Collection-f5FyYiac.js} +2 -2
- package/dist/chunks/{Collection-DaiL0uGl.js.map → Collection-f5FyYiac.js.map} +1 -1
- package/dist/chunks/{ContextMenu-BKqkclz5.js → ContextMenu-DXRtG97K.js} +2 -2
- package/dist/chunks/{ContextMenu-BKqkclz5.js.map → ContextMenu-DXRtG97K.js.map} +1 -1
- package/dist/chunks/{Dialog-D5iUTruE.js → Dialog-CQlTDhZS.js} +2 -2
- package/dist/chunks/Dialog-CQlTDhZS.js.map +1 -0
- package/dist/chunks/{Dialog-DQ_r6oon.js → Dialog-DOGDalUq.js} +18 -5
- package/dist/chunks/Dialog-DOGDalUq.js.map +1 -0
- package/dist/chunks/{FormView-6nZuwhJF.js → FormView-DaKA4Sys.js} +2 -2
- package/dist/chunks/{FormView-6nZuwhJF.js.map → FormView-DaKA4Sys.js.map} +1 -1
- package/dist/chunks/{FormView-dssyZwjn.js → FormView-Dz3mYasQ.js} +2 -2
- package/dist/chunks/{FormView-dssyZwjn.js.map → FormView-Dz3mYasQ.js.map} +1 -1
- package/dist/chunks/{ListView-X5w5jf51.js → ListView-BZz6uPLf.js} +2 -2
- package/dist/chunks/{ListView-X5w5jf51.js.map → ListView-BZz6uPLf.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-B2jA_UG0.js → MetricsMiniChartWidget-CBuso0OE.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-B2jA_UG0.js.map → MetricsMiniChartWidget-CBuso0OE.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-DbI0rPff.js → MetricsMiniChartWidget-DvKd7Qrk.js} +2 -2
- package/dist/chunks/{MetricsMiniChartWidget-DbI0rPff.js.map → MetricsMiniChartWidget-DvKd7Qrk.js.map} +1 -1
- package/dist/chunks/{PDFViewer-BYr6t1XE.js → PDFViewer-EJ9cOfPF.js} +2 -2
- package/dist/chunks/{PDFViewer-BYr6t1XE.js.map → PDFViewer-EJ9cOfPF.js.map} +1 -1
- package/dist/chunks/{PDFViewer-CQTWIY9W.js → PDFViewer-ofMGdSaj.js} +2 -2
- package/dist/chunks/{PDFViewer-CQTWIY9W.js.map → PDFViewer-ofMGdSaj.js.map} +1 -1
- package/dist/chunks/{TokenManager-rLYRinyw.js → TokenManager-Bogm6g1I.js} +4 -4
- package/dist/chunks/{TokenManager-rLYRinyw.js.map → TokenManager-Bogm6g1I.js.map} +1 -1
- package/dist/chunks/{TokenManager-Ciz9oGVy.js → TokenManager-FUEVto2z.js} +2 -2
- package/dist/chunks/{TokenManager-Ciz9oGVy.js.map → TokenManager-FUEVto2z.js.map} +1 -1
- package/dist/chunks/{WebApp-S6qUgYrt.js → WebApp-CVbMPeP2.js} +12 -12
- package/dist/chunks/{WebApp-S6qUgYrt.js.map → WebApp-CVbMPeP2.js.map} +1 -1
- package/dist/chunks/{WebApp-DBilHwQk.js → WebApp-DTxT-5qy.js} +2 -2
- package/dist/chunks/{WebApp-DBilHwQk.js.map → WebApp-DTxT-5qy.js.map} +1 -1
- package/dist/chunks/{version-D2JFQbQg.js → version-CEClyMzb.js} +2 -2
- package/dist/chunks/{version-D2JFQbQg.js.map → version-CEClyMzb.js.map} +1 -1
- package/dist/chunks/{version-7r9zrJ0L.js → version-lxcfLfYg.js} +4 -4
- package/dist/chunks/{version-7r9zrJ0L.js.map → version-lxcfLfYg.js.map} +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +5 -5
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +13 -13
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +4 -4
- package/dist/map.cjs.js +1 -1
- package/dist/map.es.js +2 -2
- package/dist/timeline.es.js +2 -2
- package/dist/web-mojo.lite.iife.js +16 -3
- package/dist/web-mojo.lite.iife.js.map +1 -1
- package/dist/web-mojo.lite.iife.min.js +73 -73
- package/dist/web-mojo.lite.iife.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/ChatView-B-l018ne.js.map +0 -1
- package/dist/chunks/ChatView-DLE0vLnI.js.map +0 -1
- package/dist/chunks/Dialog-D5iUTruE.js.map +0 -1
- package/dist/chunks/Dialog-DQ_r6oon.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e.defaultRoute||"home",this.routes=[],this.currentRoute=null,this.eventEmitter=e.eventEmitter||null,this.boundHandlePopState=this.handlePopState.bind(this)}start(){window.addEventListener("popstate",this.boundHandlePopState),this.handleCurrentLocation()}stop(){window.removeEventListener("popstate",this.boundHandlePopState)}addRoute(e,t){this.routes.push({pattern:this.normalizePattern(e),regex:this.patternToRegex(e),pageName:t,paramNames:this.extractParamNames(e)})}async navigate(e,t={}){const{replace:s=!1,state:i=null,trigger:a=!0}=t,{pageName:r,queryParams:n}=this.parseInput(e);a&&await this.handleRouteChange(r,n)}back(){window.history.back()}forward(){window.history.forward()}getCurrentRoute(){return this.currentRoute}getCurrentPath(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();return this.buildPublicUrl(e,t)}handlePopState(e){this.allowPopState?this.handleCurrentLocation():console.warn("PopStateEvent is not allowed")}async handleCurrentLocation(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();await this.handleRouteChange(e,t)}async handleRouteChange(e,t){const s="/"+e,i=this.matchRoute(s),a=this.buildPublicUrl(e,t);return i?(this.currentRoute=i,this.eventEmitter&&this.eventEmitter.emit("route:changed",{path:a,pageName:i.pageName,params:i.params,query:t,route:i}),i):(console.log("No route matched for page:",e),this.eventEmitter&&this.eventEmitter.emit("route:notfound",{path:a}),null)}matchRoute(e){for(const t of this.routes){const s=e.match(t.regex);if(s){const i={};return t.paramNames.forEach((a,r)=>{i[a]=s[r+1]}),{...t,params:i,path:e}}}return null}parseInput(e){let t=this.defaultRoute,s={};if(!e)return{pageName:t,queryParams:s};try{if(e.includes("?")){const[i,a]=e.split("?",2),r=new URLSearchParams(a);if(r.has("page")){t=r.get("page")||this.defaultRoute;for(const[n,o]of r)n!=="page"&&(s[n]=o)}else{i.startsWith("/")?t=i.substring(1)||this.defaultRoute:t=i||this.defaultRoute;for(const[n,o]of r)s[n]=o}}else e.startsWith("/")?t=e.substring(1)||this.defaultRoute:t=e}catch(i){console.warn("Failed to parse input:",e,i),t=this.defaultRoute,s={}}return{pageName:t,queryParams:s}}parseCurrentUrl(){const e=new URLSearchParams(window.location.search),t=e.get("page")||this.defaultRoute,s={};for(const[i,a]of e)i!=="page"&&(s[i]=a);return{pageName:t,queryParams:s}}buildPublicUrl(e,t={}){const s=new URLSearchParams;return s.set("page",e),Object.entries(t).forEach(([i,a])=>{a!=null&&a!==""&&s.set(i,String(a))}),"?"+s.toString()}updateBrowserUrl(e,t,s,i){const a=new URL(window.location.origin+window.location.pathname);a.searchParams.set("page",e),Object.entries(t).forEach(([n,o])=>{o!=null&&o!==""&&a.searchParams.set(n,String(o))});const r=a.toString();s?window.history.replaceState(i,"",r):window.history.pushState(i,"",r)}patternToRegex(e){let t=e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/\/:([^/?]+)\?/g,"(?:/([^/]+))?").replace(/:([^/]+)/g,"([^/]+)");return new RegExp(`^${t}$`)}extractParamNames(e){return(e.match(/:([^/?]+)\??/g)||[]).map(s=>s.replace(/[:?]/g,""))}normalizePattern(e){return e.startsWith("/")?e:`/${e}`}updateUrl(e={},t={}){const{replace:s=!1}=t,{pageName:i}=this.parseCurrentUrl();this.updateBrowserUrl(i,e,s)}buildUrl(e,t={}){return this.buildPublicUrl(e,t)}doRoutesMatch(e,t){if(!e||!t)return!1;const{pageName:s}=this.parseInput(e),{pageName:i}=this.parseInput(t);return s===i}}class Ee{constructor(){this.listeners={},this.onceListeners={},this.maxListeners=100,this.debugMode=!1,this.eventStats={}}on(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.on(s,t)),this):(this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].length>=this.maxListeners&&console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${e}`),this.listeners[e].push(t),this)}once(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.once(s,t)),this):(this.onceListeners[e]||(this.onceListeners[e]=[]),this.onceListeners[e].push(t),this)}off(e,t){if(Array.isArray(e))return e.forEach(s=>this.off(s,t)),this;if(!t)return delete this.listeners[e],delete this.onceListeners[e],this;if(this.listeners[e]){const s=this.listeners[e].indexOf(t);s!==-1&&(this.listeners[e].splice(s,1),this.listeners[e].length===0&&delete this.listeners[e])}if(this.onceListeners[e]){const s=this.onceListeners[e].indexOf(t);s!==-1&&(this.onceListeners[e].splice(s,1),this.onceListeners[e].length===0&&delete this.onceListeners[e])}return this}emit(e,t){this.updateEventStats(e),this.debugMode&&console.log(`[EventBus] Emitting: ${e}`,t);const s=[];return this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]),this.debugMode&&s.length>0&&console.log(`[EventBus] ${s.length} listener(s) for '${e}'`),s.forEach(i=>{try{i(t,e)&&(e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault())}catch(a){console.error(`Error in event listener for '${e}':`,a),this.emitError(a,e,i)}}),this}async emitAsync(e,t){const s=[];this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]);const i=s.map(a=>new Promise(r=>{try{const n=a(t,e);r(n)}catch(n){console.error(`Error in async event listener for '${e}':`,n),this.emitError(n,e,a),r()}}));return await Promise.all(i),this}removeAllListeners(){return this.listeners={},this.onceListeners={},this}listenerCount(e){const t=this.listeners[e]?this.listeners[e].length:0,s=this.onceListeners[e]?this.onceListeners[e].length:0;return t+s}eventNames(){const e=Object.keys(this.listeners),t=Object.keys(this.onceListeners);return[...new Set([...e,...t])]}setMaxListeners(e){if(typeof e!="number"||e<0)throw new Error("Max listeners must be a non-negative number");return this.maxListeners=e,this}namespace(e){const t=s=>`${e}:${s}`;return{on:(s,i)=>this.on(t(s),i),once:(s,i)=>this.once(t(s),i),off:(s,i)=>this.off(t(s),i),emit:(s,i)=>this.emit(t(s),i),emitAsync:(s,i)=>this.emitAsync(t(s),i)}}use(e){if(typeof e!="function")throw new Error("Middleware must be a function");const t=this.emit;return this.emit=(s,i)=>{try{const a=e(s,i);if(a===!1)return this;const r=a!==void 0?a:i;return t.call(this,s,r)}catch(a){return console.error("Error in event middleware:",a),t.call(this,s,i)}},this}emitError(e,t,s){t!=="error"&&setTimeout(()=>{this.emit("error",{error:e,originalEvent:t,callback:s.toString()})},0)}waitFor(e,t=null){return new Promise((s,i)=>{let a=null;const r=()=>{a&&clearTimeout(a)},n=o=>{r(),s(o)};this.once(e,n),t&&(a=setTimeout(()=>{this.off(e,n),i(new Error(`Timeout waiting for event: ${e}`))},t))})}debug(e=!0){return this.debugMode=e,console.log(e?"[EventBus] Debug mode enabled":"[EventBus] Debug mode disabled"),this}getStats(){const e=this.eventNames(),t={totalEvents:e.length,totalListeners:0,events:{},emissions:{...this.eventStats}};return e.forEach(s=>{const i=this.listenerCount(s);t.events[s]=i,t.totalListeners+=i}),t}updateEventStats(e){this.eventStats[e]||(this.eventStats[e]={count:0,firstEmission:Date.now(),lastEmission:null}),this.eventStats[e].count++,this.eventStats[e].lastEmission=Date.now()}getEventStats(e){const t=this.eventStats[e];return t?{...t,listenerCount:this.listenerCount(e),avgEmissionsPerMinute:this.calculateEmissionRate(t)}:null}calculateEmissionRate(e){if(!e.firstEmission||!e.lastEmission)return 0;const t=e.lastEmission-e.firstEmission;if(t===0)return 0;const s=t/(1e3*60);return Math.round(e.count/s*100)/100}resetStats(){return this.eventStats={},this}getTopEvents(e=10){return Object.entries(this.eventStats).map(([t,s])=>({event:t,count:s.count,rate:this.calculateEmissionRate(s),listeners:this.listenerCount(t)})).sort((t,s)=>s.count-t.count).slice(0,e)}debugInfo(){console.group("[EventBus] Debug Information"),console.log("Debug Mode:",this.debugMode),console.log("Max Listeners:",this.maxListeners);const e=this.getStats();return console.log("Total Events:",e.totalEvents),console.log("Total Listeners:",e.totalListeners),Object.keys(this.eventStats).length>0&&console.log("Top Events:",this.getTopEvents(5)),console.groupEnd(),this}}class ke{constructor(){this.config={baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"},trackDevice:!0,duidHeader:"X-Mojo-UID",duidTransport:"header"},this.interceptors={request:[],response:[]},this.duid=null,this.config.trackDevice&&this._initializeDuid()}_initializeDuid(){const e="mojo_device_uid";try{let t=localStorage.getItem(e);t?this.duid=t:(this.duid=this._generateDuid(),localStorage.setItem(e,this.duid))}catch(t){console.error("Could not access localStorage to get/set DUID.",t),this.duid=this._generateDuid()}}_generateDuid(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}configure(e){e.baseUrl&&(e.baseURL=e.baseUrl);const t=this.config.trackDevice;this.config={...this.config,...e,headers:{...this.config.headers,...e.headers}},this.config.trackDevice&&!t&&this._initializeDuid()}addInterceptor(e,t){this.interceptors[e]&&this.interceptors[e].push(t)}buildUrl(e){if(e.startsWith("http://")||e.startsWith("https://"))return e;const t=this.config.baseURL.endsWith("/")?this.config.baseURL.slice(0,-1):this.config.baseURL,s=e.startsWith("/")?e:`/${e}`;return`${t}${s}`}categorizeError(e,t=0){if(e.name==="TypeError"&&e.message.includes("fetch"))return{reason:"not_reachable",message:"Service is not reachable - please check your connection"};if(e.name==="AbortError")return{reason:"cancelled",message:"Request was cancelled"};if(e.name==="TimeoutError"||e.message.includes("timeout"))return{reason:"timed_out",message:"Request timed out - please try again"};if(t>=400){if(t===400)return{reason:"bad_request",message:"Invalid request data"};if(t===401)return{reason:"unauthorized",message:"Authentication required"};if(t===403)return{reason:"forbidden",message:"Access denied"};if(t===404)return{reason:"not_found",message:"Resource not found"};if(t===409)return{reason:"conflict",message:"Resource conflict"};if(t===422)return{reason:"validation_error",message:"Validation failed"};if(t===429)return{reason:"rate_limited",message:"Too many requests - please wait"};if(t>=500)return{reason:"server_error",message:"Server error - please try again later"};if(t>=400)return{reason:"client_error",message:"Request error"}}return e.message.includes("CORS")?{reason:"cors_error",message:"Cross-origin request blocked"}:e.message.includes("DNS")||e.message.includes("ENOTFOUND")?{reason:"dns_error",message:"Unable to resolve server address"}:{reason:"unknown_error",message:`Network error: ${e.message}`}}buildQueryString(e={}){const t=new URLSearchParams;Object.entries(e).forEach(([i,a])=>{a!=null&&(Array.isArray(a)?a.forEach(r=>t.append(`${i}[]`,r)):t.append(i,a))});const s=t.toString();return s?`?${s}`:""}async processRequestInterceptors(e){let t={...e};for(const s of this.interceptors.request)try{t=await s(t)}catch(i){throw console.error("Request interceptor error:",i),i}return t}async processResponseInterceptors(e,t){let s={success:e.ok,status:e.status,statusText:e.statusText,headers:Object.fromEntries(e.headers.entries()),data:null,errors:null,message:null,reason:null};try{const i=e.headers.get("content-type");if(i&&i.includes("application/json")){const a=await e.json();if(s.data=a,!e.ok){const r=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=a.errors||{},s.message=a.message||r.message,s.reason=r.reason}}else if(s.data=await e.text(),!e.ok){const a=this.categorizeError(new Error("HTTP Error"),e.status);s.message=a.message,s.reason=a.reason}}catch{s.errors={parse:"Failed to parse response"},s.message="Invalid response format"}for(const i of this.interceptors.response)try{s=await i(s,t)}catch(a){console.error("Response interceptor error:",a)}return s}async request(e,t,s=null,i={},a={}){let r={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(i),headers:{...this.config.headers,...a.headers},data:s,options:{timeout:this.config.timeout,...a}};if(r=await this.processRequestInterceptors(r),this.config.trackDevice&&this.duid)if(this.config.duidTransport==="header")r.headers[this.config.duidHeader]=this.duid;else if(r.method==="GET"){const l=new URL(r.url);l.searchParams.append("duid",this.duid),r.url=l.toString()}else r.data&&typeof r.data=="object"&&!(r.data instanceof FormData)&&(r.data.duid=this.duid);const n={method:r.method,headers:r.headers},o=[];r.options.timeout&&o.push(AbortSignal.timeout(r.options.timeout)),r.options.signal&&o.push(r.options.signal),o.length>1?n.signal=AbortSignal.any?AbortSignal.any(o):o[0]:o.length===1&&(n.signal=o[0]),r.data&&["POST","PUT","PATCH"].includes(r.method)&&(r.data instanceof FormData?(n.body=r.data,delete n.headers["Content-Type"]):typeof r.data=="object"?n.body=JSON.stringify(r.data):n.body=r.data);try{const l=await fetch(r.url,n);return await this.processResponseInterceptors(l,r)}catch(l){if(l.name==="AbortError")throw l;const c=this.categorizeError(l),d={success:!1,status:0,statusText:"Network Error",headers:{},data:null,errors:{network:l.message},message:c.message,reason:c.reason},h={ok:!1,status:0,statusText:"Network Error",headers:new Headers,json:async()=>({}),text:async()=>""};return await this.processResponseInterceptors(h,r),d}}async GET(e,t={},s={}){return this.request("GET",e,null,t,s)}async POST(e,t={},s={},i={}){return this.request("POST",e,t,s,i)}async PUT(e,t={},s={},i={}){return this.request("PUT",e,t,s,i)}async PATCH(e,t={},s={},i={}){return this.request("PATCH",e,t,s,i)}async DELETE(e,t={},s={}){return this.request("DELETE",e,null,t,s)}async download(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=r.headers.get("content-disposition");let o=s.filename||"download";if(n){const m=n.match(/filename="?(.+)"?/);m&&m.length>1&&(o=m[1])}const l=r.body.getReader(),c=new ReadableStream({start(m){function p(){return l.read().then(({done:g,value:y})=>{if(g){m.close();return}return m.enqueue(y),p()})}return p()}}),d=await new Response(c).blob(),h=window.URL.createObjectURL(d),f=document.createElement("a");return f.style.display="none",f.href=h,f.download=o,document.body.appendChild(f),f.click(),window.URL.revokeObjectURL(h),f.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async downloadBlob(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=await r.blob(),o=r.headers.get("content-disposition");let l=s.filename||"download";if(o){const h=o.match(/filename="?(.+)"?/);h&&h.length>1&&(l=h[1])}const c=window.URL.createObjectURL(n),d=document.createElement("a");return d.style.display="none",d.href=c,d.download=l,document.body.appendChild(d),d.click(),window.URL.revokeObjectURL(c),d.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async upload(e,t,s={}){return new Promise((i,a)=>{if(!(t instanceof File)){a(new Error("Only single File objects are supported for legacy backend compatibility"));return}const r=new XMLHttpRequest;s.onProgress&&typeof s.onProgress=="function"&&(r.upload.onprogress=s.onProgress),r.onload=function(){r.status>=200&&r.status<300?i({data:r.response,status:r.status,statusText:r.statusText,xhr:r}):a(new Error(`Upload failed: ${r.status} ${r.statusText}`))},r.onerror=function(){a(new Error("Upload failed: Network error"))},r.ontimeout=function(){a(new Error("Upload failed: Timeout"))},r.open("PUT",e),r.setRequestHeader("Content-Type",t.type),s.timeout&&(r.timeout=s.timeout),r.send(t)})}async uploadMultipart(e,t,s={},i={}){const a=new FormData;if(t instanceof FileList)Array.from(t).forEach((r,n)=>{a.append(`file_${n}`,r)});else if(t instanceof File)a.append("file",t);else if(t instanceof FormData)return this.POST(e,t,{},i);return Object.entries(s).forEach(([r,n])=>{a.append(r,n)}),this.POST(e,a,{},i)}setAuthToken(e,t="Bearer"){e?this.config.headers.Authorization=`${t} ${e}`:delete this.config.headers.Authorization}clearAuth(){delete this.config.headers.Authorization}isRetryableError(e){return["not_reachable","timed_out","server_error","dns_error"].includes(e.reason)}requiresAuth(e){return e.reason==="unauthorized"}isNetworkError(e){return["not_reachable","timed_out","cancelled","cors_error","dns_error"].includes(e.reason)}getUserMessage(e){return e.message?e.message:{not_reachable:"Unable to connect to the server. Please check your internet connection.",timed_out:"The request took too long. Please try again.",cancelled:"The request was cancelled.",unauthorized:"Please log in to continue.",forbidden:"You don't have permission to perform this action.",not_found:"The requested resource was not found.",validation_error:"Please check your input and try again.",rate_limited:"Too many requests. Please wait a moment before trying again.",server_error:"Server error. Please try again later.",cors_error:"Access blocked by security policy.",dns_error:"Unable to reach the server.",unknown_error:"An unexpected error occurred."}[e.reason]||"An error occurred. Please try again."}}const z=new ke;class K{constructor(e={}){this.config=e,this.initPluginRegistry(),this.name=e.name||"MOJO App",this.version=e.version||"1.0.0",this.debug=e.debug||!1,this.container=e.container||"#app",this.layoutType=e.layout||"portal",this.layoutConfig=e.layoutConfig||{},e.sidebar&&(this.layoutConfig.sidebarConfig=e.sidebar),e.topbar&&(this.layoutConfig.topbarConfig=e.topbar),this.layout=null,this.layoutConfig.containerId=this.container||this.containerId||"#app",this.pageContainer=e.pageContainer||"#page-container",this.basePath=e.basePath||"",this.routerMode=e.routerMode||e.router?.mode||"param",this.basePath=e.basePath||e.router?.base||"",this.defaultRoute=e.defaultRoute||"home",this.session=e.session||{},this.router=null,this.navigation=e.navigation||{},this.state={currentPage:null,previousPage:null,loading:!1},this.events=new Ee,this.rest=z,e.api&&this.rest.configure(e.api),this.router=new te({mode:this.routerMode==="param"?"params":this.routerMode,basePath:this.basePath,defaultRoute:this.defaultRoute,eventEmitter:this.events}),this.events.on("route:changed",async t=>{const{pageName:s,params:i,query:a}=t;await this.showPage(s,a,i,{fromRouter:!0})}),typeof window<"u"&&(window.MOJO=window.MOJO||{},window.MOJO.router=this.router),this.setupFocusTracking(),this.pageCache=new Map,this.pageClasses=new Map,this.componentClasses=new Map,this.modelClasses=new Map,this.currentPage=null,this.isStarted=!1,window.matchUUID?window[window.matchUUID]=this:window.MOJO?window.MOJO.app=this:window.__app__=this}async start(){if(this.isStarted){console.warn("WebApp already started");return}try{this.setupPageContainer(),this.validateDefaultRoute(),await this.setupRouter(),this.isStarted=!0,this.router.allowPopState=!1,this.events.emit("app:ready",{app:this})}catch(e){throw console.error(`Failed to start ${this.name}:`,e),this.showError("Failed to start application"),e}}async setupRouter(){if(!this.router){console.error("Router not initialized");return}this.events.on("route:notfound",async e=>{console.warn(`Route not found: ${e.path}`),this._show404(e.path)}),this.router.start(),console.log(`Router started in ${this.routerMode} mode`)}setupPageContainer(){const e=typeof this.container=="string"?document.querySelector(this.container):this.container;e&&!e.querySelector("#page-container")&&(e.innerHTML='<div id="page-container"></div>'),this.pageContainer="#page-container"}registerPage(e,t,s={}){if(typeof e!="string"||!e)return console.error("registerPage: pageName must be a non-empty string"),this;if(typeof t!="function")return console.error("registerPage: PageClass must be a constructor function"),this;if(s.containerId||(s.containerId=this.pageContainer),this.pageClasses.set(e,{PageClass:t,constructorOptions:s}),this.router){let i=s.route||`/${e}`;i.startsWith("/")||(i=`/${i}`),s.route=i,this.router.addRoute(i,e)}return this}getPage(e){return this.pageCache.get(e)}getPagePermissions(e){if(this.pageCache.has(e))return this.pageCache.get(e).permissions;const t=this.pageClasses.get(e);if(!t)return null;const{PageClass:s,constructorOptions:i}=t;return i?i.permissions:null}getOrCreatePage(e){if(this.pageCache.has(e))return this.pageCache.get(e);const t=this.pageClasses.get(e);if(!t)return console.error(`Page not registered: ${e}`),null;const{PageClass:s,constructorOptions:i}=t;try{const a={pageName:e,...i,app:this},r=new s(a);return i.route&&(r.route=i.route),this.pageCache.set(e,r),console.log(`Created page: ${e} with route: ${r.route}`),r}catch(a){return console.error(`Failed to create page ${e}:`,a),null}}async showPage(e,t={},s={},i={}){const{fromRouter:a=!1,replace:r=!1,force:n=!1}=i;try{let o,l;typeof e=="string"?(l=e,o=this.getOrCreatePage(e)):e&&typeof e=="object"&&(o=e,l=e.pageName);const c=this.currentPage;if(!o){this._show404(l,s,t,a);return}if(this.events.emit("page:showing",{page:o,pageName:o.pageName,params:s,query:t,fromRouter:a}),!o.canEnter()){this._showDeniedPage(o,s,t,a);return}c&&c!==o&&await this._exitOldPage(c),await o.onParams(s,t),c!==o&&await o.onEnter(),o.syncUrl(),this.events.emit("page:show",{page:o,pageName:o.pageName,params:s,query:t,fromRouter:a}),await o.render(),this.currentPage=o,console.log(`✅ Showing page: ${o.pageName}`,{query:t,params:s})}catch(o){console.error("Error in showPage:",o),this.showError(`Failed to load page: ${o.message}`),e!=="error"&&await this.showPage("error",{},{error:o,originalPage:e},{fromRouter:a})}}async _show404(e,t,s,i){const a=this.getOrCreatePage("404");a&&(a.setInfo&&a.setInfo(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:404",{page:null,pageName:e,params:t,query:s,fromRouter:i}))}async _showDeniedPage(e,t,s,i){const a=this.getOrCreatePage("denied");a.setDeniedPage&&a.setDeniedPage(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:denied",{page:e,pageName:e.pageName,params:t,query:s,fromRouter:i})}async _exitOldPage(e){if(e)try{await e.onExit(),await e.unmount(),this.events.emit("page:hide",{page:e})}catch(t){console.error(`Error exiting page ${e.pageName}:`,t)}}async navigate(e,t={},s={}){if(!this.router){console.error("Router not initialized");return}let i=e;if(Object.keys(t).length>0){const a=new URLSearchParams(t).toString();i+=(e.includes("?")?"&":"?")+a}return await this.router.navigate(i,s)}async navigateToDefault(e={}){return await this.showPage(this.defaultRoute,{},{},e)}back(){this.router?this.router.back():console.warn("Router not initialized")}forward(){this.router?this.router.forward():console.warn("Router not initialized")}getCurrentPage(){return this.currentPage}getPageContainer(){return this.layout&&this.layout.getPageContainer?this.layout.getPageContainer():typeof this.pageContainer=="string"?document.querySelector(this.pageContainer):this.pageContainer}async showError(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Error",{size:"md",class:"text-danger"})}catch(t){this.events.emit("notification",{message:e,type:"error"}),typeof window<"u"&&window?.console&&console.error("[WebApp] showError fallback:",t),typeof window<"u"&&alert(`Error: ${e}`)}}async showSuccess(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Success",{size:"md",class:"text-success"})}catch(t){this.events.emit("notification",{message:e,type:"success"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showSuccess fallback:",t),typeof window<"u"&&alert(`Success: ${e}`)}}async showInfo(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Information",{size:"md",class:"text-info"})}catch(t){this.events.emit("notification",{message:e,type:"info"}),typeof window<"u"&&window?.console&&console.info("[WebApp] showInfo fallback:",t),typeof window<"u"&&alert(`Info: ${e}`)}}async showWarning(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Warning",{size:"md",class:"text-warning"})}catch(t){this.events.emit("notification",{message:e,type:"warning"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showWarning fallback:",t),typeof window<"u"&&alert(`Warning: ${e}`)}}showNotification(e,t="info"){this.events.emit("notification",{message:e,type:t})}async showLoading(e={}){typeof e=="string"&&(e={message:e});try{(await Promise.resolve().then(()=>_)).default.showBusy(e)}catch(t){typeof window<"u"&&window?.console&&console.warn("[WebApp] showLoading fallback:",t,e),this.events.emit("notification",{message:e.message||"Loading...",type:"info"})}}async hideLoading(){try{(await Promise.resolve().then(()=>_)).default.hideBusy()}catch(e){typeof window<"u"&&window?.console&&console.warn("[WebApp] hideLoading fallback:",e)}}async showModelForm(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showModelForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showModelForm failed:",t),t}}async showForm(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showForm failed:",t),t}}async showDialog(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async showAlert(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async confirm(e,t="Confirm",s={}){return await(await Promise.resolve().then(()=>_)).default.confirm(e,t,s)}setupFocusTracking(){if(typeof window>"u")return;this.isFocused=!document.hidden;const e=()=>{const i=this.isFocused;this.isFocused=!document.hidden,i!==this.isFocused&&(this.isFocused?this.events.emit("browser:focus"):this.events.emit("browser:blur"))},t=()=>{this.isFocused||(this.isFocused=!0,this.events.emit("browser:focus"))},s=()=>{this.isFocused&&(this.isFocused=!1,this.events.emit("browser:blur"))};document.addEventListener("visibilitychange",e),window.addEventListener("focus",t),window.addEventListener("blur",s),this._focusHandlers={visibilitychange:e,focus:t,blur:s}}setupErrorHandling(){window.addEventListener("error",e=>{console.error("Global error:",e.error),this.debug&&this.showError(`Error: ${e.error?.message||"Unknown error"}`)}),window.addEventListener("unhandledrejection",e=>{console.error("Unhandled promise rejection:",e.reason),this.debug&&this.showError(`Promise rejected: ${e.reason?.message||"Unknown error"}`)})}escapeHtml(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}getState(e){return e?this.state[e]:this.state}setState(e){const t={...this.state};Object.assign(this.state,e),this.events.emit("state:changed",{oldState:t,newState:this.state,updates:e})}registerComponent(e,t){this.componentClasses.set(e,t)}getComponent(e){return this.componentClasses.get(e)}registerModel(e,t){this.modelClasses.set(e,t)}getModel(e){return this.modelClasses.get(e)}setupRest(){this.rest=z,z.configure(this.api)}async destroy(){console.log("Destroying WebApp..."),this.router&&this.router.stop(),this._focusHandlers&&typeof window<"u"&&(document.removeEventListener("visibilitychange",this._focusHandlers.visibilitychange),window.removeEventListener("focus",this._focusHandlers.focus),window.removeEventListener("blur",this._focusHandlers.blur));const e=Array.from(this.pageCache.values());if(await Promise.allSettled(e.map(async t=>{try{t.destroy&&await t.destroy()}catch(s){console.error("Error destroying page:",s)}})),this.layout&&this.layout.destroy)try{await this.layout.destroy()}catch(t){console.error("Error destroying layout:",t)}this.pageCache.clear(),this.pageClasses.clear(),this.componentClasses.clear(),this.modelClasses.clear(),typeof window<"u"&&window.MOJO&&delete window.MOJO.router,this.isStarted=!1,console.log(`✨ ${this.name} destroyed`)}buildPagePath(e,t,s){let i=e.route||`/${e.pageName.toLowerCase()}`;if(Object.keys(t).forEach(a=>{(typeof t[a]=="string"||typeof t[a]=="number")&&(i=i.replace(`:${a}`,t[a]))}),s&&Object.keys(s).length>0){const a=new URLSearchParams(s).toString();i+=(i.includes("?")?"&":"?")+a}return i}validateDefaultRoute(){this.pageClasses.has(this.defaultRoute)?console.log(`✅ Default route '${this.defaultRoute}' is registered`):(console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`),console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`),console.warn(" Or change default route: new WebApp({ defaultRoute: 'your-page' });"))}findFallbackPage(){const e=["404","error","denied"];for(const[t]of this.pageClasses.entries())if(!e.includes(t))return t;return null}static create(e={}){return new K(e)}initPluginRegistry(){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.app=this)}static registerPlugin(e,t){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.plugins[e]=t,console.debug(`MOJO Plugin registered: ${e}`))}}const Ae="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==";class Ie{constructor(){this.formatters=new Map,this.registerBuiltInFormatters()}escapeHtml(e){if(e==null)return"";const t={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,s=>t[s])}registerBuiltInFormatters(){this.register("date",this.date.bind(this)),this.register("time",this.time.bind(this)),this.register("datetime",this.datetime.bind(this)),this.register("datetime_tz",this.datetime_tz.bind(this)),this.register("datatime_tz",this.datetime_tz.bind(this)),this.register("date_range",this.date_range.bind(this)),this.register("datetime_range",this.datetime_range.bind(this)),this.register("relative",this.relative.bind(this)),this.register("fromNow",this.relative.bind(this)),this.register("relative_short",this.relative_short.bind(this)),this.register("iso",this.iso.bind(this)),this.register("epoch",e=>{if(e==null||e==="")return e;const t=parseFloat(e);return isNaN(t)?e:t*1e3}),this.register("number",this.number.bind(this)),this.register("currency",this.currency.bind(this)),this.register("percent",this.percent.bind(this)),this.register("filesize",this.filesize.bind(this)),this.register("ordinal",this.ordinal.bind(this)),this.register("compact",this.compact.bind(this)),this.register("add",this.add.bind(this)),this.register("subtract",this.subtract.bind(this)),this.register("multiply",this.multiply.bind(this)),this.register("divide",this.divide.bind(this)),this.register("sub",this.subtract.bind(this)),this.register("mult",this.multiply.bind(this)),this.register("div",this.divide.bind(this)),this.register("uppercase",e=>String(e).toUpperCase()),this.register("lowercase",e=>String(e).toLowerCase()),this.register("upper",e=>String(e).toUpperCase()),this.register("lower",e=>String(e).toLowerCase()),this.register("capitalize",this.capitalize.bind(this)),this.register("caps",this.capitalize.bind(this)),this.register("replace",this.replace.bind(this)),this.register("truncate",this.truncate.bind(this)),this.register("truncate_middle",this.truncate_middle.bind(this)),this.register("truncate_front",this.truncate_front.bind(this)),this.register("slug",this.slug.bind(this)),this.register("initials",this.initials.bind(this)),this.register("mask",this.mask.bind(this)),this.register("hex",this.hex.bind(this)),this.register("tohex",this.hex.bind(this)),this.register("unhex",this.unhex.bind(this)),this.register("fromhex",this.unhex.bind(this)),this.register("email",this.email.bind(this)),this.register("phone",this.phone.bind(this)),this.register("url",this.url.bind(this)),this.register("badge",this.badge.bind(this)),this.register("badgeClass",this.badgeClass.bind(this)),this.register("status",this.status.bind(this)),this.register("status_text",this.status_text.bind(this)),this.register("status_icon",this.status_icon.bind(this)),this.register("boolean",this.boolean.bind(this)),this.register("bool",this.bool.bind(this)),this.register("yesno",e=>this.boolean(e,"Yes","No")),this.register("yesnoicon",this.yesnoicon.bind(this)),this.register("icon",this.icon.bind(this)),this.register("avatar",this.avatar.bind(this)),this.register("image",this.image.bind(this)),this.register("tooltip",this.tooltip.bind(this)),this.register("linkify",this.linkify.bind(this)),this.register("clipboard",this.clipboard.bind(this)),this.register("default",this.default.bind(this)),this.register("equals",this.equals.bind(this)),this.register("json",this.json.bind(this)),this.register("raw",e=>e),this.register("custom",(e,t)=>typeof t=="function"?t(e):e),this.register("iter",this.iter.bind(this)),this.register("keys",e=>e&&typeof e=="object"&&!Array.isArray(e)?Object.keys(e):null),this.register("values",e=>e&&typeof e=="object"&&!Array.isArray(e)?Object.values(e):null),this.register("plural",this.plural.bind(this)),this.register("list",this.formatList.bind(this)),this.register("duration",this.duration.bind(this)),this.register("hash",this.hash.bind(this)),this.register("stripHtml",this.stripHtml.bind(this)),this.register("highlight",this.highlight.bind(this)),this.register("nl2br",this.nl2br.bind(this)),this.register("code",this.code.bind(this)),this.register("pre",e=>`<pre class="bg-light p-2 rounded border">${this.escapeHtml(String(e))}</pre>`)}relative_short(e){return this.relative(e,!0)}linkify(e,t={}){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a={urls:!0,emails:!0,target:"_blank",rel:"noopener noreferrer"},r=t&&typeof t=="object"?{...a,...t}:a;let n=i;if(r.urls!==!1){const o=/(^|\s)((?:https?:\/\/|www\.)[^\s<]+)/gi;n=n.replace(o,(l,c,d)=>{const h=d.startsWith("www.")?`https://${d}`:d;return`${c}<a href="${h}" target="${r.target}" rel="${r.rel}">${d}</a>`})}if(r.emails!==!1){const o=/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;n=n.replace(o,l=>`<a href="mailto:${l}">${l}</a>`)}return n}clipboard(e,t="text"){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a=t!=="icon-only",r=`
|
|
1
|
+
var MOJO=function(D){"use strict";class te{constructor(e={}){this.defaultRoute=e.defaultRoute||"home",this.routes=[],this.currentRoute=null,this.eventEmitter=e.eventEmitter||null,this.boundHandlePopState=this.handlePopState.bind(this)}start(){window.addEventListener("popstate",this.boundHandlePopState),this.handleCurrentLocation()}stop(){window.removeEventListener("popstate",this.boundHandlePopState)}addRoute(e,t){this.routes.push({pattern:this.normalizePattern(e),regex:this.patternToRegex(e),pageName:t,paramNames:this.extractParamNames(e)})}async navigate(e,t={}){const{replace:s=!1,state:i=null,trigger:a=!0}=t,{pageName:r,queryParams:n}=this.parseInput(e);a&&await this.handleRouteChange(r,n)}back(){window.history.back()}forward(){window.history.forward()}getCurrentRoute(){return this.currentRoute}getCurrentPath(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();return this.buildPublicUrl(e,t)}handlePopState(e){this.allowPopState?this.handleCurrentLocation():console.warn("PopStateEvent is not allowed")}async handleCurrentLocation(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();await this.handleRouteChange(e,t)}async handleRouteChange(e,t){const s="/"+e,i=this.matchRoute(s),a=this.buildPublicUrl(e,t);return i?(this.currentRoute=i,this.eventEmitter&&this.eventEmitter.emit("route:changed",{path:a,pageName:i.pageName,params:i.params,query:t,route:i}),i):(console.log("No route matched for page:",e),this.eventEmitter&&this.eventEmitter.emit("route:notfound",{path:a}),null)}matchRoute(e){for(const t of this.routes){const s=e.match(t.regex);if(s){const i={};return t.paramNames.forEach((a,r)=>{i[a]=s[r+1]}),{...t,params:i,path:e}}}return null}parseInput(e){let t=this.defaultRoute,s={};if(!e)return{pageName:t,queryParams:s};try{if(e.includes("?")){const[i,a]=e.split("?",2),r=new URLSearchParams(a);if(r.has("page")){t=r.get("page")||this.defaultRoute;for(const[n,l]of r)n!=="page"&&(s[n]=l)}else{i.startsWith("/")?t=i.substring(1)||this.defaultRoute:t=i||this.defaultRoute;for(const[n,l]of r)s[n]=l}}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,l])=>{l!=null&&l!==""&&a.searchParams.set(n,String(l))});const r=a.toString();s?window.history.replaceState(i,"",r):window.history.pushState(i,"",r)}patternToRegex(e){let t=e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/\/:([^/?]+)\?/g,"(?:/([^/]+))?").replace(/:([^/]+)/g,"([^/]+)");return new RegExp(`^${t}$`)}extractParamNames(e){return(e.match(/:([^/?]+)\??/g)||[]).map(s=>s.replace(/[:?]/g,""))}normalizePattern(e){return e.startsWith("/")?e:`/${e}`}updateUrl(e={},t={}){const{replace:s=!1}=t,{pageName:i}=this.parseCurrentUrl();this.updateBrowserUrl(i,e,s)}buildUrl(e,t={}){return this.buildPublicUrl(e,t)}doRoutesMatch(e,t){if(!e||!t)return!1;const{pageName:s}=this.parseInput(e),{pageName:i}=this.parseInput(t);return s===i}}class Ee{constructor(){this.listeners={},this.onceListeners={},this.maxListeners=100,this.debugMode=!1,this.eventStats={}}on(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.on(s,t)),this):(this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].length>=this.maxListeners&&console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${e}`),this.listeners[e].push(t),this)}once(e,t){if(typeof t!="function")throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(s=>this.once(s,t)),this):(this.onceListeners[e]||(this.onceListeners[e]=[]),this.onceListeners[e].push(t),this)}off(e,t){if(Array.isArray(e))return e.forEach(s=>this.off(s,t)),this;if(!t)return delete this.listeners[e],delete this.onceListeners[e],this;if(this.listeners[e]){const s=this.listeners[e].indexOf(t);s!==-1&&(this.listeners[e].splice(s,1),this.listeners[e].length===0&&delete this.listeners[e])}if(this.onceListeners[e]){const s=this.onceListeners[e].indexOf(t);s!==-1&&(this.onceListeners[e].splice(s,1),this.onceListeners[e].length===0&&delete this.onceListeners[e])}return this}emit(e,t){this.updateEventStats(e),this.debugMode&&console.log(`[EventBus] Emitting: ${e}`,t);const s=[];return this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]),this.debugMode&&s.length>0&&console.log(`[EventBus] ${s.length} listener(s) for '${e}'`),s.forEach(i=>{try{i(t,e)&&(e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault())}catch(a){console.error(`Error in event listener for '${e}':`,a),this.emitError(a,e,i)}}),this}async emitAsync(e,t){const s=[];this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]);const i=s.map(a=>new Promise(r=>{try{const n=a(t,e);r(n)}catch(n){console.error(`Error in async event listener for '${e}':`,n),this.emitError(n,e,a),r()}}));return await Promise.all(i),this}removeAllListeners(){return this.listeners={},this.onceListeners={},this}listenerCount(e){const t=this.listeners[e]?this.listeners[e].length:0,s=this.onceListeners[e]?this.onceListeners[e].length:0;return t+s}eventNames(){const e=Object.keys(this.listeners),t=Object.keys(this.onceListeners);return[...new Set([...e,...t])]}setMaxListeners(e){if(typeof e!="number"||e<0)throw new Error("Max listeners must be a non-negative number");return this.maxListeners=e,this}namespace(e){const t=s=>`${e}:${s}`;return{on:(s,i)=>this.on(t(s),i),once:(s,i)=>this.once(t(s),i),off:(s,i)=>this.off(t(s),i),emit:(s,i)=>this.emit(t(s),i),emitAsync:(s,i)=>this.emitAsync(t(s),i)}}use(e){if(typeof e!="function")throw new Error("Middleware must be a function");const t=this.emit;return this.emit=(s,i)=>{try{const a=e(s,i);if(a===!1)return this;const r=a!==void 0?a:i;return t.call(this,s,r)}catch(a){return console.error("Error in event middleware:",a),t.call(this,s,i)}},this}emitError(e,t,s){t!=="error"&&setTimeout(()=>{this.emit("error",{error:e,originalEvent:t,callback:s.toString()})},0)}waitFor(e,t=null){return new Promise((s,i)=>{let a=null;const r=()=>{a&&clearTimeout(a)},n=l=>{r(),s(l)};this.once(e,n),t&&(a=setTimeout(()=>{this.off(e,n),i(new Error(`Timeout waiting for event: ${e}`))},t))})}debug(e=!0){return this.debugMode=e,console.log(e?"[EventBus] Debug mode enabled":"[EventBus] Debug mode disabled"),this}getStats(){const e=this.eventNames(),t={totalEvents:e.length,totalListeners:0,events:{},emissions:{...this.eventStats}};return e.forEach(s=>{const i=this.listenerCount(s);t.events[s]=i,t.totalListeners+=i}),t}updateEventStats(e){this.eventStats[e]||(this.eventStats[e]={count:0,firstEmission:Date.now(),lastEmission:null}),this.eventStats[e].count++,this.eventStats[e].lastEmission=Date.now()}getEventStats(e){const t=this.eventStats[e];return t?{...t,listenerCount:this.listenerCount(e),avgEmissionsPerMinute:this.calculateEmissionRate(t)}:null}calculateEmissionRate(e){if(!e.firstEmission||!e.lastEmission)return 0;const t=e.lastEmission-e.firstEmission;if(t===0)return 0;const s=t/(1e3*60);return Math.round(e.count/s*100)/100}resetStats(){return this.eventStats={},this}getTopEvents(e=10){return Object.entries(this.eventStats).map(([t,s])=>({event:t,count:s.count,rate:this.calculateEmissionRate(s),listeners:this.listenerCount(t)})).sort((t,s)=>s.count-t.count).slice(0,e)}debugInfo(){console.group("[EventBus] Debug Information"),console.log("Debug Mode:",this.debugMode),console.log("Max Listeners:",this.maxListeners);const e=this.getStats();return console.log("Total Events:",e.totalEvents),console.log("Total Listeners:",e.totalListeners),Object.keys(this.eventStats).length>0&&console.log("Top Events:",this.getTopEvents(5)),console.groupEnd(),this}}class ke{constructor(){this.config={baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"},trackDevice:!0,duidHeader:"X-Mojo-UID",duidTransport:"header"},this.interceptors={request:[],response:[]},this.duid=null,this.config.trackDevice&&this._initializeDuid()}_initializeDuid(){const e="mojo_device_uid";try{let t=localStorage.getItem(e);t?this.duid=t:(this.duid=this._generateDuid(),localStorage.setItem(e,this.duid))}catch(t){console.error("Could not access localStorage to get/set DUID.",t),this.duid=this._generateDuid()}}_generateDuid(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}configure(e){e.baseUrl&&(e.baseURL=e.baseUrl);const t=this.config.trackDevice;this.config={...this.config,...e,headers:{...this.config.headers,...e.headers}},this.config.trackDevice&&!t&&this._initializeDuid()}addInterceptor(e,t){this.interceptors[e]&&this.interceptors[e].push(t)}buildUrl(e){if(e.startsWith("http://")||e.startsWith("https://"))return e;const t=this.config.baseURL.endsWith("/")?this.config.baseURL.slice(0,-1):this.config.baseURL,s=e.startsWith("/")?e:`/${e}`;return`${t}${s}`}categorizeError(e,t=0){if(e.name==="TypeError"&&e.message.includes("fetch"))return{reason:"not_reachable",message:"Service is not reachable - please check your connection"};if(e.name==="AbortError")return{reason:"cancelled",message:"Request was cancelled"};if(e.name==="TimeoutError"||e.message.includes("timeout"))return{reason:"timed_out",message:"Request timed out - please try again"};if(t>=400){if(t===400)return{reason:"bad_request",message:"Invalid request data"};if(t===401)return{reason:"unauthorized",message:"Authentication required"};if(t===403)return{reason:"forbidden",message:"Access denied"};if(t===404)return{reason:"not_found",message:"Resource not found"};if(t===409)return{reason:"conflict",message:"Resource conflict"};if(t===422)return{reason:"validation_error",message:"Validation failed"};if(t===429)return{reason:"rate_limited",message:"Too many requests - please wait"};if(t>=500)return{reason:"server_error",message:"Server error - please try again later"};if(t>=400)return{reason:"client_error",message:"Request error"}}return e.message.includes("CORS")?{reason:"cors_error",message:"Cross-origin request blocked"}:e.message.includes("DNS")||e.message.includes("ENOTFOUND")?{reason:"dns_error",message:"Unable to resolve server address"}:{reason:"unknown_error",message:`Network error: ${e.message}`}}buildQueryString(e={}){const t=new URLSearchParams;Object.entries(e).forEach(([i,a])=>{a!=null&&(Array.isArray(a)?a.forEach(r=>t.append(`${i}[]`,r)):t.append(i,a))});const s=t.toString();return s?`?${s}`:""}async processRequestInterceptors(e){let t={...e};for(const s of this.interceptors.request)try{t=await s(t)}catch(i){throw console.error("Request interceptor error:",i),i}return t}async processResponseInterceptors(e,t){let s={success:e.ok,status:e.status,statusText:e.statusText,headers:Object.fromEntries(e.headers.entries()),data:null,errors:null,message:null,reason:null};try{const i=e.headers.get("content-type");if(i&&i.includes("application/json")){const a=await e.json();if(s.data=a,!e.ok){const r=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=a.errors||{},s.message=a.message||r.message,s.reason=r.reason}}else if(s.data=await e.text(),!e.ok){const a=this.categorizeError(new Error("HTTP Error"),e.status);s.message=a.message,s.reason=a.reason}}catch{s.errors={parse:"Failed to parse response"},s.message="Invalid response format"}for(const i of this.interceptors.response)try{s=await i(s,t)}catch(a){console.error("Response interceptor error:",a)}return s}async request(e,t,s=null,i={},a={}){let r={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(i),headers:{...this.config.headers,...a.headers},data:s,options:{timeout:this.config.timeout,...a}};if(r=await this.processRequestInterceptors(r),this.config.trackDevice&&this.duid)if(this.config.duidTransport==="header")r.headers[this.config.duidHeader]=this.duid;else if(r.method==="GET"){const o=new URL(r.url);o.searchParams.append("duid",this.duid),r.url=o.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},l=[];r.options.timeout&&l.push(AbortSignal.timeout(r.options.timeout)),r.options.signal&&l.push(r.options.signal),l.length>1?n.signal=AbortSignal.any?AbortSignal.any(l):l[0]:l.length===1&&(n.signal=l[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 o=await fetch(r.url,n);return await this.processResponseInterceptors(o,r)}catch(o){if(o.name==="AbortError")throw o;const c=this.categorizeError(o),d={success:!1,status:0,statusText:"Network Error",headers:{},data:null,errors:{network:o.message},message:c.message,reason:c.reason},h={ok:!1,status:0,statusText:"Network Error",headers:new Headers,json:async()=>({}),text:async()=>""};return await this.processResponseInterceptors(h,r),d}}async GET(e,t={},s={}){return this.request("GET",e,null,t,s)}async POST(e,t={},s={},i={}){return this.request("POST",e,t,s,i)}async PUT(e,t={},s={},i={}){return this.request("PUT",e,t,s,i)}async PATCH(e,t={},s={},i={}){return this.request("PATCH",e,t,s,i)}async DELETE(e,t={},s={}){return this.request("DELETE",e,null,t,s)}async download(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=r.headers.get("content-disposition");let l=s.filename||"download";if(n){const m=n.match(/filename="?(.+)"?/);m&&m.length>1&&(l=m[1])}const o=r.body.getReader(),c=new ReadableStream({start(m){function p(){return o.read().then(({done:g,value:y})=>{if(g){m.close();return}return m.enqueue(y),p()})}return p()}}),d=await new Response(c).blob(),h=window.URL.createObjectURL(d),f=document.createElement("a");return f.style.display="none",f.href=h,f.download=l,document.body.appendChild(f),f.click(),window.URL.revokeObjectURL(h),f.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async downloadBlob(e,t={},s={}){const a={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete a.headers["Content-Type"];try{const r=await fetch(a.url,{method:a.method,headers:a.headers,signal:a.options.signal});if(!r.ok)throw new Error(`Download failed: ${r.status} ${r.statusText}`);const n=await r.blob(),l=r.headers.get("content-disposition");let o=s.filename||"download";if(l){const h=l.match(/filename="?(.+)"?/);h&&h.length>1&&(o=h[1])}const c=window.URL.createObjectURL(n),d=document.createElement("a");return d.style.display="none",d.href=c,d.download=o,document.body.appendChild(d),d.click(),window.URL.revokeObjectURL(c),d.remove(),{success:!0,message:"Download initiated"}}catch(r){return console.error("Download error:",r),{success:!1,message:r.message}}}async upload(e,t,s={}){return new Promise((i,a)=>{if(!(t instanceof File)){a(new Error("Only single File objects are supported for legacy backend compatibility"));return}const r=new XMLHttpRequest;s.onProgress&&typeof s.onProgress=="function"&&(r.upload.onprogress=s.onProgress),r.onload=function(){r.status>=200&&r.status<300?i({data:r.response,status:r.status,statusText:r.statusText,xhr:r}):a(new Error(`Upload failed: ${r.status} ${r.statusText}`))},r.onerror=function(){a(new Error("Upload failed: Network error"))},r.ontimeout=function(){a(new Error("Upload failed: Timeout"))},r.open("PUT",e),r.setRequestHeader("Content-Type",t.type),s.timeout&&(r.timeout=s.timeout),r.send(t)})}async uploadMultipart(e,t,s={},i={}){const a=new FormData;if(t instanceof FileList)Array.from(t).forEach((r,n)=>{a.append(`file_${n}`,r)});else if(t instanceof File)a.append("file",t);else if(t instanceof FormData)return this.POST(e,t,{},i);return Object.entries(s).forEach(([r,n])=>{a.append(r,n)}),this.POST(e,a,{},i)}setAuthToken(e,t="Bearer"){e?this.config.headers.Authorization=`${t} ${e}`:delete this.config.headers.Authorization}clearAuth(){delete this.config.headers.Authorization}isRetryableError(e){return["not_reachable","timed_out","server_error","dns_error"].includes(e.reason)}requiresAuth(e){return e.reason==="unauthorized"}isNetworkError(e){return["not_reachable","timed_out","cancelled","cors_error","dns_error"].includes(e.reason)}getUserMessage(e){return e.message?e.message:{not_reachable:"Unable to connect to the server. Please check your internet connection.",timed_out:"The request took too long. Please try again.",cancelled:"The request was cancelled.",unauthorized:"Please log in to continue.",forbidden:"You don't have permission to perform this action.",not_found:"The requested resource was not found.",validation_error:"Please check your input and try again.",rate_limited:"Too many requests. Please wait a moment before trying again.",server_error:"Server error. Please try again later.",cors_error:"Access blocked by security policy.",dns_error:"Unable to reach the server.",unknown_error:"An unexpected error occurred."}[e.reason]||"An error occurred. Please try again."}}const z=new ke;class K{constructor(e={}){this.config=e,this.initPluginRegistry(),this.name=e.name||"MOJO App",this.version=e.version||"1.0.0",this.debug=e.debug||!1,this.container=e.container||"#app",this.layoutType=e.layout||"portal",this.layoutConfig=e.layoutConfig||{},e.sidebar&&(this.layoutConfig.sidebarConfig=e.sidebar),e.topbar&&(this.layoutConfig.topbarConfig=e.topbar),this.layout=null,this.layoutConfig.containerId=this.container||this.containerId||"#app",this.pageContainer=e.pageContainer||"#page-container",this.basePath=e.basePath||"",this.routerMode=e.routerMode||e.router?.mode||"param",this.basePath=e.basePath||e.router?.base||"",this.defaultRoute=e.defaultRoute||"home",this.session=e.session||{},this.router=null,this.navigation=e.navigation||{},this.state={currentPage:null,previousPage:null,loading:!1},this.events=new Ee,this.rest=z,e.api&&this.rest.configure(e.api),this.router=new te({mode:this.routerMode==="param"?"params":this.routerMode,basePath:this.basePath,defaultRoute:this.defaultRoute,eventEmitter:this.events}),this.events.on("route:changed",async t=>{const{pageName:s,params:i,query:a}=t;await this.showPage(s,a,i,{fromRouter:!0})}),typeof window<"u"&&(window.MOJO=window.MOJO||{},window.MOJO.router=this.router),this.setupFocusTracking(),this.pageCache=new Map,this.pageClasses=new Map,this.componentClasses=new Map,this.modelClasses=new Map,this.currentPage=null,this.isStarted=!1,window.matchUUID?window[window.matchUUID]=this:window.MOJO?window.MOJO.app=this:window.__app__=this}async start(){if(this.isStarted){console.warn("WebApp already started");return}try{this.setupPageContainer(),this.validateDefaultRoute(),await this.setupRouter(),this.isStarted=!0,this.router.allowPopState=!1,this.events.emit("app:ready",{app:this})}catch(e){throw console.error(`Failed to start ${this.name}:`,e),this.showError("Failed to start application"),e}}async setupRouter(){if(!this.router){console.error("Router not initialized");return}this.events.on("route:notfound",async e=>{console.warn(`Route not found: ${e.path}`),this._show404(e.path)}),this.router.start(),console.log(`Router started in ${this.routerMode} mode`)}setupPageContainer(){const e=typeof this.container=="string"?document.querySelector(this.container):this.container;e&&!e.querySelector("#page-container")&&(e.innerHTML='<div id="page-container"></div>'),this.pageContainer="#page-container"}registerPage(e,t,s={}){if(typeof e!="string"||!e)return console.error("registerPage: pageName must be a non-empty string"),this;if(typeof t!="function")return console.error("registerPage: PageClass must be a constructor function"),this;if(s.containerId||(s.containerId=this.pageContainer),this.pageClasses.set(e,{PageClass:t,constructorOptions:s}),this.router){let i=s.route||`/${e}`;i.startsWith("/")||(i=`/${i}`),s.route=i,this.router.addRoute(i,e)}return this}getPage(e){return this.pageCache.get(e)}getPagePermissions(e){if(this.pageCache.has(e))return this.pageCache.get(e).permissions;const t=this.pageClasses.get(e);if(!t)return null;const{PageClass:s,constructorOptions:i}=t;return i?i.permissions:null}getOrCreatePage(e){if(this.pageCache.has(e))return this.pageCache.get(e);const t=this.pageClasses.get(e);if(!t)return console.error(`Page not registered: ${e}`),null;const{PageClass:s,constructorOptions:i}=t;try{const a={pageName:e,...i,app:this},r=new s(a);return i.route&&(r.route=i.route),this.pageCache.set(e,r),console.log(`Created page: ${e} with route: ${r.route}`),r}catch(a){return console.error(`Failed to create page ${e}:`,a),null}}async showPage(e,t={},s={},i={}){const{fromRouter:a=!1,replace:r=!1,force:n=!1}=i;try{let l,o;typeof e=="string"?(o=e,l=this.getOrCreatePage(e)):e&&typeof e=="object"&&(l=e,o=e.pageName);const c=this.currentPage;if(!l){this._show404(o,s,t,a);return}if(this.events.emit("page:showing",{page:l,pageName:l.pageName,params:s,query:t,fromRouter:a}),!l.canEnter()){this._showDeniedPage(l,s,t,a);return}c&&c!==l&&await this._exitOldPage(c),await l.onParams(s,t),c!==l&&await l.onEnter(),l.syncUrl(),this.events.emit("page:show",{page:l,pageName:l.pageName,params:s,query:t,fromRouter:a}),await l.render(),this.currentPage=l,console.log(`✅ Showing page: ${l.pageName}`,{query:t,params:s})}catch(l){console.error("Error in showPage:",l),this.showError(`Failed to load page: ${l.message}`),e!=="error"&&await this.showPage("error",{},{error:l,originalPage:e},{fromRouter:a})}}async _show404(e,t,s,i){const a=this.getOrCreatePage("404");a&&(a.setInfo&&a.setInfo(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:404",{page:null,pageName:e,params:t,query:s,fromRouter:i}))}async _showDeniedPage(e,t,s,i){const a=this.getOrCreatePage("denied");a.setDeniedPage&&a.setDeniedPage(e),await this._exitOldPage(this.currentPage),await a.render(),this.currentPage=a,this.events.emit("page:denied",{page:e,pageName:e.pageName,params:t,query:s,fromRouter:i})}async _exitOldPage(e){if(e)try{await e.onExit(),await e.unmount(),this.events.emit("page:hide",{page:e})}catch(t){console.error(`Error exiting page ${e.pageName}:`,t)}}async navigate(e,t={},s={}){if(!this.router){console.error("Router not initialized");return}let i=e;if(Object.keys(t).length>0){const a=new URLSearchParams(t).toString();i+=(e.includes("?")?"&":"?")+a}return await this.router.navigate(i,s)}async navigateToDefault(e={}){return await this.showPage(this.defaultRoute,{},{},e)}back(){this.router?this.router.back():console.warn("Router not initialized")}forward(){this.router?this.router.forward():console.warn("Router not initialized")}getCurrentPage(){return this.currentPage}getPageContainer(){return this.layout&&this.layout.getPageContainer?this.layout.getPageContainer():typeof this.pageContainer=="string"?document.querySelector(this.pageContainer):this.pageContainer}async showError(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Error",{size:"md",class:"text-danger"})}catch(t){this.events.emit("notification",{message:e,type:"error"}),typeof window<"u"&&window?.console&&console.error("[WebApp] showError fallback:",t),typeof window<"u"&&alert(`Error: ${e}`)}}async showSuccess(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Success",{size:"md",class:"text-success"})}catch(t){this.events.emit("notification",{message:e,type:"success"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showSuccess fallback:",t),typeof window<"u"&&alert(`Success: ${e}`)}}async showInfo(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Information",{size:"md",class:"text-info"})}catch(t){this.events.emit("notification",{message:e,type:"info"}),typeof window<"u"&&window?.console&&console.info("[WebApp] showInfo fallback:",t),typeof window<"u"&&alert(`Info: ${e}`)}}async showWarning(e){try{await(await Promise.resolve().then(()=>_)).default.alert(e,"Warning",{size:"md",class:"text-warning"})}catch(t){this.events.emit("notification",{message:e,type:"warning"}),typeof window<"u"&&window?.console&&console.warn("[WebApp] showWarning fallback:",t),typeof window<"u"&&alert(`Warning: ${e}`)}}showNotification(e,t="info"){this.events.emit("notification",{message:e,type:t})}async showLoading(e={}){typeof e=="string"&&(e={message:e});try{(await Promise.resolve().then(()=>_)).default.showBusy(e)}catch(t){typeof window<"u"&&window?.console&&console.warn("[WebApp] showLoading fallback:",t,e),this.events.emit("notification",{message:e.message||"Loading...",type:"info"})}}async hideLoading(){try{(await Promise.resolve().then(()=>_)).default.hideBusy()}catch(e){typeof window<"u"&&window?.console&&console.warn("[WebApp] hideLoading fallback:",e)}}async showModelForm(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showModelForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showModelForm failed:",t),t}}async showForm(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showForm(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showForm failed:",t),t}}async showDialog(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async showAlert(e={}){try{return await(await Promise.resolve().then(()=>_)).default.showDialog(e)}catch(t){throw typeof window<"u"&&window?.console&&console.error("[WebApp] showDialog failed:",t),t}}async confirm(e,t="Confirm",s={}){return await(await Promise.resolve().then(()=>_)).default.confirm(e,t,s)}setupFocusTracking(){if(typeof window>"u")return;this.isFocused=!document.hidden;const e=()=>{const i=this.isFocused;this.isFocused=!document.hidden,i!==this.isFocused&&(this.isFocused?this.events.emit("browser:focus"):this.events.emit("browser:blur"))},t=()=>{this.isFocused||(this.isFocused=!0,this.events.emit("browser:focus"))},s=()=>{this.isFocused&&(this.isFocused=!1,this.events.emit("browser:blur"))};document.addEventListener("visibilitychange",e),window.addEventListener("focus",t),window.addEventListener("blur",s),this._focusHandlers={visibilitychange:e,focus:t,blur:s}}setupErrorHandling(){window.addEventListener("error",e=>{console.error("Global error:",e.error),this.debug&&this.showError(`Error: ${e.error?.message||"Unknown error"}`)}),window.addEventListener("unhandledrejection",e=>{console.error("Unhandled promise rejection:",e.reason),this.debug&&this.showError(`Promise rejected: ${e.reason?.message||"Unknown error"}`)})}escapeHtml(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}getState(e){return e?this.state[e]:this.state}setState(e){const t={...this.state};Object.assign(this.state,e),this.events.emit("state:changed",{oldState:t,newState:this.state,updates:e})}registerComponent(e,t){this.componentClasses.set(e,t)}getComponent(e){return this.componentClasses.get(e)}registerModel(e,t){this.modelClasses.set(e,t)}getModel(e){return this.modelClasses.get(e)}setupRest(){this.rest=z,z.configure(this.api)}async destroy(){console.log("Destroying WebApp..."),this.router&&this.router.stop(),this._focusHandlers&&typeof window<"u"&&(document.removeEventListener("visibilitychange",this._focusHandlers.visibilitychange),window.removeEventListener("focus",this._focusHandlers.focus),window.removeEventListener("blur",this._focusHandlers.blur));const e=Array.from(this.pageCache.values());if(await Promise.allSettled(e.map(async t=>{try{t.destroy&&await t.destroy()}catch(s){console.error("Error destroying page:",s)}})),this.layout&&this.layout.destroy)try{await this.layout.destroy()}catch(t){console.error("Error destroying layout:",t)}this.pageCache.clear(),this.pageClasses.clear(),this.componentClasses.clear(),this.modelClasses.clear(),typeof window<"u"&&window.MOJO&&delete window.MOJO.router,this.isStarted=!1,console.log(`✨ ${this.name} destroyed`)}buildPagePath(e,t,s){let i=e.route||`/${e.pageName.toLowerCase()}`;if(Object.keys(t).forEach(a=>{(typeof t[a]=="string"||typeof t[a]=="number")&&(i=i.replace(`:${a}`,t[a]))}),s&&Object.keys(s).length>0){const a=new URLSearchParams(s).toString();i+=(i.includes("?")?"&":"?")+a}return i}validateDefaultRoute(){this.pageClasses.has(this.defaultRoute)?console.log(`✅ Default route '${this.defaultRoute}' is registered`):(console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`),console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`),console.warn(" Or change default route: new WebApp({ defaultRoute: 'your-page' });"))}findFallbackPage(){const e=["404","error","denied"];for(const[t]of this.pageClasses.entries())if(!e.includes(t))return t;return null}static create(e={}){return new K(e)}initPluginRegistry(){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.app=this)}static registerPlugin(e,t){typeof window<"u"&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.plugins[e]=t,console.debug(`MOJO Plugin registered: ${e}`))}}const Ae="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==";class Ie{constructor(){this.formatters=new Map,this.registerBuiltInFormatters()}escapeHtml(e){if(e==null)return"";const t={"&":"&","<":"<",">":">",'"':""","'":"'"};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 l=/(^|\s)((?:https?:\/\/|www\.)[^\s<]+)/gi;n=n.replace(l,(o,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 l=/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;n=n.replace(l,o=>`<a href="mailto:${o}">${o}</a>`)}return n}clipboard(e,t="text"){if(e==null)return"";const s=String(e),i=this.escapeHtml(s),a=t!=="icon-only",r=`
|
|
2
2
|
<button type="button"
|
|
3
3
|
class="btn btn-sm btn-outline-secondary ms-1 p-0 border-0 bg-transparent"
|
|
4
4
|
title="Copy"
|
|
@@ -11,14 +11,14 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
11
11
|
${a?`<span class="font-monospace">${i}</span>`:""}
|
|
12
12
|
${r}
|
|
13
13
|
</span>
|
|
14
|
-
`}nl2br(e){return e==null?"":this.escapeHtml(String(e)).replace(/\r\n|\r|\n/g,"<br>")}code(e,t=""){if(e==null)return"";const s=t?`language-${this.escapeHtml(String(t))}`:"",i=this.escapeHtml(String(e));return`<pre class="bg-light p-2 rounded border"><code class="${s}">${i}</code></pre>`}register(e,t){if(typeof t!="function")throw new Error(`Formatter must be a function, got ${typeof t}`);return this.formatters.set(e.toLowerCase(),t),this}apply(e,t,...s){try{const i=this.formatters.get(e.toLowerCase());return i?i(t,...s):(console.warn(`Formatter '${e}' not found`),t)}catch(i){return console.error(`Error in formatter '${e}':`,i),t}}pipe(e,t,s=null){return t?this.parsePipeString(t,s).reduce((a,r)=>this.apply(r.name,a,...r.args),e):e}parsePipeString(e,t=null){const s=[],i=e.split("|").map(a=>a.trim());for(const a of i){const r=this.parseFormatter(a,t);r&&s.push(r)}return s}parseFormatter(e,t=null){const s=e.match(/^([a-zA-Z_]\w*)\s*\((.*)\)$/);if(s){const[,a,r]=s,n=r?this.parseArguments(r,t):[];return{name:a,args:n}}const i=e.match(/^([a-zA-Z_]\w*)(?::(.+))?$/);if(i){const[,a,r]=i,n=r?this.parseColonArguments(r,t):[];return{name:a,args:n}}return null}parseArguments(e,t=null){const s=[];let i="",a=!1,r=null,n=0;for(let o=0;o<e.length;o++){const l=e[o];!a&&(l==='"'||l==="'")?(a=!0,r=l,i+=l):a&&l===r&&e[o-1]!=="\\"?(a=!1,r=null,i+=l):!a&&l==="{"?(n++,i+=l):!a&&l==="}"?(n--,i+=l):!a&&n===0&&l===","?(s.push(this.parseValue(i.trim(),t)),i=""):i+=l}return i.trim()&&s.push(this.parseValue(i.trim(),t)),s}parseColonArguments(e,t=null){const s=[];let i="",a=!1,r=null;for(let n=0;n<e.length;n++){const o=e[n];!a&&(o==='"'||o==="'")?(a=!0,r=o,i+=o):a&&o===r&&e[n-1]!=="\\"?(a=!1,r=null,i+=o):!a&&o===":"?(s.push(this.parseValue(i.trim(),t)),i=""):i+=o}return i.trim()&&s.push(this.parseValue(i.trim(),t)),s}parseValue(e,t=null){if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(e==="true")return!0;if(e==="false")return!1;if(e==="null")return null;if(e!=="undefined"){if(!isNaN(e)&&e!=="")return Number(e);if(e.startsWith("{")&&e.endsWith("}"))try{return JSON.parse(e)}catch{}if(t&&this.isIdentifier(e)){if(!e.includes(".")&&Object.prototype.hasOwnProperty.call(t,e))return t[e];if(t.get&&typeof t.get=="function"){const s=t.get(e);if(s!==void 0)return s}if(t.getContextValue&&typeof t.getContextValue=="function"){const s=t.getContextValue(e);if(s!==void 0)return s}if(e.includes(".")){const s=window.MOJOUtils||(typeof require<"u"?require("./MOJOUtils.js").default:null);if(s){const i=s.getNestedValue(t,e);if(i!==void 0)return i}}}return e}}isIdentifier(e){return/^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(e)}date(e,t="MM/DD/YYYY"){if(!e)return"";e=this.normalizeEpoch(e);let s;if(e instanceof Date)s=e;else if(typeof e=="string")if(/^\d{4}-\d{2}-\d{2}$/.test(e)){const[n,o,l]=e.split("-").map(Number);s=new Date(n,o-1,l)}else s=new Date(e);else s=new Date(e);if(isNaN(s.getTime()))return String(e);const i={YYYY:s.getFullYear(),YY:String(s.getFullYear()).slice(-2),MMMM:s.toLocaleDateString("en-US",{month:"long"}),MMM:s.toLocaleDateString("en-US",{month:"short"}),MM:String(s.getMonth()+1).padStart(2,"0"),M:s.getMonth()+1,dddd:s.toLocaleDateString("en-US",{weekday:"long"}),ddd:s.toLocaleDateString("en-US",{weekday:"short"}),DD:String(s.getDate()).padStart(2,"0"),D:s.getDate()};let a=t;const r=new RegExp(`(${Object.keys(i).join("|")})`,"g");return a=a.replace(r,n=>i[n]||n),a}time(e,t="HH:mm:ss"){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const i=s.getHours(),a={HH:String(i).padStart(2,"0"),H:i,hh:String(i%12||12).padStart(2,"0"),h:i%12||12,mm:String(s.getMinutes()).padStart(2,"0"),m:s.getMinutes(),ss:String(s.getSeconds()).padStart(2,"0"),s:s.getSeconds(),A:i>=12?"PM":"AM",a:i>=12?"pm":"am"};let r=t;const n=Object.keys(a).sort((o,l)=>l.length-o.length);for(const o of n)r=r.replace(new RegExp(o,"g"),a[o]);return r}datetime(e,t="MM/DD/YYYY",s="HH:mm:ss"){e=this.normalizeEpoch(e);const i=this.date(e,t),a=this.time(e,s);return i&&a?`${i} ${a}`:""}datetime_tz(e,t="MM/DD/YYYY",s="HH:mm:ss",i={}){if(!e)return"";e=this.normalizeEpoch(e);const a=e instanceof Date?e:new Date(e);if(isNaN(a.getTime()))return String(e);const r=i&&i.locale||"en-US",n=i&&i.timeZone?i.timeZone:void 0,o=()=>{let T="";try{const j=new Intl.DateTimeFormat(r,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(P=>P.type==="timeZoneName");if(T=j?j.value:"",T&&/^GMT[+-]/i.test(T))try{const R=new Intl.DateTimeFormat(r,{timeStyle:"short",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(Ke=>Ke.type==="timeZoneName");R&&R.value&&!/^GMT[+-]/i.test(R.value)&&(T=R.value)}catch{}if(T&&/\s/.test(T)){const P=T.split(/\s+/).map(R=>R[0]).join("").toUpperCase();P.length>=2&&P.length<=4&&(T=P)}}catch{T=""}return T};if(!n){const T=this.date(a,t),O=this.time(a,s),j=o();return T&&O?`${T} ${O} ${j}`.trim():""}const l=new Intl.DateTimeFormat(r,{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(a),c=T=>{const O=l.find(j=>j.type===T);return O?O.value:""},d=c("year"),h=c("month"),f=c("day"),m=c("hour"),p=c("minute"),g=c("second"),y=h?String(parseInt(h,10)):"",b=f?String(parseInt(f,10)):"",w=m?String(parseInt(m,10)):"",C=m?parseInt(m,10)%12||12:"",$=m?parseInt(m,10)>=12?"PM":"AM":"",S=$?$.toLowerCase():"",M=new Intl.DateTimeFormat(r,{timeZone:n,month:"long"}).format(a),L=new Intl.DateTimeFormat(r,{timeZone:n,month:"short"}).format(a),A=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"long"}).format(a),U=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"short"}).format(a),pe={YYYY:d,YY:d?d.slice(-2):"",MMMM:M,MMM:L,MM:h,M:y,dddd:A,ddd:U,DD:f,D:b},ge={HH:m,H:w,hh:C!==""?String(C).padStart(2,"0"):"",h:C!==""?String(C):"",mm:p,m:p?String(parseInt(p,10)):"",ss:g,s:g?String(parseInt(g,10)):"",A:$,a:S},I=(T,O)=>{if(!T)return"";const j=new RegExp(`(${Object.keys(O).sort((P,R)=>R.length-P.length).join("|")})`,"g");return T.replace(j,P=>O[P]??P)},N=I(t,pe),De=I(s,ge),Ge=o();return N&&De?`${N} ${De} ${Ge}`.trim():""}normalizeEpoch(e){if(typeof e!="number"&&(e=Number(e)),isNaN(e))return"";if(e<1e11)return e*1e3;if(e>1e12&&e<1e13)return e;throw new Error("Value doesn't look like epoch seconds or ms")}date_range(e,t=null,s="MM/DD/YYYY"){if(!e)return"";const i=t||new Date,a=this.date(e,s),r=this.date(i,s);return!a||!r?"":`${a} - ${r}`}datetime_range(e,t=null,s="MM/DD/YYYY",i="HH:mm"){if(!e)return"";const a=t||new Date,r=this.datetime(e,s,i),n=this.datetime(a,s,i);return!r||!n?"":`${r} - ${n}`}relative(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const a=s-new Date,r=Math.abs(a),n=Math.floor(r/1e3),o=Math.floor(n/60),l=Math.floor(o/60),c=Math.floor(l/24),d=a>0;if(t)return c>365?Math.floor(c/365)+"y":c>30?Math.floor(c/30)+"mo":c>7?Math.floor(c/7)+"w":c>0?c+"d":l>0?l+"h":o>0?o+"m":"now";if(c>365){const h=Math.floor(c/365),f=d?"in ":"",m=d?"":" ago";return f+h+" year"+(h>1?"s":"")+m}if(c>30){const h=Math.floor(c/30),f=d?"in ":"",m=d?"":" ago";return f+h+" month"+(h>1?"s":"")+m}if(c>7){const h=Math.floor(c/7),f=d?"in ":"",m=d?"":" ago";return f+h+" week"+(h>1?"s":"")+m}if(c===1)return d?"tomorrow":"yesterday";if(c>0){const h=d?"in ":"",f=d?"":" ago";return h+c+" days"+f}if(l>0){const h=d?"in ":"",f=d?"":" ago";return h+l+" hour"+(l>1?"s":"")+f}if(o>0){const h=d?"in ":"",f=d?"":" ago";return h+o+" minute"+(o>1?"s":"")+f}if(n>30){const h=d?"in ":"",f=d?"":" ago";return h+n+" seconds"+f}return"just now"}iso(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);return isNaN(s.getTime())?String(e):t?s.toISOString().split("T")[0]:s.toISOString()}number(e,t=2,s="en-US"){const i=parseFloat(e);return isNaN(i)?String(e):i.toLocaleString(s,{minimumFractionDigits:t,maximumFractionDigits:t})}currency(e,t="$",s=2){const i=parseInt(e);if(isNaN(i))return String(e);const a=Math.abs(i).toString(),r=i<0?"-":"";let n,o;a.length<=2?(n="0",o=a.padStart(2,"0")):(n=a.slice(0,-2),o=a.slice(-2)),n=n.replace(/\B(?=(\d{3})+(?!\d))/g,",");let l;if(s===0)parseInt(o)>=50&&(n=(parseInt(n.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),l=n;else if(s===2)l=`${n}.${o}`;else{const c=o.slice(0,s).padEnd(s,"0");l=`${n}.${c}`}return r+t+l}percent(e,t=0,s=!0){const i=parseFloat(e);if(isNaN(i))return String(e);const a=s?i*100:i;return this.number(a,t)+"%"}filesize(e,t=!1,s=1){const i=parseInt(e);if(isNaN(i))return String(e);const a=t?["B","KiB","MiB","GiB","TiB"]:["B","KB","MB","GB","TB"],r=t?1024:1e3;let n=i,o=0;for(;n>=r&&o<a.length-1;)n/=r,o++;const l=o===0?0:s;return`${n.toFixed(l)} ${a[o]}`}ordinal(e,t=!1){const s=parseInt(e);if(isNaN(s))return String(e);const i=s%10,a=s%100;let r="th";return i===1&&a!==11?r="st":i===2&&a!==12?r="nd":i===3&&a!==13&&(r="rd"),t?r:s+r}compact(e,t=1){const s=parseFloat(e);if(isNaN(s))return String(e);const i=Math.abs(s),a=s<0?"-":"";return i>=1e9?a+(i/1e9).toFixed(t)+"B":i>=1e6?a+(i/1e6).toFixed(t)+"M":i>=1e3?a+(i/1e3).toFixed(t)+"K":String(s)}add(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s+i}subtract(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s-i}multiply(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)?e:s*i}divide(e,t){const s=parseFloat(e),i=parseFloat(t);return isNaN(s)||isNaN(i)||i===0?e:s/i}capitalize(e,t=!0){const s=String(e);return s?t?s.replace(/\b\w/g,i=>i.toUpperCase()):s.charAt(0).toUpperCase()+s.slice(1):""}replace(e,t,s="",i="g"){if(e==null)return"";const a=String(e);if(t==null||t==="")return a;if(t instanceof RegExp)return a.replace(t,String(s));const r=String(t),n=r.match(/^\/(.+)\/([a-z]*)$/i);if(n){const[,o,l]=n;try{return a.replace(new RegExp(o,l),String(s))}catch{}}return String(i).includes("g")?a.split(r).join(String(s)):a.replace(r,String(s))}truncate(e,t=50,s="..."){const i=String(e);return i.length<=t?i:i.substring(0,t)+s}truncate_front(e,t=8,s="..."){const i=String(e);return i.length<=t?i:`${s}${i.slice(-t)}`}truncate_middle(e,t=8,s="***"){const i=String(e);if(i.length<=t)return i;const a=Math.floor(t/2),r=i.substring(0,a),n=i.substring(i.length-a);return`${r}${s}${n}`}slug(e,t="-"){return String(e).toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,t).replace(new RegExp(`${t}+`,"g"),t).replace(new RegExp(`^${t}|${t}$`,"g"),"")}initials(e,t=2){return String(e).split(/\s+/).filter(a=>a.length>0).slice(0,t).map(a=>a.charAt(0).toUpperCase()).join("")}mask(e,t="*",s=4){const i=String(e);if(i.length<=s)return i;const a=t.repeat(Math.max(0,i.length-s)),r=i.slice(-s);return a+r}email(e,t={}){const s=String(e).trim();if(!s)return"";if(t.link===!1)return s;const i=t.subject?`?subject=${encodeURIComponent(t.subject)}`:"",a=t.body?`&body=${encodeURIComponent(t.body)}`:"",r=t.class?` class="${t.class}"`:"";return`<a href="mailto:${s}${i}${a}"${r}>${s}</a>`}phone(e,t="US",s=!0){let i=String(e).replace(/\D/g,""),a=i;return t==="US"&&(i.length===10?a=`(${i.slice(0,3)}) ${i.slice(3,6)}-${i.slice(6)}`:i.length===11&&i[0]==="1"&&(a=`+1 (${i.slice(1,4)}) ${i.slice(4,7)}-${i.slice(7)}`)),s?`<a href="tel:${i}">${a}</a>`:a}url(e,t=null,s=!0){let i=String(e).trim();return i?(/^https?:\/\//.test(i)||(i="https://"+i),`<a href="${i}"${s?' target="_blank"':""}${s?' rel="noopener noreferrer"':""}>${t||i}</a>`):""}badge(e,t="auto"){if(Array.isArray(e))return e.map(r=>this.badge(r,t)).join(" ");const s=String(e),i=t==="auto"?this.inferBadgeType(s):t;return`<span class="badge ${i?`bg-${i}`:"bg-secondary"}">${s}</span>`}badgeClass(e,t="auto"){const s=String(e),i=t==="auto"?this.inferBadgeType(s):t;return i?`bg-${i}`:"bg-secondary"}inferBadgeType(e){const t=e.toLowerCase();return["active","pass","success","complete","completed","approved","done","true","on","yes"].includes(t)?"success":["error","failed","fail","rejected","deleted","cancelled","false","off","no","declined"].includes(t)?"danger":["warning","pending","review","processing","uploading"].includes(t)?"warning":["info","new","draft"].includes(t)?"info":(["inactive","disabled","archived","suspended"].includes(t),"secondary")}status(e){return this._status(e)}status_icon(e){return this._status(e,{},{},!1,!0)}status_text(e){return this._status(e,{},{},!0,!1)}_status(e,t={},s={},i=!1,a=!1){const r=String(e).toLowerCase(),n={active:"bi bi-check-circle-fill",approved:"bi bi-check-circle-fill",declined:"bi bi-x-circle-fill",inactive:"bi bi-pause-circle-fill",pending:"bi bi-clock-fill",success:"bi bi-check-circle-fill",error:"bi bi-exclamation-triangle-fill",warning:"bi bi-exclamation-triangle-fill"},o={active:"success",approved:"success",declined:"danger",inactive:"secondary",pending:"warning",success:"success",error:"danger",warning:"warning"},l=t[r]||n[r]||"",c=s[r]||o[r]||"secondary";let d="";!i&&l&&(d=`<i class="${l}"></i>`);let h="";return a||(h=e),`<span class="text-${c}">${d}${d?" ":""}${h}</span>`}boolean(e,t="True",s="False",i=!1){const a=e?t:s;return i?`<span class="text-${e?"success":"danger"}">${a}</span>`:a}bool(e){return e==null||e===0||e===""||e===!1||e==="false"?!1:e===!0||e==="true"?!0:!(Array.isArray(e)&&e.length===0||e&&typeof e=="object"&&e.constructor===Object&&Object.keys(e).length===0)}icon(e,t={}){const s=String(e).toLowerCase(),i=t[s]||"";return i?`<i class="${i}"></i>`:""}yesnoicon(e,t="bi bi-check-circle-fill text-success",s="bi bi-x-circle-fill text-danger"){return e?`<i class="${t}"></i>`:`<i class="${s}"></i>`}image(e,t="thumbnail",s="img-fluid",i=""){const a=this._extractImageUrl(e,t);return a?`<img src="${a}" class="${s}" alt="${i}" />`:""}avatar(e,t="md",s="rounded-circle",i=""){const a=this._extractImageUrl(e,"square_sm")||Ae,r={xs:"width: 1.5rem; height: 1.5rem;",sm:"width: 2rem; height: 2rem;",md:"width: 3rem; height: 3rem;",lg:"width: 4rem; height: 4rem;",xl:"width: 5rem; height: 5rem;"},n=r[t]||r.md,l=`object-fit-cover ${s}`.trim();return`<img src="${a}" class="${l}" style="${n}" alt="${i}" />`}tooltip(e,t="",s="top",i=""){if(e==null)return"";const a=String(e),r=i==="html"?t:this.escapeHtml(t);return`<span data-bs-toggle="tooltip" data-bs-placement="${s}" ${i==="html"?'data-bs-html="true"':""} data-bs-title="${r}">${a}</span>`}_extractImageUrl(e,t="thumbnail"){if(!e)return null;if(typeof e=="string")return e;if(typeof e=="object"){if(e.attributes&&(e=e.attributes),t==="thumbnail"&&e.thumbnail&&typeof e.thumbnail=="string")return e.thumbnail;if(e.renditions&&typeof e.renditions=="object"){const s=e.renditions[t];if(s&&s.url)return s.url;const i=Object.values(e.renditions);if(i.length>0&&i[0].url)return i[0].url}if(e.url)return e.url}return null}default(e,t=""){return e==null||e===""?t:e}equals(e,t,s,i=""){return e==t?s:i}plural(e,t,s=null,i=!0){if(e==null||t===null||t===void 0)return i?`${e} ${t}`:t||"";const a=parseInt(e);if(isNaN(a))return i?`${e} ${t}`:t||"";const r=Math.abs(a)===1?t:s||t+"s";return i?`${a} ${r}`:r}formatList(e,t={}){if(!Array.isArray(e))return String(e);const{conjunction:s="and",limit:i=null,moreText:a="others"}=t;if(e.length===0)return"";if(e.length===1)return String(e[0]);let r=e.slice(),n=!1;if(i&&e.length>i&&(r=e.slice(0,i),n=!0),n){const o=e.length-i;return`${r.join(", ")}, ${s} ${o} ${a}`}return r.length===2?`${r[0]} ${s} ${r[1]}`:`${r.slice(0,-1).join(", ")}, ${s} ${r[r.length-1]}`}duration(e,t="ms",s=!1,i=2){if(e==null)return"";const a=parseFloat(e);if(isNaN(a))return String(e);let r;switch(t){case"s":case"sec":case"seconds":r=a*1e3;break;case"m":case"min":case"minutes":r=a*6e4;break;case"h":case"hr":case"hours":r=a*36e5;break;case"d":case"day":case"days":r=a*864e5;break;case"ms":case"milliseconds":default:r=a}const n=[{name:"day",short:"d",value:864e5},{name:"hour",short:"h",value:36e5},{name:"minute",short:"m",value:6e4},{name:"second",short:"s",value:1e3}];if(r===0)return s?"0s":"0 seconds";const o=Math.abs(r),l=r<0?"-":"",c=[];let d=o;for(const h of n)if(d>=h.value){const f=Math.floor(d/h.value);d=d%h.value;const m=s?h.short:f===1?h.name:h.name+"s";if(c.push(s?`${f}${m}`:`${f} ${m}`),c.length>=i)break}return c.length===0?s?`${Math.round(o)}ms`:`${Math.round(o)} milliseconds`:l+(s?c.join(""):c.join(" "))}hash(e,t=8,s="",i="..."){if(e==null)return"";const a=String(e);return a.length<=t?s+a:s+a.substring(0,t)+i}stripHtml(e){return e==null?"":String(e).replace(/<[^>]*>/g,"")}highlight(e,t,s="highlight"){if(e==null||!t)return String(e||"");const i=String(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`(${i})`,"gi");return String(e).replace(a,`<mark class="${s}">$1</mark>`)}hex(e,t=!1,s=!1){if(e==null)return"";let i="";const a=r=>Array.from(r).map(n=>n.toString(16).padStart(2,"0")).join("");if(typeof e=="number"){let r=Math.abs(Math.trunc(e)).toString(16);r.length%2&&(r="0"+r),i=r}else if(e instanceof Uint8Array)i=a(e);else if(e instanceof ArrayBuffer)i=a(new Uint8Array(e));else if(Array.isArray(e)&&e.every(r=>typeof r=="number"))i=a(Uint8Array.from(e.map(r=>r&255)));else{const n=new TextEncoder().encode(String(e));i=a(n)}return t&&(i=i.toUpperCase()),(s?"0x":"")+i}unhex(e){if(e==null)return"";let t=String(e).trim();if((t.startsWith("0x")||t.startsWith("0X"))&&(t=t.slice(2)),t=t.replace(/\s+/g,""),t.length===0)return"";t.length%2!==0&&(t="0"+t);const s=new Uint8Array(t.length/2);for(let i=0;i<t.length;i+=2){const a=parseInt(t.slice(i,i+2),16);if(Number.isNaN(a))return String(e);s[i/2]=a}try{return new TextDecoder().decode(s)}catch{let a="";for(const r of s)a+=String.fromCharCode(r);return a}}json(e,t=2){try{return JSON.stringify(e,null,t)}catch{return String(e)}}has(e){return this.formatters.has(e.toLowerCase())}unregister(e){return this.formatters.delete(e.toLowerCase())}listFormatters(){return Array.from(this.formatters.keys()).sort()}iter(e){return e==null?[]:Array.isArray(e)?e:typeof e=="object"?Object.entries(e).map(([t,s])=>({key:t,value:s})):[{key:"0",value:e}]}}const V=new Ie;window.dataFormatter=V;class x{static getContextData(e,t){if(!t||e==null)return;let s=t,i="",a=0,r=-1;for(let o=0;o<t.length;o++){const l=t[o];if(l==="(")a++;else if(l===")")a--;else if(l==="|"&&a===0){r=o;break}}r>-1&&(s=t.substring(0,r).trim(),i=t.substring(r+1).trim());const n=this.getNestedValue(e,s);return i?V.pipe(n,i,e):n}static getNestedValue(e,t){if(!t||e==null)return;if(!t.includes(".")){if(t in e){const a=e[t];return typeof a=="function"?a.call(e):a}return}const s=t.split(".");let i=e;for(let a=0;a<s.length;a++){const r=s[a];if(i==null)return;if(a===0)if(i.hasOwnProperty(r)){const n=i[r];typeof n=="function"?i=n.call(e):i=n}else return;else{if(i&&typeof i.getContextValue=="function"){const n=s.slice(a).join(".");return i.getContextValue(n)}if(Array.isArray(i)&&!isNaN(r))i=i[parseInt(r)];else if(i.hasOwnProperty(r))i=i[r];else if(typeof i[r]=="function")i=i[r].call(i);else return}}return i}static isNullOrUndefined(e){return e==null}static deepClone(e){if(e===null||typeof e!="object")return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof Array)return e.map(t=>this.deepClone(t));if(e instanceof Object){const t={};for(const s in e)e.hasOwnProperty(s)&&(t[s]=this.deepClone(e[s]));return t}}static deepMerge(e,...t){if(!t.length)return e;const s=t.shift();if(this.isObject(e)&&this.isObject(s))for(const i in s)this.isObject(s[i])?(e[i]||Object.assign(e,{[i]:{}}),this.deepMerge(e[i],s[i])):Object.assign(e,{[i]:s[i]});return this.deepMerge(e,...t)}static isObject(e){return e&&typeof e=="object"&&!Array.isArray(e)}static debounce(e,t){let s;return function(...a){const r=()=>{clearTimeout(s),e(...a)};clearTimeout(s),s=setTimeout(r,t)}}static throttle(e,t){let s;return function(...i){s||(e.apply(this,i),s=!0,setTimeout(()=>s=!1,t))}}static generateId(e=""){const t=Date.now().toString(36),s=Math.random().toString(36).substr(2,9);return e?`${e}_${t}_${s}`:`${t}_${s}`}static escapeHtml(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};return String(e).replace(/[&<>"'`=\/]/g,s=>t[s])}static checkPasswordStrength(e){if(!e||typeof e!="string")return{score:0,strength:"invalid",feedback:["Password must be a non-empty string"],details:{length:0,hasLowercase:!1,hasUppercase:!1,hasNumbers:!1,hasSpecialChars:!1,hasCommonPatterns:!1,isCommonPassword:!1}};const t=[],s={length:e.length,hasLowercase:/[a-z]/.test(e),hasUppercase:/[A-Z]/.test(e),hasNumbers:/[0-9]/.test(e),hasSpecialChars:/[^a-zA-Z0-9]/.test(e),hasCommonPatterns:!1,isCommonPassword:!1};let i=0;e.length<6?t.push("Password should be at least 6 characters long"):e.length<8?(i+=1,t.push("Consider using at least 8 characters for better security")):e.length<12?i+=3:i+=4,s.hasLowercase?i+=1:t.push("Include lowercase letters"),s.hasUppercase?i+=1:t.push("Include uppercase letters"),s.hasNumbers?i+=1:t.push("Include numbers"),s.hasSpecialChars?i+=2:t.push("Include special characters (!@#$%^&* etc.)");const a=[/123/,/abc/i,/qwerty/i,/asdf/i,/(.)\1{2,}/,/password/i,/admin/i,/user/i,/login/i];for(const o of a)if(o.test(e)){s.hasCommonPatterns=!0,i-=1,t.push("Avoid common patterns and dictionary words");break}["123456","password","123456789","12345678","12345","1234567","1234567890","qwerty","abc123","111111","123123","admin","letmein","welcome","monkey","password123","123qwe","qwerty123","000000","dragon","sunshine","princess","azerty","1234","iloveyou","trustno1","superman","shadow","master","jennifer"].includes(e.toLowerCase())&&(s.isCommonPassword=!0,i=Math.max(0,i-3),t.push("This password is too common and easily guessed"));let n;return i<2?n="very-weak":i<4?n="weak":i<6?n="fair":i<8?n="good":n="strong",i>=7&&t.length===0?t.push("Strong password! Consider using a password manager."):i>=5&&t.length<=1&&t.push("Good password strength. Consider adding more variety."),{score:Math.max(0,i),strength:n,feedback:t,details:s}}static generatePassword(e={}){const s={...{length:12,includeLowercase:!0,includeUppercase:!0,includeNumbers:!0,includeSpecialChars:!0,customChars:"",excludeAmbiguous:!1},...e};if(s.length<4)throw new Error("Password length must be at least 4 characters");let i="abcdefghijklmnopqrstuvwxyz",a="ABCDEFGHIJKLMNOPQRSTUVWXYZ",r="0123456789",n="!@#$%^&*()_+-=[]{}|;:,.<>?";s.excludeAmbiguous&&(i=i.replace(/[il]/g,""),a=a.replace(/[IOL]/g,""),r=r.replace(/[01]/g,""),n=n.replace(/[|]/g,""));let o="";const l=[];if(s.customChars?o=s.customChars:(s.includeLowercase&&(o+=i,l.push(i[Math.floor(Math.random()*i.length)])),s.includeUppercase&&(o+=a,l.push(a[Math.floor(Math.random()*a.length)])),s.includeNumbers&&(o+=r,l.push(r[Math.floor(Math.random()*r.length)])),s.includeSpecialChars&&(o+=n,l.push(n[Math.floor(Math.random()*n.length)]))),!o)throw new Error("No character types selected for password generation");let c="";for(const d of l)c+=d;for(let d=c.length;d<s.length;d++)c+=o[Math.floor(Math.random()*o.length)];return c.split("").sort(()=>Math.random()-.5).join("")}static parseQueryString(e){const t={},s=new URLSearchParams(e);for(const[i,a]of s.entries())t[i]=a;return t}static toQueryString(e){return new URLSearchParams(e).toString()}static wrapData(e,t=null,s=3){return!e||typeof e!="object"||e instanceof Date||e instanceof RegExp||e instanceof Error||s<=0||typeof e.getContextValue=="function"?e:Array.isArray(e)?e.map(i=>i&&typeof i=="object"&&!i.getContextValue?new se(i,t):i):new se(e,t)}}class se{constructor(e,t=null){if(Object.defineProperty(this,"_data",{value:e,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(this,"_rootContext",{value:t,writable:!1,enumerable:!1,configurable:!1}),e&&typeof e=="object"){for(const s in e)if(e.hasOwnProperty(s)){const i=e[s];this[s]=x.wrapData(i,t)}}}getContextValue(e){let t=e,s="",i=0,a=-1;for(let n=0;n<e.length;n++){const o=e[n];if(o==="(")i++;else if(o===")")i--;else if(o==="|"&&i===0){a=n;break}}a>-1&&(t=e.substring(0,a).trim(),s=e.substring(a+1).trim());let r;return t in this&&t!=="_data"&&t!=="_rootContext"?r=this[t]:r=x.getNestedValue(this._data,t),s&&r!==void 0?V.pipe(r,s,this._rootContext||this._data):r}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}x.DataWrapper=se,typeof window<"u"&&(window.utils=x);const Te=Object.prototype.toString,B=Array.isArray||function(u){return Te.call(u)==="[object Array]"},Y=function(u){return typeof u=="function"},be=function(u){return u!==null&&typeof u=="object"};function Me(){return typeof window>"u"?null:window.MOJO?.dataFormatter?window.MOJO.dataFormatter:window.dataFormatter?window.dataFormatter:null}const ye=function(u){const e={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};return String(u).replace(/[&<>"'`=\/]/g,function(t){return e[t]})};class we{constructor(e){this.string=e,this.tail=e,this.pos=0}eos(){return this.tail===""}scan(e){const t=this.tail.match(e);if(!t||t.index!==0)return"";const s=t[0];return this.tail=this.tail.substring(s.length),this.pos+=s.length,s}scanUntil(e){const t=this.tail.search(e);let s;switch(t){case-1:s=this.tail,this.tail="";break;case 0:s="";break;default:s=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=s.length,s}}class Q{constructor(e,t){this.view=e,this.cache={".":this.view},this.parent=t,this.view?._cacheId||this.view&&typeof this.view=="object"&&(this.view._cacheId=Math.random().toString(36).substring(2))}push(e){return new Q(e,this)}lookup(e){if(this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;if(this.renderCache.has(i))return this.renderCache.get(i)}if(e===".")return this.view;if(e&&e.startsWith(".")){let i=e.substring(1),a=!1,r=null;const n=i.indexOf("|");if(n!==-1&&(r=i.substring(n+1).trim(),i=i.substring(0,n).trim()),i.endsWith("|iter")&&(i=i.substring(0,i.length-5),a=!0),this.view&&typeof this.view=="object"){let o;if(typeof this.view.getContextValue=="function")try{o=this.view.getContextValue(i),o!==void 0&&Y(o)&&(o=o.call(this.view))}catch{o=void 0}if(o===void 0&&i in this.view&&(o=this.view[i],Y(o)&&(o=o.call(this.view))),r&&o!==void 0)try{const l=Me();l&&typeof l.pipe=="function"&&(o=l.pipe(o,r,this))}catch{}return B(o)?a?o:o.length>0:be(o)?a?Object.entries(o).map(([l,c])=>({key:l,value:c})):Object.keys(o).length>0:o}return}const t=this.cache;let s;if(t.hasOwnProperty(e))s=t[e];else{let i=this,a,r,n,o=!1;for(;i;){if(i.view&&typeof i.view.getContextValue=="function")try{a=i.view.getContextValue(e),a!==void 0&&(o=!0)}catch{o=!1}if(!o)if(e.indexOf(".")>0)for(a=i.view,r=e.split("."),n=0;a!=null&&n<r.length;)if(a&&typeof a.getContextValue=="function"&&n<r.length)try{const l=r.slice(n).join(".");a=a.getContextValue(l),n=r.length,a!==void 0&&(o=!0)}catch{n===r.length-1&&(o=ie(a,r[n])||ve(a,r[n])),a=a[r[n++]]}else n===r.length-1&&(o=ie(a,r[n])||ve(a,r[n])),a=a[r[n++]];else a=i.view[e],o=ie(i.view,e);if(o){s=a;break}i=i.parent}t[e]=s}if(Y(s)&&(s=s.call(this.view)),this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;this.renderCache.set(i,s)}return s}}function ie(u,e){return u!=null&&typeof u=="object"&&e in u}function ve(u,e){return u!=null&&typeof u!="object"&&u.hasOwnProperty&&u.hasOwnProperty(e)}class Ce{constructor(){this.templateCache=new Map}clearCache(){this.templateCache.clear()}parse(e,t){t=t||["{{","}}"];const s=e+":"+t.join(":");let i=this.templateCache.get(s);return i==null&&(i=this.parseTemplate(e,t),this.templateCache.set(s,i)),i}parseTemplate(e,t){if(!e)return[];const s=t[0],i=t[1],a=new we(e),r=[];let n,o,l,c,d;const h=new RegExp(ae(s)+"\\s*"),f=new RegExp("\\s*"+ae(i)),m=new RegExp("\\s*"+ae("}"+i));for(;!a.eos();){if(n=a.pos,l=a.scanUntil(h),l)for(let p=0;p<l.length;++p)c=l.charAt(p),c===`
|
|
15
|
-
`?r.push(["text",c]):r.push(["text",c]);if(!a.scan(h))break;if(o=a.scan(/[#^\/>{&=!]/),o||(o="name"),a.scan(/\s*/),o==="="?(l=a.scanUntil(/\s*=/),a.scan(/\s*=/),a.scanUntil(f)):o==="{"?(l=a.scanUntil(m),a.scan(m),o="&"):l=a.scanUntil(f),a.scan(f),o==="#"||o==="^")d=[o,l,n,a.pos],r.push(d);else if(o==="/"){let p;for(let g=r.length-1;g>=0;--g)if((r[g][0]==="#"||r[g][0]==="^")&&r[g][1]===l){p=r[g];break}p&&p.length===4&&p.push(a.pos),r.push([o,l,n,a.pos])}else r.push([o,l,n,a.pos])}return this.nestSections(this.squashTokens(r))}squashTokens(e){const t=[];let s,i;for(let a=0;a<e.length;++a)s=e[a],s&&(s[0]==="text"&&i&&i[0]==="text"?(i[1]+=s[1],i[3]=s[3]):(t.push(s),i=s));return t}nestSections(e){const t=[];let s=t;const i=[];for(let a=0;a<e.length;++a){const r=e[a];switch(r[0]){case"#":case"^":const n=[r[0],r[1],r[2],r[3],[],r[4]||null];s.push(n),i.push(n),s=n[4];break;case"/":const o=i.pop();o&&(o[5]=r[2],s=i.length>0?i[i.length-1][4]:t);break;default:s.push(r)}}return t}render(e,t,s,i){const a=this.getConfigTags(i)||["{{","}}"],r=this.parse(e,a),n=new Map;return this.renderTokens(r,new Q(t),s,e,i,n)}renderTokens(e,t,s,i,a,r){r&&!t.renderCache&&(t.renderCache=r);let n="";for(let o=0;o<e.length;++o){const l=e[o];let c;switch(l[0]){case"#":if(c=t.lookup(l[1]),!c)continue;const d=l[4];if(!d||!B(d)){console.warn(`MUSTACHE WARNING - Section ${l[1]} has no child tokens:`,l);continue}if(B(c))for(let h=0;h<c.length;++h){const f=t.push(c[h]);t.renderCache&&(f.renderCache=t.renderCache);const m=this.renderTokens(d,f,s,i,a,r);n+=m}else if(typeof c=="object"||typeof c=="string"||typeof c=="number"){const h=t.push(c);t.renderCache&&(h.renderCache=t.renderCache),n+=this.renderTokens(d,h,s,i,a,r)}else if(Y(c)){const h=i==null?null:i.slice(l[3],l[5]);c=c.call(t.view,h,f=>this.render(f,t.view,s,a)),c!=null&&(n+=c)}else c&&(n+=this.renderTokens(d,t,s,i,a,r));break;case"^":if(c=t.lookup(l[1]),!c||B(c)&&c.length===0){const h=l[4];h&&B(h)&&(n+=this.renderTokens(h,t,s,i,a,r))}break;case">":if(!s)continue;c=Y(s)?s(l[1]):s[l[1]],c!=null&&(n+=this.render(c,t.view,s,a));break;case"&":c=t.lookup(l[1]),c!=null&&(n+=c);break;case"name":c=t.lookup(l[1]),c!=null&&(n+=ye(c));break;case"text":n+=l[1];break}}return n}getConfigTags(e){return be(e)&&B(e.tags)?e.tags:null}}function ae(u){return u.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const re=new Ce,E={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:we,Context:Q,Writer:Ce,escape:ye,clearCache(){return re.clearCache()},parse(u,e){return re.parse(u,e)},render(u,e,t,s){if(typeof u!="string")throw new TypeError('Invalid template! Template should be a "string"');return e&&typeof e=="object"&&!e.getContextValue&&typeof e.toJSON!="function"&&(e=x.wrapData(e)),re.render(u,e,t,s)}};class Ve{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=new Map}bind(e){if(this.unbind(),!e)return;const t=async n=>{const o=n.target.closest("[data-action]");if(o&&this.shouldHandle(o,n)){const c=o.getAttribute("data-action");if(this.hideTooltip(o),await this.dispatch(c,n,o)){n.preventDefault(),n.stopPropagation(),n.handledByChild=!0;return}}const l=n.target.closest("a[href], [data-page]");if(l&&!l.hasAttribute("data-action")&&this.shouldHandle(l,n)){if(n.ctrlKey||n.metaKey||n.shiftKey||n.button===1)return;if(l.tagName==="A"){const c=l.getAttribute("href");if(c&&c!=="#"&&!c.startsWith("#")&&(this.view.isExternalLink(c)||l.hasAttribute("data-external")))return}this.hideTooltip(l),n.preventDefault(),n.stopPropagation(),n.handledByChild=!0,l.hasAttribute("data-page")?await this.view.handlePageNavigation(l):await this.view.handleHrefNavigation(l)}},s=n=>{const o=n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n))return;const l=o.getAttribute("data-change-action");this.dispatchChange(l,n,o).then(c=>{c&&(n.stopPropagation(),n.handledByChild=!0)})},i=n=>{const o=n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n)||!n.target.matches('[data-filter="live-search"]'))return;const c=o.getAttribute("data-change-action"),d=parseInt(o.getAttribute("data-filter-debounce"))||300,h=`${c}-${o.getAttribute("data-container")||"default"}`;this.debounceTimers.has(h)&&clearTimeout(this.debounceTimers.get(h));const f=setTimeout(()=>{this.debounceTimers.delete(h),this.dispatchChange(c,n,o).then(m=>{m&&(n.stopPropagation(),n.handledByChild=!0)})},d);this.debounceTimers.set(h,f)},a=n=>{if(n.target.matches('[data-filter="search"]'))return;const o=n.target.closest("[data-keydown-action]")||n.target.closest("[data-change-action]");if(!o||!this.shouldHandle(o,n))return;let l=["Enter"];if(o.getAttribute("data-change-keys")&&(l=o.getAttribute("data-change-keys").split(",").map(c=>c.trim())),l.includes("*")||l.includes(n.key)){const c=o.getAttribute("data-keydown-action")||o.getAttribute("data-change-action");this.dispatch(c,n,o).then(d=>{d&&(n.preventDefault(),n.stopPropagation(),n.handledByChild=!0)})}},r=n=>{const o=n.target.closest("form[data-action]");if(!o||!this.shouldHandle(o,n))return;n.preventDefault();const l=o.getAttribute("data-action");this.dispatch(l,n,o)};e.addEventListener("click",t),e.addEventListener("change",s),e.addEventListener("input",i),e.addEventListener("keydown",a),e.addEventListener("submit",r),this.domListeners.push({el:e,type:"click",fn:t},{el:e,type:"change",fn:s},{el:e,type:"input",fn:i},{el:e,type:"keydown",fn:a},{el:e,type:"submit",fn:r})}unbind(){for(const{el:e,type:t,fn:s}of this.domListeners)e.removeEventListener(t,s);this.domListeners=[];for(const e of this.debounceTimers.values())clearTimeout(e);this.debounceTimers.clear()}hideDropdown(e){const s=e.closest(".dropdown-menu").closest(".dropdown");if(!s)return;const i=s.querySelector('[data-bs-toggle="dropdown"]');i&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(i)?.hide()}hideTooltip(e){if(e&&window.bootstrap?.Tooltip){const t=window.bootstrap.Tooltip.getInstance(e);t&&t.dispose()}}hideAllTooltips(){window.bootstrap?.Tooltip&&document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.hide()})}async dispatch(e,t,s){const i=this.view,a=l=>l.includes("-")?l.split("-").map(c=>c[0].toUpperCase()+c.slice(1)).join(""):l[0].toUpperCase()+l.slice(1),r=`handleAction${a(e)}`;if(typeof i[r]=="function")try{return t.preventDefault(),await i[r](t,s),!0}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const n=`onAction${a(e)}`;if(typeof i[n]=="function")try{return await i[n](t,s)?(!!s.closest(".dropdown-menu")&&this.hideDropdown(s),t.preventDefault(),t.stopPropagation(),!0):!1}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}const o=`onPassThruAction${a(e)}`;if(typeof i[o]=="function")try{return await i[o](t,s),!1}catch(l){return console.error(`Error in action ${e}:`,l),i.handleActionError(e,l,t,s),!0}if(typeof i.onActionDefault=="function")try{return await i.onActionDefault(e,t,s)}catch(l){return console.error(`Error in default action handler for ${e}:`,l),i.handleActionError(e,l,t,s),!0}return i.emit?.(`action:${e}`,{action:e,event:t,element:s}),!1}async dispatchChange(e,t,s){const i=this.view,r=`onChange${(n=>n.includes("-")?n.split("-").map(o=>o[0].toUpperCase()+o.slice(1)).join(""):n[0].toUpperCase()+n.slice(1))(e)}`;if(typeof i[r]=="function")try{return await i[r](t,s),!0}catch(n){return console.error(`Error in onChange ${e}:`,n),i.handleActionError?.(e,n,t,s),!0}return await this.dispatch(e,t,s)}shouldHandle(e,t){return!!(this.owns(e)||this.contains(e)&&!t.handledByChild)}owns(e){const t=this.view.element;if(!t||!t.contains(e))return!1;for(const s of Object.values(this.view.children))if(s.element&&s.element.contains(e))return!1;return!0}contains(e){return!!this.view.element&&this.view.element.contains(e)}}const ne={on(u,e,t){this._listeners||(this._listeners={}),this._listeners[u]||(this._listeners[u]=[]);const s={callback:e,context:t,fn:t?e.bind(t):e};return this._listeners[u].push(s),this},off(u,e,t){return!this._listeners||!this._listeners[u]?this:(e?(this._listeners[u]=this._listeners[u].filter(s=>s.callback!==e||arguments.length===3&&s.context!==t),this._listeners[u].length===0&&delete this._listeners[u]):delete this._listeners[u],this)},once(u,e,t){const s=(...i)=>{this.off(u,s),(t?e.bind(t):e).apply(t||this,i)};return this.on(u,s),this},emit(u,...e){if(!this._listeners||!this._listeners[u])return this;const t=this._listeners[u].slice();for(const s of t)try{s.fn.apply(s.context||this,e)}catch(i){console&&console.error&&console.error(`Error in ${u} event handler:`,i)}return this}};typeof window<"u"&&(window.Mustache=E);class v{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??v._genId(),this.containerId=e.containerId??null,this.container=e.container??null,typeof this.container=="string"&&(this.containerId=this.container,this.container=null),this.parent=e.parent??null,this.children=e.children??{},this.template=e.template||e.templateUrl||"",this.data=e.data??{},this.isRendering=!1,this.lastRenderTime=0,this.mounted=!1,this.debug=e.debug??!1,this.app=e.app??null,this.cacheTemplate=e.cacheTemplate??!0,this.enableTooltips=e.enableTooltips??!1,this.options={...e},this.element=this._ensureElement(),this.events=new Ve(this),e.model&&this.setModel(e.model)}async onInit(){}async onInitView(){this.initialized||(this.initialized=!0,await this.onInit())}async onBeforeRender(){}async onAfterRender(){}async onBeforeMount(){}async onAfterMount(){}async onBeforeUnmount(){}async onAfterUnmount(){}async onBeforeDestroy(){}async onAfterDestroy(){}setModel(e={}){let t=e!==this.model;if(!t)return this;this.model&&this.model.off&&this.model.off("change",this._onModelChange,this),this.model=e,this.model&&this.model.on&&this.model.on("change",this._onModelChange,this);for(const s in this.children){const i=this.children[s];i&&typeof i.setModel=="function"&&i.setModel(e)}return t&&this._onModelChange(),this}_onModelChange(){this.isMounted()&&this.render()}setTemplate(e){return this.template=e??"",this}addChild(e,t){try{if(!e||typeof e!="object")return this;t&&((t.containerId||t.container)&&(e.containerId=t.containerId||t.container),t.id&&(e.id=t.id)),e.parent=this,this.getApp()&&(e.app=this.app),this.children[e.id]=e}catch(s){v._warn("addChild error",s)}return e}removeChild(e){try{const t=typeof e=="string"?e:e&&e.id;if(!t)return this;const s=this.children[t];s&&(Promise.resolve(s.destroy()).catch(i=>v._warn("removeChild destroy error",i)),delete this.children[t])}catch(t){v._warn("removeChild error",t)}return this}getChild(e){return this.children[e]}async updateData(e,t=!1){return Object.assign(this.data,e),t&&this.isMounted()&&await this.render(),this}toggleClass(e,t){return t===void 0&&(t=!this.element.classList.contains(e)),this.element.classList.toggle(e,t),this}addClass(e){return this.element.classList.add(e),this}setClass(e){return this.element.className=e,this}removeClass(e){return this.element.classList.remove(e),this}canRender(){if(this.isRendering)return!1;const e=Date.now();if(this.options.renderCooldown>0&&e-this.lastRenderTime<this.options.renderCooldown)return v._warn(`View ${this.id}: Render called too quickly, cooldown active`),!1;if(this.options.noAppend&&this.parent)if(this.parent.contains(this.containerId||this.container)){if(this.containerId&&!document.getElementById(this.containerId))return!1;if(this.container&&!document.contains(this.container))return!1}else return!1;return!0}async render(e=!0,t=null){const s=Date.now();if(!this.canRender())return this;this.isRendering=!0,this.lastRenderTime=s;try{this.initialized||await this.onInitView(),this.unbindEvents(),await this.onBeforeRender(),this.getViewData&&(this.data=await this.getViewData());const i=await this.renderTemplate();this.element.innerHTML=i,e&&!this.isMounted()&&await this.mount(t),await this._renderChildren(),await this.onAfterRender(),this.bindEvents()}catch(i){v._warn(`Render error in ${this.id}`,i)}finally{this.isRendering=!1}return this}async _renderChildren(){for(const e in this.children){const t=this.children[e];t&&(t.parent=this,await Promise.resolve(t.render()).catch(s=>v._warn(`Child render error (${e})`,s)))}}async _unmountChildren(){for(const e in this.children){const t=this.children[e];t&&t.unbindEvents()}}isMounted(){return this.element?.isConnected}getChildElementById(e,t=null){const s=e.startsWith("#")?e.substring(1):e;return t?t.querySelector(`#${s}`):this.element.querySelector(`#${s}`)}getChildElement(e){if(e.startsWith("#"))return this.getChildElementById(e);let t=this.element?.querySelector(`[data-container="${e}"]`);return t||this.getChildElementById(e)}getContainer(){return this.replaceById?this.parent?this.parent.getChildElementById(this.id):null:this.containerId?this.parent?this.parent.getChildElement(this.containerId):this.getChildElementById(this.containerId,document.body):null}async mount(e=null){if(await this.onBeforeMount(),e||(e=this.getContainer()),this.containerId&&!e){console.error(`Container not found for ${this.containerId}`);return}e&&this.replaceById?e.replaceWith(this.element):e?e.replaceChildren(this.element):!this.containerId&&this.parent?this.parent.element.appendChild(this.element):!this.containerId&&!this.parent&&this.options.allowAppendToBody?(console.log("APPENDING TO BODY!!!!"),document.body.appendChild(this.element)):console.error(`Container not found for ${this.containerId}`),await this.onAfterMount(),this.mounted=!0}async unmount(){!this.element||!this.element.parentNode||(await this.onBeforeUnmount(),await this._unmountChildren(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.events.unbind(),await this.onAfterUnmount(),this.mounted=!1)}async destroy(){try{this.events.unbind();for(const e in this.children){const t=this.children[e];t&&await Promise.resolve(t.destroy()).catch(s=>v._warn(`Child destroy error (${e})`,s))}this.mounted=!1,this.element&&this.element.parentNode&&(await this.onBeforeDestroy(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),await this.onAfterDestroy())}catch(e){v._warn(`Destroy error in ${this.id}`,e)}}_ensureElement(){try{if(this.element&&this.element.tagName?.toLowerCase()===this.tagName)return this._syncAttrs(),this.element;const e=document.createElement(this.tagName);return this.element=e,this.el=e,this._syncAttrs(),e}catch(e){v._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||v._genId(),t}}_syncAttrs(){try{if(!this.element)return;this.id&&(this.element.id=this.id),this.element.className=this.className||"",this.style==null?this.element.removeAttribute("style"):this.element.style.cssText=String(this.style)}catch(e){v._warn("_syncAttrs error",e)}}bindEvents(){this.events.bind(this.element),this.enableTooltips&&this.initializeTooltips()}unbindEvents(){this.events.unbind(),this.enableTooltips&&this.disposeTooltips()}async renderTemplate(){const e=await this.getTemplate();if(!e)return"";const t=this.getPartials();return E.render(e,this,t)}renderTemplateString(e,t,s){return E.render(e,t,s)}getPartials(){return{}}async getTemplate(){if(this._templateCache&&this.cacheTemplate)return this._templateCache;const e=this.template||this.templateUrl;if(!e)throw new Error("Template not found");let t="";if(typeof e=="string")if(e.includes("<")||e.includes("{"))t=e;else try{let s=e;this.app||(this.app=this.getApp()),this.app&&this.app.basePath&&!s.startsWith("/")&&!s.startsWith("http://")&&!s.startsWith("https://")&&(s=`${this.app.basePath.endsWith("/")?this.app.basePath.slice(0,-1):this.app.basePath}/${s}`);const i=await fetch(s);if(!i.ok)throw new Error(`HTTP ${i.status}: ${i.statusText}`);t=await i.text()}catch(s){v._warn(`Failed to load template from ${e}: ${s}`),this.showError?.(`Failed to load template from ${e}: ${s.message}`)}else typeof e=="function"&&(t=await this.template(this.data,this.state));return this.cacheTemplate&&t&&(this._templateCache=t),t}getContextValue(e){const t=x.getContextData(this,e);return e&&e.startsWith("data.")&&t&&typeof t=="object"?x.wrapData(t,this):e&&e.startsWith("model.")&&e!=="model"&&t&&typeof t=="object"&&typeof t.getContextValue!="function"?x.wrapData(t,null):t}async handlePageNavigation(e){const t=e.getAttribute("data-page"),s=e.getAttribute("data-params");let i={};if(s)try{i=JSON.parse(s)}catch{console.warn("Invalid JSON in data-params:",s)}const a=this.getApp();if(a){a.showPage(t,i);return}const r=this.findRouter();r&&typeof r.navigateToPage=="function"?await r.navigateToPage(t,i):console.error(`No router found for page navigation to '${t}'`)}async handleHrefNavigation(e){const t=e.getAttribute("href");if(this.isExternalLink(t)||e.hasAttribute("data-external"))return;const s=this.findRouter();if(s){if(s.options&&s.options.mode==="param"&&t.startsWith("?")){const a="/"+t;await s.navigate(a);return}if(s.options&&s.options.mode==="hash"&&t.startsWith("#")){await s.navigate(t);return}const i=this.hrefToRoutePath(t);await s.navigate(i)}else console.warn("No router found for navigation, using default behavior"),window.location.href=t}isExternalLink(e){return e?e.startsWith("/")&&this.getApp()?!e.startsWith(this.findRouter().basePath):e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//"):!0}hrefToRoutePath(e){if(e.startsWith("/")){const t=this.findRouter();if(t&&t.options&&t.options.base){const s=t.options.base;if(e.startsWith(s))return e.substring(s.length)||"/"}return e}return e.startsWith("./")?e.substring(2):e}findRouter(){return this.getApp(),this.app?.router||null}getApp(){if(this.app)return this.app;const e=[window.__app__,window.MOJO?.app,window.APP,window.app,window.WebApp,window.matchUUID?window[window.matchUUID]():window[window.matchUUID]];return this.app=e.find(t=>t&&typeof t.showPage=="function")||null,this.app}handleActionError(e,t,s,i){this.showError(`Action '${e}' failed: ${t}`,s,i)}escapeHtml(e){if(typeof e!="string")return e;const t=document.createElement("div");return t.textContent=e,t.innerHTML}contains(e){if(typeof e=="string"){if(!this.element)return!1;e=document.getElementById(e)}return e?this.element.contains(e):!1}initializeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.disposeTooltips(),[...this.element.querySelectorAll('[data-bs-toggle="tooltip"]')].map(t=>{const s=t.getAttribute("data-tooltip-theme"),i=t.getAttribute("data-tooltip-size");let a="";s&&(a+=`tooltip-${s} `),i&&(a+=`tooltip-${i}`);const r={},n=a.trim();return n&&(r.customClass=n),new window.bootstrap.Tooltip(t,r)})}disposeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.element.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.dispose()})}async showError(e){console.error(`View ${this.id} error:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showError=="function"){await t.showError(e);return}alert(`Error: ${e}`)}async showSuccess(e){this.debug&&console.log(`View ${this.id} success:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showSuccess=="function"){await t.showSuccess(e);return}alert(`Success: ${e}`)}async showInfo(e){console.info(`View ${this.id} info:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showInfo=="function"){await t.showInfo(e);return}alert(`Info: ${e}`)}async showWarning(e){console.warn(`View ${this.id} warning:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showWarning=="function"){await t.showWarning(e);return}alert(`Warning: ${e}`)}async onActionCopyToClipboard(e,t){try{const i=(t?.closest("[data-clipboard]")||t)?.getAttribute("data-clipboard")||"";if(!i)return!0;if(navigator.clipboard&&window.isSecureContext)await navigator.clipboard.writeText(i);else{const n=document.createElement("textarea");n.value=i,document.body.appendChild(n),n.select(),document.execCommand("copy"),document.body.removeChild(n)}const a=t.querySelector("i"),r=a&&a.className;return a&&(a.className="bi bi-check",setTimeout(()=>{a.className=r},1e3)),!0}catch(s){return console.warn("Copy to clipboard failed:",s),!0}}static _genId(){return`view_${Math.random().toString(36).substr(2,9)}`}static _warn(e,t){try{t?console.warn(`[View] ${e}:`,t):console.warn(`[View] ${e}`)}catch{}}}Object.assign(v.prototype,ne);class W extends v{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",!this.id&&this.constructor.pageName&&!e.pageName&&(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,console.log(`Page ${this.pageName} constructed with route: ${this.route}`)}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&typeof document<"u"&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} entered`)}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} exiting`)}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){console.log(`Default action '${e}' triggered on page: ${this.pageName}`)}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){!e||!this.element||(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element&&this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&(t.type==="checkbox"?e[t.name]=t.checked:t.type==="radio"?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e}restoreFormData(e){!e||!this.element||Object.entries(e).forEach(([t,s])=>{const i=this.element.querySelector(`[name="${t}"]`);if(i)if(i.type==="checkbox")i.checked=s;else if(i.type==="radio"){const a=this.element.querySelector(`[name="${t}"][value="${s}"]`);a&&(a.checked=!0)}else i.value=s})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if(!(typeof document>"u")){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([t,s])=>{if(t!=="title"&&t!=="description"){let i=document.querySelector(`meta[name="${t}"]`);i||(i=document.createElement("meta"),i.name=t,document.head.appendChild(i)),i.content=s}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`
|
|
14
|
+
`}nl2br(e){return e==null?"":this.escapeHtml(String(e)).replace(/\r\n|\r|\n/g,"<br>")}code(e,t=""){if(e==null)return"";const s=t?`language-${this.escapeHtml(String(t))}`:"",i=this.escapeHtml(String(e));return`<pre class="bg-light p-2 rounded border"><code class="${s}">${i}</code></pre>`}register(e,t){if(typeof t!="function")throw new Error(`Formatter must be a function, got ${typeof t}`);return this.formatters.set(e.toLowerCase(),t),this}apply(e,t,...s){try{const i=this.formatters.get(e.toLowerCase());return i?i(t,...s):(console.warn(`Formatter '${e}' not found`),t)}catch(i){return console.error(`Error in formatter '${e}':`,i),t}}pipe(e,t,s=null){return t?this.parsePipeString(t,s).reduce((a,r)=>this.apply(r.name,a,...r.args),e):e}parsePipeString(e,t=null){const s=[],i=e.split("|").map(a=>a.trim());for(const a of i){const r=this.parseFormatter(a,t);r&&s.push(r)}return s}parseFormatter(e,t=null){const s=e.match(/^([a-zA-Z_]\w*)\s*\((.*)\)$/);if(s){const[,a,r]=s,n=r?this.parseArguments(r,t):[];return{name:a,args:n}}const i=e.match(/^([a-zA-Z_]\w*)(?::(.+))?$/);if(i){const[,a,r]=i,n=r?this.parseColonArguments(r,t):[];return{name:a,args:n}}return null}parseArguments(e,t=null){const s=[];let i="",a=!1,r=null,n=0;for(let l=0;l<e.length;l++){const o=e[l];!a&&(o==='"'||o==="'")?(a=!0,r=o,i+=o):a&&o===r&&e[l-1]!=="\\"?(a=!1,r=null,i+=o):!a&&o==="{"?(n++,i+=o):!a&&o==="}"?(n--,i+=o):!a&&n===0&&o===","?(s.push(this.parseValue(i.trim(),t)),i=""):i+=o}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 l=e[n];!a&&(l==='"'||l==="'")?(a=!0,r=l,i+=l):a&&l===r&&e[n-1]!=="\\"?(a=!1,r=null,i+=l):!a&&l===":"?(s.push(this.parseValue(i.trim(),t)),i=""):i+=l}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,l,o]=e.split("-").map(Number);s=new Date(n,l-1,o)}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((l,o)=>o.length-l.length);for(const l of n)r=r.replace(new RegExp(l,"g"),a[l]);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,l=()=>{let T="";try{const j=new Intl.DateTimeFormat(r,{hour:"2-digit",minute:"2-digit",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(P=>P.type==="timeZoneName");if(T=j?j.value:"",T&&/^GMT[+-]/i.test(T))try{const R=new Intl.DateTimeFormat(r,{timeStyle:"short",timeZoneName:"short",...n?{timeZone:n}:{}}).formatToParts(a).find(Ke=>Ke.type==="timeZoneName");R&&R.value&&!/^GMT[+-]/i.test(R.value)&&(T=R.value)}catch{}if(T&&/\s/.test(T)){const P=T.split(/\s+/).map(R=>R[0]).join("").toUpperCase();P.length>=2&&P.length<=4&&(T=P)}}catch{T=""}return T};if(!n){const T=this.date(a,t),O=this.time(a,s),j=l();return T&&O?`${T} ${O} ${j}`.trim():""}const o=new Intl.DateTimeFormat(r,{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hourCycle:"h23"}).formatToParts(a),c=T=>{const O=o.find(j=>j.type===T);return O?O.value:""},d=c("year"),h=c("month"),f=c("day"),m=c("hour"),p=c("minute"),g=c("second"),y=h?String(parseInt(h,10)):"",b=f?String(parseInt(f,10)):"",w=m?String(parseInt(m,10)):"",C=m?parseInt(m,10)%12||12:"",$=m?parseInt(m,10)>=12?"PM":"AM":"",S=$?$.toLowerCase():"",M=new Intl.DateTimeFormat(r,{timeZone:n,month:"long"}).format(a),L=new Intl.DateTimeFormat(r,{timeZone:n,month:"short"}).format(a),A=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"long"}).format(a),U=new Intl.DateTimeFormat(r,{timeZone:n,weekday:"short"}).format(a),pe={YYYY:d,YY:d?d.slice(-2):"",MMMM:M,MMM:L,MM:h,M:y,dddd:A,ddd:U,DD:f,D:b},ge={HH:m,H:w,hh:C!==""?String(C).padStart(2,"0"):"",h:C!==""?String(C):"",mm:p,m:p?String(parseInt(p,10)):"",ss:g,s:g?String(parseInt(g,10)):"",A:$,a:S},I=(T,O)=>{if(!T)return"";const j=new RegExp(`(${Object.keys(O).sort((P,R)=>R.length-P.length).join("|")})`,"g");return T.replace(j,P=>O[P]??P)},N=I(t,pe),De=I(s,ge),Ge=l();return N&&De?`${N} ${De} ${Ge}`.trim():""}normalizeEpoch(e){if(typeof e!="number"&&(e=Number(e)),isNaN(e))return"";if(e<1e11)return e*1e3;if(e>1e12&&e<1e13)return e;throw new Error("Value doesn't look like epoch seconds or ms")}date_range(e,t=null,s="MM/DD/YYYY"){if(!e)return"";const i=t||new Date,a=this.date(e,s),r=this.date(i,s);return!a||!r?"":`${a} - ${r}`}datetime_range(e,t=null,s="MM/DD/YYYY",i="HH:mm"){if(!e)return"";const a=t||new Date,r=this.datetime(e,s,i),n=this.datetime(a,s,i);return!r||!n?"":`${r} - ${n}`}relative(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const a=s-new Date,r=Math.abs(a),n=Math.floor(r/1e3),l=Math.floor(n/60),o=Math.floor(l/60),c=Math.floor(o/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":o>0?o+"h":l>0?l+"m":"now";if(c>365){const h=Math.floor(c/365),f=d?"in ":"",m=d?"":" ago";return f+h+" year"+(h>1?"s":"")+m}if(c>30){const h=Math.floor(c/30),f=d?"in ":"",m=d?"":" ago";return f+h+" month"+(h>1?"s":"")+m}if(c>7){const h=Math.floor(c/7),f=d?"in ":"",m=d?"":" ago";return f+h+" week"+(h>1?"s":"")+m}if(c===1)return d?"tomorrow":"yesterday";if(c>0){const h=d?"in ":"",f=d?"":" ago";return h+c+" days"+f}if(o>0){const h=d?"in ":"",f=d?"":" ago";return h+o+" hour"+(o>1?"s":"")+f}if(l>0){const h=d?"in ":"",f=d?"":" ago";return h+l+" minute"+(l>1?"s":"")+f}if(n>30){const h=d?"in ":"",f=d?"":" ago";return h+n+" seconds"+f}return"just now"}iso(e,t=!1){if(!e)return"";e=this.normalizeEpoch(e);const s=e instanceof Date?e:new Date(e);return isNaN(s.getTime())?String(e):t?s.toISOString().split("T")[0]:s.toISOString()}number(e,t=2,s="en-US"){const i=parseFloat(e);return isNaN(i)?String(e):i.toLocaleString(s,{minimumFractionDigits:t,maximumFractionDigits:t})}currency(e,t="$",s=2){const i=parseInt(e);if(isNaN(i))return String(e);const a=Math.abs(i).toString(),r=i<0?"-":"";let n,l;a.length<=2?(n="0",l=a.padStart(2,"0")):(n=a.slice(0,-2),l=a.slice(-2)),n=n.replace(/\B(?=(\d{3})+(?!\d))/g,",");let o;if(s===0)parseInt(l)>=50&&(n=(parseInt(n.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),o=n;else if(s===2)o=`${n}.${l}`;else{const c=l.slice(0,s).padEnd(s,"0");o=`${n}.${c}`}return r+t+o}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,l=0;for(;n>=r&&l<a.length-1;)n/=r,l++;const o=l===0?0:s;return`${n.toFixed(o)} ${a[l]}`}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[,l,o]=n;try{return a.replace(new RegExp(l,o),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),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"},l={active:"success",approved:"success",declined:"danger",inactive:"secondary",pending:"warning",success:"success",error:"danger",warning:"warning"},o=t[r]||n[r]||"",c=s[r]||l[r]||"secondary";let d="";!i&&o&&(d=`<i class="${o}"></i>`);let h="";return a||(h=e),`<span class="text-${c}">${d}${d?" ":""}${h}</span>`}boolean(e,t="True",s="False",i=!1){const a=e?t:s;return i?`<span class="text-${e?"success":"danger"}">${a}</span>`:a}bool(e){return e==null||e===0||e===""||e===!1||e==="false"?!1:e===!0||e==="true"?!0:!(Array.isArray(e)&&e.length===0||e&&typeof e=="object"&&e.constructor===Object&&Object.keys(e).length===0)}icon(e,t={}){const s=String(e).toLowerCase(),i=t[s]||"";return i?`<i class="${i}"></i>`:""}yesnoicon(e,t="bi bi-check-circle-fill text-success",s="bi bi-x-circle-fill text-danger"){return e?`<i class="${t}"></i>`:`<i class="${s}"></i>`}image(e,t="thumbnail",s="img-fluid",i=""){const a=this._extractImageUrl(e,t);return a?`<img src="${a}" class="${s}" alt="${i}" />`:""}avatar(e,t="md",s="rounded-circle",i=""){const a=this._extractImageUrl(e,"square_sm")||Ae,r={xs:"width: 1.5rem; height: 1.5rem;",sm:"width: 2rem; height: 2rem;",md:"width: 3rem; height: 3rem;",lg:"width: 4rem; height: 4rem;",xl:"width: 5rem; height: 5rem;"},n=r[t]||r.md,o=`object-fit-cover ${s}`.trim();return`<img src="${a}" class="${o}" 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 l=e.length-i;return`${r.join(", ")}, ${s} ${l} ${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;case"ms":case"milliseconds":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 l=Math.abs(r),o=r<0?"-":"",c=[];let d=l;for(const h of n)if(d>=h.value){const f=Math.floor(d/h.value);d=d%h.value;const m=s?h.short:f===1?h.name:h.name+"s";if(c.push(s?`${f}${m}`:`${f} ${m}`),c.length>=i)break}return c.length===0?s?`${Math.round(l)}ms`:`${Math.round(l)} milliseconds`:o+(s?c.join(""):c.join(" "))}hash(e,t=8,s="",i="..."){if(e==null)return"";const a=String(e);return a.length<=t?s+a:s+a.substring(0,t)+i}stripHtml(e){return e==null?"":String(e).replace(/<[^>]*>/g,"")}highlight(e,t,s="highlight"){if(e==null||!t)return String(e||"");const i=String(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`(${i})`,"gi");return String(e).replace(a,`<mark class="${s}">$1</mark>`)}hex(e,t=!1,s=!1){if(e==null)return"";let i="";const a=r=>Array.from(r).map(n=>n.toString(16).padStart(2,"0")).join("");if(typeof e=="number"){let r=Math.abs(Math.trunc(e)).toString(16);r.length%2&&(r="0"+r),i=r}else if(e instanceof Uint8Array)i=a(e);else if(e instanceof ArrayBuffer)i=a(new Uint8Array(e));else if(Array.isArray(e)&&e.every(r=>typeof r=="number"))i=a(Uint8Array.from(e.map(r=>r&255)));else{const n=new TextEncoder().encode(String(e));i=a(n)}return t&&(i=i.toUpperCase()),(s?"0x":"")+i}unhex(e){if(e==null)return"";let t=String(e).trim();if((t.startsWith("0x")||t.startsWith("0X"))&&(t=t.slice(2)),t=t.replace(/\s+/g,""),t.length===0)return"";t.length%2!==0&&(t="0"+t);const s=new Uint8Array(t.length/2);for(let i=0;i<t.length;i+=2){const a=parseInt(t.slice(i,i+2),16);if(Number.isNaN(a))return String(e);s[i/2]=a}try{return new TextDecoder().decode(s)}catch{let a="";for(const r of s)a+=String.fromCharCode(r);return a}}json(e,t=2){try{return JSON.stringify(e,null,t)}catch{return String(e)}}has(e){return this.formatters.has(e.toLowerCase())}unregister(e){return this.formatters.delete(e.toLowerCase())}listFormatters(){return Array.from(this.formatters.keys()).sort()}iter(e){return e==null?[]:Array.isArray(e)?e:typeof e=="object"?Object.entries(e).map(([t,s])=>({key:t,value:s})):[{key:"0",value:e}]}}const V=new Ie;window.dataFormatter=V;class x{static getContextData(e,t){if(!t||e==null)return;let s=t,i="",a=0,r=-1;for(let l=0;l<t.length;l++){const o=t[l];if(o==="(")a++;else if(o===")")a--;else if(o==="|"&&a===0){r=l;break}}r>-1&&(s=t.substring(0,r).trim(),i=t.substring(r+1).trim());const n=this.getNestedValue(e,s);return i?V.pipe(n,i,e):n}static getNestedValue(e,t){if(!t||e==null)return;if(!t.includes(".")){if(t in e){const a=e[t];return typeof a=="function"?a.call(e):a}return}const s=t.split(".");let i=e;for(let a=0;a<s.length;a++){const r=s[a];if(i==null)return;if(a===0)if(i.hasOwnProperty(r)){const n=i[r];typeof n=="function"?i=n.call(e):i=n}else return;else{if(i&&typeof i.getContextValue=="function"){const n=s.slice(a).join(".");return i.getContextValue(n)}if(Array.isArray(i)&&!isNaN(r))i=i[parseInt(r)];else if(i.hasOwnProperty(r))i=i[r];else if(typeof i[r]=="function")i=i[r].call(i);else return}}return i}static isNullOrUndefined(e){return e==null}static deepClone(e){if(e===null||typeof e!="object")return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof Array)return e.map(t=>this.deepClone(t));if(e instanceof Object){const t={};for(const s in e)e.hasOwnProperty(s)&&(t[s]=this.deepClone(e[s]));return t}}static deepMerge(e,...t){if(!t.length)return e;const s=t.shift();if(this.isObject(e)&&this.isObject(s))for(const i in s)this.isObject(s[i])?(e[i]||Object.assign(e,{[i]:{}}),this.deepMerge(e[i],s[i])):Object.assign(e,{[i]:s[i]});return this.deepMerge(e,...t)}static isObject(e){return e&&typeof e=="object"&&!Array.isArray(e)}static debounce(e,t){let s;return function(...a){const r=()=>{clearTimeout(s),e(...a)};clearTimeout(s),s=setTimeout(r,t)}}static throttle(e,t){let s;return function(...i){s||(e.apply(this,i),s=!0,setTimeout(()=>s=!1,t))}}static generateId(e=""){const t=Date.now().toString(36),s=Math.random().toString(36).substr(2,9);return e?`${e}_${t}_${s}`:`${t}_${s}`}static escapeHtml(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};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 l of a)if(l.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 l="";const o=[];if(s.customChars?l=s.customChars:(s.includeLowercase&&(l+=i,o.push(i[Math.floor(Math.random()*i.length)])),s.includeUppercase&&(l+=a,o.push(a[Math.floor(Math.random()*a.length)])),s.includeNumbers&&(l+=r,o.push(r[Math.floor(Math.random()*r.length)])),s.includeSpecialChars&&(l+=n,o.push(n[Math.floor(Math.random()*n.length)]))),!l)throw new Error("No character types selected for password generation");let c="";for(const d of o)c+=d;for(let d=c.length;d<s.length;d++)c+=l[Math.floor(Math.random()*l.length)];return c.split("").sort(()=>Math.random()-.5).join("")}static parseQueryString(e){const t={},s=new URLSearchParams(e);for(const[i,a]of s.entries())t[i]=a;return t}static toQueryString(e){return new URLSearchParams(e).toString()}static wrapData(e,t=null,s=3){return!e||typeof e!="object"||e instanceof Date||e instanceof RegExp||e instanceof Error||s<=0||typeof e.getContextValue=="function"?e:Array.isArray(e)?e.map(i=>i&&typeof i=="object"&&!i.getContextValue?new se(i,t):i):new se(e,t)}}class se{constructor(e,t=null){if(Object.defineProperty(this,"_data",{value:e,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(this,"_rootContext",{value:t,writable:!1,enumerable:!1,configurable:!1}),e&&typeof e=="object"){for(const s in e)if(e.hasOwnProperty(s)){const i=e[s];this[s]=x.wrapData(i,t)}}}getContextValue(e){let t=e,s="",i=0,a=-1;for(let n=0;n<e.length;n++){const l=e[n];if(l==="(")i++;else if(l===")")i--;else if(l==="|"&&i===0){a=n;break}}a>-1&&(t=e.substring(0,a).trim(),s=e.substring(a+1).trim());let r;return t in this&&t!=="_data"&&t!=="_rootContext"?r=this[t]:r=x.getNestedValue(this._data,t),s&&r!==void 0?V.pipe(r,s,this._rootContext||this._data):r}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}x.DataWrapper=se,typeof window<"u"&&(window.utils=x);const Te=Object.prototype.toString,B=Array.isArray||function(u){return Te.call(u)==="[object Array]"},Y=function(u){return typeof u=="function"},be=function(u){return u!==null&&typeof u=="object"};function Me(){return typeof window>"u"?null:window.MOJO?.dataFormatter?window.MOJO.dataFormatter:window.dataFormatter?window.dataFormatter:null}const ye=function(u){const e={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};return String(u).replace(/[&<>"'`=\/]/g,function(t){return e[t]})};class we{constructor(e){this.string=e,this.tail=e,this.pos=0}eos(){return this.tail===""}scan(e){const t=this.tail.match(e);if(!t||t.index!==0)return"";const s=t[0];return this.tail=this.tail.substring(s.length),this.pos+=s.length,s}scanUntil(e){const t=this.tail.search(e);let s;switch(t){case-1:s=this.tail,this.tail="";break;case 0:s="";break;default:s=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=s.length,s}}class Q{constructor(e,t){this.view=e,this.cache={".":this.view},this.parent=t,this.view?._cacheId||this.view&&typeof this.view=="object"&&(this.view._cacheId=Math.random().toString(36).substring(2))}push(e){return new Q(e,this)}lookup(e){if(this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;if(this.renderCache.has(i))return this.renderCache.get(i)}if(e===".")return this.view;if(e&&e.startsWith(".")){let i=e.substring(1),a=!1,r=null;const n=i.indexOf("|");if(n!==-1&&(r=i.substring(n+1).trim(),i=i.substring(0,n).trim()),i.endsWith("|iter")&&(i=i.substring(0,i.length-5),a=!0),this.view&&typeof this.view=="object"){let l;if(typeof this.view.getContextValue=="function")try{l=this.view.getContextValue(i),l!==void 0&&Y(l)&&(l=l.call(this.view))}catch{l=void 0}if(l===void 0&&i in this.view&&(l=this.view[i],Y(l)&&(l=l.call(this.view))),r&&l!==void 0)try{const o=Me();o&&typeof o.pipe=="function"&&(l=o.pipe(l,r,this))}catch{}return B(l)?a?l:l.length>0:be(l)?a?Object.entries(l).map(([o,c])=>({key:o,value:c})):Object.keys(l).length>0:l}return}const t=this.cache;let s;if(t.hasOwnProperty(e))s=t[e];else{let i=this,a,r,n,l=!1;for(;i;){if(i.view&&typeof i.view.getContextValue=="function")try{a=i.view.getContextValue(e),a!==void 0&&(l=!0)}catch{l=!1}if(!l)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 o=r.slice(n).join(".");a=a.getContextValue(o),n=r.length,a!==void 0&&(l=!0)}catch{n===r.length-1&&(l=ie(a,r[n])||ve(a,r[n])),a=a[r[n++]]}else n===r.length-1&&(l=ie(a,r[n])||ve(a,r[n])),a=a[r[n++]];else a=i.view[e],l=ie(i.view,e);if(l){s=a;break}i=i.parent}t[e]=s}if(Y(s)&&(s=s.call(this.view)),this.renderCache&&this.view?._cacheId){const i=`${this.view._cacheId}:${e}`;this.renderCache.set(i,s)}return s}}function ie(u,e){return u!=null&&typeof u=="object"&&e in u}function ve(u,e){return u!=null&&typeof u!="object"&&u.hasOwnProperty&&u.hasOwnProperty(e)}class Ce{constructor(){this.templateCache=new Map}clearCache(){this.templateCache.clear()}parse(e,t){t=t||["{{","}}"];const s=e+":"+t.join(":");let i=this.templateCache.get(s);return i==null&&(i=this.parseTemplate(e,t),this.templateCache.set(s,i)),i}parseTemplate(e,t){if(!e)return[];const s=t[0],i=t[1],a=new we(e),r=[];let n,l,o,c,d;const h=new RegExp(ae(s)+"\\s*"),f=new RegExp("\\s*"+ae(i)),m=new RegExp("\\s*"+ae("}"+i));for(;!a.eos();){if(n=a.pos,o=a.scanUntil(h),o)for(let p=0;p<o.length;++p)c=o.charAt(p),c===`
|
|
15
|
+
`?r.push(["text",c]):r.push(["text",c]);if(!a.scan(h))break;if(l=a.scan(/[#^\/>{&=!]/),l||(l="name"),a.scan(/\s*/),l==="="?(o=a.scanUntil(/\s*=/),a.scan(/\s*=/),a.scanUntil(f)):l==="{"?(o=a.scanUntil(m),a.scan(m),l="&"):o=a.scanUntil(f),a.scan(f),l==="#"||l==="^")d=[l,o,n,a.pos],r.push(d);else if(l==="/"){let p;for(let g=r.length-1;g>=0;--g)if((r[g][0]==="#"||r[g][0]==="^")&&r[g][1]===o){p=r[g];break}p&&p.length===4&&p.push(a.pos),r.push([l,o,n,a.pos])}else r.push([l,o,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 l=i.pop();l&&(l[5]=r[2],s=i.length>0?i[i.length-1][4]:t);break;default:s.push(r)}}return t}render(e,t,s,i){const a=this.getConfigTags(i)||["{{","}}"],r=this.parse(e,a),n=new Map;return this.renderTokens(r,new Q(t),s,e,i,n)}renderTokens(e,t,s,i,a,r){r&&!t.renderCache&&(t.renderCache=r);let n="";for(let l=0;l<e.length;++l){const o=e[l];let c;switch(o[0]){case"#":if(c=t.lookup(o[1]),!c)continue;const d=o[4];if(!d||!B(d)){console.warn(`MUSTACHE WARNING - Section ${o[1]} has no child tokens:`,o);continue}if(B(c))for(let h=0;h<c.length;++h){const f=t.push(c[h]);t.renderCache&&(f.renderCache=t.renderCache);const m=this.renderTokens(d,f,s,i,a,r);n+=m}else if(typeof c=="object"||typeof c=="string"||typeof c=="number"){const h=t.push(c);t.renderCache&&(h.renderCache=t.renderCache),n+=this.renderTokens(d,h,s,i,a,r)}else if(Y(c)){const h=i==null?null:i.slice(o[3],o[5]);c=c.call(t.view,h,f=>this.render(f,t.view,s,a)),c!=null&&(n+=c)}else c&&(n+=this.renderTokens(d,t,s,i,a,r));break;case"^":if(c=t.lookup(o[1]),!c||B(c)&&c.length===0){const h=o[4];h&&B(h)&&(n+=this.renderTokens(h,t,s,i,a,r))}break;case">":if(!s)continue;c=Y(s)?s(o[1]):s[o[1]],c!=null&&(n+=this.render(c,t.view,s,a));break;case"&":c=t.lookup(o[1]),c!=null&&(n+=c);break;case"name":c=t.lookup(o[1]),c!=null&&(n+=ye(c));break;case"text":n+=o[1];break}}return n}getConfigTags(e){return be(e)&&B(e.tags)?e.tags:null}}function ae(u){return u.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const re=new Ce,E={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:we,Context:Q,Writer:Ce,escape:ye,clearCache(){return re.clearCache()},parse(u,e){return re.parse(u,e)},render(u,e,t,s){if(typeof u!="string")throw new TypeError('Invalid template! Template should be a "string"');return e&&typeof e=="object"&&!e.getContextValue&&typeof e.toJSON!="function"&&(e=x.wrapData(e)),re.render(u,e,t,s)}};class Ve{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=new Map}bind(e){if(this.unbind(),!e)return;const t=async n=>{const l=n.target.closest("[data-action]");if(l&&this.shouldHandle(l,n)){const c=l.getAttribute("data-action");if(this.hideTooltip(l),await this.dispatch(c,n,l)){n.preventDefault(),n.stopPropagation(),n.handledByChild=!0;return}}const o=n.target.closest("a[href], [data-page]");if(o&&!o.hasAttribute("data-action")&&this.shouldHandle(o,n)){if(n.ctrlKey||n.metaKey||n.shiftKey||n.button===1)return;if(o.tagName==="A"){const c=o.getAttribute("href");if(c&&c!=="#"&&!c.startsWith("#")&&(this.view.isExternalLink(c)||o.hasAttribute("data-external")))return}this.hideTooltip(o),n.preventDefault(),n.stopPropagation(),n.handledByChild=!0,o.hasAttribute("data-page")?await this.view.handlePageNavigation(o):await this.view.handleHrefNavigation(o)}},s=n=>{const l=n.target.closest("[data-change-action]");if(!l||!this.shouldHandle(l,n))return;const o=l.getAttribute("data-change-action");this.dispatchChange(o,n,l).then(c=>{c&&(n.stopPropagation(),n.handledByChild=!0)})},i=n=>{const l=n.target.closest("[data-change-action]");if(!l||!this.shouldHandle(l,n)||!n.target.matches('[data-filter="live-search"]'))return;const c=l.getAttribute("data-change-action"),d=parseInt(l.getAttribute("data-filter-debounce"))||300,h=`${c}-${l.getAttribute("data-container")||"default"}`;this.debounceTimers.has(h)&&clearTimeout(this.debounceTimers.get(h));const f=setTimeout(()=>{this.debounceTimers.delete(h),this.dispatchChange(c,n,l).then(m=>{m&&(n.stopPropagation(),n.handledByChild=!0)})},d);this.debounceTimers.set(h,f)},a=n=>{if(n.target.matches('[data-filter="search"]'))return;const l=n.target.closest("[data-keydown-action]")||n.target.closest("[data-change-action]");if(!l||!this.shouldHandle(l,n))return;let o=["Enter"];if(l.getAttribute("data-change-keys")&&(o=l.getAttribute("data-change-keys").split(",").map(c=>c.trim())),o.includes("*")||o.includes(n.key)){const c=l.getAttribute("data-keydown-action")||l.getAttribute("data-change-action");this.dispatch(c,n,l).then(d=>{d&&(n.preventDefault(),n.stopPropagation(),n.handledByChild=!0)})}},r=n=>{const l=n.target.closest("form[data-action]");if(!l||!this.shouldHandle(l,n))return;n.preventDefault();const o=l.getAttribute("data-action");this.dispatch(o,n,l)};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=o=>o.includes("-")?o.split("-").map(c=>c[0].toUpperCase()+c.slice(1)).join(""):o[0].toUpperCase()+o.slice(1),r=`handleAction${a(e)}`;if(typeof i[r]=="function")try{return t.preventDefault(),await i[r](t,s),!0}catch(o){return console.error(`Error in action ${e}:`,o),i.handleActionError(e,o,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(o){return console.error(`Error in action ${e}:`,o),i.handleActionError(e,o,t,s),!0}const l=`onPassThruAction${a(e)}`;if(typeof i[l]=="function")try{return await i[l](t,s),!1}catch(o){return console.error(`Error in action ${e}:`,o),i.handleActionError(e,o,t,s),!0}if(typeof i.onActionDefault=="function")try{return await i.onActionDefault(e,t,s)}catch(o){return console.error(`Error in default action handler for ${e}:`,o),i.handleActionError(e,o,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(l=>l[0].toUpperCase()+l.slice(1)).join(""):n[0].toUpperCase()+n.slice(1))(e)}`;if(typeof i[r]=="function")try{return await i[r](t,s),!0}catch(n){return console.error(`Error in onChange ${e}:`,n),i.handleActionError?.(e,n,t,s),!0}return await this.dispatch(e,t,s)}shouldHandle(e,t){return!!(this.owns(e)||this.contains(e)&&!t.handledByChild)}owns(e){const t=this.view.element;if(!t||!t.contains(e))return!1;for(const s of Object.values(this.view.children))if(s.element&&s.element.contains(e))return!1;return!0}contains(e){return!!this.view.element&&this.view.element.contains(e)}}const ne={on(u,e,t){this._listeners||(this._listeners={}),this._listeners[u]||(this._listeners[u]=[]);const s={callback:e,context:t,fn:t?e.bind(t):e};return this._listeners[u].push(s),this},off(u,e,t){return!this._listeners||!this._listeners[u]?this:(e?(this._listeners[u]=this._listeners[u].filter(s=>s.callback!==e||arguments.length===3&&s.context!==t),this._listeners[u].length===0&&delete this._listeners[u]):delete this._listeners[u],this)},once(u,e,t){const s=(...i)=>{this.off(u,s),(t?e.bind(t):e).apply(t||this,i)};return this.on(u,s),this},emit(u,...e){if(!this._listeners||!this._listeners[u])return this;const t=this._listeners[u].slice();for(const s of t)try{s.fn.apply(s.context||this,e)}catch(i){console&&console.error&&console.error(`Error in ${u} event handler:`,i)}return this}};typeof window<"u"&&(window.Mustache=E);class v{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??v._genId(),this.containerId=e.containerId??null,this.container=e.container??null,typeof this.container=="string"&&(this.containerId=this.container,this.container=null),this.parent=e.parent??null,this.children=e.children??{},this.template=e.template||e.templateUrl||"",this.data=e.data??{},this.isRendering=!1,this.lastRenderTime=0,this.mounted=!1,this.debug=e.debug??!1,this.app=e.app??null,this.cacheTemplate=e.cacheTemplate??!0,this.enableTooltips=e.enableTooltips??!1,this.options={...e},this.element=this._ensureElement(),this.events=new Ve(this),e.model&&this.setModel(e.model)}async onInit(){}async onInitView(){this.initialized||(this.initialized=!0,await this.onInit())}async onBeforeRender(){}async onAfterRender(){}async onBeforeMount(){}async onAfterMount(){}async onBeforeUnmount(){}async onAfterUnmount(){}async onBeforeDestroy(){}async onAfterDestroy(){}setModel(e={}){let t=e!==this.model;if(!t)return this;this.model&&this.model.off&&this.model.off("change",this._onModelChange,this),this.model=e,this.model&&this.model.on&&this.model.on("change",this._onModelChange,this);for(const s in this.children){const i=this.children[s];i&&typeof i.setModel=="function"&&i.setModel(e)}return t&&this._onModelChange(),this}_onModelChange(){this.isMounted()&&this.render()}setTemplate(e){return this.template=e??"",this}addChild(e,t){try{if(!e||typeof e!="object")return this;t&&((t.containerId||t.container)&&(e.containerId=t.containerId||t.container),t.id&&(e.id=t.id)),e.parent=this,this.getApp()&&(e.app=this.app),this.children[e.id]=e}catch(s){v._warn("addChild error",s)}return e}removeChild(e){try{const t=typeof e=="string"?e:e&&e.id;if(!t)return this;const s=this.children[t];s&&(Promise.resolve(s.destroy()).catch(i=>v._warn("removeChild destroy error",i)),delete this.children[t])}catch(t){v._warn("removeChild error",t)}return this}getChild(e){return this.children[e]}async updateData(e,t=!1){return Object.assign(this.data,e),t&&this.isMounted()&&await this.render(),this}toggleClass(e,t){return t===void 0&&(t=!this.element.classList.contains(e)),this.element.classList.toggle(e,t),this}addClass(e){return this.element.classList.add(e),this}setClass(e){return this.element.className=e,this}removeClass(e){return this.element.classList.remove(e),this}canRender(){if(this.isRendering)return!1;const e=Date.now();if(this.options.renderCooldown>0&&e-this.lastRenderTime<this.options.renderCooldown)return v._warn(`View ${this.id}: Render called too quickly, cooldown active`),!1;if(this.options.noAppend&&this.parent)if(this.parent.contains(this.containerId||this.container)){if(this.containerId&&!document.getElementById(this.containerId))return!1;if(this.container&&!document.contains(this.container))return!1}else return!1;return!0}async render(e=!0,t=null){const s=Date.now();if(!this.canRender())return this;this.isRendering=!0,this.lastRenderTime=s;try{this.initialized||await this.onInitView(),this.unbindEvents(),await this.onBeforeRender(),this.getViewData&&(this.data=await this.getViewData());const i=await this.renderTemplate();this.element.innerHTML=i,e&&!this.isMounted()&&await this.mount(t),await this._renderChildren(),await this.onAfterRender(),this.bindEvents()}catch(i){v._warn(`Render error in ${this.id}`,i)}finally{this.isRendering=!1}return this}async _renderChildren(){for(const e in this.children){const t=this.children[e];t&&(t.parent=this,await Promise.resolve(t.render()).catch(s=>v._warn(`Child render error (${e})`,s)))}}async _unmountChildren(){for(const e in this.children){const t=this.children[e];t&&t.unbindEvents()}}isMounted(){return this.element?.isConnected}getChildElementById(e,t=null){const s=e.startsWith("#")?e.substring(1):e;return t?t.querySelector(`#${s}`):this.element.querySelector(`#${s}`)}getChildElement(e){if(e.startsWith("#"))return this.getChildElementById(e);let t=this.element?.querySelector(`[data-container="${e}"]`);return t||this.getChildElementById(e)}getContainer(){return this.replaceById?this.parent?this.parent.getChildElementById(this.id):null:this.containerId?this.parent?this.parent.getChildElement(this.containerId):this.getChildElementById(this.containerId,document.body):null}async mount(e=null){if(await this.onBeforeMount(),e||(e=this.getContainer()),this.containerId&&!e){console.error(`Container not found for ${this.containerId}`);return}e&&this.replaceById?e.replaceWith(this.element):e?e.replaceChildren(this.element):!this.containerId&&this.parent?this.parent.element.appendChild(this.element):!this.containerId&&!this.parent&&this.options.allowAppendToBody?(console.log("APPENDING TO BODY!!!!"),document.body.appendChild(this.element)):console.error(`Container not found for ${this.containerId}`),await this.onAfterMount(),this.mounted=!0}async unmount(){!this.element||!this.element.parentNode||(await this.onBeforeUnmount(),await this._unmountChildren(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.events.unbind(),await this.onAfterUnmount(),this.mounted=!1)}async destroy(){try{this.events.unbind();for(const e in this.children){const t=this.children[e];t&&await Promise.resolve(t.destroy()).catch(s=>v._warn(`Child destroy error (${e})`,s))}this.mounted=!1,this.element&&this.element.parentNode&&(await this.onBeforeDestroy(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),await this.onAfterDestroy())}catch(e){v._warn(`Destroy error in ${this.id}`,e)}}_ensureElement(){try{if(this.element&&this.element.tagName?.toLowerCase()===this.tagName)return this._syncAttrs(),this.element;const e=document.createElement(this.tagName);return this.element=e,this.el=e,this._syncAttrs(),e}catch(e){v._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||v._genId(),t}}_syncAttrs(){try{if(!this.element)return;this.id&&(this.element.id=this.id),this.element.className=this.className||"",this.style==null?this.element.removeAttribute("style"):this.element.style.cssText=String(this.style)}catch(e){v._warn("_syncAttrs error",e)}}bindEvents(){this.events.bind(this.element),this.enableTooltips&&this.initializeTooltips()}unbindEvents(){this.events.unbind(),this.enableTooltips&&this.disposeTooltips()}async renderTemplate(){const e=await this.getTemplate();if(!e)return"";const t=this.getPartials();return E.render(e,this,t)}renderTemplateString(e,t,s){return E.render(e,t,s)}getPartials(){return{}}async getTemplate(){if(this._templateCache&&this.cacheTemplate)return this._templateCache;const e=this.template||this.templateUrl;if(!e)throw new Error("Template not found");let t="";if(typeof e=="string")if(e.includes("<")||e.includes("{"))t=e;else try{let s=e;this.app||(this.app=this.getApp()),this.app&&this.app.basePath&&!s.startsWith("/")&&!s.startsWith("http://")&&!s.startsWith("https://")&&(s=`${this.app.basePath.endsWith("/")?this.app.basePath.slice(0,-1):this.app.basePath}/${s}`);const i=await fetch(s);if(!i.ok)throw new Error(`HTTP ${i.status}: ${i.statusText}`);t=await i.text()}catch(s){v._warn(`Failed to load template from ${e}: ${s}`),this.showError?.(`Failed to load template from ${e}: ${s.message}`)}else typeof e=="function"&&(t=await this.template(this.data,this.state));return this.cacheTemplate&&t&&(this._templateCache=t),t}getContextValue(e){const t=x.getContextData(this,e);return e&&e.startsWith("data.")&&t&&typeof t=="object"?x.wrapData(t,this):e&&e.startsWith("model.")&&e!=="model"&&t&&typeof t=="object"&&typeof t.getContextValue!="function"?x.wrapData(t,null):t}async handlePageNavigation(e){const t=e.getAttribute("data-page"),s=e.getAttribute("data-params");let i={};if(s)try{i=JSON.parse(s)}catch{console.warn("Invalid JSON in data-params:",s)}const a=this.getApp();if(a){a.showPage(t,i);return}const r=this.findRouter();r&&typeof r.navigateToPage=="function"?await r.navigateToPage(t,i):console.error(`No router found for page navigation to '${t}'`)}async handleHrefNavigation(e){const t=e.getAttribute("href");if(this.isExternalLink(t)||e.hasAttribute("data-external"))return;const s=this.findRouter();if(s){if(s.options&&s.options.mode==="param"&&t.startsWith("?")){const a="/"+t;await s.navigate(a);return}if(s.options&&s.options.mode==="hash"&&t.startsWith("#")){await s.navigate(t);return}const i=this.hrefToRoutePath(t);await s.navigate(i)}else console.warn("No router found for navigation, using default behavior"),window.location.href=t}isExternalLink(e){return e?e.startsWith("/")&&this.getApp()?!e.startsWith(this.findRouter().basePath):e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//"):!0}hrefToRoutePath(e){if(e.startsWith("/")){const t=this.findRouter();if(t&&t.options&&t.options.base){const s=t.options.base;if(e.startsWith(s))return e.substring(s.length)||"/"}return e}return e.startsWith("./")?e.substring(2):e}findRouter(){return this.getApp(),this.app?.router||null}getApp(){if(this.app)return this.app;const e=[window.__app__,window.MOJO?.app,window.APP,window.app,window.WebApp,window.matchUUID?window[window.matchUUID]():window[window.matchUUID]];return this.app=e.find(t=>t&&typeof t.showPage=="function")||null,this.app}handleActionError(e,t,s,i){this.showError(`Action '${e}' failed: ${t}`,s,i)}escapeHtml(e){if(typeof e!="string")return e;const t=document.createElement("div");return t.textContent=e,t.innerHTML}contains(e){if(typeof e=="string"){if(!this.element)return!1;e=document.getElementById(e)}return e?this.element.contains(e):!1}initializeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.disposeTooltips(),[...this.element.querySelectorAll('[data-bs-toggle="tooltip"]')].map(t=>{const s=t.getAttribute("data-tooltip-theme"),i=t.getAttribute("data-tooltip-size");let a="";s&&(a+=`tooltip-${s} `),i&&(a+=`tooltip-${i}`);const r={},n=a.trim();return n&&(r.customClass=n),new window.bootstrap.Tooltip(t,r)})}disposeTooltips(){if(!this.element||!window.bootstrap?.Tooltip)return;this.element.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(t=>{const s=window.bootstrap.Tooltip.getInstance(t);s&&s.dispose()})}async showError(e){console.error(`View ${this.id} error:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showError=="function"){await t.showError(e);return}alert(`Error: ${e}`)}async showSuccess(e){this.debug&&console.log(`View ${this.id} success:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showSuccess=="function"){await t.showSuccess(e);return}alert(`Success: ${e}`)}async showInfo(e){console.info(`View ${this.id} info:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showInfo=="function"){await t.showInfo(e);return}alert(`Info: ${e}`)}async showWarning(e){console.warn(`View ${this.id} warning:`,e);const t=this.getApp?this.getApp():this.app||null;if(t&&typeof t.showWarning=="function"){await t.showWarning(e);return}alert(`Warning: ${e}`)}async onActionCopyToClipboard(e,t){try{const i=(t?.closest("[data-clipboard]")||t)?.getAttribute("data-clipboard")||"";if(!i)return!0;if(navigator.clipboard&&window.isSecureContext)await navigator.clipboard.writeText(i);else{const n=document.createElement("textarea");n.value=i,document.body.appendChild(n),n.select(),document.execCommand("copy"),document.body.removeChild(n)}const a=t.querySelector("i"),r=a&&a.className;return a&&(a.className="bi bi-check",setTimeout(()=>{a.className=r},1e3)),!0}catch(s){return console.warn("Copy to clipboard failed:",s),!0}}static _genId(){return`view_${Math.random().toString(36).substr(2,9)}`}static _warn(e,t){try{t?console.warn(`[View] ${e}:`,t):console.warn(`[View] ${e}`)}catch{}}}Object.assign(v.prototype,ne);class W extends v{constructor(e={}){e.tagName=e.tagName||"main",e.className=e.className||"mojo-page";const t=e.pageName||"";t&&!e.id&&(e.id="page_"+t.toLowerCase().replace(/\s+/g,"_")),super(e),this.pageName=e.pageName||this.constructor.pageName||"",this.route=e.route||this.constructor.route||"",this.title=e.title||this.pageName||"",!this.id&&this.constructor.pageName&&!e.pageName&&(this.id="page_"+this.constructor.pageName.toLowerCase().replace(/\s+/g,"_")),this.pageIcon=e.icon||e.pageIcon||this.constructor.pageIcon||"bi bi-file-text",this.displayName=e.displayName||this.constructor.displayName||this.pageName||"",this.pageDescription=e.pageDescription||this.constructor.pageDescription||"",this.params={},this.query={},this.matched=!1,this.isActive=!1,this.pageOptions={title:e.title||this.pageName||"Untitled Page",description:e.description||"",requiresAuth:e.requiresAuth||!1,...e.pageOptions},this.savedState=null,console.log(`Page ${this.pageName} constructed with route: ${this.route}`)}async onParams(e={},t={}){this.params=e,this.query=t}canEnter(){if(this.options.permissions){const e=this.getApp().activeUser;if(!e||!e.hasPermission(this.options.permissions))return!1}return!(this.options.requiresGroup&&!this.getApp().activeGroup)}async onEnter(){this.isActive=!0,await this.onInitView(),this.savedState&&(this.restoreState(this.savedState),this.savedState=null),this.pageOptions&&this.pageOptions.title&&typeof document<"u"&&(document.title=this.pageOptions.title),this.emit("activated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} entered`)}async onExit(){this.savedState=this.captureState(),this.isActive=!1,this.emit("deactivated",{page:this.getMetadata()}),console.log(`Page ${this.pageName} exiting`)}getMetadata(){return{name:this.pageName,displayName:this.displayName||this.pageName,icon:this.pageIcon,description:this.pageDescription,route:this.route,isActive:this.isActive}}async onActionDefault(e){console.log(`Default action '${e}' triggered on page: ${this.pageName}`)}async makeActive(){this.getApp().showPage(this)}async onActionNavigate(e,t){e.preventDefault();const s=t.dataset.page;this.getApp().showPage(s)}captureState(){return this.element?{scrollTop:this.element.scrollTop,formData:this.captureFormData(),custom:this.captureCustomState()}:null}restoreState(e){!e||!this.element||(this.element.scrollTop=e.scrollTop||0,this.restoreFormData(e.formData),e.custom&&this.restoreCustomState(e.custom))}captureFormData(){const e={};return this.element&&this.element.querySelectorAll("input, select, textarea").forEach(t=>{t.name&&(t.type==="checkbox"?e[t.name]=t.checked:t.type==="radio"?t.checked&&(e[t.name]=t.value):e[t.name]=t.value)}),e}restoreFormData(e){!e||!this.element||Object.entries(e).forEach(([t,s])=>{const i=this.element.querySelector(`[name="${t}"]`);if(i)if(i.type==="checkbox")i.checked=s;else if(i.type==="radio"){const a=this.element.querySelector(`[name="${t}"][value="${s}"]`);a&&(a.checked=!0)}else i.value=s})}captureCustomState(){return{}}restoreCustomState(e){}setMeta(e={}){if(!(typeof document>"u")){if(e.title&&(document.title=e.title,this.pageOptions.title=e.title),e.description){let t=document.querySelector('meta[name="description"]');t||(t=document.createElement("meta"),t.name="description",document.head.appendChild(t)),t.content=e.description,this.pageOptions.description=e.description}Object.entries(e).forEach(([t,s])=>{if(t!=="title"&&t!=="description"){let i=document.querySelector(`meta[name="${t}"]`);i||(i=document.createElement("meta"),i.name=t,document.head.appendChild(i)),i.content=s}})}}showError(e){if(super.showError(e),this.element){const t=document.createElement("div");t.className="alert alert-danger alert-dismissible fade show",t.innerHTML=`
|
|
16
16
|
${e}
|
|
17
17
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
18
18
|
`,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},5e3)}}showSuccess(e){if(super.showSuccess(e),this.element){const t=document.createElement("div");t.className="alert alert-success alert-dismissible fade show",t.innerHTML=`
|
|
19
19
|
${e}
|
|
20
20
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
21
|
-
`,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),typeof document<"u"&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),typeof document<"u"&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){if(this.app&&this.app.router)return this.app.router.navigate(e,s);if(typeof window<"u"&&window.MOJO?.router)return window.MOJO.router.navigate(e,s);console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return typeof e=="string"&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class t extends W{constructor(i={}){super({...e,...i})}}return t.template=e.template,t.pageName=e.pageName,t.route=e.route,t}}class X{constructor(e={},t={}){this.endpoint=t.endpoint||this.constructor.endpoint||"",this.id=e.id||null,this.attributes={...e},this._=this.attributes,this.originalAttributes={...e},this.errors={},this.loading=!1,this.rest=z,this.options={idAttribute:"id",timestamps:!0,...t}}getContextValue(e){return this.get(e)}get(e){return!e.includes(".")&&!e.includes("|")&&this[e]!==void 0?typeof this[e]=="function"?this[e]():this[e]:x.getContextData(this.attributes,e)}set(e,t,s={}){const i=JSON.parse(JSON.stringify(this.attributes));let a=!1;if(e!=null){if(typeof e=="object"){for(const[r,n]of Object.entries(e))a=this._setNestedAttribute(r,n)||a;e.id!==void 0&&(this.id=e.id)}else e==="id"?(this.id=t,a=!0):a=this._setNestedAttribute(e,t);if(a&&!s.silent)if(this.emit("change",this),typeof e=="string")this.emit(`change:${e}`,t,this);else for(const[r,n]of Object.entries(e)){const o=this._getNestedValue(r);JSON.stringify(this._getNestedValue(r,i))!==JSON.stringify(o)&&this.emit(`change:${r}`,o,this)}}}_setNestedAttribute(e,t){if(!e.includes(".")){const l=this.attributes[e];return this.attributes[e]=t,this[e]=t,l!==t}const s=e.split("."),i=s[0];(!this.attributes[i]||typeof this.attributes[i]!="object")&&(this.attributes[i]={}),(!this[i]||typeof this[i]!="object")&&(this[i]={});const a=this._getNestedValue(e);let r=this.attributes[i],n=this[i];for(let l=1;l<s.length-1;l++){const c=s[l];(!r[c]||typeof r[c]!="object")&&(r[c]={}),(!n[c]||typeof n[c]!="object")&&(n[c]={}),r=r[c],n=n[c]}const o=s[s.length-1];return r[o]=t,n[o]=t,JSON.stringify(a)!==JSON.stringify(t)}_getNestedValue(e,t=this.attributes){if(!e.includes("."))return t[e];const s=e.split(".");let i=t;for(const a of s){if(i==null||typeof i!="object")return;i=i[a]}return i}getData(){return this.attributes}getId(){return this.id}async fetch(e={}){let t=e.url;if(!t){const r=e.id||this.getId();if(!r&&this.options.requiresId!==!1)throw new Error("Model: ID is required for fetching");t=this.buildUrl(r)}const s=JSON.stringify({url:t,params:e.params});if(e.debounceMs&&e.debounceMs>0)return this._debouncedFetch(s,e);if(this.currentRequest&&this.currentRequestKey!==s&&(console.info("Model: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===s)return console.info("Model: Duplicate request in progress, returning existing promise"),this.currentRequest;const i=Date.now();if(this.lastFetchTime&&i-this.lastFetchTime<100)return console.info("Model: Rate limited, skipping fetch"),this;this.loading=!0,this.errors={},this.lastFetchTime=i,this.currentRequestKey=s,this.abortController=new AbortController,this.currentRequest=this._performFetch(t,e,this.abortController);try{return await this.currentRequest}catch(r){if(r.name==="AbortError")return console.info("Model: Request was cancelled"),this;throw r}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _debouncedFetch(e,t){return this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((s,i)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const a=await this.fetch({...t,debounceMs:0});s(a)}catch(a){i(a)}},t.debounceMs)})}async _performFetch(e,t,s){try{t.graph&&(!t.params||!t.params.graph)&&(t.params||(t.params={}),t.params.graph=t.graph);const i=await this.rest.GET(e,t.params,{signal:s.signal});return i.success?i.data.status?(this.originalAttributes={...this.attributes},i.data.data&&this.set(i.data.data),this.errors={}):this.errors=i.data:this.errors=i.errors||{},i}catch(i){if(i.name==="AbortError")throw console.info("Model: Fetch was cancelled"),i;return this.errors={fetch:i.message},{success:!1,error:i.message,status:i.status||500}}finally{this.loading=!1}}async save(e,t={}){const s=!this.id,i=s?"POST":"PUT",a=s?this.buildUrl():this.buildUrl(this.id);this.loading=!0,this.errors={};try{const r=await this.rest[i](a,e,t.params);return r.success?r.data.status?(this.originalAttributes={...this.attributes},this.set(r.data.data),this.errors={}):this.errors=r.data:this.errors=r.errors||{},r}catch(r){return{success:!1,error:r.message,status:r.status||500}}finally{this.loading=!1}}async destroy(e={}){if(!this.id)return this.errors={destroy:"Cannot destroy model without ID"},{success:!1,error:"Cannot destroy model without ID",status:400};const t=this.buildUrl(this.id);this.loading=!0,this.errors={};try{const s=await this.rest.DELETE(t,e.params);return s.success?(this.attributes={},this.originalAttributes={},this.id=null,this.errors={}):this.errors=s.errors||{},s}catch(s){return this.errors={destroy:s.message},{success:!1,error:s.message,status:s.status||500}}finally{this.loading=!1}}isDirty(){return JSON.stringify(this.attributes)!==JSON.stringify(this.originalAttributes)}getChangedAttributes(){const e={};for(const[t,s]of Object.entries(this.attributes))this.originalAttributes[t]!==s&&(e[t]=s);return e}reset(){this.attributes={...this.originalAttributes},this._=this.attributes,this.errors={}}buildUrl(e=null){let t=this.endpoint;return e&&(t=t.endsWith("/")?`${t}${e}`:`${t}/${e}`),t}toJSON(){return{id:this.id,...this.attributes}}validate(){if(this.errors={},this.constructor.validations)for(const[e,t]of Object.entries(this.constructor.validations))this.validateField(e,t);return Object.keys(this.errors).length===0}validateField(e,t){const s=this.get(e),i=Array.isArray(t)?t:[t];for(const a of i)if(typeof a=="function"){const r=a(s,this);if(r!==!0){this.errors[e]=r||`${e} is invalid`;break}}else if(typeof a=="object"){if(a.required&&(s==null||s==="")){this.errors[e]=a.message||`${e} is required`;break}if(a.minLength&&s&&s.length<a.minLength){this.errors[e]=a.message||`${e} must be at least ${a.minLength} characters`;break}if(a.maxLength&&s&&s.length>a.maxLength){this.errors[e]=a.message||`${e} must be no more than ${a.maxLength} characters`;break}if(a.pattern&&s&&!a.pattern.test(s)){this.errors[e]=a.message||`${e} format is invalid`;break}}}static async find(e,t={}){const s=new this({},t);return await s.fetch({id:e,...t}),s}static create(e={},t={}){return new this(e,t)}cancel(){return this.currentRequest&&this.abortController?(console.info("Model: Manually cancelling active request"),this.abortController.abort(),!0):this.debouncedFetchTimeout?(clearTimeout(this.debouncedFetchTimeout),this.debouncedFetchTimeout=null,!0):!1}isFetching(){return!!this.currentRequest}async showError(e){await Dialog.alert(e,"Error",{size:"md",class:"text-danger"})}}Object.assign(X.prototype,ne);class J{constructor(e={},t=null){if(Array.isArray(e)?(t=e,e=t||{}):t=t||e.data||[],this.ModelClass=e.ModelClass||X,this.models=[],this.loading=!1,this.errors={},this.meta={},this.rest=z,t&&this.add(t),this.params={start:0,size:e.size||10,...e.params},this.endpoint=e.endpoint||this.ModelClass.endpoint||"",!this.endpoint){let s=new this.ModelClass;this.endpoint=s.endpoint}this.restEnabled=!!this.endpoint,e.restEnabled!==void 0&&(this.restEnabled=e.restEnabled),this.options={parse:!0,reset:!0,preloaded:!1,...e}}getModelName(){return this.ModelClass.name}async fetch(e={}){const t=JSON.stringify({...this.params,...e});if(this.currentRequest&&this.currentRequestKey!==t&&(console.info("Collection: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===t)return console.info("Collection: Duplicate request in progress, returning existing promise"),this.currentRequest;const s=Date.now();if(this.options.rateLimiting&&this.lastFetchTime&&s-this.lastFetchTime<100)return console.info("Collection: Rate limited, skipping fetch"),{success:!0,message:"Rate limited, skipping fetch",data:{data:this.toJSON()}};if(!this.restEnabled)return console.info("Collection: REST disabled, skipping fetch"),{success:!0,message:"REST disabled, skipping fetch",data:{data:this.toJSON()}};if(this.options.preloaded&&this.models.length>0)return console.info("Collection: Using preloaded data, skipping fetch"),{success:!0,message:"Using preloaded data, skipping fetch",data:{data:this.toJSON()}};const a=this.buildUrl();this.loading=!0,this.errors={},this.lastFetchTime=s,this.currentRequestKey=t,this.abortController=new AbortController,this.currentRequest=this._performFetch(a,e,this.abortController);try{return await this.currentRequest}catch(r){return r.name==="AbortError"?(console.info("Collection: Request was cancelled"),{success:!1,error:"Request cancelled",status:0}):{success:!1,error:r.message,status:r.status||500}}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _performFetch(e,t,s){const i={...this.params,...t};console.log("Fetching collection data from",e,i);try{this.emit("fetch:start");const a=await this.rest.GET(e,i,{signal:s.signal});if(a.success&&a.data.status){const r=this.options.parse?this.parse(a):a.data;(this.options.reset||t.reset!==!1)&&this.reset(),this.add(r,{silent:t.silent}),this.errors={},this.emit("fetch:success")}else a.data&&a.data.error?(this.errors=a.data,this.emit("fetch:error",{message:a.data.error,error:a.data})):(this.errors=a.errors||{},this.emit("fetch:error",{error:a.errors}));return a}catch(a){return a.name==="AbortError"?(console.info("Collection: Fetch was cancelled"),{success:!1,error:"Request cancelled",status:0}):(this.errors={fetch:a.message},this.emit("fetch:error",{message:a.message,error:a}),{success:!1,error:a.message,status:a.status||500})}finally{this.loading=!1,this.emit("fetch:end")}}async updateParams(e,t=!1,s=0){return await this.setParams({...this.params,...e},t,s)}async setParams(e,t=!1,s=0){return this.params=e,t&&this.restEnabled?s>0?(this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((i,a)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const r=await this.fetch();i(r)}catch(r){a(r)}},s)})):this.fetch():Promise.resolve(this)}async fetchOne(e,t={}){if(!e)return console.warn("Collection: fetchOne requires an ID"),null;if(!this.restEnabled)return console.info("Collection: REST disabled, cannot fetch single item"),null;try{const s=new this.ModelClass({id:e},{endpoint:this.endpoint,collection:this}),i=await s.fetch(t);if(i.success){if(t.addToCollection===!0){const a=this.get(s.id);a?t.merge!==!1&&a.set(s.attributes):this.add(s,{silent:t.silent})}return s}else return console.warn("Collection: fetchOne failed -",i.error||"Unknown error"),null}catch(s){return console.error("Collection: fetchOne error -",s.message),null}}async download(e="json",t={}){if(!this.restEnabled)return console.warn("Collection: REST is not enabled, cannot download from remote."),{success:!1,message:"Remote downloads are not enabled for this collection."};const s=this.buildUrl(),i={...this.params};delete i.start,delete i.size,i.download_format=e;const a=`export-${this.getModelName().toLowerCase()}`,r=this._buildDateRangeSuffix(i),n=`${a}${r}.${e}`,l={json:"application/json",csv:"text/csv"}[e]||"*/*";return i.filename=n,this.rest.download(s,i,{...t,filename:n,headers:{Accept:l}})}_buildDateRangeSuffix(e={}){const t=e.dr_start,s=e.dr_end;if(!t&&!s)return"";const i=n=>n?String(n).replace(/[^\dA-Za-z_-]/g,"-"):"",a=[],r=e.dr_field||"daterange";return a.push(i(r)),t&&a.push(`from-${i(e.dr_start)}`),s&&a.push(`to-${i(e.dr_end)}`),`-${a.filter(Boolean).join("-")}`}parse(e){return e.data&&Array.isArray(e.data.data)?(this.meta={size:e.data.size||10,start:e.data.start||0,count:e.data.count||0,status:e.data.status,graph:e.data.graph,...e.meta},e.data.data):Array.isArray(e.data)?e.data:Array.isArray(e)?e:[e]}add(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r;a instanceof this.ModelClass?r=a:r=new this.ModelClass(a,{endpoint:this.endpoint,collection:this});const n=this.models.findIndex(o=>o.id===r.id);n!==-1?t.merge!==!1&&this.models[n].set(r.attributes):(this.models.push(r),i.push(r))}return!t.silent&&i.length>0&&(this.emit("add",{models:i,collection:this}),this.emit("update",{collection:this})),i}remove(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r=-1;if(typeof a=="string"||typeof a=="number"?r=this.models.findIndex(n=>n.id==a):r=this.models.indexOf(a),r!==-1){const n=this.models.splice(r,1)[0];i.push(n)}}return!t.silent&&i.length>0&&(this.emit("remove",{models:i,collection:this}),this.emit("update",{collection:this})),i}reset(e=null,t={}){const s=[...this.models];return this.models=[],e&&this.add(e,{silent:!0,...t}),t.silent||this.emit("reset",{collection:this,previousModels:s}),this}get(e){return this.models.find(t=>t.id==e)}at(e){return this.models[e]}length(){return this.models.length}isEmpty(){return this.models.length===0}where(e){return typeof e=="function"?this.models.filter(e):typeof e=="object"?this.models.filter(t=>Object.entries(e).every(([s,i])=>t.get(s)===i)):[]}findWhere(e){const t=this.where(e);return t.length>0?t[0]:void 0}forEach(e,t){if(typeof e!="function")throw new TypeError("Callback must be a function");return this.models.forEach((s,i)=>{e.call(t,s,i,this)}),this}sort(e,t={}){if(typeof e=="string"){const s=e;e=(i,a)=>{const r=i.get(s),n=a.get(s);return r<n?-1:r>n?1:0}}return this.models.sort(e),t.silent||this.trigger("sort",{collection:this}),this}toJSON(){return this.models.map(e=>e.toJSON())}cancel(){return this.currentRequest&&this.abortController?(console.info("Collection: Manually cancelling active request"),this.abortController.abort(),!0):!1}isFetching(){return!!this.currentRequest}buildUrl(){return this.endpoint}*[Symbol.iterator](){for(const e of this.models)yield e}static fromArray(e,t=[],s={}){const i=new this(e,s);return i.add(t,{silent:!0}),i}}Object.assign(J.prototype,ne);class q{static _plugins=[];static _renderers=new Map;static register(e){return!e||typeof e!="object"?(console.warn("[FormPlugins] register called with invalid plugin:",e),()=>{}):!e.id||typeof e.id!="string"?(console.warn('[FormPlugins] plugin must have a unique string "id"',e),()=>{}):(this.unregister(e.id),e.fieldTypes&&typeof e.fieldTypes=="object"&&Object.entries(e.fieldTypes).forEach(([t,s])=>{typeof s=="function"?this._renderers.set(t,{renderer:s,pluginId:e.id}):console.warn(`[FormPlugins] renderer for type "${t}" is not a function`)}),this._plugins.push(e),()=>this.unregister(e.id))}static unregister(e){if(e){this._plugins=this._plugins.filter(t=>t.id!==e);for(const[t,s]of this._renderers.entries())s?.pluginId===e&&this._renderers.delete(t)}}static getRenderer(e){return this._renderers.get(e)?.renderer||null}static hasRenderer(e){return this._renderers.has(e)}static getPlugins(){return[...this._plugins]}static _invoke(e,t,...s){const i=e?.[t];if(typeof i=="function")try{return i.apply(e,s)}catch(a){console.error(`[FormPlugins] ${t} error from plugin "${e.id}":`,a)}}static onFormBuilderInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormBuilderInit",e))}static onFormViewInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormViewInit",e))}static onFormViewAfterRender(e){this._plugins.forEach(t=>this._invoke(t,"onAfterRender",e))}static onFieldInit(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldInit",e,t,s))}static onFieldChange(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldChange",e,t,s))}}class Z{constructor(e={}){this.fields=e.fields||[],this.structureOnly=e.structureOnly||!1,this.fields.forEach(t=>{t.cols&&!t.columns?(t.columns=t.cols,delete t.cols):t.columns||(t.columns=12),t.type==="group"&&t.fields&&t.fields.forEach(s=>{s.cols&&!s.columns?(s.columns=s.cols,delete s.cols):s.columns||(s.columns=12)})}),this.options={formClass:"needs-validation",formMethod:"POST",formAction:"",groupClass:"row mb-3",fieldWrapper:"",labelClass:"form-label",inputClass:"form-control",errorClass:"invalid-feedback",helpClass:"form-text",submitButton:!1,resetButton:!1,...e.options},this.buttons=e.buttons||[],this.data=e.data||{},this.errors=e.errors||{},this.initializeTemplates()}initializeTemplates(){q.onFormBuilderInit?.(this),this.templates={input:`
|
|
21
|
+
`,this.element.insertBefore(t,this.element.firstChild),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t)},3e3)}}async onBeforeRender(){await super.onBeforeRender(),this.setMeta({title:this.pageOptions.title,description:this.pageOptions.description})}async onAfterMount(){await super.onAfterMount(),typeof document<"u"&&this.pageName&&document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}async onBeforeDestroy(){await super.onBeforeDestroy(),typeof document<"u"&&this.pageName&&document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g,"-")}`)}navigate(e,t={},s={}){if(this.app&&this.app.router)return this.app.router.navigate(e,s);if(typeof window<"u"&&window.MOJO?.router)return window.MOJO.router.navigate(e,s);console.error("No router available for navigation")}getRoute(){if(this.route){let e=this.route;return typeof e=="string"&&e.startsWith("/")&&(e=e.substring(1)),e}return this.pageName}syncUrl(e=!0){this.updateBrowserUrl(this.query,!1,!1)}updateBrowserUrl(e=null,t=!1,s=!1){this.getApp(),this.app.router.updateBrowserUrl(this.getRoute(),e,t,s)}static define(e){class t extends W{constructor(i={}){super({...e,...i})}}return t.template=e.template,t.pageName=e.pageName,t.route=e.route,t}}class X{constructor(e={},t={}){this.endpoint=t.endpoint||this.constructor.endpoint||"",this.id=e.id||null,this.attributes={...e},this._=this.attributes,this.originalAttributes={...e},this.errors={},this.loading=!1,this.rest=z,this.options={idAttribute:"id",timestamps:!0,...t}}getContextValue(e){return this.get(e)}get(e){return!e.includes(".")&&!e.includes("|")&&this[e]!==void 0?typeof this[e]=="function"?this[e]():this[e]:x.getContextData(this.attributes,e)}set(e,t,s={}){const i=JSON.parse(JSON.stringify(this.attributes));let a=!1;if(e!=null){if(typeof e=="object"){for(const[r,n]of Object.entries(e))a=this._setNestedAttribute(r,n)||a;e.id!==void 0&&(this.id=e.id)}else e==="id"?(this.id=t,a=!0):a=this._setNestedAttribute(e,t);if(a&&!s.silent)if(this.emit("change",this),typeof e=="string")this.emit(`change:${e}`,t,this);else for(const[r,n]of Object.entries(e)){const l=this._getNestedValue(r);JSON.stringify(this._getNestedValue(r,i))!==JSON.stringify(l)&&this.emit(`change:${r}`,l,this)}}}_setNestedAttribute(e,t){if(!e.includes(".")){const o=this.attributes[e];return this.attributes[e]=t,this[e]=t,o!==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 o=1;o<s.length-1;o++){const c=s[o];(!r[c]||typeof r[c]!="object")&&(r[c]={}),(!n[c]||typeof n[c]!="object")&&(n[c]={}),r=r[c],n=n[c]}const l=s[s.length-1];return r[l]=t,n[l]=t,JSON.stringify(a)!==JSON.stringify(t)}_getNestedValue(e,t=this.attributes){if(!e.includes("."))return t[e];const s=e.split(".");let i=t;for(const a of s){if(i==null||typeof i!="object")return;i=i[a]}return i}getData(){return this.attributes}getId(){return this.id}async fetch(e={}){let t=e.url;if(!t){const r=e.id||this.getId();if(!r&&this.options.requiresId!==!1)throw new Error("Model: ID is required for fetching");t=this.buildUrl(r)}const s=JSON.stringify({url:t,params:e.params});if(e.debounceMs&&e.debounceMs>0)return this._debouncedFetch(s,e);if(this.currentRequest&&this.currentRequestKey!==s&&(console.info("Model: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===s)return console.info("Model: Duplicate request in progress, returning existing promise"),this.currentRequest;const i=Date.now();if(this.lastFetchTime&&i-this.lastFetchTime<100)return console.info("Model: Rate limited, skipping fetch"),this;this.loading=!0,this.errors={},this.lastFetchTime=i,this.currentRequestKey=s,this.abortController=new AbortController,this.currentRequest=this._performFetch(t,e,this.abortController);try{return await this.currentRequest}catch(r){if(r.name==="AbortError")return console.info("Model: Request was cancelled"),this;throw r}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _debouncedFetch(e,t){return this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((s,i)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const a=await this.fetch({...t,debounceMs:0});s(a)}catch(a){i(a)}},t.debounceMs)})}async _performFetch(e,t,s){try{t.graph&&(!t.params||!t.params.graph)&&(t.params||(t.params={}),t.params.graph=t.graph);const i=await this.rest.GET(e,t.params,{signal:s.signal});return i.success?i.data.status?(this.originalAttributes={...this.attributes},i.data.data&&this.set(i.data.data),this.errors={}):this.errors=i.data:this.errors=i.errors||{},i}catch(i){if(i.name==="AbortError")throw console.info("Model: Fetch was cancelled"),i;return this.errors={fetch:i.message},{success:!1,error:i.message,status:i.status||500}}finally{this.loading=!1}}async save(e,t={}){const s=!this.id,i=s?"POST":"PUT",a=s?this.buildUrl():this.buildUrl(this.id);this.loading=!0,this.errors={};try{const r=await this.rest[i](a,e,t.params);return r.success?r.data.status?(this.originalAttributes={...this.attributes},this.set(r.data.data),this.errors={}):this.errors=r.data:this.errors=r.errors||{},r}catch(r){return{success:!1,error:r.message,status:r.status||500}}finally{this.loading=!1}}async destroy(e={}){if(!this.id)return this.errors={destroy:"Cannot destroy model without ID"},{success:!1,error:"Cannot destroy model without ID",status:400};const t=this.buildUrl(this.id);this.loading=!0,this.errors={};try{const s=await this.rest.DELETE(t,e.params);return s.success?(this.attributes={},this.originalAttributes={},this.id=null,this.errors={}):this.errors=s.errors||{},s}catch(s){return this.errors={destroy:s.message},{success:!1,error:s.message,status:s.status||500}}finally{this.loading=!1}}isDirty(){return JSON.stringify(this.attributes)!==JSON.stringify(this.originalAttributes)}getChangedAttributes(){const e={};for(const[t,s]of Object.entries(this.attributes))this.originalAttributes[t]!==s&&(e[t]=s);return e}reset(){this.attributes={...this.originalAttributes},this._=this.attributes,this.errors={}}buildUrl(e=null){let t=this.endpoint;return e&&(t=t.endsWith("/")?`${t}${e}`:`${t}/${e}`),t}toJSON(){return{id:this.id,...this.attributes}}validate(){if(this.errors={},this.constructor.validations)for(const[e,t]of Object.entries(this.constructor.validations))this.validateField(e,t);return Object.keys(this.errors).length===0}validateField(e,t){const s=this.get(e),i=Array.isArray(t)?t:[t];for(const a of i)if(typeof a=="function"){const r=a(s,this);if(r!==!0){this.errors[e]=r||`${e} is invalid`;break}}else if(typeof a=="object"){if(a.required&&(s==null||s==="")){this.errors[e]=a.message||`${e} is required`;break}if(a.minLength&&s&&s.length<a.minLength){this.errors[e]=a.message||`${e} must be at least ${a.minLength} characters`;break}if(a.maxLength&&s&&s.length>a.maxLength){this.errors[e]=a.message||`${e} must be no more than ${a.maxLength} characters`;break}if(a.pattern&&s&&!a.pattern.test(s)){this.errors[e]=a.message||`${e} format is invalid`;break}}}static async find(e,t={}){const s=new this({},t);return await s.fetch({id:e,...t}),s}static create(e={},t={}){return new this(e,t)}cancel(){return this.currentRequest&&this.abortController?(console.info("Model: Manually cancelling active request"),this.abortController.abort(),!0):this.debouncedFetchTimeout?(clearTimeout(this.debouncedFetchTimeout),this.debouncedFetchTimeout=null,!0):!1}isFetching(){return!!this.currentRequest}async showError(e){await Dialog.alert(e,"Error",{size:"md",class:"text-danger"})}}Object.assign(X.prototype,ne);class J{constructor(e={},t=null){if(Array.isArray(e)?(t=e,e=t||{}):t=t||e.data||[],this.ModelClass=e.ModelClass||X,this.models=[],this.loading=!1,this.errors={},this.meta={},this.rest=z,t&&this.add(t),this.params={start:0,size:e.size||10,...e.params},this.endpoint=e.endpoint||this.ModelClass.endpoint||"",!this.endpoint){let s=new this.ModelClass;this.endpoint=s.endpoint}this.restEnabled=!!this.endpoint,e.restEnabled!==void 0&&(this.restEnabled=e.restEnabled),this.options={parse:!0,reset:!0,preloaded:!1,...e}}getModelName(){return this.ModelClass.name}async fetch(e={}){const t=JSON.stringify({...this.params,...e});if(this.currentRequest&&this.currentRequestKey!==t&&(console.info("Collection: Cancelling previous request for new parameters"),this.abortController?.abort(),this.currentRequest=null),this.currentRequest&&this.currentRequestKey===t)return console.info("Collection: Duplicate request in progress, returning existing promise"),this.currentRequest;const s=Date.now();if(this.options.rateLimiting&&this.lastFetchTime&&s-this.lastFetchTime<100)return console.info("Collection: Rate limited, skipping fetch"),{success:!0,message:"Rate limited, skipping fetch",data:{data:this.toJSON()}};if(!this.restEnabled)return console.info("Collection: REST disabled, skipping fetch"),{success:!0,message:"REST disabled, skipping fetch",data:{data:this.toJSON()}};if(this.options.preloaded&&this.models.length>0)return console.info("Collection: Using preloaded data, skipping fetch"),{success:!0,message:"Using preloaded data, skipping fetch",data:{data:this.toJSON()}};const a=this.buildUrl();this.loading=!0,this.errors={},this.lastFetchTime=s,this.currentRequestKey=t,this.abortController=new AbortController,this.currentRequest=this._performFetch(a,e,this.abortController);try{return await this.currentRequest}catch(r){return r.name==="AbortError"?(console.info("Collection: Request was cancelled"),{success:!1,error:"Request cancelled",status:0}):{success:!1,error:r.message,status:r.status||500}}finally{this.currentRequest=null,this.currentRequestKey=null,this.abortController=null}}async _performFetch(e,t,s){const i={...this.params,...t};console.log("Fetching collection data from",e,i);try{this.emit("fetch:start");const a=await this.rest.GET(e,i,{signal:s.signal});if(a.success&&a.data.status){const r=this.options.parse?this.parse(a):a.data;(this.options.reset||t.reset!==!1)&&this.reset(),this.add(r,{silent:t.silent}),this.errors={},this.emit("fetch:success")}else a.data&&a.data.error?(this.errors=a.data,this.emit("fetch:error",{message:a.data.error,error:a.data})):(this.errors=a.errors||{},this.emit("fetch:error",{error:a.errors}));return a}catch(a){return a.name==="AbortError"?(console.info("Collection: Fetch was cancelled"),{success:!1,error:"Request cancelled",status:0}):(this.errors={fetch:a.message},this.emit("fetch:error",{message:a.message,error:a}),{success:!1,error:a.message,status:a.status||500})}finally{this.loading=!1,this.emit("fetch:end")}}async updateParams(e,t=!1,s=0){return await this.setParams({...this.params,...e},t,s)}async setParams(e,t=!1,s=0){return this.params=e,t&&this.restEnabled?s>0?(this.debouncedFetchTimeout&&clearTimeout(this.debouncedFetchTimeout),this.cancel(),new Promise((i,a)=>{this.debouncedFetchTimeout=setTimeout(async()=>{try{const r=await this.fetch();i(r)}catch(r){a(r)}},s)})):this.fetch():Promise.resolve(this)}async fetchOne(e,t={}){if(!e)return console.warn("Collection: fetchOne requires an ID"),null;if(!this.restEnabled)return console.info("Collection: REST disabled, cannot fetch single item"),null;try{const s=new this.ModelClass({id:e},{endpoint:this.endpoint,collection:this}),i=await s.fetch(t);if(i.success){if(t.addToCollection===!0){const a=this.get(s.id);a?t.merge!==!1&&a.set(s.attributes):this.add(s,{silent:t.silent})}return s}else return console.warn("Collection: fetchOne failed -",i.error||"Unknown error"),null}catch(s){return console.error("Collection: fetchOne error -",s.message),null}}async download(e="json",t={}){if(!this.restEnabled)return console.warn("Collection: REST is not enabled, cannot download from remote."),{success:!1,message:"Remote downloads are not enabled for this collection."};const s=this.buildUrl(),i={...this.params};delete i.start,delete i.size,i.download_format=e;const a=`export-${this.getModelName().toLowerCase()}`,r=this._buildDateRangeSuffix(i),n=`${a}${r}.${e}`,o={json:"application/json",csv:"text/csv"}[e]||"*/*";return i.filename=n,this.rest.download(s,i,{...t,filename:n,headers:{Accept:o}})}_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(l=>l.id===r.id);n!==-1?t.merge!==!1&&this.models[n].set(r.attributes):(this.models.push(r),i.push(r))}return!t.silent&&i.length>0&&(this.emit("add",{models:i,collection:this}),this.emit("update",{collection:this})),i}remove(e,t={}){const s=Array.isArray(e)?e:[e],i=[];for(const a of s){let r=-1;if(typeof a=="string"||typeof a=="number"?r=this.models.findIndex(n=>n.id==a):r=this.models.indexOf(a),r!==-1){const n=this.models.splice(r,1)[0];i.push(n)}}return!t.silent&&i.length>0&&(this.emit("remove",{models:i,collection:this}),this.emit("update",{collection:this})),i}reset(e=null,t={}){const s=[...this.models];return this.models=[],e&&this.add(e,{silent:!0,...t}),t.silent||this.emit("reset",{collection:this,previousModels:s}),this}get(e){return this.models.find(t=>t.id==e)}at(e){return this.models[e]}length(){return this.models.length}isEmpty(){return this.models.length===0}where(e){return typeof e=="function"?this.models.filter(e):typeof e=="object"?this.models.filter(t=>Object.entries(e).every(([s,i])=>t.get(s)===i)):[]}findWhere(e){const t=this.where(e);return t.length>0?t[0]:void 0}forEach(e,t){if(typeof e!="function")throw new TypeError("Callback must be a function");return this.models.forEach((s,i)=>{e.call(t,s,i,this)}),this}sort(e,t={}){if(typeof e=="string"){const s=e;e=(i,a)=>{const r=i.get(s),n=a.get(s);return r<n?-1:r>n?1:0}}return this.models.sort(e),t.silent||this.trigger("sort",{collection:this}),this}toJSON(){return this.models.map(e=>e.toJSON())}cancel(){return this.currentRequest&&this.abortController?(console.info("Collection: Manually cancelling active request"),this.abortController.abort(),!0):!1}isFetching(){return!!this.currentRequest}buildUrl(){return this.endpoint}*[Symbol.iterator](){for(const e of this.models)yield e}static fromArray(e,t=[],s={}){const i=new this(e,s);return i.add(t,{silent:!0}),i}}Object.assign(J.prototype,ne);class q{static _plugins=[];static _renderers=new Map;static register(e){return!e||typeof e!="object"?(console.warn("[FormPlugins] register called with invalid plugin:",e),()=>{}):!e.id||typeof e.id!="string"?(console.warn('[FormPlugins] plugin must have a unique string "id"',e),()=>{}):(this.unregister(e.id),e.fieldTypes&&typeof e.fieldTypes=="object"&&Object.entries(e.fieldTypes).forEach(([t,s])=>{typeof s=="function"?this._renderers.set(t,{renderer:s,pluginId:e.id}):console.warn(`[FormPlugins] renderer for type "${t}" is not a function`)}),this._plugins.push(e),()=>this.unregister(e.id))}static unregister(e){if(e){this._plugins=this._plugins.filter(t=>t.id!==e);for(const[t,s]of this._renderers.entries())s?.pluginId===e&&this._renderers.delete(t)}}static getRenderer(e){return this._renderers.get(e)?.renderer||null}static hasRenderer(e){return this._renderers.has(e)}static getPlugins(){return[...this._plugins]}static _invoke(e,t,...s){const i=e?.[t];if(typeof i=="function")try{return i.apply(e,s)}catch(a){console.error(`[FormPlugins] ${t} error from plugin "${e.id}":`,a)}}static onFormBuilderInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormBuilderInit",e))}static onFormViewInit(e){this._plugins.forEach(t=>this._invoke(t,"onFormViewInit",e))}static onFormViewAfterRender(e){this._plugins.forEach(t=>this._invoke(t,"onAfterRender",e))}static onFieldInit(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldInit",e,t,s))}static onFieldChange(e,t,s){this._plugins.forEach(i=>this._invoke(i,"onFieldChange",e,t,s))}}class Z{constructor(e={}){this.fields=e.fields||[],this.structureOnly=e.structureOnly||!1,this.fields.forEach(t=>{t.cols&&!t.columns?(t.columns=t.cols,delete t.cols):t.columns||(t.columns=12),t.type==="group"&&t.fields&&t.fields.forEach(s=>{s.cols&&!s.columns?(s.columns=s.cols,delete s.cols):s.columns||(s.columns=12)})}),this.options={formClass:"needs-validation",formMethod:"POST",formAction:"",groupClass:"row mb-3",fieldWrapper:"",labelClass:"form-label",inputClass:"form-control",errorClass:"invalid-feedback",helpClass:"form-text",submitButton:!1,resetButton:!1,...e.options},this.buttons=e.buttons||[],this.data=e.data||{},this.errors=e.errors||{},this.initializeTemplates()}initializeTemplates(){q.onFormBuilderInit?.(this),this.templates={input:`
|
|
22
22
|
<div class="mojo-form-control">
|
|
23
23
|
{{#label}}
|
|
24
24
|
<label for="{{fieldId}}" class="{{labelClass}}">
|
|
@@ -358,8 +358,8 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
358
358
|
${e}
|
|
359
359
|
${t}
|
|
360
360
|
</form>
|
|
361
|
-
`}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
|
|
362
|
-
<div class="${
|
|
361
|
+
`}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 l=this.fields[r];let o=l.columns||12;if(typeof o=="object"&&o!==null&&(o=o.md||o.sm||o.xs||12),a+o<=12)i.push(l),a+=o,r++;else break}let n=i.length>1;if(i.length===1&&s.columns){let l=s.columns;typeof l=="object"&&l!==null&&(l=l.md||l.sm||l.xs||12),n=n||l<12}if(n){const l=i.map(o=>this.buildGroupHTML(o)).join("");e.push(`<div class="row">${l}</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 l=this.fields[r];let o=l.columns||12;if(typeof o=="object"&&o!==null&&(o=o.md||o.sm||o.xs||12),a+o<=12)i.push(l),a+=o,r++;else break}const n=i.map(l=>this.buildFieldHTML(l)).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 l=[];if(typeof t=="object"&&t!==null){if(t.xs&&l.push(`col-${t.xs}`),t.sm&&l.push(`col-sm-${t.sm}`),t.md&&l.push(`col-md-${t.md}`),t.lg&&l.push(`col-lg-${t.lg}`),t.xl&&l.push(`col-xl-${t.xl}`),t.xxl&&l.push(`col-xxl-${t.xxl}`),!t.md&&(t.xs||t.sm)){const d=t.sm||t.xs;l.push(`col-md-${d}`)}l.length===0&&l.push("col-md-12")}else l.push(`col-md-${t}`);n.xs&&l.push(`col-${n.xs}`),n.sm&&l.push(`col-sm-${n.sm}`),n.lg&&l.push(`col-lg-${n.lg}`),n.xl&&l.push(`col-xl-${n.xl}`);const o=l.join(" "),c=i.map(d=>d.type==="group"?this.buildGroupHTML(d):this.buildFieldHTML(d)).join("");return`
|
|
362
|
+
<div class="${o}">
|
|
363
363
|
<div class="mojo-form-group ${a}">
|
|
364
364
|
${s?`<div class="${r}">${this.escapeHtml(s)}</div>`:""}
|
|
365
365
|
<div class="row">
|
|
@@ -367,18 +367,18 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
367
367
|
</div>
|
|
368
368
|
</div>
|
|
369
369
|
</div>
|
|
370
|
-
`}buildFieldHTML(e){const{type:t,columns:s,class:i=""}=e;let a="";const r=q&&typeof q.getRenderer=="function"?q.getRenderer(t):null;if(typeof r=="function")try{const
|
|
370
|
+
`}buildFieldHTML(e){const{type:t,columns:s,class:i=""}=e;let a="";const r=q&&typeof q.getRenderer=="function"?q.getRenderer(t):null;if(typeof r=="function")try{const l=r(this,e);l!=null&&(a=String(l))}catch(l){console.error("FormPlugins custom renderer error:",l)}if(!a)switch(t){case"text":a=this.renderTextField(e);break;case"email":a=this.renderEmailField(e);break;case"password":a=this.renderPasswordField(e);break;case"number":a=this.renderNumberField(e);break;case"tel":a=this.renderTelField(e);break;case"url":a=this.renderUrlField(e);break;case"search":a=this.renderSearchField(e);break;case"hex":a=this.renderHexField(e);break;case"textarea":a=this.renderTextareaField(e);break;case"htmlpreview":a=this.renderHtmlPreviewField(e);break;case"json":a=this.renderJsonField(e);break;case"select":a=this.renderSelectField(e);break;case"multiselect":a=this.renderMultiSelectField(e);break;case"checkbox":a=this.renderCheckboxField(e);break;case"toggle":case"switch":a=this.renderSwitchField(e);break;case"radio":a=this.renderRadioField(e);break;case"date":a=this.renderDateField(e);break;case"datetime":a=this.renderDateTimeField(e);break;case"time":a=this.renderTimeField(e);break;case"file":a=this.renderFileField(e);break;case"image":a=this.renderImageField(e);break;case"color":a=this.renderColorField(e);break;case"range":a=this.renderRangeField(e);break;case"hidden":a=this.renderHiddenField(e);break;case"button":a=this.renderButton(e);break;case"divider":a=this.renderDivider(e);break;case"html":a=this.renderHtmlField(e);break;case"heading":case"header":a=this.renderHeaderField(e);break;case"tag":case"tags":a=this.renderTagField(e);break;case"collection":a=this.renderCollectionField(e);break;case"collectionmultiselect":case"collection-multiselect":a=this.renderCollectionMultiSelectField(e);break;case"datepicker":a=this.renderDatePickerField(e);break;case"daterange":a=this.renderDateRangeField(e);break;case"checklistdropdown":a=this.renderChecklistDropdownField(e);break;case"buttongroup":a=this.renderButtonGroupField(e);break;case"combo":case"combobox":case"autocomplete":a=this.renderComboField(e);break;case"tabset":a=this.renderTabsetField(e);break;default:console.warn(`Unknown field type: ${t}`),a=this.renderTextField(e)}let n;return this.isAutoSizingField(e)?n=`col ${i}`.trim():n=`col-${s} ${i}`.trim(),`<div class="${n}">${a}</div>`}getFieldId(e){return e?`field_${e.replace(/[.\s\[\]]/g,"_")}`:`field_${Math.random().toString(36).substr(2,9)}`}renderTextField(e){return this.renderInputField(e,"text")}renderEmailField(e){return this.renderInputField(e,"email")}renderPasswordField(e){const t=e.passwordUsage||"current",s=t==="new"||t==="new-password"?"new-password":"current-password",i={...e.attributes||{},autocomplete:e.attributes&&e.attributes.autocomplete||s};return this.renderInputField({...e,showToggle:e.showToggle!==!1,attributes:i},"password")}renderNumberField(e){const{min:t,max:s,step:i=1,...a}=e,r=[];return t!==void 0&&r.push(`min="${t}"`),s!==void 0&&r.push(`max="${s}"`),i!==void 0&&r.push(`step="${i}"`),this.renderInputField({...a,attributes:{...a.attributes,...r.reduce((n,l)=>{const[o,c]=l.split("=");return n[o]=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,l,o,c,d;switch(t){case"color":n=s?"^#?[0-9A-Fa-f]{6}$":"^[0-9A-Fa-f]{6}$",l=6,o=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})?$",l=s?4:3,o=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]+$",l=i||1,o=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]+$",l=i||1,o=a||64,c=s?"#ABCDEF or ABCDEF":"ABCDEF",d=d||"Enter hexadecimal characters only"}const h={...r,pattern:n,minLength:l,maxLength:o,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:l=!1,readonly:o=!1,class:c="",attributes:d={},help:h=e.helpText||e.help||""}=e,f=`${this.options.inputClass} ${c}`.trim(),m=this.errors[s],p=this.getFieldValue(s)??a,g=Object.entries(d).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),y=this.getFieldId(s),b={labelClass:this.options.labelClass,inputClass:f,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:y,name:s,type:t,fieldValue:this.escapeHtml(p),label:i?this.escapeHtml(i):null,placeholder:r?this.escapeHtml(r):null,help:h?this.escapeHtml(h):null,error:m?this.escapeHtml(m):null,required:n,disabled:l,readonly:o,attrs:g,showCopy:!!e.showCopy};if(t==="password"&&(e.showToggle||e.strengthMeter||e.capsLockWarning)){const w={...b,showToggle:!!e.showToggle,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return E.render(this.templates.password,w)}if(t==="password"){const w={...b,showToggle:e.showToggle!==!1,strengthMeter:!!e.strengthMeter,capsLockWarning:!!e.capsLockWarning};return E.render(this.templates.password,w)}return E.render(this.templates.input,b)}renderTextareaField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:l=!1,rows:o=3,cols:c,class:d="",attributes:h={},help:f=e.helpText||e.help||""}=e,m=`${this.options.inputClass} ${d}`.trim(),p=this.errors[t],g=this.getFieldValue(t)??i,y=Object.entries(h).map(([C,$])=>`${C}="${this.escapeHtml($)}"`).join(" "),b=this.getFieldId(t),w={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:b,name:t,fieldValue:g,label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:f?this.escapeHtml(f):null,error:p?this.escapeHtml(p):null,rows:o||3,required:r,disabled:n,readonly:l,showCopy:!!e.showCopy,attrs:y};return E.render(this.templates.textarea,w)}renderHtmlPreviewField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:l=!1,rows:o=5,class:c="",attributes:d={},help:h=e.helpText||e.help||""}=e,f=`${this.options.inputClass} ${c}`.trim(),m=this.errors[t],p=this.getFieldValue(t)??i,g=Object.entries(d).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),y=this.getFieldId(t),b={labelClass:this.options.labelClass,inputClass:f,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:y,name:t,fieldValue:p,label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:h?this.escapeHtml(h):null,error:m?this.escapeHtml(m):null,rows:o||5,required:r,disabled:n,readonly:l,attrs:g,showCopy:!!e.showCopy};return E.render(this.templates.htmlpreview,b)}renderJsonField(e){const{name:t,label:s,placeholder:i="",required:a=!1,disabled:r=!1,readonly:n=!1,rows:l=3,class:o="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=this.getFieldValue(e.name)??e.value??{};let f=h;if(typeof h=="object"&&h!==null)try{f=JSON.stringify(h,null,2)}catch{f="{}"}else typeof h!="string"&&(f=String(h));const m=`${this.options.inputClass} ${o}`.trim(),p=this.errors[t],g=this.getFieldId(t),y=Object.entries({...c,"data-field-type":"json"}).map(([w,C])=>`${w}="${this.escapeHtml(C)}"`).join(" "),b={labelClass:this.options.labelClass,inputClass:m,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,fieldValue:f,label:s?this.escapeHtml(s):null,placeholder:i?this.escapeHtml(i):null,help:d?this.escapeHtml(d):null,error:p?this.escapeHtml(p):null,rows:l||3,required:a,disabled:r,readonly:n,attrs:y};return E.render(this.templates.textarea,b)}renderSelectField(e){const{name:t,label:s,options:i=[],value:a="",required:r=!1,disabled:n=!1,multiple:l=!1,searchable:o=!1,class:c="",attributes:d={},help:h=e.helpText||e.help||"",start:f=e.start,end:m=e.end,step:p=e.step,format:g=e.format,prefix:y=e.prefix,suffix:b=e.suffix}=e,w=`form-select ${c}`.trim(),C=this.errors[t],S=this.getFieldValue(t)??a,M=Object.entries(d).map(([I,N])=>`${I}="${this.escapeHtml(N)}"`).join(" "),L=this.getFieldId(t);let A=[...i];if(f!==void 0&&m!==void 0){const I=p!==void 0?p:1,N=this.generateSelectOptions(f,m,I,{format:g,prefix:y,suffix:b});A=[...A,...N]}let U="";Array.isArray(A)&&(U=A.map(I=>{if(typeof I=="string"){const N=I===S?"selected":"";return`<option value="${this.escapeHtml(I)}" ${N}>${this.escapeHtml(I)}</option>`}else if(I&&typeof I=="object"){const N=I.value==S?"selected":"";return`<option value="${this.escapeHtml(I.value)}" ${N}>${this.escapeHtml(I.label||I.text||I.value)}</option>`}return""}).join(""));const pe=o?`
|
|
371
371
|
<input type="text"
|
|
372
372
|
class="form-control form-control-sm mb-2"
|
|
373
373
|
placeholder="Search options..."
|
|
374
374
|
data-filter="live-search"
|
|
375
375
|
data-change-action="filter-select-options"
|
|
376
376
|
data-target="${L}">
|
|
377
|
-
`:"",ge={labelClass:this.options.labelClass,inputClass:w,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:L,name:t,label:s?this.escapeHtml(s):null,help:h?this.escapeHtml(h):null,error:C?this.escapeHtml(C):null,searchInput:
|
|
377
|
+
`:"",ge={labelClass:this.options.labelClass,inputClass:w,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:L,name:t,label:s?this.escapeHtml(s):null,help:h?this.escapeHtml(h):null,error:C?this.escapeHtml(C):null,searchInput:o?pe:null,optionsHTML:U,required:r,disabled:n,multiple:l,attrs:M};return E.render(this.templates.select,ge)}renderMultiSelectField(e){const{name:t,label:s,options:i=[],value:a=[],required:r=!1,disabled:n=!1,maxHeight:l=300,help:o=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`
|
|
378
378
|
<div class="multiselect-placeholder"
|
|
379
379
|
data-field-name="${t}"
|
|
380
380
|
data-field-type="multiselect"
|
|
381
|
-
data-field-config='${JSON.stringify({name:t,value:h,placeholder:c,maxHeight:
|
|
381
|
+
data-field-config='${JSON.stringify({name:t,value:h,placeholder:c,maxHeight:l,disabled:n,required:r})}'>
|
|
382
382
|
<input type="hidden" name="${t}" value="${this.escapeHtml(JSON.stringify(h))}">
|
|
383
383
|
<select class="form-select${d?" is-invalid":""}"
|
|
384
384
|
multiple
|
|
@@ -388,7 +388,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
388
388
|
</select>
|
|
389
389
|
<small class="form-text text-muted">This will be enhanced with MultiSelectDropdown component</small>
|
|
390
390
|
</div>
|
|
391
|
-
`}renderCheckboxField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,class:n="",attributes:
|
|
391
|
+
`}renderCheckboxField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,class:n="",attributes:l={},help:o=e.helpText||e.help||""}=e,c=this.errors[t],d=this.getFieldValue(t)??i,h=d===!0||d==="true"||d==="1",f=Object.entries(l).map(([g,y])=>`${g}="${this.escapeHtml(y)}"`).join(" "),m=this.getFieldId(t),p={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:m,name:t,label:this.escapeHtml(s),help:o?this.escapeHtml(o):null,error:c?this.escapeHtml(c):null,value:this.escapeHtml(i),fieldClass:n,checked:h,required:a,disabled:r,attrs:f};return E.render(this.templates.checkbox,p)}renderSwitchField(e){const{name:t,label:s,value:i=!1,required:a=!1,disabled:r=!1,size:n="md",class:l="",attributes:o={},help:c=e.helpText||e.help||""}=e,d=this.errors[t],h=this.getFieldValue(t)??i,f=h===!0||h==="true"||h==="1",m=Object.entries(o).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),p=this.getFieldId(t),g=n!=="md"?`form-switch-${n}`:"",y={helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:p,name:t,label:this.escapeHtml(s),help:c?this.escapeHtml(c):null,error:d?this.escapeHtml(d):null,value:this.escapeHtml(i),sizeClass:g,fieldClass:l,checked:f,required:a,disabled:r,attrs:m};return E.render(this.templates.switch,y)}renderRadioField(e){const{name:t,label:s,options:i=[],value:a="",disabled:r=!1,inline:n=!1,class:l="",attributes:o={},help:c=e.helpText||e.help||""}=e,d=this.errors[t],h=this.getFieldValue(t)??a,f=Object.entries(o).map(([p,g])=>`${p}="${this.escapeHtml(g)}"`).join(" ");let m="";return Array.isArray(i)&&(m=i.map((p,g)=>{const y=`${t}_${g}`,b=typeof p=="string"?p:p.value,w=typeof p=="string"?p:p.label||p.text||p.value,C=b===h?"checked":"";return`
|
|
392
392
|
<div class="form-check ${n?"form-check-inline":""}">
|
|
393
393
|
<input
|
|
394
394
|
type="radio"
|
|
@@ -409,19 +409,19 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
409
409
|
<div class="mojo-form-control">
|
|
410
410
|
${s?`<fieldset>
|
|
411
411
|
<legend class="${this.options.labelClass}">${this.escapeHtml(s)}</legend>
|
|
412
|
-
<div class="${
|
|
412
|
+
<div class="${l}">
|
|
413
413
|
${m}
|
|
414
414
|
</div>
|
|
415
|
-
</fieldset>`:`<div class="${
|
|
415
|
+
</fieldset>`:`<div class="${l}">${m}</div>`}
|
|
416
416
|
${c?`<div class="${this.options.helpClass}">${this.escapeHtml(c)}</div>`:""}
|
|
417
417
|
${d?`<div class="${this.options.errorClass}">${this.escapeHtml(d)}</div>`:""}
|
|
418
418
|
</div>
|
|
419
|
-
`}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:
|
|
419
|
+
`}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:l="",attributes:o={},help:c=e.helpText||e.help||""}=e,d=`${this.options.inputClass} ${l}`.trim(),h=this.errors[t],f=Object.entries(o).map(([g,y])=>`${g}="${this.escapeHtml(y)}"`).join(" "),m=this.getFieldId(t),p={labelClass:this.options.labelClass,inputClass:d,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:m,name:t,label:s?this.escapeHtml(s):null,help:c?this.escapeHtml(c):null,error:h?this.escapeHtml(h):null,accept:n,required:i,disabled:a,multiple:r,attrs:f};return E.render(this.templates.file,p)}renderImageField(e){const{name:t,label:s,required:i=!1,disabled:a=!1,accept:r="image/*",class:n="",attributes:l={},help:o=e.helpText||e.help||"",size:c="md",allowDrop:d=!0,placeholder:h="Drop image here or click to upload"}=e,f=`${this.options.inputClass} ${n}`.trim(),m=this.errors[t],p=this.getFieldId(t),g=`${p}_dropzone`,y=`${p}_preview`,b={xs:{width:48,height:48,containerClass:"image-field-xs"},sm:{width:96,height:96,containerClass:"image-field-sm"},md:{width:150,height:150,containerClass:"image-field-md"},lg:{width:200,height:200,containerClass:"image-field-lg"},xl:{width:300,height:300,containerClass:"image-field-xl"}},w=b[c]||b.md,C=Object.entries(l).map(([L,A])=>`${L}="${this.escapeHtml(A)}"`).join(" "),$=this.getFieldValue(t),S=this.extractImageUrl($,c),M={labelClass:this.options.labelClass,inputClass:f,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:p,name:t,label:s?this.escapeHtml(s):null,help:o?this.escapeHtml(o):null,error:m?this.escapeHtml(m):null,dropZoneId:g,previewId:y,containerClass:w.containerClass,width:w.width,height:w.height,accept:r,imageUrl:S,placeholderText:a?"No image":this.escapeHtml(h),cursor:a?"default":"pointer",allowDrop:d,showRemove:!a,required:i,disabled:a,attrs:C};return E.render(this.templates.image,M)}extractImageUrl(e,t="md"){if(!e)return null;if(typeof e=="string")return e;if(typeof e=="object"&&e.url){if(e.renditions){const s={xs:["thumbnail_sm","thumbnail","square_sm"],sm:["thumbnail","thumbnail_sm","square_sm"],md:["thumbnail_md","thumbnail","thumbnail_lg"],lg:["thumbnail_lg","thumbnail_md","thumbnail"],xl:["original","thumbnail_lg"]},i=s[t]||s.md;for(const a of i)if(e.renditions[a]&&e.renditions[a].url)return e.renditions[a].url}return e.url}return null}renderColorField(e){const{name:t,label:s,value:i="",placeholder:a="",required:r=!1,disabled:n=!1,readonly:l=!1,class:o="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=`${this.options.inputClass} ${o}`.trim(),f=this.errors[t],m=this.getFieldValue(t)??i,p=Object.entries(c).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),g=this.getFieldId(t),y={labelClass:this.options.labelClass,inputClass:h,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,fieldValue:this.escapeHtml(m),label:s?this.escapeHtml(s):null,placeholder:a?this.escapeHtml(a):null,help:d?this.escapeHtml(d):null,error:f?this.escapeHtml(f):null,required:r,disabled:n,readonly:l,attrs:p};return E.render(this.templates.color,y)}renderRangeField(e){const{name:t,label:s,min:i=0,max:a=100,step:r=1,value:n=i,disabled:l=!1,class:o="",attributes:c={},help:d=e.helpText||e.help||""}=e,h=`${this.options.inputClass} ${o}`.trim(),f=this.errors[t],m=this.getFieldValue(t)??n,p=Object.entries(c).map(([b,w])=>`${b}="${this.escapeHtml(w)}"`).join(" "),g=this.getFieldId(t),y={labelClass:this.options.labelClass,inputClass:h,helpClass:this.options.helpClass,errorClass:this.options.errorClass,fieldId:g,name:t,label:s?this.escapeHtml(s):null,help:d?this.escapeHtml(d):null,error:f?this.escapeHtml(f):null,min:i,max:a,step:r,fieldValue:m,disabled:l,attrs:p};return E.render(this.templates.range,y)}renderHiddenField(e){const{name:t,value:s=""}=e,i=this.getFieldValue(t)??s;return`<input type="hidden" name="${t}" value="${this.escapeHtml(i)}">`}renderButton(e){const{name:t="",label:s="Button",type:i="button",action:a="",class:r="btn-secondary",disabled:n=!1,attributes:l={}}=e;let o=a;o||(i==="submit"?o="submit-form":i==="reset"&&(o="reset-form"));const c=Object.entries(l).map(([d,h])=>`${d}="${this.escapeHtml(h)}"`).join(" ");return`
|
|
420
420
|
<button
|
|
421
421
|
type="button"
|
|
422
422
|
${t?`name="${t}"`:""}
|
|
423
423
|
class="btn ${r}"
|
|
424
|
-
${
|
|
424
|
+
${o?`data-action="${o}"`:""}
|
|
425
425
|
${n?"disabled":""}
|
|
426
426
|
${c}
|
|
427
427
|
>
|
|
@@ -436,24 +436,24 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
436
436
|
<div class="form-html ${s}">
|
|
437
437
|
${t}
|
|
438
438
|
</div>
|
|
439
|
-
`}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)}"`:"",
|
|
439
|
+
`}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)}"`:"",l=i?` class="${this.escapeHtml(i)}"`:"";return`<h${r}${n}${l}>${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?`
|
|
440
440
|
<div class="form-actions mt-3">
|
|
441
441
|
${e}
|
|
442
442
|
</div>
|
|
443
|
-
`:""}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:
|
|
443
|
+
`:""}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:l=!1,maxTags:o=50,allowDuplicates:c=!1,separator:d=",",help:h=e.helpText||e.help||""}=e,f=this.getFieldId(t),m=this.errors[t],p=this.getFieldValue(t)??i;return`
|
|
444
444
|
<div class="mojo-form-control">
|
|
445
445
|
${s?`<label for="${f}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
|
|
446
446
|
<div class="tag-input-placeholder"
|
|
447
447
|
data-field-name="${t}"
|
|
448
448
|
data-field-type="tag"
|
|
449
|
-
data-field-config='${JSON.stringify({name:t,value:p,placeholder:a,maxTags:
|
|
449
|
+
data-field-config='${JSON.stringify({name:t,value:p,placeholder:a,maxTags:o,allowDuplicates:c,separator:d,disabled:n,readonly:l,required:r})}'>
|
|
450
450
|
<input type="text"
|
|
451
451
|
id="${f}"
|
|
452
452
|
name="${t}_display"
|
|
453
453
|
class="${this.options.inputClass}${m?" is-invalid":""}"
|
|
454
454
|
placeholder="${this.escapeHtml(a)}"
|
|
455
455
|
${n?"disabled":""}
|
|
456
|
-
${
|
|
456
|
+
${l?"readonly":""}
|
|
457
457
|
|
|
458
458
|
<input type="hidden" name="${t}" value="${this.escapeHtml(p)}">
|
|
459
459
|
<small class="form-text text-muted">This will be enhanced with TagInput component</small>
|
|
@@ -461,20 +461,20 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
461
461
|
${h?`<div class="${this.options.helpClass}">${this.escapeHtml(h)}</div>`:""}
|
|
462
462
|
${m?`<div class="${this.options.errorClass}">${this.escapeHtml(m)}</div>`:""}
|
|
463
463
|
</div>
|
|
464
|
-
`}renderCollectionField(e){const{name:t,label:s,value:i="",placeholder:a="Search...",required:r=!1,disabled:n=!1,readonly:
|
|
464
|
+
`}renderCollectionField(e){const{name:t,label:s,value:i="",placeholder:a="Search...",required:r=!1,disabled:n=!1,readonly:l=!1,Collection:o,labelField:c="name",valueField:d="id",maxItems:h=10,emptyFetch:f=!1,debounceMs:m=300,requiresActiveGroup:p=!1,help:g=e.helpText||e.help||""}=e,y=this.getFieldId(t),b=this.errors[t],w=this.getFieldValue(t)??i;return`
|
|
465
465
|
<div class="mojo-form-control">
|
|
466
466
|
${s?`<label for="${y}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
|
|
467
467
|
<div class="collection-select-placeholder"
|
|
468
468
|
data-field-name="${t}"
|
|
469
469
|
data-field-type="collection"
|
|
470
|
-
data-field-config='${JSON.stringify({name:t,value:w,placeholder:a,labelField:c,valueField:d,maxItems:h,emptyFetch:f,debounceMs:m,disabled:n,readonly:
|
|
470
|
+
data-field-config='${JSON.stringify({name:t,value:w,placeholder:a,labelField:c,valueField:d,maxItems:h,emptyFetch:f,debounceMs:m,disabled:n,readonly:l,required:r,requiresActiveGroup:p})}'>
|
|
471
471
|
<input type="text"
|
|
472
472
|
id="${y}"
|
|
473
473
|
name="${t}_display"
|
|
474
474
|
class="${this.options.inputClass}${b?" is-invalid":""}"
|
|
475
475
|
placeholder="${this.escapeHtml(a)}"
|
|
476
476
|
${n?"disabled":""}
|
|
477
|
-
${
|
|
477
|
+
${l?"readonly":""}
|
|
478
478
|
|
|
479
479
|
<input type="hidden" name="${t}" value="${this.escapeHtml(w)}">
|
|
480
480
|
<small class="form-text text-muted">This will be enhanced with CollectionSelect component</small>
|
|
@@ -482,36 +482,36 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
482
482
|
${g?`<div class="${this.options.helpClass}">${this.escapeHtml(g)}</div>`:""}
|
|
483
483
|
${b?`<div class="${this.options.errorClass}">${this.escapeHtml(b)}</div>`:""}
|
|
484
484
|
</div>
|
|
485
|
-
`}renderCollectionMultiSelectField(e){const{name:t,label:s,value:i=[],required:a=!1,disabled:r=!1,Collection:n,collectionParams:
|
|
485
|
+
`}renderCollectionMultiSelectField(e){const{name:t,label:s,value:i=[],required:a=!1,disabled:r=!1,Collection:n,collectionParams:l={},labelField:o="name",valueField:c="id",excludeIds:d=[],ignoreIds:h=[],size:f=8,maxHeight:m=null,showSelectAll:p=!0,enableSearch:g=!1,searchPlaceholder:y="Search...",searchDebounce:b=400,requiresActiveGroup:w=!1,help:C=e.helpText||e.help||""}=e;this.getFieldId(t);const $=this.errors[t],S=this.getFieldValue(t)??i;return`
|
|
486
486
|
<div class="mojo-form-control">
|
|
487
487
|
${s?`<label class="${this.options.labelClass}">${this.escapeHtml(s)}${a?'<span class="text-danger">*</span>':""}</label>`:""}
|
|
488
488
|
<div class="collection-multiselect-placeholder"
|
|
489
489
|
data-field-name="${t}"
|
|
490
490
|
data-field-type="collectionmultiselect"
|
|
491
|
-
data-field-config='${JSON.stringify({name:t,value:S,labelField:
|
|
491
|
+
data-field-config='${JSON.stringify({name:t,value:S,labelField:o,valueField:c,excludeIds:d,ignoreIds:h,size:f,maxHeight:m,showSelectAll:p,enableSearch:g,searchPlaceholder:y,searchDebounce:b,disabled:r,required:a,requiresActiveGroup:w})}'>
|
|
492
492
|
<input type="hidden" name="${t}" value="${this.escapeHtml(JSON.stringify(S))}">
|
|
493
493
|
<small class="form-text text-muted">This will be enhanced with CollectionMultiSelect component</small>
|
|
494
494
|
</div>
|
|
495
495
|
${C?`<div class="${this.options.helpClass}">${this.escapeHtml(C)}</div>`:""}
|
|
496
496
|
${$?`<div class="${this.options.errorClass}">${this.escapeHtml($)}</div>`:""}
|
|
497
497
|
</div>
|
|
498
|
-
`}renderDatePickerField(e){const{name:t,label:s,value:i="",placeholder:a="Select date...",required:r=!1,disabled:n=!1,readonly:
|
|
498
|
+
`}renderDatePickerField(e){const{name:t,label:s,value:i="",placeholder:a="Select date...",required:r=!1,disabled:n=!1,readonly:l=!1,min:o=null,max:c=null,format:d="YYYY-MM-DD",displayFormat:h="MMM DD, YYYY",help:f=e.helpText||e.help||""}=e,m=this.getFieldId(t),p=this.errors[t],g=this.getFieldValue(t)??i;return`
|
|
499
499
|
<div class="mojo-form-control">
|
|
500
500
|
${s?`<label for="${m}" class="${this.options.labelClass}">${this.escapeHtml(s)}${r?'<span class="text-danger">*</span>':""}</label>`:""}
|
|
501
501
|
<div class="date-picker-placeholder"
|
|
502
502
|
data-field-name="${t}"
|
|
503
503
|
data-field-type="datepicker"
|
|
504
|
-
data-field-config='${JSON.stringify({name:t,value:g,placeholder:a,min:
|
|
504
|
+
data-field-config='${JSON.stringify({name:t,value:g,placeholder:a,min:o,max:c,format:d,displayFormat:h,disabled:n,readonly:l,required:r})}'>
|
|
505
505
|
<input type="date"
|
|
506
506
|
id="${m}"
|
|
507
507
|
name="${t}"
|
|
508
508
|
class="${this.options.inputClass}${p?" is-invalid":""}"
|
|
509
509
|
value="${this.escapeHtml(g)}"
|
|
510
510
|
placeholder="${this.escapeHtml(a)}"
|
|
511
|
-
${
|
|
511
|
+
${o?`min="${o}"`:""}
|
|
512
512
|
${c?`max="${c}"`:""}
|
|
513
513
|
${n?"disabled":""}
|
|
514
|
-
${
|
|
514
|
+
${l?"readonly":""}
|
|
515
515
|
${r?"required":""}
|
|
516
516
|
|
|
517
517
|
<small class="form-text text-muted">This will be enhanced with Easepick DatePicker</small>
|
|
@@ -519,13 +519,13 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
519
519
|
${f?`<div class="${this.options.helpClass}">${this.escapeHtml(f)}</div>`:""}
|
|
520
520
|
${p?`<div class="${this.options.errorClass}">${this.escapeHtml(p)}</div>`:""}
|
|
521
521
|
</div>
|
|
522
|
-
`}renderDateRangeField(e){const{name:t,startName:s,endName:i,fieldName:a,label:r,startDate:n="",endDate:
|
|
522
|
+
`}renderDateRangeField(e){const{name:t,startName:s,endName:i,fieldName:a,label:r,startDate:n="",endDate:l="",placeholder:o="Select date range...",required:c=!1,disabled:d=!1,readonly:h=!1,min:f=null,max:m=null,format:p="YYYY-MM-DD",displayFormat:g="MMM DD, YYYY",outputFormat:y="date",separator:b=" - ",help:w=e.helpText||e.help||""}=e,C=this.getFieldId(t||s||"daterange"),$=this.errors[t],S=s||(t?t+"_start":""),M=i||(t?t+"_end":""),L=this.getFieldValue(S)||n,A=this.getFieldValue(M)||l;return`
|
|
523
523
|
<div class="mojo-form-control">
|
|
524
524
|
${r?`<label for="${C}" class="${this.options.labelClass}">${this.escapeHtml(r)}${c?'<span class="text-danger">*</span>':""}</label>`:""}
|
|
525
525
|
<div class="date-range-picker-placeholder"
|
|
526
526
|
data-field-name="${t||s||"daterange"}"
|
|
527
527
|
data-field-type="daterange"
|
|
528
|
-
data-field-config='${JSON.stringify({name:t,startName:s,endName:i,fieldName:a,startDate:L,endDate:A,placeholder:
|
|
528
|
+
data-field-config='${JSON.stringify({name:t,startName:s,endName:i,fieldName:a,startDate:L,endDate:A,placeholder:o,min:f,max:m,format:p,displayFormat:g,outputFormat:y,separator:b,disabled:d,readonly:h,required:c})}'>
|
|
529
529
|
<div class="row g-2">
|
|
530
530
|
<div class="col">
|
|
531
531
|
<input type="date"
|
|
@@ -564,25 +564,25 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
564
564
|
${w?`<div class="${this.options.helpClass}">${this.escapeHtml(w)}</div>`:""}
|
|
565
565
|
${$?`<div class="${this.options.errorClass}">${this.escapeHtml($)}</div>`:""}
|
|
566
566
|
</div>
|
|
567
|
-
`}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 E.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 E.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:
|
|
567
|
+
`}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 E.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 E.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:l=e.helpText||e.help||""}=e,o=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`
|
|
568
568
|
<div class="mojo-form-control">
|
|
569
569
|
${s?`<label class="${this.options.labelClass}">${this.escapeHtml(s)}${a?'<span class="text-danger">*</span>':""}</label>`:""}
|
|
570
570
|
<div class="combobox-placeholder"
|
|
571
571
|
data-field-name="${t}"
|
|
572
572
|
data-field-type="combobox"
|
|
573
|
-
data-field-config='${JSON.stringify({name:t,value:h,placeholder:
|
|
573
|
+
data-field-config='${JSON.stringify({name:t,value:h,placeholder:o,maxHeight:n,allowCustom:c,disabled:r,required:a})}'>
|
|
574
574
|
<input type="text"
|
|
575
575
|
class="form-control${d?" is-invalid":""}"
|
|
576
576
|
value="${this.escapeHtml(h)}"
|
|
577
|
-
placeholder="${this.escapeHtml(
|
|
577
|
+
placeholder="${this.escapeHtml(o)}"
|
|
578
578
|
${r?"disabled":""}
|
|
579
579
|
${a?"required":""}>
|
|
580
580
|
<small class="form-text text-muted">This will be enhanced with ComboBox component</small>
|
|
581
581
|
</div>
|
|
582
|
-
${
|
|
582
|
+
${l?`<div class="${this.options.helpClass}">${this.escapeHtml(l)}</div>`:""}
|
|
583
583
|
${d?`<div class="${this.options.errorClass}">${this.escapeHtml(d)}</div>`:""}
|
|
584
584
|
</div>
|
|
585
|
-
`}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((
|
|
585
|
+
`}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((o,c)=>{const d=`${r}-pane-${c}`,h=c===0;return`
|
|
586
586
|
<li class="nav-item" role="presentation">
|
|
587
587
|
<button class="nav-link ${h?"active":""}"
|
|
588
588
|
id="${d}-tab"
|
|
@@ -592,10 +592,10 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
592
592
|
role="tab"
|
|
593
593
|
aria-controls="${d}"
|
|
594
594
|
aria-selected="${h}">
|
|
595
|
-
${this.escapeHtml(
|
|
595
|
+
${this.escapeHtml(o.label||`Tab ${c+1}`)}
|
|
596
596
|
</button>
|
|
597
597
|
</li>
|
|
598
|
-
`}).join(""),
|
|
598
|
+
`}).join(""),l=t.map((o,c)=>{const d=`${r}-pane-${c}`,h=c===0,f=(o.fields||[]).map(m=>m.type==="group"?this.buildGroupHTML(m):this.buildFieldHTML(m)).join("");return`
|
|
599
599
|
<div class="tab-pane fade ${h?"show active":""}"
|
|
600
600
|
id="${d}"
|
|
601
601
|
role="tabpanel"
|
|
@@ -611,10 +611,10 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
611
611
|
${n}
|
|
612
612
|
</ul>
|
|
613
613
|
<div class="${a}">
|
|
614
|
-
${
|
|
614
|
+
${l}
|
|
615
615
|
</div>
|
|
616
616
|
</div>
|
|
617
|
-
`}generateSelectOptions(e,t,s=1,i={}){const{format:a,prefix:r="",suffix:n=""}=i,
|
|
617
|
+
`}generateSelectOptions(e,t,s=1,i={}){const{format:a,prefix:r="",suffix:n=""}=i,l=[],o=e<=t?Math.abs(s):-Math.abs(s);for(let c=e;e<=t?c<=t:c>=t;c+=o){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}`,l.push({value:c,label:d})}return l}formatOrdinal(e){const t=e%10,s=e%100;return t===1&&s!==11?e+"st":t===2&&s!==12?e+"nd":t===3&&s!==13?e+"rd":e+"th"}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}}const Le={enableFileDrop(u={}){if(this._fileDropConfig={acceptedTypes:u.acceptedTypes||["*/*"],maxFileSize:u.maxFileSize||10*1024*1024,dropZoneSelector:u.dropZoneSelector||null,visualFeedback:u.visualFeedback!==!1,multiple:u.multiple||!1,validateOnDrop:u.validateOnDrop!==!1,dragOverClass:u.dragOverClass||"drag-over",dragActiveClass:u.dragActiveClass||"drag-active"},this._fileDropState={isDragActive:!1,dragCounter:0},this._boundFileDropHandlers={dragEnter:this._onFileDropDragEnter.bind(this),dragOver:this._onFileDropDragOver.bind(this),dragLeave:this._onFileDropDragLeave.bind(this),drop:this._onFileDropDrop.bind(this),preventDefault:this._onFileDropPreventDefault.bind(this)},this.element)this._setupFileDropListeners();else{const t=this.onAfterRender.bind(this);this.onAfterRender=async()=>{await t(),this._setupFileDropListeners()}}const e=this.onBeforeDestroy.bind(this);this.onBeforeDestroy=async()=>{this._cleanupFileDropListeners(),await e()}},_setupFileDropListeners(){if(!this._fileDropConfig)return;const u=this._getFileDropZone();if(!u){console.warn("FileDropMixin: Drop zone not found");return}this._fileDropZone=u,u.addEventListener("dragenter",this._boundFileDropHandlers.dragEnter),u.addEventListener("dragover",this._boundFileDropHandlers.dragOver),u.addEventListener("dragleave",this._boundFileDropHandlers.dragLeave),u.addEventListener("drop",this._boundFileDropHandlers.drop),["dragenter","dragover","dragleave","drop"].forEach(e=>{document.addEventListener(e,this._boundFileDropHandlers.preventDefault)})},_cleanupFileDropListeners(){this._boundFileDropHandlers&&(this._fileDropZone&&(this._fileDropZone.removeEventListener("dragenter",this._boundFileDropHandlers.dragEnter),this._fileDropZone.removeEventListener("dragover",this._boundFileDropHandlers.dragOver),this._fileDropZone.removeEventListener("dragleave",this._boundFileDropHandlers.dragLeave),this._fileDropZone.removeEventListener("drop",this._boundFileDropHandlers.drop)),["dragenter","dragover","dragleave","drop"].forEach(u=>{document.removeEventListener(u,this._boundFileDropHandlers.preventDefault)}),this._fileDropZone=null,this._boundFileDropHandlers=null,this._fileDropConfig=null,this._fileDropState=null)},_getFileDropZone(){return this._fileDropConfig.dropZoneSelector?this.element.querySelector(this._fileDropConfig.dropZoneSelector):this.element},_onFileDropPreventDefault(u){u.preventDefault(),u.stopPropagation()},_onFileDropDragEnter(u){this._onFileDropPreventDefault(u),this._fileDropState.dragCounter++,this._fileDropState.isDragActive||(this._fileDropState.isDragActive=!0,this._applyFileDropVisualFeedback(!0))},_onFileDropDragOver(u){this._onFileDropPreventDefault(u),u.dataTransfer.dropEffect="copy"},_onFileDropDragLeave(u){this._onFileDropPreventDefault(u),this._fileDropState.dragCounter--,this._fileDropState.dragCounter<=0&&(this._fileDropState.isDragActive=!1,this._fileDropState.dragCounter=0,this._applyFileDropVisualFeedback(!1))},async _onFileDropDrop(u){this._onFileDropPreventDefault(u),this._fileDropState.isDragActive=!1,this._fileDropState.dragCounter=0,this._applyFileDropVisualFeedback(!1);const e=Array.from(u.dataTransfer.files);if(e.length===0)return;const t=this._fileDropConfig.multiple?e:[e[0]];let s={valid:!0,errors:[]};if(this._fileDropConfig.validateOnDrop&&(s=this._validateFileDropFiles(t),!s.valid)){typeof this.onFileDropError=="function"&&await this.onFileDropError(new Error(s.errors.join(", ")),u,t);return}if(typeof this.onFileDrop=="function")try{await this.onFileDrop(t,u,s)}catch(i){typeof this.onFileDropError=="function"?await this.onFileDropError(i,u,t):console.error("FileDropMixin: Error in onFileDrop callback:",i)}else console.warn("FileDropMixin: No onFileDrop method found on view")},_applyFileDropVisualFeedback(u){if(!this._fileDropConfig.visualFeedback||!this._fileDropZone)return;const{dragOverClass:e,dragActiveClass:t}=this._fileDropConfig;u?this._fileDropZone.classList.add(e,t):this._fileDropZone.classList.remove(e,t)},_validateFileDropFiles(u){const e=[],t=this._fileDropConfig;for(const s of u){if(!this._isFileDropTypeAccepted(s.type)){e.push(`File type "${s.type}" is not accepted for file "${s.name}"`);continue}s.size>t.maxFileSize&&e.push(`File "${s.name}" (${this._formatFileDropSize(s.size)}) exceeds maximum size (${this._formatFileDropSize(t.maxFileSize)})`)}return{valid:e.length===0,errors:e}},_isFileDropTypeAccepted(u){const{acceptedTypes:e}=this._fileDropConfig;return e.includes("*/*")?!0:e.some(t=>{if(t===u)return!0;if(t.endsWith("/*")){const s=t.split("/")[0];return u.startsWith(s+"/")}return!1})},_formatFileDropSize(u){if(u===0)return"0 Bytes";const e=1024,t=["Bytes","KB","MB","GB"],s=Math.floor(Math.log(u)/Math.log(e));return parseFloat((u/Math.pow(e,s)).toFixed(2))+" "+t[s]}};function _e(u){Object.assign(u.prototype,Le)}class le extends v{constructor(e={}){const{name:t,value:s="",placeholder:i="Add tags...",maxTags:a=50,allowDuplicates:r=!1,separator:n=",",trimTags:l=!0,minLength:o=1,maxLength:c=50,disabled:d=!1,readonly:h=!1,class:f="",tagClass:m="badge bg-primary",inputClass:p="form-control",...g}=e;super({tagName:"div",className:`tag-input-view ${f}`,...g}),this.name=t,this.placeholder=i,this.maxTags=a,this.allowDuplicates=r,this.separator=n,this.trimTags=l,this.minLength=o,this.maxLength=c,this.disabled=d,this.readonly=h,this.tagClass=m,this.inputClass=p,this.tags=[],this.focusedTagIndex=-1,s&&(this.tags=this.parseTagString(s))}async renderTemplate(){const e=this.renderTags(),t=this.renderHiddenInput(),s=this.renderInput();return`
|
|
618
618
|
<div class="tag-input-container">
|
|
619
619
|
<div class="tag-input-wrapper border rounded p-2"
|
|
620
620
|
data-action="focus-input"
|
|
@@ -660,7 +660,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
660
660
|
value="${this.escapeHtml(this.getTagString())}"
|
|
661
661
|
class="tag-input-hidden">
|
|
662
662
|
`:""}async onAfterRender(){await super.onAfterRender(),this.updateTagCount()}async onActionFocusInput(e,t){this.focus()}focus(){const e=this.element.querySelector(".tag-input-field");e&&!this.disabled&&e.focus(),this.focusedTagIndex=-1}async onActionRemoveTag(e,t){e.stopPropagation();const s=parseInt(t.getAttribute("data-tag-index"));s>=0&&s<this.tags.length&&await this.removeTag(s)}async onChangeInputChange(e,t){const s=t.value,i=s.slice(-1);if(i===this.separator||i===`
|
|
663
|
-
`){e.preventDefault();const a=s.slice(0,-1);a.trim()&&(await this.addTag(a),t.value="");return}}bindEvents(){this.__bnd_keydown||(this.__bnd_keydown=this.handleInputKeydown.bind(this)),this.element.addEventListener("keydown",this.__bnd_keydown),this.events.bind(this.element)}unbindEvents(){this.__bnd_keydown&&this.element.removeEventListener("keydown",this.__bnd_keydown),this.events.unbind()}handleInputKeydown(e){const t=e.target,s=t.value||"";switch(e.key){case"Enter":case"Tab":case",":s.trim()&&(e.preventDefault(),this.addTag(s),t.value="");break;case"Backspace":s===""&&this.tags.length>0&&(e.preventDefault(),this.focusedTagIndex>=0?(this.removeTag(this.focusedTagIndex),this.focusedTagIndex==0?this.focus():this.focusTag(this.focusedTagIndex-1)):this.removeTag(this.tags.length-1));break;case"ArrowLeft":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex-1;i>=0?this.focusTag(i):this.focus()}else this.focusTag(this.tags.length-1);break;case"ArrowRight":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex+1;i<this.tags.length?this.focusTag(i):this.focus()}else this.focusTag(0);break;case"Escape":t.value="",t.blur();break}}async addTag(e){if(this.readonly||this.disabled)return!1;const t=this.trimTags?e.trim():e;return this.isValidTag(t)?!this.allowDuplicates&&this.tags.includes(t)?(this.showTagError(`Tag "${t}" already exists`),!1):this.tags.length>=this.maxTags?(this.showTagError(`Maximum ${this.maxTags} tags allowed`),!1):(this.tags.push(t),await this.updateDisplay(),this.emit("tag:added",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0):!1}async removeTag(e){if(this.readonly||this.disabled)return!1;if(e>=0&&e<this.tags.length){const t=this.tags[e];return this.tags.splice(e,1),await this.updateDisplay(),this.emit("tag:removed",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0}return!1}async removeTagByValue(e){const t=this.tags.indexOf(e);return t>=0?await this.removeTag(t):!1}async clearTags(){if(this.readonly||this.disabled)return!1;const e=[...this.tags];return this.tags=[],await this.updateDisplay(),this.emit("tags:cleared",{oldTags:e}),this.emit("change",{value:"",tags:[]}),!0}async setTags(e){let t=[];Array.isArray(e)?t=e:typeof e=="string"&&(t=this.parseTagString(e)),t=t.filter(s=>this.isValidTag(s)).slice(0,this.maxTags),this.allowDuplicates||(t=[...new Set(t)]),this.tags=t,await this.updateDisplay(),this.emit("tags:set",{tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags})}isValidTag(e){return!(typeof e!="string"||e.length<this.minLength||e.length>this.maxLength||e.trim()==="")}parseTagString(e){return e?e.split(this.separator).map(t=>this.trimTags?t.trim():t).filter(t=>t.length>0):[]}getTagString(){return this.tags.join(this.separator)}getTags(){return[...this.tags]}focusTag(e){const t=this.element.querySelectorAll(".tag-item");t[e]&&(this.focusedTagIndex=e,console.log(`Focused tag index: ${e}`),t[e].focus())}async updateDisplay(){const e=this.element.querySelector(".tags-container");e&&(e.innerHTML=this.renderTags());const t=this.element.querySelector(".tag-input-hidden");t&&(t.value=this.getTagString()),this.updateTagCount()}updateTagCount(){const e=this.element.querySelector(".tag-count");e&&(e.textContent=this.tags.length)}showTagError(e){let t=this.element.querySelector(".tag-error");if(!t){t=document.createElement("div"),t.className="tag-error small text-danger mt-1";const s=this.element.querySelector(".tag-input-feedback");s&&s.parentNode.insertBefore(t,s.nextSibling)}t.textContent=e,setTimeout(()=>{t.parentNode&&t.remove()},3e3)}setEnabled(e){this.disabled=!e;const t=this.element.querySelector(".tag-input-field");t&&(t.disabled=this.disabled);const s=this.element.querySelector(".tag-input-wrapper");s&&s.classList.toggle("disabled",this.disabled)}setReadonly(e){this.readonly=e;const t=this.element.querySelector(".tag-input-field");t&&(t.style.display=e?"none":""),this.element.querySelectorAll(".tag-remove").forEach(i=>{i.style.display=e?"none":""})}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}getFormValue(){return this.getTagString()}async setFormValue(e){await this.setTags(e)}static create(e={}){return new
|
|
663
|
+
`){e.preventDefault();const a=s.slice(0,-1);a.trim()&&(await this.addTag(a),t.value="");return}}bindEvents(){this.__bnd_keydown||(this.__bnd_keydown=this.handleInputKeydown.bind(this)),this.element.addEventListener("keydown",this.__bnd_keydown),this.events.bind(this.element)}unbindEvents(){this.__bnd_keydown&&this.element.removeEventListener("keydown",this.__bnd_keydown),this.events.unbind()}handleInputKeydown(e){const t=e.target,s=t.value||"";switch(e.key){case"Enter":case"Tab":case",":s.trim()&&(e.preventDefault(),this.addTag(s),t.value="");break;case"Backspace":s===""&&this.tags.length>0&&(e.preventDefault(),this.focusedTagIndex>=0?(this.removeTag(this.focusedTagIndex),this.focusedTagIndex==0?this.focus():this.focusTag(this.focusedTagIndex-1)):this.removeTag(this.tags.length-1));break;case"ArrowLeft":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex-1;i>=0?this.focusTag(i):this.focus()}else this.focusTag(this.tags.length-1);break;case"ArrowRight":if(s===""&&this.tags.length>0)if(e.preventDefault(),this.focusedTagIndex>=0){const i=this.focusedTagIndex+1;i<this.tags.length?this.focusTag(i):this.focus()}else this.focusTag(0);break;case"Escape":t.value="",t.blur();break}}async addTag(e){if(this.readonly||this.disabled)return!1;const t=this.trimTags?e.trim():e;return this.isValidTag(t)?!this.allowDuplicates&&this.tags.includes(t)?(this.showTagError(`Tag "${t}" already exists`),!1):this.tags.length>=this.maxTags?(this.showTagError(`Maximum ${this.maxTags} tags allowed`),!1):(this.tags.push(t),await this.updateDisplay(),this.emit("tag:added",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0):!1}async removeTag(e){if(this.readonly||this.disabled)return!1;if(e>=0&&e<this.tags.length){const t=this.tags[e];return this.tags.splice(e,1),await this.updateDisplay(),this.emit("tag:removed",{tag:t,tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags}),!0}return!1}async removeTagByValue(e){const t=this.tags.indexOf(e);return t>=0?await this.removeTag(t):!1}async clearTags(){if(this.readonly||this.disabled)return!1;const e=[...this.tags];return this.tags=[],await this.updateDisplay(),this.emit("tags:cleared",{oldTags:e}),this.emit("change",{value:"",tags:[]}),!0}async setTags(e){let t=[];Array.isArray(e)?t=e:typeof e=="string"&&(t=this.parseTagString(e)),t=t.filter(s=>this.isValidTag(s)).slice(0,this.maxTags),this.allowDuplicates||(t=[...new Set(t)]),this.tags=t,await this.updateDisplay(),this.emit("tags:set",{tags:this.tags}),this.emit("change",{value:this.getTagString(),tags:this.tags})}isValidTag(e){return!(typeof e!="string"||e.length<this.minLength||e.length>this.maxLength||e.trim()==="")}parseTagString(e){return e?e.split(this.separator).map(t=>this.trimTags?t.trim():t).filter(t=>t.length>0):[]}getTagString(){return this.tags.join(this.separator)}getTags(){return[...this.tags]}focusTag(e){const t=this.element.querySelectorAll(".tag-item");t[e]&&(this.focusedTagIndex=e,console.log(`Focused tag index: ${e}`),t[e].focus())}async updateDisplay(){const e=this.element.querySelector(".tags-container");e&&(e.innerHTML=this.renderTags());const t=this.element.querySelector(".tag-input-hidden");t&&(t.value=this.getTagString()),this.updateTagCount()}updateTagCount(){const e=this.element.querySelector(".tag-count");e&&(e.textContent=this.tags.length)}showTagError(e){let t=this.element.querySelector(".tag-error");if(!t){t=document.createElement("div"),t.className="tag-error small text-danger mt-1";const s=this.element.querySelector(".tag-input-feedback");s&&s.parentNode.insertBefore(t,s.nextSibling)}t.textContent=e,setTimeout(()=>{t.parentNode&&t.remove()},3e3)}setEnabled(e){this.disabled=!e;const t=this.element.querySelector(".tag-input-field");t&&(t.disabled=this.disabled);const s=this.element.querySelector(".tag-input-wrapper");s&&s.classList.toggle("disabled",this.disabled)}setReadonly(e){this.readonly=e;const t=this.element.querySelector(".tag-input-field");t&&(t.style.display=e?"none":""),this.element.querySelectorAll(".tag-remove").forEach(i=>{i.style.display=e?"none":""})}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}getFormValue(){return this.getTagString()}async setFormValue(e){await this.setTags(e)}static create(e={}){return new le(e)}}class Pe extends v{constructor(e={}){super({tagName:"div",className:"collection-dropdown-view dropdown-menu show w-100 position-absolute",style:"max-height: 250px; overflow-y: auto; z-index: 1000;",template:`
|
|
664
664
|
{{#data.loading}}
|
|
665
665
|
<div class="dropdown-item text-center">
|
|
666
666
|
<div class="spinner-border spinner-border-sm" role="status">
|
|
@@ -793,7 +793,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
793
793
|
<div class="invalid-feedback d-block">{{error}}</div>
|
|
794
794
|
{{/error}}
|
|
795
795
|
</div>
|
|
796
|
-
`,...e}),this.name=e.name||"collection_multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.excludeIds=e.excludeIds||[],this.ignoreIds=e.ignoreIds||[],this.itemTemplate=e.itemTemplate||null,this.collectionParams=e.collectionParams||{},this.defaultParamsOption=e.defaultParams||null,this.baseParams={},this.requiresActiveGroup=e.requiresActiveGroup||!1,this.size=e.size||8,this.maxHeight=e.maxHeight||this.size*42,this.showSelectAll=e.showSelectAll!==!1,this.enableSearch=e.enableSearch||!1,this.searchPlaceholder=e.searchPlaceholder||"Search...",this.searchDebounce=e.searchDebounce||400,this.selectedValues=Array.isArray(e.value)?e.value:[],this.loading=!1,this.items=[],this.searchView=null,this.listView=null}onInit(){this.collection&&this.setupCollection()}setupCollection(){if(this.baseParams={...this.collection.params},Object.keys(this.collectionParams).length>0&&(Object.assign(this.baseParams,this.collectionParams),Object.assign(this.collection.params,this.collectionParams)),this.defaultParamsOption){const e=typeof this.defaultParamsOption=="function"?this.defaultParamsOption():this.defaultParamsOption;e&&(Object.assign(this.baseParams,e),Object.assign(this.collection.params,e))}if(this.requiresActiveGroup){const e=this.getApp();e?.activeGroup?.id&&(this.baseParams.group=e.activeGroup.id,this.collection.params.group=e.activeGroup.id)}this.collection.on("fetch:start",()=>{this.loading=!0,this.updateListView()}),this.collection.on("fetch:end",()=>{this.loading=!1,this.buildItems(),this.updateListView()}),this.collection.isEmpty()||this.buildItems()}async onAfterRender(){await super.onAfterRender(),this.enableSearch&&this.createSearchView(),this.createListView(),this.collection?.isEmpty()&&this.collection.fetch()}createSearchView(){const e=this.element?.querySelector(".collection-multiselect-search-container");e&&(this.searchView=new Oe({placeholder:this.searchPlaceholder,debounce:this.searchDebounce}),this.searchView.on("search",t=>{this.handleSearch(t)}),this.searchView.render(!0,e))}createListView(){const e=this.element?.querySelector(".collection-multiselect-list-container");if(!e)return;const t=this.selectedValues.length,s=this.items.length,i=s-t;this.listView=new He({items:this.items,loading:this.loading,maxHeight:this.maxHeight,showSelectAll:this.showSelectAll,selectedCount:t,totalCount:s,unselectedCount:i,allSelected:t===s&&s>0,noneSelected:t===0,customItemTemplate:this.itemTemplate}),this.listView.on("toggle",a=>{this.handleToggle(a)}),this.listView.on("select-all",()=>{this.selectAll()}),this.listView.on("deselect-all",()=>{this.deselectAll()}),this.listView.render(!0,e)}updateListView(){if(this.listView){const e=this.selectedValues.length,t=this.items.length,s=t-e;this.listView.updateState({items:this.items,loading:this.loading,selectedCount:e,totalCount:t,unselectedCount:s,allSelected:e===t&&t>0,noneSelected:e===0}),this.listView.render(!1)}}buildItems(){const e=this.collection.models.filter(t=>{const s=this.getFieldValue(t,this.valueField);return!(s==null||this.excludeIds.includes(s)||this.ignoreIds.some(i=>i==s))});this.items=e.map((t,s)=>{const i=t.toJSON?t.toJSON():t,a=this.getFieldValue(t,this.valueField),r={label:this.getFieldValue(t,this.labelField),value:a,index:s,selected:this.selectedValues.some(n=>n==a),disabled:this.disabled,model:i};return this.itemTemplate&&(r.customContent=this.renderItemTemplate(r)),r})}renderItemTemplate(e){if(!this.itemTemplate)return"";try{return this.renderTemplateString(this.itemTemplate,e)}catch(t){return console.error("Error rendering item template:",t),e.label}}getFieldValue(e,t){if(!(!e||!t))return typeof e.get=="function"?e.get(t)??x.getNestedValue(e,t):x.getNestedValue(e,t)}handleSearch(e){const t={...this.baseParams};e&&(t.search=e),this.collection.updateParams(t,!0)}handleToggle({value:e,index:t,shiftKey:s,element:i}){if(s&&this.listView.lastClickedIndex>=0){const a=Math.min(this.listView.lastClickedIndex,t),r=Math.max(this.listView.lastClickedIndex,t),n=!this.items[t].selected;for(let
|
|
796
|
+
`,...e}),this.name=e.name||"collection_multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.collection=e.collection,this.labelField=e.labelField||"name",this.valueField=e.valueField||"id",this.excludeIds=e.excludeIds||[],this.ignoreIds=e.ignoreIds||[],this.itemTemplate=e.itemTemplate||null,this.collectionParams=e.collectionParams||{},this.defaultParamsOption=e.defaultParams||null,this.baseParams={},this.requiresActiveGroup=e.requiresActiveGroup||!1,this.size=e.size||8,this.maxHeight=e.maxHeight||this.size*42,this.showSelectAll=e.showSelectAll!==!1,this.enableSearch=e.enableSearch||!1,this.searchPlaceholder=e.searchPlaceholder||"Search...",this.searchDebounce=e.searchDebounce||400,this.selectedValues=Array.isArray(e.value)?e.value:[],this.loading=!1,this.items=[],this.searchView=null,this.listView=null}onInit(){this.collection&&this.setupCollection()}setupCollection(){if(this.baseParams={...this.collection.params},Object.keys(this.collectionParams).length>0&&(Object.assign(this.baseParams,this.collectionParams),Object.assign(this.collection.params,this.collectionParams)),this.defaultParamsOption){const e=typeof this.defaultParamsOption=="function"?this.defaultParamsOption():this.defaultParamsOption;e&&(Object.assign(this.baseParams,e),Object.assign(this.collection.params,e))}if(this.requiresActiveGroup){const e=this.getApp();e?.activeGroup?.id&&(this.baseParams.group=e.activeGroup.id,this.collection.params.group=e.activeGroup.id)}this.collection.on("fetch:start",()=>{this.loading=!0,this.updateListView()}),this.collection.on("fetch:end",()=>{this.loading=!1,this.buildItems(),this.updateListView()}),this.collection.isEmpty()||this.buildItems()}async onAfterRender(){await super.onAfterRender(),this.enableSearch&&this.createSearchView(),this.createListView(),this.collection?.isEmpty()&&this.collection.fetch()}createSearchView(){const e=this.element?.querySelector(".collection-multiselect-search-container");e&&(this.searchView=new Oe({placeholder:this.searchPlaceholder,debounce:this.searchDebounce}),this.searchView.on("search",t=>{this.handleSearch(t)}),this.searchView.render(!0,e))}createListView(){const e=this.element?.querySelector(".collection-multiselect-list-container");if(!e)return;const t=this.selectedValues.length,s=this.items.length,i=s-t;this.listView=new He({items:this.items,loading:this.loading,maxHeight:this.maxHeight,showSelectAll:this.showSelectAll,selectedCount:t,totalCount:s,unselectedCount:i,allSelected:t===s&&s>0,noneSelected:t===0,customItemTemplate:this.itemTemplate}),this.listView.on("toggle",a=>{this.handleToggle(a)}),this.listView.on("select-all",()=>{this.selectAll()}),this.listView.on("deselect-all",()=>{this.deselectAll()}),this.listView.render(!0,e)}updateListView(){if(this.listView){const e=this.selectedValues.length,t=this.items.length,s=t-e;this.listView.updateState({items:this.items,loading:this.loading,selectedCount:e,totalCount:t,unselectedCount:s,allSelected:e===t&&t>0,noneSelected:e===0}),this.listView.render(!1)}}buildItems(){const e=this.collection.models.filter(t=>{const s=this.getFieldValue(t,this.valueField);return!(s==null||this.excludeIds.includes(s)||this.ignoreIds.some(i=>i==s))});this.items=e.map((t,s)=>{const i=t.toJSON?t.toJSON():t,a=this.getFieldValue(t,this.valueField),r={label:this.getFieldValue(t,this.labelField),value:a,index:s,selected:this.selectedValues.some(n=>n==a),disabled:this.disabled,model:i};return this.itemTemplate&&(r.customContent=this.renderItemTemplate(r)),r})}renderItemTemplate(e){if(!this.itemTemplate)return"";try{return this.renderTemplateString(this.itemTemplate,e)}catch(t){return console.error("Error rendering item template:",t),e.label}}getFieldValue(e,t){if(!(!e||!t))return typeof e.get=="function"?e.get(t)??x.getNestedValue(e,t):x.getNestedValue(e,t)}handleSearch(e){const t={...this.baseParams};e&&(t.search=e),this.collection.updateParams(t,!0)}handleToggle({value:e,index:t,shiftKey:s,element:i}){if(s&&this.listView.lastClickedIndex>=0){const a=Math.min(this.listView.lastClickedIndex,t),r=Math.max(this.listView.lastClickedIndex,t),n=!this.items[t].selected;for(let l=a;l<=r;l++){const o=this.items[l];o.disabled||(n?this.selectedValues.includes(o.value)||this.selectedValues.push(o.value):this.selectedValues=this.selectedValues.filter(c=>c!=o.value),o.selected=n)}this.updateListView()}else{const a=this.items[t];a.selected?(this.selectedValues=this.selectedValues.filter(r=>r!=e),a.selected=!1):(this.selectedValues.push(e),a.selected=!0),i&&this.listView&&(this.listView.updateItemCheckbox(i,a.selected),this.listView.selectedCount=this.selectedValues.length,this.listView.unselectedCount=this.items.length-this.selectedValues.length,this.listView.allSelected=this.selectedValues.length===this.items.length&&this.items.length>0,this.listView.noneSelected=this.selectedValues.length===0,this.listView.updateActionButtons())}this.emit("change",{value:this.selectedValues,name:this.name})}selectAll(){this.selectedValues=this.items.filter(e=>!e.disabled).map(e=>e.value),this.items.forEach(e=>{e.disabled||(e.selected=!0)}),this.updateListView(),this.emit("change",{value:this.selectedValues,name:this.name})}deselectAll(){this.selectedValues=[],this.items.forEach(e=>e.selected=!1),this.updateListView(),this.emit("change",{value:this.selectedValues,name:this.name})}async onBeforeDestroy(){await super.onBeforeDestroy(),this.searchView&&this.searchView.destroy(),this.listView&&this.listView.destroy()}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}setExcludeIds(e){this.excludeIds=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}setIgnoreIds(e){this.ignoreIds=Array.isArray(e)?e:[],this.buildItems(),this.updateListView()}async refresh(){await this.collection.fetch()}getFormValue(){return this.selectedValues}setFormValue(e){this.setValue(e)}}class je extends v{constructor(e={}){super({tagName:"div",className:"multiselect-items",template:`
|
|
797
797
|
{{#items.length}}
|
|
798
798
|
<div class="multiselect-list" style="max-height: {{maxHeight}}px; overflow-y: auto;">
|
|
799
799
|
{{#items}}
|
|
@@ -851,7 +851,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
851
851
|
<div class="invalid-feedback d-block">{{error}}</div>
|
|
852
852
|
{{/error}}
|
|
853
853
|
</div>
|
|
854
|
-
`,...e}),this.name=e.name||"multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.placeholder=e.placeholder||e.placeHolder||"Select...",this.maxHeight=e.maxHeight||300,this.showSelectedLabels=e.showSelectedLabels!==!1,this.maxLabelsToShow=e.maxLabelsToShow||3,this.options=e.options||[],this.selectedValues=Array.isArray(e.value)?e.value:[],this.buttonText=this.computeButtonText(),this.listView=null}computeButtonText(){const e=this.selectedValues.length;return e===0?this.placeholder||"Select...":this.showSelectedLabels&&e<=this.maxLabelsToShow?this.selectedValues.map(s=>{const i=this.options.find(a=>(typeof a=="string"?a:a.value)===s);return typeof i=="string"?i:i?.label||i?.value||s}).join(", "):`${e} selected`}async onAfterRender(){await super.onAfterRender(),this.createListView()}createListView(){const e=this.element?.querySelector('[data-container="items"]');if(!e)return;const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView=new je({items:t,maxHeight:this.maxHeight}),this.listView.on("toggle",s=>{this.handleToggle(s)}),this.listView.on("close-dropdown",()=>{this.closeDropdown()}),this.listView.render(!0,e)}closeDropdown(){const e=this.element?.querySelector(".dropdown-toggle");if(e&&window.bootstrap?.Dropdown){const t=window.bootstrap.Dropdown.getInstance(e);t&&t.hide()}}handleToggle(e){const{value:t,selected:s}=e;s?this.selectedValues.includes(t)||this.selectedValues.push(t):this.selectedValues=this.selectedValues.filter(i=>i!==t),this.updateButtonText(),this.emit("change",{value:this.selectedValues,name:this.name})}updateButtonText(){const e=this.element?.querySelector(".multiselect-button-text");if(!e)return;const t=this.selectedValues.length;this.buttonText=this.computeButtonText(),e.textContent=this.buttonText,t===0?e.classList.add("text-muted"):e.classList.remove("text-muted")}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:e?[e]:[],this.listView&&this.listView.setValue(this.selectedValues),this.updateButtonText()}setOptions(e){if(this.options=e,this.listView){const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView.updateItems(t)}}clear(){this.setValue([])}getFormValue(){return this.getValue()}setFormValue(e){this.setValue(e)}async onBeforeDestroy(){await super.onBeforeDestroy(),this.listView&&this.listView.destroy()}}class
|
|
854
|
+
`,...e}),this.name=e.name||"multiselect",this.label=e.label||"",this.help=e.help||"",this.error=e.error||"",this.required=e.required||!1,this.disabled=e.disabled||!1,this.placeholder=e.placeholder||e.placeHolder||"Select...",this.maxHeight=e.maxHeight||300,this.showSelectedLabels=e.showSelectedLabels!==!1,this.maxLabelsToShow=e.maxLabelsToShow||3,this.options=e.options||[],this.selectedValues=Array.isArray(e.value)?e.value:[],this.buttonText=this.computeButtonText(),this.listView=null}computeButtonText(){const e=this.selectedValues.length;return e===0?this.placeholder||"Select...":this.showSelectedLabels&&e<=this.maxLabelsToShow?this.selectedValues.map(s=>{const i=this.options.find(a=>(typeof a=="string"?a:a.value)===s);return typeof i=="string"?i:i?.label||i?.value||s}).join(", "):`${e} selected`}async onAfterRender(){await super.onAfterRender(),this.createListView()}createListView(){const e=this.element?.querySelector('[data-container="items"]');if(!e)return;const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView=new je({items:t,maxHeight:this.maxHeight}),this.listView.on("toggle",s=>{this.handleToggle(s)}),this.listView.on("close-dropdown",()=>{this.closeDropdown()}),this.listView.render(!0,e)}closeDropdown(){const e=this.element?.querySelector(".dropdown-toggle");if(e&&window.bootstrap?.Dropdown){const t=window.bootstrap.Dropdown.getInstance(e);t&&t.hide()}}handleToggle(e){const{value:t,selected:s}=e;s?this.selectedValues.includes(t)||this.selectedValues.push(t):this.selectedValues=this.selectedValues.filter(i=>i!==t),this.updateButtonText(),this.emit("change",{value:this.selectedValues,name:this.name})}updateButtonText(){const e=this.element?.querySelector(".multiselect-button-text");if(!e)return;const t=this.selectedValues.length;this.buttonText=this.computeButtonText(),e.textContent=this.buttonText,t===0?e.classList.add("text-muted"):e.classList.remove("text-muted")}getValue(){return this.selectedValues}setValue(e){this.selectedValues=Array.isArray(e)?e:e?[e]:[],this.listView&&this.listView.setValue(this.selectedValues),this.updateButtonText()}setOptions(e){if(this.options=e,this.listView){const t=this.options.map((s,i)=>{const a=typeof s=="string"?s:s.value,r=typeof s=="string"?s:s.label||s.text||s.value,n=typeof s=="object"?s.disabled:!1;return{id:`${this.name}_${i}`,value:a,label:r,index:i,selected:this.selectedValues.includes(a),disabled:n}});this.listView.updateItems(t)}}clear(){this.setValue([])}getFormValue(){return this.getValue()}setFormValue(e){this.setValue(e)}async onBeforeDestroy(){await super.onBeforeDestroy(),this.listView&&this.listView.destroy()}}class oe extends v{constructor(e={}){const{name:t,value:s="",format:i="YYYY-MM-DD",displayFormat:a="MMM DD, YYYY",min:r=null,max:n=null,placeholder:l="Select date...",disabled:o=!1,readonly:c=!1,required:d=!1,class:h="",inputClass:f="form-control",autoApply:m=!0,inline:p=!1,...g}=e;super({tagName:"div",className:`date-picker-view ${h}`,...g}),this.name=t,this.format=i,this.displayFormat=a,this.min=r,this.max=n,this.placeholder=l,this.disabled=o,this.readonly=c,this.required=d,this.inputClass=f,this.autoApply=m,this.inline=p,this.currentValue=s,this.picker=null,this.useNative=!1,this.easepickLoaded=!1,this.checkEasepickAvailability()}async checkEasepickAvailability(){if(typeof window<"u"&&window.easepick)return this.easepickLoaded=!0,!0;try{return await this.loadEasepick(),this.easepickLoaded=!0,!0}catch(e){return console.warn("Easepick failed to load, falling back to native date input:",e),this.useNative=!0,!1}}async loadEasepick(){return new Promise((e,t)=>{if(window.easepick){e();return}const s=document.createElement("link");s.rel="stylesheet",s.href="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",document.head.appendChild(s);const i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js",i.onload=()=>{window.easepick?e():t(new Error("Easepick not available after loading"))},i.onerror=()=>t(new Error("Failed to load Easepick script")),document.head.appendChild(i)})}async renderTemplate(){const e=this.getInputId(),t=this.useNative?"date":"text",s=this.formatValueForInput(this.currentValue);return`
|
|
855
855
|
<div class="date-picker-container">
|
|
856
856
|
<input
|
|
857
857
|
type="${t}"
|
|
@@ -870,7 +870,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
870
870
|
/>
|
|
871
871
|
<div class="date-picker-feedback"></div>
|
|
872
872
|
</div>
|
|
873
|
-
`}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
|
|
873
|
+
`}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 oe(e)}}class ce extends v{constructor(e={}){const{name:t,startName:s,endName:i,fieldName:a,startDate:r="",endDate:n="",format:l="YYYY-MM-DD",displayFormat:o="MMM DD, YYYY",outputFormat:c="date",min:d=null,max:h=null,placeholder:f="Select date range...",startPlaceholder:m="Start date...",endPlaceholder:p="End date...",disabled:g=!1,readonly:y=!1,required:b=!1,class:w="",inputClass:C="form-control",autoApply:$=!0,inline:S=!1,separator:M=" - ",...L}=e;super({tagName:"div",className:`date-range-picker-view ${w}`,...L}),this.name=t,this.startName=s,this.endName=i,this.fieldName=a,this.format=l,this.displayFormat=o,this.outputFormat=c,this.min=d,this.max=h,this.placeholder=f,this.startPlaceholder=m,this.endPlaceholder=p,this.disabled=g,this.readonly=y,this.required=b,this.inputClass=C,this.autoApply=$,this.inline=S,this.separator=M,this.currentStartDate=r,this.currentEndDate=n,this.picker=null,this.useNative=!1,this.easepickLoaded=!1,this.checkEasepickAvailability()}async checkEasepickAvailability(){if(typeof window<"u"&&window.easepick)return this.easepickLoaded=!0,!0;try{return await this.loadEasepick(),this.easepickLoaded=!0,!0}catch(e){return console.warn("Easepick failed to load, falling back to native date inputs:",e),this.useNative=!0,!1}}async loadEasepick(){return new Promise((e,t)=>{if(window.easepick){e();return}const s=document.createElement("link");s.rel="stylesheet",s.href="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",document.head.appendChild(s);const i=document.createElement("script");i.src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js",i.onload=()=>{window.easepick?e():t(new Error("Easepick not available after loading"))},i.onerror=()=>t(new Error("Failed to load Easepick script")),document.head.appendChild(i)})}async renderTemplate(){const e=this.getInputId(),t=this.getDisplayValue();if(this.useNative)return this.renderNativeTemplate(e);const s=this.startName||(this.name?`${this.name}_start`:""),i=this.endName||(this.name?`${this.name}_end`:""),a=this.currentStartDate?this.formatForOutput(this.currentStartDate):"",r=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";return`
|
|
874
874
|
<div class="date-range-picker-container">
|
|
875
875
|
<input
|
|
876
876
|
type="text"
|
|
@@ -940,7 +940,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
940
940
|
|
|
941
941
|
<div class="date-range-picker-feedback"></div>
|
|
942
942
|
</div>
|
|
943
|
-
`}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):"",
|
|
943
|
+
`}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):"",l=r?this.normalizeDateFromPicker(r):"";this.handleRangeChange(n,l)}),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"],l=parseInt(i)-1;return`${n[l]} ${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();case"date":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();case"date":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):"",l=this.currentEndDate?this.formatForOutput(this.currentEndDate):"";s&&(s.value=n),i&&(i.value=l),a&&(a.value=this.getDisplayValue()),r&&(r.value=this.name||"")}hasError(){return this.currentStartDate&&this.currentEndDate?new Date(this.currentEndDate)<new Date(this.currentStartDate):!1}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=String(e),t.innerHTML}setRange(e,t){if(this.currentStartDate=e,this.currentEndDate=t,this.picker&&this.easepickLoaded){const s=this.normalizeDateValue(e),i=this.normalizeDateValue(t);this.picker.setDateRange(s||null,i||null)}else if(this.useNative){const s=this.element?.querySelector(`[name="${this.name}_start"]`),i=this.element?.querySelector(`[name="${this.name}_end"]`);s&&(s.value=this.formatDate(e,"YYYY-MM-DD")),i&&(i.value=this.formatDate(t,"YYYY-MM-DD"))}else{const s=this.getInputElement();s&&(s.value=this.getDisplayValue())}this.updateHiddenInputs(),this.emit("range:set",{startDate:e,endDate:t})}applyInitialRange(e){if(!e||!(this.currentStartDate||this.currentEndDate))return;const t=this.normalizeDateValue(this.currentStartDate),s=this.normalizeDateValue(this.currentEndDate);(t||s)&&e.setDateRange(t||null,s||null)}normalizeDateValue(e){if(!e&&e!==0)return null;if(e instanceof Date)return isNaN(e)?null:e;const t=String(e).trim();if(!t)return null;if(/^\d{4}-\d{2}-\d{2}$/.test(t)){const[i,a,r]=t.split("-").map(Number),n=new Date(i,a-1,r);return isNaN(n)?null:n}if(/^-?\d+$/.test(t)){const i=Number(t),a=t.length<=10?i*1e3:i,r=new Date(a);return isNaN(r)?null:r}const s=new Date(t);return isNaN(s)?null:s}getRange(){return{start:this.currentStartDate,end:this.currentEndDate,combined:this.getCombinedValue()}}clear(){this.setRange("","")}setStartDate(e){this.setRange(e,this.currentEndDate)}setEndDate(e){this.setRange(this.currentStartDate,e)}getStartDate(){return this.currentStartDate}getEndDate(){return this.currentEndDate}setEnabled(e){this.disabled=!e,this.element?.querySelectorAll("input")?.forEach(s=>{s.disabled=this.disabled})}setReadonly(e){this.readonly=e,this.element?.querySelectorAll('input:not([type="hidden"])')?.forEach(s=>{s.readonly=e})}focus(){const e=this.getInputElement();e&&e.focus()}show(){this.picker&&this.easepickLoaded&&this.picker.show()}hide(){this.picker&&this.easepickLoaded&&this.picker.hide()}getFormValue(){return this.getRange()}async setFormValue(e){if(typeof e=="string")try{const t=JSON.parse(e);this.setRange(t.start,t.end)}catch{this.setRange(e,"")}else e&&typeof e=="object"&&this.setRange(e.start||"",e.end||"")}async onBeforeDestroy(){if(this.picker&&this.easepickLoaded)try{this.picker.destroy()}catch(e){console.warn("Error destroying Easepick range picker instance:",e)}this.picker=null,await super.onBeforeDestroy()}static create(e={}){return new ce(e)}}class de extends v{constructor(e={}){const{name:t,value:s="",placeholder:i="Select or type...",options:a=[],allowCustom:r=!0,showDescription:n=!0,minChars:l=0,maxSuggestions:o=10,disabled:c=!1,readonly:d=!1,required:h=!1,class:f="",inputClass:m="form-control",onSelect:p=null,onChange:g=null,...y}=e;super({tagName:"div",className:`combo-input ${f}`,...y}),this.name=t,this.placeholder=i,this.options=this.normalizeOptions(a),this.allowCustom=r,this.showDescription=n,this.minChars=l,this.maxSuggestions=o,this.disabled=c,this.readonly=d,this.required=h,this.inputClass=m,this.onSelectCallback=p,this.onChangeCallback=g,this.currentValue=s,this.inputValue=this.getDisplayValue(s),this.filteredOptions=[],this.highlightedIndex=-1,this.isOpen=!1,this.selectedOption=this.findOptionByValue(s)}normalizeOptions(e){return Array.isArray(e)?e.map(t=>typeof t=="string"?{value:t,label:t}:typeof t=="object"&&t.value!==void 0?{value:t.value,label:t.label||String(t.value),description:t.description||t.label||"",meta:t.meta||{}}:null).filter(t=>t!==null):[]}findOptionByValue(e){return this.options.find(t=>t.value===e)||null}getDisplayValue(e){const t=this.findOptionByValue(e);return t?t.label:e}async renderTemplate(){return`
|
|
944
944
|
<div class="combo-input-container position-relative">
|
|
945
945
|
<div class="input-wrapper position-relative">
|
|
946
946
|
${this.renderInput()}
|
|
@@ -1050,7 +1050,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1050
1050
|
{{label}}
|
|
1051
1051
|
</button>
|
|
1052
1052
|
{{/items}}
|
|
1053
|
-
`}async onInit(){await super.onInit()}async onAfterRender(){if(await super.onAfterRender(),this.input=this.element.querySelector(".combobox-input"),this.dropdown=this.element.querySelector(".combobox-dropdown"),this.dropdownItems=this.element.querySelector('[data-region="dropdown-items"]'),this.noMatchDiv=this.element.querySelector(".combobox-no-match"),this.value&&this.input){const e=this.options.find(t=>t.value===this.value);e?this.input.value=e.label||e.value:this.allowCustom&&(this.input.value=this.value)}this.renderItems(),this.setupEventListeners()}setupEventListeners(){this.input.addEventListener("focus",()=>this.openDropdown()),this.input.addEventListener("input",e=>this.handleInput(e)),this.input.addEventListener("keydown",e=>this.handleKeydown(e)),document.addEventListener("click",e=>{this.element.contains(e.target)||this.closeDropdown()})}handleInput(e){const t=e.target.value.toLowerCase();this.filteredOptions=this.options.filter(s=>(s.label||s.value).toLowerCase().includes(t)),this.highlightedIndex=-1,this.renderItems(),this.openDropdown(),!this.allowCustom&&this.noMatchDiv&&(this.noMatchDiv.style.display=this.filteredOptions.length===0?"block":"none"),this.value=e.target.value,this.emit("change",{value:this.value})}handleKeydown(e){if(!this.isOpen&&(e.key==="ArrowDown"||e.key==="ArrowUp")){this.openDropdown(),e.preventDefault();return}if(this.isOpen)switch(e.key){case"ArrowDown":e.preventDefault(),this.highlightedIndex=Math.min(this.highlightedIndex+1,this.filteredOptions.length-1),this.renderItems(),this.scrollToHighlighted();break;case"ArrowUp":e.preventDefault(),this.highlightedIndex=Math.max(this.highlightedIndex-1,-1),this.renderItems(),this.scrollToHighlighted();break;case"Enter":e.preventDefault(),this.highlightedIndex>=0&&this.selectItem(this.filteredOptions[this.highlightedIndex]);break;case"Escape":e.preventDefault(),this.closeDropdown();break;case"Tab":this.closeDropdown();break}}scrollToHighlighted(){if(this.highlightedIndex<0)return;const t=this.dropdownItems.querySelectorAll(".combobox-item")[this.highlightedIndex];t&&t.scrollIntoView({block:"nearest"})}openDropdown(){this.disabled||this.isOpen||(this.isOpen=!0,this.dropdown.classList.add("show"),this.input.value===""&&(this.filteredOptions=[...this.options],this.renderItems()))}closeDropdown(){this.isOpen&&(this.isOpen=!1,this.dropdown.classList.remove("show"),this.highlightedIndex=-1,this.allowCustom||!this.options.find(t=>t.value===this.input.value||t.label===this.input.value)&&this.input.value!==""&&(this.input.value=this.value))}selectItem(e){const t=e.value,s=e.label||e.value;this.input.value=s,this.value=t,this.closeDropdown(),this.filteredOptions=[...this.options],this.highlightedIndex=-1,this.emit("change",{value:this.value,label:s})}renderItems(){const e=this.filteredOptions.map((s,i)=>({value:s.value,label:s.label||s.value,index:i,highlighted:i===this.highlightedIndex})),t=E.render(this.itemTemplate,{items:e});this.dropdownItems.innerHTML=t}async onActionComboboxInput(e,t){}async onActionComboboxToggle(e,t){this.isOpen?this.closeDropdown():(this.input.focus(),this.openDropdown())}async onActionSelectItem(e,t){const s=t.getAttribute("data-value"),i=this.options.find(a=>a.value===s);i&&this.selectItem(i)}getValue(){return this.value}setValue(e){if(this.value=e,!this.input)return;const t=this.options.find(s=>s.value===e);t?this.input.value=t.label||t.value:this.allowCustom&&(this.input.value=e)}setFormValue(e){this.setValue(e)}getTemplateData(){return{placeholder:this.placeholder,value:this.input?this.input.value:this.value,disabled:this.disabled,required:this.required,maxHeight:this.maxHeight,allowCustom:this.allowCustom}}}class H extends v{constructor(e={}){const{formConfig:t=e.config,fields:s,model:i=null,data:a={},defaults:r=null,errors:n={},fileHandling:o="base64",autosaveModelField:l=!1,...c}=e;super({tagName:"div",className:"form-view",...c}),q.onFormViewInit?.(this),this.model=i,this.defaults=r||a,this._originalData=a,this.errors=n,this.loading=!1,this.fileHandling=o,this.autosaveModelField=l,this.customComponents=new Map,this.fieldStatusManagers=new Map,this.saveTimeouts=new Map,this.pendingSaveFields=new Map,this.batchSaveTimeout=null,this.isSaving=!1,this.data=this.prepareFormData(),this.formConfig=t||{fields:s||[]},this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:n})}prepareFormData(){const e={...this.defaults};if(this.model)if(this.model.attributes&&typeof this.model.attributes=="object")Object.assign(e,this.model.attributes);else if(typeof this.model.toJSON=="function"){const t=this.model.toJSON();Object.assign(e,t)}else typeof this.model=="object"&&this.model.constructor===Object&&Object.assign(e,this.model);return this._originalData&&Object.assign(e,this._originalData),e}getFormConfig(){const e={...this.formConfig},t=this.getApp();return this.formConfig.fields&&Array.isArray(this.formConfig.fields)?e.fields=this.formConfig.fields.filter(s=>s.permissions?t.activeUser?.hasPermission(s.permissions):!0):e.fields=[],e}async renderTemplate(){return this.formBuilder.buildFormHTML()}async onAfterRender(){await super.onAfterRender(),this.data=this.prepareFormData(),this.populateFormValues(),this.initializeFormComponents(),this.initializeChangeHandlers();const e=this.getFormElement();e&&e.addEventListener("submit",t=>(t.preventDefault(),!1)),q.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&&q.onFieldInit?.(this,a,i)}})};t(this.formConfig?.fields||[])}catch(t){console.warn("FormPlugins.onFieldInit error:",t)}this.element.querySelectorAll("[data-component]").forEach(t=>{t.getAttribute("data-component"),t.getAttribute("data-field")})}initializeChangeHandlers(){if(!this.element)return;const e=this.element.querySelectorAll("input:not([data-action]), select:not([data-action]), textarea:not([data-action])");console.log("FormView: initializeChangeHandlers - found",e.length,"inputs"),e.forEach(t=>{console.log("FormView: Processing input:",t.type,t.name,t.getAttribute("data-change-action")),!(t.hasAttribute("data-component")||t.classList.contains("form-check-input"))&&(t.addEventListener("change",s=>{if(this._isPopulating)return;const i=t.name;if(i){let a=t.value;if(t.type==="checkbox")a=t.checked;else if(t.type==="radio"){if(!t.checked)return}else if(t.multiple&&t.selectedOptions)a=Array.from(t.selectedOptions).map(r=>r.value);else if(t.type==="file"){const r=t.getAttribute("data-change-action");if(r==="image-selected"){this.onChangeImageSelected(s,t);return}else if(r==="file-selected"){this.onChangeFileSelected(s,t);return}}this.handleFieldChange(i,a)}}),(t.type==="text"||t.type==="email"||t.type==="url"||t.tagName==="TEXTAREA")&&t.addEventListener("input",s=>{if(this._isPopulating)return;const i=t.name;i&&this.handleFieldChange(i,t.value)}))})}initializeTagInputs(){this.element.querySelectorAll('[data-field-type="tag"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new oe({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeMultiSelectDropdowns(){this.element.querySelectorAll('[data-field-type="multiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new Re({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Select...",label:r.label,containerId:null});let o=a.value??x.getContextData(this.data,s);o&&n.setFormValue(o),n.render(!0,t),this.customComponents.set(s,n),n.on("change",l=>{this.handleFieldChange(s,l.value)})}catch(s){console.error("MultiSelectDropdown initialization failed:",s)}})}initializeComboBoxes(){this.element.querySelectorAll('[data-field-type="combobox"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new ze({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Type or select...",containerId:null});let o=a.value??x.getContextData(this.data,s);o&&n.setFormValue(o),n.render(!0,t),this.customComponents.set(s,n),n.on("change",l=>{this.handleFieldChange(s,l.value)})}catch(s){console.error("ComboBox initialization failed:",s)}})}initializeCollectionSelects(){this.element.querySelectorAll('[data-field-type="collection"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r||!r.Collection)return;const n=new r.Collection;r.collectionParams&&(n.params={...n.params,...r.collectionParams});const o=new Ne({...a,collection:n,defaultParams:r.defaultParams||null,containerId:null});let l=x.getContextData(this.data,s);l&&o.setFormValue(l),o.render(!0,t),this.customComponents.set(s,o),o.on("change",c=>{this.handleFieldChange(s,c.value)})}catch{}})}initializeCollectionMultiSelects(){this.element.querySelectorAll('[data-field-type="collectionmultiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r||!r.Collection)return;const n=new r.Collection;r.collectionParams&&(n.params={...n.params,...r.collectionParams});const o=new qe({...a,collection:n,defaultParams:r.defaultParams||null,itemTemplate:r.itemTemplate||null,containerId:null});let l=x.getContextData(this.data,s);l&&o.setFormValue(l),o.render(!0,t),this.customComponents.set(s,o),o.on("change",c=>{this.handleFieldChange(s,c.value)})}catch(s){console.error("CollectionMultiSelect initialization failed:",s)}})}initializeDatePickers(){this.element.querySelectorAll('[data-field-type="datepicker"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new le({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeDateRangePickers(){this.element.querySelectorAll('[data-field-type="daterange"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new ce({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.combined)})}catch{}})}initializeComboInputs(){this.element.querySelectorAll('[data-field-type="combo"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new de({...a,containerId:null});let n=x.getContextData(this.data,s);n&&r.setValue(n),r.render(!0,t),this.customComponents.set(s,r),r.on("change",o=>{this.handleFieldChange(s,o.value)}),r.on("select",o=>{this.emit("field:select",{field:s,value:o.value,option:o.option,meta:o.meta})})}catch(s){console.error("ComboInput initialization failed:",s)}})}handleFieldChange(e,t){this._isPopulating||(this.data[e]=t,this.autosaveModelField&&this.model?this.handleFieldSave(e,t):this.model&&this.options.allowModelChange&&(this._isFormDrivenChange=!0,this.model.set(e,t)),this.emit("field:change",{field:e,value:t}),q.onFieldChange?.(this,e,t))}async handleFieldSave(e,t){if(!this.model)return;this.pendingSaveFields.set(e,t),this.getFieldStatusManager(e).showStatus("saving"),this.batchSaveTimeout&&clearTimeout(this.batchSaveTimeout),this.batchSaveTimeout=setTimeout(async()=>{await this.executeBatchSave()},300)}async executeBatchSave(){if(this.isSaving||this.pendingSaveFields.size===0)return;const e=Object.fromEntries(this.pendingSaveFields),t=Array.from(this.pendingSaveFields.keys());try{if(this.isSaving=!0,this.pendingSaveFields.clear(),this.batchSaveTimeout=null,this._isFormDrivenChange=!0,typeof this.model.save=="function"){const s=await this.model.save(e);if(!s||!s.success||s.data&&!s.data.status){const i=s?.data?.error||s?.error||s?.message||"Save failed";this.getApp()?.toast?.error(i),this.revertFields(t),t.forEach(a=>{this.getFieldStatusManager(a).showStatus("error",{message:i})});return}}else Object.entries(e).forEach(([s,i])=>{this.model.set(s,i)});t.forEach(s=>{this.getFieldStatusManager(s).showStatus("saved")})}catch(s){console.error("Batch save error:",s),this.getApp()?.toast?.error(s.message||"An error occurred while saving"),this.revertFields(t),t.forEach(i=>{this.getFieldStatusManager(i).showStatus("error",{message:s.message})})}finally{this.isSaving=!1}}revertFields(e){if(!this.model)return;const t=this._isPopulating;this._isPopulating=!0;try{e.forEach(s=>{const i=this.model.get(s);this.data[s]=i;const a=this.element?.querySelector(`[name="${s}"]`);if(a){const r=this.getFormFieldConfig(s);r?this.setFieldValue(a,r,i):a.type==="checkbox"?a.checked=!!i:a.value=i??""}})}finally{this._isPopulating=t}}getFieldStatusManager(e){if(!this.fieldStatusManagers.has(e)){const t=this.element.querySelector(`[name="${e}"]`);if(t){const s=new Be(t);this.fieldStatusManagers.set(e,s)}}return this.fieldStatusManagers.get(e)}refreshForm(){this.data=this.prepareFormData(),this.element&&this.populateFormValues()}getChangeReason(e,t){if(e instanceof File)return e.size===0||e.name===""||e.name==="blob"?"empty file, no change":`file upload: ${e.name}, ${e.size} bytes`;if(typeof e=="string"&&e.startsWith("data:image/"))return"base64 image upload";if(typeof e=="boolean"||typeof t=="boolean"){const s=!!e;return`boolean: ${t==null?!1:!!t} → ${s}`}return e==null||String(e).trim(),t==null||String(t).trim(),t==null?"was null/undefined, now has value":e==null?"was value, now null/undefined":"text content changed"}setFormData(e){this._originalData={...this._originalData,...e},this.refreshForm()}async onActionSubmitForm(e,t){e.preventDefault();const s=await this.handleSubmit();s.success?(this.data=s.data,this.emit("submit",{data:s.data,result:s.result,form:this,event:e}),!this.model&&this.formConfig.onSubmit&&typeof this.formConfig.onSubmit=="function"&&await this.formConfig.onSubmit(s.data,this)):this.emit("error",{error:s.error,result:s,form:this})}async onActionResetForm(e,t){const s=this.getFormElement();s&&(s.reset(),this.data={},this.clearAllErrors(),this.emit("reset",{form:this,event:e}))}async onActionClickImageUpload(e,t){console.log("FormView: onActionClickImageUpload called"),console.log("FormView: element:",t);const s=t.getAttribute("data-field-id");if(console.log("FormView: fieldId:",s),!s){console.error("FormView: No fieldId attribute found");return}const i=this.element.querySelector(`#${s}`);console.log("FormView: fileInput:",i),i&&!i.disabled?(i.click(),console.log("FormView: fileInput.click() called")):i?console.log("FormView: fileInput is disabled"):console.error("FormView: fileInput not found for fieldId:",s)}async onActionRemoveImage(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",i.dispatchEvent(new Event("change",{bubbles:!0}))),delete this.data[s],this.emit("change",{field:s,value:null,form:this}),await this.updateField(s)}async onActionClearColor(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",this.handleFieldChange(s,""),await this.updateField(s))}async onActionPreviewHtml(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector(`#${s}`);if(!i)return;const a=i.value||"";(await Promise.resolve().then(()=>_)).default.showHtmlPreview({html:a,title:"HTML Preview"})}async onActionSelectButtonOption(e,t){const s=t.getAttribute("data-field"),i=t.getAttribute("data-value");if(!s||!i)return;this.data[s]=i;const a=t.closest(".btn-group");a&&(a.querySelectorAll("button").forEach(n=>{n.classList.remove("active"),n.classList.add("btn-outline-primary"),n.classList.remove("btn-primary")}),t.classList.add("active"),t.classList.remove("btn-outline-primary"),t.classList.add("btn-primary")),this.emit("field:changed",{field:s,value:i,form:this}),this.emit("change",{field:s,value:i,form:this}),this.emit("form:changed",await this.getFormData())}async onActionApplyFilter(e,t){const s=t.closest(".dropdown"),i=s?.querySelectorAll('input[type="checkbox"]');if(!i||i.length===0)return;const a=i[0].getAttribute("data-field");if(!a)return;const r=[];i.forEach(o=>{o.checked&&r.push(o.value)}),this.data[a]=r;const n=s.querySelector('[data-bs-toggle="dropdown"]');n&&window.bootstrap?.Dropdown&&window.bootstrap.Dropdown.getInstance(n)?.hide(),this.emit("field:changed",{field:a,value:r,form:this}),this.emit("change",{field:a,value:r,form:this}),this.emit("form:changed",await this.getFormData())}async onChangeValidateField(e,t){const s=t.name;if(s){const i=t.value;this.handleFieldChange(s,i),this.validateField(s)}}async onChangeToggleSwitch(e,t){const s=t.getAttribute("data-field");if(s){const i=t.checked;this.handleFieldChange(s,i),this.emit("switch:toggle",{field:s,checked:i,form:this})}}async onChangeImageSelected(e,t){console.log("FormView: onChangeImageSelected called"),console.log("FormView: element:",t),console.log("FormView: element.files:",t.files);const s=t.getAttribute("data-field"),i=t.files[0];if(console.log("FormView: fieldName:",s),console.log("FormView: file:",i),s&&i){console.log("FormView: fieldName and file exist, processing...");const a=this.findFieldConfig(s);console.log("FormView: fieldConfig:",a);const r=URL.createObjectURL(i);if(console.log("FormView: previewUrl created:",r),a&&a.imageSize){console.log("FormView: Image cropping is required, imageSize:",a.imageSize);try{const n=window.MOJO?.plugins?.ImageCropView;if(console.log("FormView: ImageCropView available?",!!n),!n){console.log("FormView: ImageCropView not available, falling back to normal handling"),this.data[s]=i,await this.updateImagePreview(s,r),this.emit("image:selected",{field:s,file:i,form:this});return}const o=await n.showDialog(r,{title:`Crop ${a.label||s}`,cropAndScale:a.imageSize,size:"lg"});if(o.action==="crop"&&o.data){const c=await(await fetch(o.data)).blob(),d=new File([c],i.name,{type:i.type||"image/png"});this.data[s]=d,await this.updateImagePreview(s,o.data),this.emit("image:selected",{field:s,file:d,originalFile:i,cropped:!0,cropData:o.cropData,form:this}),this.emit("change",{field:s,value:d,form:this})}else t.value=""}catch(n){console.error("FormView: Error during image cropping:",n),console.log("FormView: Falling back to normal image handling after error"),this.data[s]=i,await this.updateImagePreview(s,r),this.emit("image:selected",{field:s,file:i,form:this}),this.emit("change",{field:s,value:i,form:this})}}else console.log("FormView: Normal image handling (no cropping)"),this.data[s]=i,console.log("FormView: File stored in this.data["+s+"]"),await this.updateImagePreview(s,r),console.log("FormView: updateImagePreview completed"),this.emit("image:selected",{field:s,file:i,form:this}),console.log("FormView: image:selected event emitted")}else console.log("FormView: Missing fieldName or file - not processing")}async onChangeFileSelected(e,t){const s=Array.from(t.files);t.multiple?this.data[t.name]=t.files:this.data[t.name]=s[0]||null,this.emit("file:selected",{field:t.name,files:s,form:this}),this.emit("change",{field:t.name,value:s,form:this})}async onChangeRangeChanged(e,t){const s=t.name,i=t.value,a=t.getAttribute("data-target");if(a){const r=this.element.querySelector(`#${a}`);r&&(r.textContent=i)}s&&this.handleFieldChange(s,i),this.emit("range:changed",{field:s,value:i,form:this})}async onChangeFilterSearch(e,t){const s=t.value;this.emit("search",{query:s,field:t.name,form:this})}async onChangeFilterSelectOptions(e,t){const s=t.value.toLowerCase(),i=t.getAttribute("data-target"),a=i?this.element.querySelector(`#${i}`):null;a&&a.querySelectorAll("option").forEach(n=>{const o=n.textContent.toLowerCase();n.style.display=o.includes(s)?"":"none"})}async onFileDrop(e,t,s){const i=t.target.closest(".image-drop-zone");if(!i)return;const a=i.getAttribute("data-field");if(!a)return;const r=e[0],n=this.element.querySelector(`input[name="${a}"]`);if(n){const l=new DataTransfer;l.items.add(r),n.files=l.files,n.dispatchEvent(new Event("change",{bubbles:!0}))}this.data[a]=r;const o=URL.createObjectURL(r);await this.updateImagePreview(a,o),this.emit("image:dropped",{field:a,file:r,form:this})}async onFileDropError(e,t,s){this.showError(`File upload error: ${e.message}`),this.emit("file:error",{error:e,files:s,form:this})}getFormElement(){return this.element?this.element.querySelector("form"):null}getFormFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}}return null};return t(this.formConfig.fields||[])}async getFormData(){const e=this.getFormElement();if(!e)return this.fileHandling==="multipart"?new FormData:{};if(this.fileHandling==="multipart"){const t=new FormData(e);for(const[s,i]of Object.entries(this.data))if(i instanceof File)t.set(s,i);else if(i instanceof FileList)for(let a=0;a<i.length;a++)t.append(`${s}[${a}]`,i[a]);return t}else{const t=new FormData(e),s={};for(const[n,o]of t.entries())s[n]?(Array.isArray(s[n])||(s[n]=[s[n]]),s[n].push(o)):s[n]=o;e.querySelectorAll('input[type="checkbox"]').forEach(n=>{s[n.name]=n.checked}),e.querySelectorAll('input[type="number"]').forEach(n=>{if(n.name&&s[n.name]!==void 0&&s[n.name]!==""){const o=Number(s[n.name]);isNaN(o)||(s[n.name]=o)}}),this.formConfig.fields?.forEach(n=>{if(n.type==="select"&&n.name&&s[n.name]!==void 0){const o=this.getFormFieldConfig(n.name);if(o?.options&&Array.isArray(o.options)&&o.options.every(c=>{const d=typeof c=="object"?c.value:c;return d===""||!isNaN(Number(d))})&&s[n.name]!==""){const c=Number(s[n.name]);isNaN(c)||(s[n.name]=c)}}}),e.querySelectorAll('[data-field-type="json"]').forEach(n=>{try{s[n.name]=JSON.parse(n.value)}catch{s[n.name]=n.value}}),this.customComponents.forEach((n,o)=>{n.getFormValue?s[o]=n.getFormValue():n.getValue&&(s[o]=n.getValue())});for(const[n,o]of Object.entries(this.data))if(o instanceof File)try{s[n]=await this.fileToBase64(o)}catch{s[n]=null}else if(o instanceof FileList){const l=[];for(let c=0;c<o.length;c++)try{l.push(await this.fileToBase64(o[c]))}catch{l.push(null)}s[n]=l}return s}}_onModelChange(){this.isSaving||(this.data=this.prepareFormData(),this.isMounted()&&(this._isFormDrivenChange||this.syncFormWithModel(),this._isFormDrivenChange=!1))}syncFormWithModel(){!this.model||!this.element||this.formDataMatchesModelData(this.data)||this.populateFormValues()}formDataMatchesModelData(e){if(!this.formConfig?.fields||!this.element)return!0;for(const t of this.formConfig.fields)if(t.type==="group"&&t.fields){for(const s of t.fields)if(!this.fieldValueMatchesModel(s,e))return!1}else if(!this.fieldValueMatchesModel(t,e))return!1;return!0}fieldValueMatchesModel(e,t){if(!e.name)return!0;const s=this.element.querySelector(`[name="${e.name}"]`);if(!s)return!0;const i=this.getFieldCurrentValue(s,e),a=x.getContextData(t,e.name);return this.valuesAreDifferent(i,a)===!1}getFieldCurrentValue(e,t){switch(t.type){case"checkbox":case"toggle":case"switch":return e.checked;case"radio":const s=this.element.querySelector(`[name="${t.name}"]:checked`);return s?s.value:"";case"select":return e.multiple?Array.from(e.selectedOptions).map(i=>i.value):e.value;case"file":case"image":return null;case"json":try{return e.value?JSON.parse(e.value):null}catch{return e.value}default:return e.value}}setFieldValue(e,t,s){switch(t.type){case"checkbox":case"toggle":case"switch":e.checked=!!s;break;case"radio":const i=this.element.querySelector(`[name="${t.name}"][value="${s}"]`);i&&(i.checked=!0);break;case"select":e.multiple&&Array.isArray(s)?Array.from(e.options).forEach(a=>{a.selected=s.includes(a.value)}):e.value=s??"";break;case"file":case"image":break;case"json":if(typeof s=="object"&&s!==null)try{e.value=JSON.stringify(s,null,2)}catch{e.value="{}"}else typeof s=="string"?e.value=s:e.value=String(s||"");break;default:e.value=s||"";break}e.dispatchEvent(new Event("change",{bubbles:!0}))}setDefaults(e){this.defaults={...this.defaults,...e},this.refreshForm()}async handleSubmit(){try{const e=await this.getFormData();if(this.formConfig.validateOnSubmit!==!1&&!this.validate())return this.focusFirstError(),{success:!1,data:e,error:"Form validation failed"};if(this.model&&typeof this.model.save=="function"){const t=await this.saveModel(e);if(t&&t.success!==!1)return{success:!0,data:e,result:t};{const s=t?.message||t?.error||"Save failed. Please try again.";return{success:!1,data:e,result:t,error:s}}}else return e}catch(e){return{success:!1,error:e.message||"An error occurred while submitting the form"}}}async saveModel(e=null){if(!this.model||typeof this.model.save!="function")throw new Error("No model available for saving");e||(e=await this.getFormData());const t=this.getChangedData(e);if(!t||Object.keys(t).length===0)return{success:!0,message:"No changes to save",data:e};try{return this._isFormDrivenChange=!0,await this.model.save(t)}catch(s){throw s}}getChangedData(e){if(!this.model)return e;const t=this.getOriginalModelData();let s;return e instanceof FormData?s=this.getChangedFormData(e,t):s=this.getChangedObjectData(e,t),s}getOriginalModelData(){return this.model.attributes?this.model.attributes:typeof this.model.toJSON=="function"?this.model.toJSON():{}}getChangedFormData(e,t){const s=new FormData;let i=!1;for(const[a,r]of e.entries())if(r instanceof File)r.size===0||r.name===""||r.name==="blob"||(s.set(a,r),i=!0);else{const n=t[a];r!==n&&r!==String(n)&&(s.set(a,r),i=!0)}return i?s:null}getChangedObjectData(e,t){const s={};let i=!1;const a=new Set([...Object.keys(t),...Object.keys(e)]),r=(n,o)=>o.split(".").reduce((l,c)=>l&&typeof l=="object"?l[c]:void 0,n);for(const n of a){const o=this.findFieldConfig(n);if(!o)continue;const l=e[n],c=r(t,n),d=o.type||"text";this.valuesAreDifferent(l,c,d,o)&&(s[n]=l,i=!0)}return i?s:null}valuesAreDifferent(e,t,s="text",i={}){if(e instanceof File)return e.size>0&&e.name!==""&&e.name!=="blob";if(typeof e=="string"&&e.startsWith("data:image/"))return!0;if(s==="collection"&&typeof t=="object"&&t!==null&&t!==void 0&&typeof e=="string"){if(e==="0")return t!==null;const n=i.valueField||"id";if(t[n]==e)return!1}if(s==="switch"||s==="checkbox"||s==="toggle")return!!e!==!!t;const a=e==null?"":String(e).trim(),r=t==null?"":String(t).trim();return a!==r}validate(){const e=this.getFormElement();if(!e)return!1;const t=e.checkValidity();return t||e.classList.add("was-validated"),t}validateField(e){const t=this.getFormElement();if(!t)return!1;const s=t.elements[e];if(!s)return!1;const i=s.checkValidity();return i?(s.classList.remove("is-invalid"),s.classList.add("is-valid"),delete this.errors[e]):(s.classList.remove("is-valid"),s.classList.add("is-invalid"),this.errors[e]=s.validationMessage),i}focusFirstError(){const e=this.getFormElement();if(!e)return;const t=e.querySelector(":invalid");if(!t)return;const s=t.closest(".tab-pane");if(s&&!s.classList.contains("active")){const i=s.id,a=e.querySelector(`[role="tab"][aria-controls="${i}"], [data-bs-target="#${i}"]`);if(a){const r=window.bootstrap?.Tab?.getOrCreateInstance?window.bootstrap.Tab.getOrCreateInstance(a):null;r&&typeof r.show=="function"?r.show():(e.querySelectorAll('[role="tab"].nav-link').forEach(l=>{const c=l===a;l.classList.toggle("active",c),l.setAttribute("aria-selected",c?"true":"false")}),e.querySelectorAll(".tab-pane").forEach(l=>l.classList.remove("show","active")),s.classList.add("show","active"))}}t.focus(),t.scrollIntoView({behavior:"smooth",block:"center"})}clearAllErrors(){const e=this.getFormElement();if(!e)return;this.errors={},e.classList.remove("was-validated"),e.querySelectorAll(".is-invalid").forEach(i=>i.classList.remove("is-invalid")),e.querySelectorAll(".is-valid").forEach(i=>i.classList.remove("is-valid"))}setLoading(e){this.loading=e;const t=this.getFormElement();if(!t)return;const s=t.querySelectorAll("input, select, textarea, button"),i=t.querySelector('button[type="submit"]');if(e)s.forEach(a=>a.disabled=!0),i&&(i.innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Loading...');else if(s.forEach(a=>a.disabled=!1),i){const a=this.formConfig.options?.submitButton||"Submit";i.innerHTML=typeof a=="string"?a:"Submit"}}showError(e){if(this.emit("error",{message:e,form:this}),this.element){this.element.querySelectorAll(".alert").forEach(i=>i.remove());const s=document.createElement("div");s.className="alert alert-danger alert-dismissible fade show",s.innerHTML=`
|
|
1053
|
+
`}async onInit(){await super.onInit()}async onAfterRender(){if(await super.onAfterRender(),this.input=this.element.querySelector(".combobox-input"),this.dropdown=this.element.querySelector(".combobox-dropdown"),this.dropdownItems=this.element.querySelector('[data-region="dropdown-items"]'),this.noMatchDiv=this.element.querySelector(".combobox-no-match"),this.value&&this.input){const e=this.options.find(t=>t.value===this.value);e?this.input.value=e.label||e.value:this.allowCustom&&(this.input.value=this.value)}this.renderItems(),this.setupEventListeners()}setupEventListeners(){this.input.addEventListener("focus",()=>this.openDropdown()),this.input.addEventListener("input",e=>this.handleInput(e)),this.input.addEventListener("keydown",e=>this.handleKeydown(e)),document.addEventListener("click",e=>{this.element.contains(e.target)||this.closeDropdown()})}handleInput(e){const t=e.target.value.toLowerCase();this.filteredOptions=this.options.filter(s=>(s.label||s.value).toLowerCase().includes(t)),this.highlightedIndex=-1,this.renderItems(),this.openDropdown(),!this.allowCustom&&this.noMatchDiv&&(this.noMatchDiv.style.display=this.filteredOptions.length===0?"block":"none"),this.value=e.target.value,this.emit("change",{value:this.value})}handleKeydown(e){if(!this.isOpen&&(e.key==="ArrowDown"||e.key==="ArrowUp")){this.openDropdown(),e.preventDefault();return}if(this.isOpen)switch(e.key){case"ArrowDown":e.preventDefault(),this.highlightedIndex=Math.min(this.highlightedIndex+1,this.filteredOptions.length-1),this.renderItems(),this.scrollToHighlighted();break;case"ArrowUp":e.preventDefault(),this.highlightedIndex=Math.max(this.highlightedIndex-1,-1),this.renderItems(),this.scrollToHighlighted();break;case"Enter":e.preventDefault(),this.highlightedIndex>=0&&this.selectItem(this.filteredOptions[this.highlightedIndex]);break;case"Escape":e.preventDefault(),this.closeDropdown();break;case"Tab":this.closeDropdown();break}}scrollToHighlighted(){if(this.highlightedIndex<0)return;const t=this.dropdownItems.querySelectorAll(".combobox-item")[this.highlightedIndex];t&&t.scrollIntoView({block:"nearest"})}openDropdown(){this.disabled||this.isOpen||(this.isOpen=!0,this.dropdown.classList.add("show"),this.input.value===""&&(this.filteredOptions=[...this.options],this.renderItems()))}closeDropdown(){this.isOpen&&(this.isOpen=!1,this.dropdown.classList.remove("show"),this.highlightedIndex=-1,this.allowCustom||!this.options.find(t=>t.value===this.input.value||t.label===this.input.value)&&this.input.value!==""&&(this.input.value=this.value))}selectItem(e){const t=e.value,s=e.label||e.value;this.input.value=s,this.value=t,this.closeDropdown(),this.filteredOptions=[...this.options],this.highlightedIndex=-1,this.emit("change",{value:this.value,label:s})}renderItems(){const e=this.filteredOptions.map((s,i)=>({value:s.value,label:s.label||s.value,index:i,highlighted:i===this.highlightedIndex})),t=E.render(this.itemTemplate,{items:e});this.dropdownItems.innerHTML=t}async onActionComboboxInput(e,t){}async onActionComboboxToggle(e,t){this.isOpen?this.closeDropdown():(this.input.focus(),this.openDropdown())}async onActionSelectItem(e,t){const s=t.getAttribute("data-value"),i=this.options.find(a=>a.value===s);i&&this.selectItem(i)}getValue(){return this.value}setValue(e){if(this.value=e,!this.input)return;const t=this.options.find(s=>s.value===e);t?this.input.value=t.label||t.value:this.allowCustom&&(this.input.value=e)}setFormValue(e){this.setValue(e)}getTemplateData(){return{placeholder:this.placeholder,value:this.input?this.input.value:this.value,disabled:this.disabled,required:this.required,maxHeight:this.maxHeight,allowCustom:this.allowCustom}}}class H extends v{constructor(e={}){const{formConfig:t=e.config,fields:s,model:i=null,data:a={},defaults:r=null,errors:n={},fileHandling:l="base64",autosaveModelField:o=!1,...c}=e;super({tagName:"div",className:"form-view",...c}),q.onFormViewInit?.(this),this.model=i,this.defaults=r||a,this._originalData=a,this.errors=n,this.loading=!1,this.fileHandling=l,this.autosaveModelField=o,this.customComponents=new Map,this.fieldStatusManagers=new Map,this.saveTimeouts=new Map,this.pendingSaveFields=new Map,this.batchSaveTimeout=null,this.isSaving=!1,this.data=this.prepareFormData(),this.formConfig=t||{fields:s||[]},this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:n})}prepareFormData(){const e={...this.defaults};if(this.model)if(this.model.attributes&&typeof this.model.attributes=="object")Object.assign(e,this.model.attributes);else if(typeof this.model.toJSON=="function"){const t=this.model.toJSON();Object.assign(e,t)}else typeof this.model=="object"&&this.model.constructor===Object&&Object.assign(e,this.model);return this._originalData&&Object.assign(e,this._originalData),e}getFormConfig(){const e={...this.formConfig},t=this.getApp();return this.formConfig.fields&&Array.isArray(this.formConfig.fields)?e.fields=this.formConfig.fields.filter(s=>s.permissions?t.activeUser?.hasPermission(s.permissions):!0):e.fields=[],e}async renderTemplate(){return this.formBuilder.buildFormHTML()}async onAfterRender(){await super.onAfterRender(),this.data=this.prepareFormData(),this.populateFormValues(),this.initializeFormComponents(),this.initializeChangeHandlers();const e=this.getFormElement();e&&e.addEventListener("submit",t=>(t.preventDefault(),!1)),q.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&&q.onFieldInit?.(this,a,i)}})};t(this.formConfig?.fields||[])}catch(t){console.warn("FormPlugins.onFieldInit error:",t)}this.element.querySelectorAll("[data-component]").forEach(t=>{t.getAttribute("data-component"),t.getAttribute("data-field")})}initializeChangeHandlers(){if(!this.element)return;const e=this.element.querySelectorAll("input:not([data-action]), select:not([data-action]), textarea:not([data-action])");console.log("FormView: initializeChangeHandlers - found",e.length,"inputs"),e.forEach(t=>{console.log("FormView: Processing input:",t.type,t.name,t.getAttribute("data-change-action")),!(t.hasAttribute("data-component")||t.classList.contains("form-check-input"))&&(t.addEventListener("change",s=>{if(this._isPopulating)return;const i=t.name;if(i){let a=t.value;if(t.type==="checkbox")a=t.checked;else if(t.type==="radio"){if(!t.checked)return}else if(t.multiple&&t.selectedOptions)a=Array.from(t.selectedOptions).map(r=>r.value);else if(t.type==="file"){const r=t.getAttribute("data-change-action");if(r==="image-selected"){this.onChangeImageSelected(s,t);return}else if(r==="file-selected"){this.onChangeFileSelected(s,t);return}}this.handleFieldChange(i,a)}}),(t.type==="text"||t.type==="email"||t.type==="url"||t.tagName==="TEXTAREA")&&t.addEventListener("input",s=>{if(this._isPopulating)return;const i=t.name;i&&this.handleFieldChange(i,t.value)}))})}initializeTagInputs(){this.element.querySelectorAll('[data-field-type="tag"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new le({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeMultiSelectDropdowns(){this.element.querySelectorAll('[data-field-type="multiselect"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new Re({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Select...",label:r.label,containerId:null});let l=a.value??x.getContextData(this.data,s);l&&n.setFormValue(l),n.render(!0,t),this.customComponents.set(s,n),n.on("change",o=>{this.handleFieldChange(s,o.value)})}catch(s){console.error("MultiSelectDropdown initialization failed:",s)}})}initializeComboBoxes(){this.element.querySelectorAll('[data-field-type="combobox"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=this.getFormFieldConfig(s);if(!r)return;const n=new ze({...a,options:r.options||[],placeholder:r.placeholder||a.placeholder||"Type or select...",containerId:null});let l=a.value??x.getContextData(this.data,s);l&&n.setFormValue(l),n.render(!0,t),this.customComponents.set(s,n),n.on("change",o=>{this.handleFieldChange(s,o.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 l=new Ne({...a,collection:n,defaultParams:r.defaultParams||null,containerId:null});let o=x.getContextData(this.data,s);o&&l.setFormValue(o),l.render(!0,t),this.customComponents.set(s,l),l.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 l=new qe({...a,collection:n,defaultParams:r.defaultParams||null,itemTemplate:r.itemTemplate||null,containerId:null});let o=x.getContextData(this.data,s);o&&l.setFormValue(o),l.render(!0,t),this.customComponents.set(s,l),l.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 oe({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.value)})}catch{}})}initializeDateRangePickers(){this.element.querySelectorAll('[data-field-type="daterange"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new ce({...a,containerId:null});r.render(!0,t),this.customComponents.set(s,r),r.on("change",n=>{this.handleFieldChange(s,n.combined)})}catch{}})}initializeComboInputs(){this.element.querySelectorAll('[data-field-type="combo"]').forEach(t=>{try{const s=t.getAttribute("data-field-name"),i=t.getAttribute("data-field-config"),a=JSON.parse(i),r=new de({...a,containerId:null});let n=x.getContextData(this.data,s);n&&r.setValue(n),r.render(!0,t),this.customComponents.set(s,r),r.on("change",l=>{this.handleFieldChange(s,l.value)}),r.on("select",l=>{this.emit("field:select",{field:s,value:l.value,option:l.option,meta:l.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}),q.onFieldChange?.(this,e,t))}async handleFieldSave(e,t){if(!this.model)return;this.pendingSaveFields.set(e,t),this.getFieldStatusManager(e).showStatus("saving"),this.batchSaveTimeout&&clearTimeout(this.batchSaveTimeout),this.batchSaveTimeout=setTimeout(async()=>{await this.executeBatchSave()},300)}async executeBatchSave(){if(this.isSaving||this.pendingSaveFields.size===0)return;const e=Object.fromEntries(this.pendingSaveFields),t=Array.from(this.pendingSaveFields.keys());try{if(this.isSaving=!0,this.pendingSaveFields.clear(),this.batchSaveTimeout=null,this._isFormDrivenChange=!0,typeof this.model.save=="function"){const s=await this.model.save(e);if(!s||!s.success||s.data&&!s.data.status){const i=s?.data?.error||s?.error||s?.message||"Save failed";this.getApp()?.toast?.error(i),this.revertFields(t),t.forEach(a=>{this.getFieldStatusManager(a).showStatus("error",{message:i})});return}}else Object.entries(e).forEach(([s,i])=>{this.model.set(s,i)});t.forEach(s=>{this.getFieldStatusManager(s).showStatus("saved")})}catch(s){console.error("Batch save error:",s),this.getApp()?.toast?.error(s.message||"An error occurred while saving"),this.revertFields(t),t.forEach(i=>{this.getFieldStatusManager(i).showStatus("error",{message:s.message})})}finally{this.isSaving=!1}}revertFields(e){if(!this.model)return;const t=this._isPopulating;this._isPopulating=!0;try{e.forEach(s=>{const i=this.model.get(s);this.data[s]=i;const a=this.element?.querySelector(`[name="${s}"]`);if(a){const r=this.getFormFieldConfig(s);r?this.setFieldValue(a,r,i):a.type==="checkbox"?a.checked=!!i:a.value=i??""}})}finally{this._isPopulating=t}}getFieldStatusManager(e){if(!this.fieldStatusManagers.has(e)){const t=this.element.querySelector(`[name="${e}"]`);if(t){const s=new Be(t);this.fieldStatusManagers.set(e,s)}}return this.fieldStatusManagers.get(e)}refreshForm(){this.data=this.prepareFormData(),this.element&&this.populateFormValues()}getChangeReason(e,t){if(e instanceof File)return e.size===0||e.name===""||e.name==="blob"?"empty file, no change":`file upload: ${e.name}, ${e.size} bytes`;if(typeof e=="string"&&e.startsWith("data:image/"))return"base64 image upload";if(typeof e=="boolean"||typeof t=="boolean"){const s=!!e;return`boolean: ${t==null?!1:!!t} → ${s}`}return e==null||String(e).trim(),t==null||String(t).trim(),t==null?"was null/undefined, now has value":e==null?"was value, now null/undefined":"text content changed"}setFormData(e){this._originalData={...this._originalData,...e},this.refreshForm()}async onActionSubmitForm(e,t){e.preventDefault();const s=await this.handleSubmit();s.success?(this.data=s.data,this.emit("submit",{data:s.data,result:s.result,form:this,event:e}),!this.model&&this.formConfig.onSubmit&&typeof this.formConfig.onSubmit=="function"&&await this.formConfig.onSubmit(s.data,this)):this.emit("error",{error:s.error,result:s,form:this})}async onActionResetForm(e,t){const s=this.getFormElement();s&&(s.reset(),this.data={},this.clearAllErrors(),this.emit("reset",{form:this,event:e}))}async onActionClickImageUpload(e,t){console.log("FormView: onActionClickImageUpload called"),console.log("FormView: element:",t);const s=t.getAttribute("data-field-id");if(console.log("FormView: fieldId:",s),!s){console.error("FormView: No fieldId attribute found");return}const i=this.element.querySelector(`#${s}`);console.log("FormView: fileInput:",i),i&&!i.disabled?(i.click(),console.log("FormView: fileInput.click() called")):i?console.log("FormView: fileInput is disabled"):console.error("FormView: fileInput not found for fieldId:",s)}async onActionRemoveImage(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",i.dispatchEvent(new Event("change",{bubbles:!0}))),delete this.data[s],this.emit("change",{field:s,value:null,form:this}),await this.updateField(s)}async onActionClearColor(e,t){const s=t.getAttribute("data-field");if(!s)return;const i=this.element.querySelector(`input[name="${s}"]`);i&&(i.value="",this.handleFieldChange(s,""),await this.updateField(s))}async onActionPreviewHtml(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector(`#${s}`);if(!i)return;const a=i.value||"";(await Promise.resolve().then(()=>_)).default.showHtmlPreview({html:a,title:"HTML Preview"})}async onActionSelectButtonOption(e,t){const s=t.getAttribute("data-field"),i=t.getAttribute("data-value");if(!s||!i)return;this.data[s]=i;const a=t.closest(".btn-group");a&&(a.querySelectorAll("button").forEach(n=>{n.classList.remove("active"),n.classList.add("btn-outline-primary"),n.classList.remove("btn-primary")}),t.classList.add("active"),t.classList.remove("btn-outline-primary"),t.classList.add("btn-primary")),this.emit("field:changed",{field:s,value:i,form:this}),this.emit("change",{field:s,value:i,form:this}),this.emit("form:changed",await this.getFormData())}async onActionApplyFilter(e,t){const s=t.closest(".dropdown"),i=s?.querySelectorAll('input[type="checkbox"]');if(!i||i.length===0)return;const a=i[0].getAttribute("data-field");if(!a)return;const r=[];i.forEach(l=>{l.checked&&r.push(l.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 l=await n.showDialog(r,{title:`Crop ${a.label||s}`,cropAndScale:a.imageSize,size:"lg"});if(l.action==="crop"&&l.data){const c=await(await fetch(l.data)).blob(),d=new File([c],i.name,{type:i.type||"image/png"});this.data[s]=d,await this.updateImagePreview(s,l.data),this.emit("image:selected",{field:s,file:d,originalFile:i,cropped:!0,cropData:l.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 l=n.textContent.toLowerCase();n.style.display=l.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 o=new DataTransfer;o.items.add(r),n.files=o.files,n.dispatchEvent(new Event("change",{bubbles:!0}))}this.data[a]=r;const l=URL.createObjectURL(r);await this.updateImagePreview(a,l),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,l]of t.entries())s[n]?(Array.isArray(s[n])||(s[n]=[s[n]]),s[n].push(l)):s[n]=l;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 l=Number(s[n.name]);isNaN(l)||(s[n.name]=l)}}),this.formConfig.fields?.forEach(n=>{if(n.type==="select"&&n.name&&s[n.name]!==void 0){const l=this.getFormFieldConfig(n.name);if(l?.options&&Array.isArray(l.options)&&l.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,l)=>{n.getFormValue?s[l]=n.getFormValue():n.getValue&&(s[l]=n.getValue())});for(const[n,l]of Object.entries(this.data))if(l instanceof File)try{s[n]=await this.fileToBase64(l)}catch{s[n]=null}else if(l instanceof FileList){const o=[];for(let c=0;c<l.length;c++)try{o.push(await this.fileToBase64(l[c]))}catch{o.push(null)}s[n]=o}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,l)=>l.split(".").reduce((o,c)=>o&&typeof o=="object"?o[c]:void 0,n);for(const n of a){const l=this.findFieldConfig(n);if(!l)continue;const o=e[n],c=r(t,n),d=l.type||"text";this.valuesAreDifferent(o,c,d,l)&&(s[n]=o,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(o=>{const c=o===a;o.classList.toggle("active",c),o.setAttribute("aria-selected",c?"true":"false")}),e.querySelectorAll(".tab-pane").forEach(o=>o.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=`
|
|
1054
1054
|
${e}
|
|
1055
1055
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
1056
1056
|
`,this.element.insertBefore(s,this.element.firstChild),setTimeout(()=>{s.parentNode&&s.remove()},5e3)}}async updateField(e){this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async updateImagePreview(e,t){const s=this.element.querySelector(`[data-field="${e}"].image-drop-zone`);if(!s)return;let i=s.querySelector("img");const a=s.querySelector(".bi-image")?.parentElement,r=s.getAttribute("data-field-id");if(t){if(i)i.src=t;else{const n=`${r}_preview`;s.innerHTML=`
|
|
@@ -1067,7 +1067,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1067
1067
|
style="opacity: 0.8;">
|
|
1068
1068
|
<i class="bi bi-x"></i>
|
|
1069
1069
|
</button>
|
|
1070
|
-
`}a&&(a.style.display="none")}}findFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}if(i.tabs&&Array.isArray(i.tabs)){for(const a of i.tabs)if(a.fields&&Array.isArray(a.fields)){const r=t(a.fields);if(r)return r}}}return null};return t(this.formConfig.fields||[])}async fileToBase64(e){return new Promise((t,s)=>{const i=new FileReader;i.onload=()=>t(i.result),i.onerror=s,i.readAsDataURL(e)})}hasFiles(e){if(e instanceof FormData){for(const[t,s]of e.entries())if(s instanceof File)return!0;return!1}else{for(const t of Object.values(e))if(t instanceof File||Array.isArray(t)&&t.some(s=>s instanceof File))return!0;return!1}}reset(){const e=this.getFormElement();e&&e.reset(),this.data={},this.errors={},this.clearAllErrors(),this.emit("reset",{form:this})}async updateConfig(e){this.formConfig={...this.formConfig,...e},this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async onBeforeDestroy(){const e=[];for(const t of this.customComponents.values())t.destroy&&e.push(t.destroy());await Promise.all(e),this.customComponents.clear(),Object.values(this.data).forEach(t=>{typeof t=="string"&&t.startsWith("blob:")&&URL.revokeObjectURL(t)}),await super.onBeforeDestroy()}initializePasswordFields(){if(!this.element)return;this.element.querySelectorAll('input[data-field-type="password"], input[type="password"]').forEach(t=>{this.updatePasswordStrengthUI(t);const s=a=>{this.updatePasswordStrengthUI(a.target)};t.addEventListener("input",s);const i=a=>{if(typeof a.getModifierState=="function"){const r=a.getModifierState("CapsLock");this.updateCapsLockWarning(t,!!r)}};t.addEventListener("keydown",i),t.addEventListener("keyup",i),this.updateCapsLockWarning(t,!1)})}async onActionTogglePassword(e,t){e.preventDefault();const s=t.getAttribute("data-target");let i=null;if(s&&(i=this.element.querySelector("#"+s)),!i){const n=t.closest(".input-group");n&&(i=n.querySelector('input[type="password"], input[data-field-type="password"], input[type="text"]'))}if(!i)return;const a=i.type==="password";i.type=a?"text":"password",t.setAttribute("aria-pressed",a?"true":"false"),t.setAttribute("aria-label",a?"Hide password":"Show password");const r=t.querySelector("i");r&&(r.classList.toggle("bi-eye",!a),r.classList.toggle("bi-eye-slash",a)),i.focus();try{const n=i.value?.length??0;i.setSelectionRange(n,n)}catch{}}async onActionCopyToClipboard(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector("#"+s);if(i)try{await navigator.clipboard.writeText(i.value);const a=t.querySelector("i");if(a){const r=a.className;a.className="bi bi-check2",t.classList.add("btn-success"),t.classList.remove("btn-outline-secondary"),setTimeout(()=>{a.className=r,t.classList.remove("btn-success"),t.classList.add("btn-outline-secondary")},1500)}this.app&&this.app.toast&&this.app.toast.success("Copied to clipboard!")}catch(a){console.error("Failed to copy to clipboard:",a),this.app&&this.app.toast&&this.app.toast.error("Failed to copy to clipboard")}}computePasswordStrength(e=""){const t=e.length;let s=0;t>=6&&s++,t>=8&&s++,t>=12&&s++;const i=/[a-z]/.test(e),a=/[A-Z]/.test(e),r=/\d/.test(e),n=/[^A-Za-z0-9]/.test(e),
|
|
1070
|
+
`}a&&(a.style.display="none")}}findFieldConfig(e){const t=s=>{for(const i of s){if(i.name===e)return i;if(i.fields&&Array.isArray(i.fields)){const a=t(i.fields);if(a)return a}if(i.tabs&&Array.isArray(i.tabs)){for(const a of i.tabs)if(a.fields&&Array.isArray(a.fields)){const r=t(a.fields);if(r)return r}}}return null};return t(this.formConfig.fields||[])}async fileToBase64(e){return new Promise((t,s)=>{const i=new FileReader;i.onload=()=>t(i.result),i.onerror=s,i.readAsDataURL(e)})}hasFiles(e){if(e instanceof FormData){for(const[t,s]of e.entries())if(s instanceof File)return!0;return!1}else{for(const t of Object.values(e))if(t instanceof File||Array.isArray(t)&&t.some(s=>s instanceof File))return!0;return!1}}reset(){const e=this.getFormElement();e&&e.reset(),this.data={},this.errors={},this.clearAllErrors(),this.emit("reset",{form:this})}async updateConfig(e){this.formConfig={...this.formConfig,...e},this.formBuilder=new Z({...this.getFormConfig(),data:this.data,errors:this.errors}),await this.render()}async onBeforeDestroy(){const e=[];for(const t of this.customComponents.values())t.destroy&&e.push(t.destroy());await Promise.all(e),this.customComponents.clear(),Object.values(this.data).forEach(t=>{typeof t=="string"&&t.startsWith("blob:")&&URL.revokeObjectURL(t)}),await super.onBeforeDestroy()}initializePasswordFields(){if(!this.element)return;this.element.querySelectorAll('input[data-field-type="password"], input[type="password"]').forEach(t=>{this.updatePasswordStrengthUI(t);const s=a=>{this.updatePasswordStrengthUI(a.target)};t.addEventListener("input",s);const i=a=>{if(typeof a.getModifierState=="function"){const r=a.getModifierState("CapsLock");this.updateCapsLockWarning(t,!!r)}};t.addEventListener("keydown",i),t.addEventListener("keyup",i),this.updateCapsLockWarning(t,!1)})}async onActionTogglePassword(e,t){e.preventDefault();const s=t.getAttribute("data-target");let i=null;if(s&&(i=this.element.querySelector("#"+s)),!i){const n=t.closest(".input-group");n&&(i=n.querySelector('input[type="password"], input[data-field-type="password"], input[type="text"]'))}if(!i)return;const a=i.type==="password";i.type=a?"text":"password",t.setAttribute("aria-pressed",a?"true":"false"),t.setAttribute("aria-label",a?"Hide password":"Show password");const r=t.querySelector("i");r&&(r.classList.toggle("bi-eye",!a),r.classList.toggle("bi-eye-slash",a)),i.focus();try{const n=i.value?.length??0;i.setSelectionRange(n,n)}catch{}}async onActionCopyToClipboard(e,t){e.preventDefault();const s=t.getAttribute("data-target");if(!s)return;const i=this.element.querySelector("#"+s);if(i)try{await navigator.clipboard.writeText(i.value);const a=t.querySelector("i");if(a){const r=a.className;a.className="bi bi-check2",t.classList.add("btn-success"),t.classList.remove("btn-outline-secondary"),setTimeout(()=>{a.className=r,t.classList.remove("btn-success"),t.classList.add("btn-outline-secondary")},1500)}this.app&&this.app.toast&&this.app.toast.success("Copied to clipboard!")}catch(a){console.error("Failed to copy to clipboard:",a),this.app&&this.app.toast&&this.app.toast.error("Failed to copy to clipboard")}}computePasswordStrength(e=""){const t=e.length;let s=0;t>=6&&s++,t>=8&&s++,t>=12&&s++;const i=/[a-z]/.test(e),a=/[A-Z]/.test(e),r=/\d/.test(e),n=/[^A-Za-z0-9]/.test(e),l=[i,a,r,n].filter(Boolean).length;return l>=2&&s++,l>=3&&s++,s=Math.max(0,Math.min(4,s)),[{percent:0,label:"Too short",barClass:"bg-secondary"},{percent:25,label:"Weak",barClass:"bg-danger"},{percent:50,label:"Fair",barClass:"bg-warning"},{percent:75,label:"Good",barClass:"bg-info"},{percent:100,label:"Strong",barClass:"bg-success"}][s]}updatePasswordStrengthUI(e){if(!e||!e.id)return;const t=this.element.querySelector(`#${e.id}_strength_bar`),s=this.element.querySelector(`#${e.id}_strength_text`);if(!t&&!s)return;const{percent:i,label:a,barClass:r}=this.computePasswordStrength(e.value||"");t&&(t.className=`progress-bar ${r}`,t.style.width=`${i}%`,t.setAttribute("aria-valuenow",String(i))),s&&(s.textContent=a)}updateCapsLockWarning(e,t){if(!e||!e.id)return;const s=this.element.querySelector(`#${e.id}_caps_warning`);s&&(t?s.classList.remove("d-none"):s.classList.add("d-none"))}}class Be{constructor(e){this.fieldElement=e,this.statusContainer=this.findOrCreateStatusContainer(),this.timeouts=new Map}findOrCreateStatusContainer(){let e=this.fieldElement.parentElement.querySelector(".field-status-label-inline");if(!e){const t=this.findFieldLabel();t&&(e=t.querySelector(".field-status-label-inline"))}return e||(e=this.createStatusContainer()),e}createStatusContainer(){const e=this.getFieldType();this.getPlacementStrategy(e);const t=document.createElement("div");return this.createLabelInlineContainer(t)}getFieldType(){const e=this.fieldElement.tagName.toLowerCase(),t=this.fieldElement.type?.toLowerCase(),s=this.fieldElement.className;return t==="checkbox"||s.includes("form-check-input")||s.includes("form-switch")?"toggle":e==="select"?"select":e==="textarea"?"textarea":"input"}getPlacementStrategy(e){return"label-inline"}createLabelInlineContainer(e){e.className="field-status-label-inline",e.innerHTML=this.getStatusHTML();const t=this.findFieldLabel();return t?t.appendChild(e):this.fieldElement.parentElement.appendChild(e),e}findFieldLabel(){if(this.fieldElement.id){const s=document.querySelector(`label[for="${this.fieldElement.id}"]`);if(s)return s}const e=this.fieldElement.parentElement.querySelector("label");if(e)return e;const t=this.fieldElement.closest("label");return t||null}createInputOverlayContainer(e){e.className="field-status-overlay",e.innerHTML=this.getStatusHTML();const t=this.fieldElement.parentElement;return getComputedStyle(t).position==="static"&&(t.style.position="relative"),t.appendChild(e),e}createFullOverlayContainer(e){e.className="field-status-full-overlay d-none",e.innerHTML=`
|
|
1071
1071
|
<div class="saving-indicator">
|
|
1072
1072
|
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
|
1073
1073
|
<span class="visually-hidden">Saving...</span>
|
|
@@ -1088,7 +1088,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1088
1088
|
</div>
|
|
1089
1089
|
<i class="bi bi-check-circle text-success d-none" data-status="saved"></i>
|
|
1090
1090
|
<i class="bi bi-exclamation-circle text-danger d-none" data-status="error"></i>
|
|
1091
|
-
`}showStatus(e,t={}){this.clearTimeout(e),this.showStandardStatus(e,t)}showStandardStatus(e,t={}){this.hideAllStatuses();const s=this.statusContainer.querySelector(`[data-status="${e}"]`);s&&(s.classList.remove("d-none"),s.classList.add("d-inline-block","show"),e==="saved"?this.setTimeout(e,()=>this.hideStatus(e),2500):e==="error"&&(t.message&&(s.title=t.message),this.setTimeout(e,()=>this.hideStatus(e),6e3)))}showFullOverlayStatus(e,t={}){this.statusContainer.querySelectorAll(".saving-indicator, .success-indicator, .error-indicator").forEach(a=>a.classList.add("d-none")),this.statusContainer.classList.remove("d-none");let i;switch(e){case"saving":i=".saving-indicator";break;case"saved":i=".success-indicator",this.setTimeout(e,()=>this.hideStatus(e),2500);break;case"error":if(i=".error-indicator",t.message){const a=this.statusContainer.querySelector(".error-indicator span");a&&(a.textContent=t.message)}this.setTimeout(e,()=>this.hideStatus(e),6e3);break}if(i){const a=this.statusContainer.querySelector(i);a&&a.classList.remove("d-none")}}hideStatus(e){const t=this.statusContainer.querySelector(`[data-status="${e}"]`);t&&(t.classList.remove("show"),t.classList.add("hide"),setTimeout(()=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","hide"),t.title=""},300))}hideAllStatuses(){this.statusContainer.querySelectorAll("[data-status]").forEach(t=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","show","hide"),t.title=""})}setTimeout(e,t,s){const i=setTimeout(t,s);this.timeouts.set(e,i)}clearTimeout(e){this.timeouts.has(e)&&(clearTimeout(this.timeouts.get(e)),this.timeouts.delete(e))}destroy(){this.timeouts.forEach(e=>clearTimeout(e)),this.timeouts.clear()}}_e(H);const xe=Object.freeze(Object.defineProperty({__proto__:null,FormView:H,default:H},Symbol.toStringTag,{value:"Module"}));class Ue extends W{constructor(e={}){super({title:"Form Page",description:"A page for submitting forms",icon:"form",fields:[],template:'<div data-container="form-view-container"></div>',className:"form-page container-sm",...e})}async onInit(){await super.onInit(),this.formView=new H({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView),this.getApp().activeGroup&&this.formView.setModel(this.getApp().activeGroup)}async onEnter(){await super.onEnter(),this.formView&&await this.recreateFormView()}async onGroupChange(e){this.formView&&await this.recreateFormView()}async recreateFormView(){this.formView&&(await this.formView.destroy(),this.removeChild(this.formView)),this.formView=new H({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView),this.getApp().activeGroup&&this.formView.setModel(this.getApp().activeGroup)}}let k=class
|
|
1091
|
+
`}showStatus(e,t={}){this.clearTimeout(e),this.showStandardStatus(e,t)}showStandardStatus(e,t={}){this.hideAllStatuses();const s=this.statusContainer.querySelector(`[data-status="${e}"]`);s&&(s.classList.remove("d-none"),s.classList.add("d-inline-block","show"),e==="saved"?this.setTimeout(e,()=>this.hideStatus(e),2500):e==="error"&&(t.message&&(s.title=t.message),this.setTimeout(e,()=>this.hideStatus(e),6e3)))}showFullOverlayStatus(e,t={}){this.statusContainer.querySelectorAll(".saving-indicator, .success-indicator, .error-indicator").forEach(a=>a.classList.add("d-none")),this.statusContainer.classList.remove("d-none");let i;switch(e){case"saving":i=".saving-indicator";break;case"saved":i=".success-indicator",this.setTimeout(e,()=>this.hideStatus(e),2500);break;case"error":if(i=".error-indicator",t.message){const a=this.statusContainer.querySelector(".error-indicator span");a&&(a.textContent=t.message)}this.setTimeout(e,()=>this.hideStatus(e),6e3);break}if(i){const a=this.statusContainer.querySelector(i);a&&a.classList.remove("d-none")}}hideStatus(e){const t=this.statusContainer.querySelector(`[data-status="${e}"]`);t&&(t.classList.remove("show"),t.classList.add("hide"),setTimeout(()=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","hide"),t.title=""},300))}hideAllStatuses(){this.statusContainer.querySelectorAll("[data-status]").forEach(t=>{t.classList.add("d-none"),t.classList.remove("d-inline-block","show","hide"),t.title=""})}setTimeout(e,t,s){const i=setTimeout(t,s);this.timeouts.set(e,i)}clearTimeout(e){this.timeouts.has(e)&&(clearTimeout(this.timeouts.get(e)),this.timeouts.delete(e))}destroy(){this.timeouts.forEach(e=>clearTimeout(e)),this.timeouts.clear()}}_e(H);const xe=Object.freeze(Object.defineProperty({__proto__:null,FormView:H,default:H},Symbol.toStringTag,{value:"Module"}));class Ue extends W{constructor(e={}){super({title:"Form Page",description:"A page for submitting forms",icon:"form",fields:[],template:'<div data-container="form-view-container"></div>',className:"form-page container-sm",...e})}async onInit(){await super.onInit(),this.formView=new H({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView),this.getApp().activeGroup&&this.formView.setModel(this.getApp().activeGroup)}async onEnter(){await super.onEnter(),this.formView&&await this.recreateFormView()}async onGroupChange(e){this.formView&&await this.recreateFormView()}async recreateFormView(){this.formView&&(await this.formView.destroy(),this.removeChild(this.formView)),this.formView=new H({containerId:"form-view-container",fields:this.options.fields,autosaveModelField:!0}),this.addChild(this.formView),this.getApp().activeGroup&&this.formView.setModel(this.getApp().activeGroup)}}let k=class F extends v{static _openDialogs=[];static _baseZIndex={backdrop:1050,modal:1055};static getFullscreenAwareZIndex(){return document.querySelector(".table-fullscreen")?{backdrop:10040,modal:10050}:this._baseZIndex}static _busyIndicator=null;static _busyCounter=0;static _busyTimeout=null;static fixAllBackdropStacking(){const e=document.querySelectorAll(".modal-backdrop"),t=F._openDialogs;if(e.length===0||t.length===0)return;const s=[...t].sort((i,a)=>(i._dialogZIndex||0)-(a._dialogZIndex||0));e.forEach((i,a)=>{if(a<s.length){const n=s[a]._dialogZIndex-5;i.style.zIndex=n;const o=document.querySelector(".table-fullscreen")||document.body;i.parentNode!==o&&o.appendChild(i)}})}static updateAllBackdropStacking(){F.fixAllBackdropStacking()}static showBusy(e={}){const{timeout:t=3e4,message:s="Loading..."}=e;if(this._busyCounter++,this._busyCounter===1){if(this._busyTimeout&&clearTimeout(this._busyTimeout),!this._busyIndicator){const r=this.getFullscreenAwareZIndex().modal+1e3;this._busyIndicator=document.createElement("div"),this._busyIndicator.className="mojo-busy-indicator",this._busyIndicator.innerHTML=`
|
|
1092
1092
|
<div class="mojo-busy-spinner">
|
|
1093
1093
|
<div class="spinner-border text-light" role="status">
|
|
1094
1094
|
<span class="visually-hidden">Loading...</span>
|
|
@@ -1121,7 +1121,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1121
1121
|
${this.title?`<h5 class="modal-title" id="${this.titleId}">${this.title}</h5>`:""}
|
|
1122
1122
|
${e}
|
|
1123
1123
|
</div>
|
|
1124
|
-
`}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
|
|
1124
|
+
`}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 l=Object.keys(a).filter(o=>o.startsWith("data-")).map(o=>`${o}="${a[o]}"`).join(" ");return`<li><a class="dropdown-item" data-action="${a.action}" ${l}>${r}${n}</a></li>`}return""}).join("");return`
|
|
1125
1125
|
<div class="dropdown">
|
|
1126
1126
|
<button class="${s}" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
1127
1127
|
<i class="${t}"></i>
|
|
@@ -1147,14 +1147,14 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1147
1147
|
${t.icon?`<i class="bi ${t.icon} me-1"></i>`:""}
|
|
1148
1148
|
${t.text||"Button"}
|
|
1149
1149
|
</button>
|
|
1150
|
-
`}).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
|
|
1150
|
+
`}).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 l=Math.min(window.innerHeight*this.maxHeightPercent,window.innerHeight-r),o=Math.max(this.minWidth,Math.ceil(a.width+20)),c=Math.max(this.minHeight,Math.ceil(a.height));this.maxHeight&&(l=Math.min(this.maxHeight,l),e.style.maxHeight=`${l}px`),o=Math.min(o,n);const d=a.height>l;e.style.maxWidth=`${o}px`,e.style.width=`${o}px`,d&&(e.classList.contains("modal-dialog-scrollable")||e.classList.add("modal-dialog-scrollable"),t.style.maxHeight=`${l}px`,c=l),this.autoSizedWidth=o,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=F._openDialogs.length,i=F.getFullscreenAwareZIndex().modal+t*20;this.element.style.zIndex=i,this._dialogZIndex=i,this._backdropZIndex=i-10,F._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(()=>{F.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=F._openDialogs.indexOf(this);t>-1&&F._openDialogs.splice(t,1),F._openDialogs.length>0&&(document.body.classList.add("modal-open"),setTimeout(()=>{F.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 v){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=`
|
|
1151
1151
|
<div class="text-center py-4">
|
|
1152
1152
|
<div class="spinner-border text-primary mb-3" role="status">
|
|
1153
1153
|
<span class="visually-hidden">Loading...</span>
|
|
1154
1154
|
</div>
|
|
1155
1155
|
<p>${t}</p>
|
|
1156
1156
|
</div>
|
|
1157
|
-
`:this.bodyView&&s.replaceChildren(this.bodyView.element))}async onBeforeDestroy(){this.headerView&&await this.headerView.destroy(),this.bodyView&&await this.bodyView.destroy(),this.footerView&&await this.footerView.destroy(),await super.onBeforeDestroy(),this.modal&&(this.modal.dispose(),this.modal=null)}static async showCode(e={}){const t=new
|
|
1157
|
+
`:this.bodyView&&s.replaceChildren(this.bodyView.element))}async onBeforeDestroy(){this.headerView&&await this.headerView.destroy(),this.bodyView&&await this.bodyView.destroy(),this.footerView&&await this.footerView.destroy(),await super.onBeforeDestroy(),this.modal&&(this.modal.dispose(),this.modal=null)}static async showCode(e={}){const t=new F({title:e.title||"Source Code",size:e.size||"lg",scrollable:!0,body:F.formatCode(e.code,e.language),buttons:[{text:"Copy to Clipboard",class:"btn-primary",icon:"bi-clipboard",action:"copy"},{text:"Close",class:"btn-secondary",dismiss:!0}]});t.on("action:copy",async()=>{if(navigator.clipboard)try{await navigator.clipboard.writeText(e.code),t.showCopySuccess()}catch(a){console.error("Failed to copy:",a)}});const i=document.querySelector(".table-fullscreen")||document.body;return await t.render(!0,i),window.Prism&&t.element&&window.Prism.highlightAllUnder(t.element),t.show(),t.on("hidden",()=>{t.destroy(),t.element.remove()}),t}static formatCode(e,t="javascript"){let s;window.Prism&&window.Prism.languages[t]?s=window.Prism.highlight(e,window.Prism.languages[t],t):s=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");const i=window.Prism?`language-${t}`:"",a=`
|
|
1158
1158
|
max-height: 60vh;
|
|
1159
1159
|
overflow-y: auto;
|
|
1160
1160
|
background: #1e1e1e;
|
|
@@ -1196,22 +1196,22 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1196
1196
|
<i class="bi bi-arrow-clockwise"></i> Refresh
|
|
1197
1197
|
</button>
|
|
1198
1198
|
</div>
|
|
1199
|
-
<iframe
|
|
1199
|
+
<iframe
|
|
1200
1200
|
id="html-preview-frame"
|
|
1201
|
-
class="border rounded w-100"
|
|
1201
|
+
class="border rounded w-100"
|
|
1202
1202
|
style="height: ${e.height||500}px; background: white;"
|
|
1203
1203
|
sandbox="allow-same-origin"
|
|
1204
1204
|
frameborder="0"
|
|
1205
1205
|
></iframe>
|
|
1206
1206
|
</div>
|
|
1207
|
-
`,n=new
|
|
1207
|
+
`,n=new F({title:s,size:i,scrollable:!1,body:r,buttons:[{text:"Close",class:"btn-secondary",dismiss:!0}]});n.on("action:refresh-preview",async d=>{const h=n.element.querySelector("#html-preview-frame");if(!h)return;const f=h.contentDocument||h.contentWindow.document;f.open(),f.write(t),f.close()});const o=document.querySelector(".table-fullscreen")||document.body;await n.render(!0,o);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:l=!0,buttons:o=[{text:"OK",class:"btn-primary",value:!0}],rejectOnDismiss:c=!1,...d}=e,h=i??a??r??s??"",f=new F({title:t,body:h,size:n,centered:l,buttons:o,...d}),p=document.querySelector(".table-fullscreen")||document.body;return await f.render(!0,p),new Promise((g,y)=>{let b=!1;f.element.querySelectorAll(".modal-footer button").forEach((C,$)=>{const S=o[$];S&&C.addEventListener("click",async M=>{if(b)return;const L=S.value!==void 0?S.value:S.action??$;if(typeof S.handler=="function")try{const A=await S.handler({dialog:f,button:S,index:$,event:M});if(A===null||A===!1)return;const U=A===!0||A===void 0?L:A;b=!0,S.dismiss||f.hide(),g(U)}catch(A){console.error("Dialog button handler error:",A);return}else b=!0,S.dismiss||f.hide(),g(L)})}),f.on("hidden",()=>{b||(b=!0,c?y(new Error("Dialog dismissed")):g(null)),setTimeout(()=>{f.destroy(),f.element.remove()},100)}),f.show()})}static async alert(e={}){typeof e=="string"&&(e={message:e,title:"Alert"});const{message:t="",title:s="Alert",type:i="info",...a}=e;let r="",n="";switch(i){case"success":r='<i class="bi bi-check-circle-fill text-success me-2"></i>',n="text-success";break;case"warning":r='<i class="bi bi-exclamation-triangle-fill text-warning me-2"></i>',n="text-warning";break;case"danger":case"error":r='<i class="bi bi-x-circle-fill text-danger me-2"></i>',n="text-danger";break;default:r='<i class="bi bi-info-circle-fill text-info me-2"></i>',n="text-info"}return F.showDialog({title:`<span class="${n}">${r}${s}</span>`,body:`<p>${t}</p>`,size:"sm",centered:!0,buttons:[{text:"OK",class:"btn-primary",value:!0}],...a})}static async confirm(e,t="Confirm",s={}){typeof e=="object"&&(s=e,e=s.message,t=s.title||t);const i=new F({title:t,body:`<p>${e}</p>`,size:s.size||"sm",centered:!0,backdrop:"static",buttons:[{text:s.cancelText||"Cancel",class:"btn-secondary",dismiss:!0,action:"cancel"},{text:s.confirmText||"Confirm",class:s.confirmClass||"btn-primary",action:"confirm"}],...s}),r=document.querySelector(".table-fullscreen")||document.body;return await i.render(!0,r),i.show(),new Promise(n=>{let l=!1;i.on("action:confirm",()=>{l=!0,i.hide()}),i.on("hidden",()=>{i.destroy(),i.element.remove(),n(l)})})}static async prompt(e,t="Input",s={}){const i=`prompt-input-${Date.now()}`,a=s.defaultValue||"",r=s.inputType||"text",n=s.placeholder||"",l=new F({title:t,body:`
|
|
1208
1208
|
<p>${e}</p>
|
|
1209
1209
|
<input type="${r}"
|
|
1210
1210
|
class="form-control"
|
|
1211
1211
|
id="${i}"
|
|
1212
1212
|
value="${a}"
|
|
1213
1213
|
placeholder="${n}">
|
|
1214
|
-
`,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
|
|
1214
|
+
`,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 l.render(!0,c),l.show(),l.on("shown",()=>{const d=l.element.querySelector(`#${i}`);d&&(d.focus(),d.select())}),new Promise(d=>{let h=null;l.on("action:ok",()=>{const f=l.element.querySelector(`#${i}`);h=f?f.value:null,l.hide()}),l.on("hidden",()=>{l.destroy(),l.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",...l}=e,o=(await Promise.resolve().then(()=>xe)).default,c=new o({fileHandling:e.fileHandling||"base64",data:e.data,defaults:e.defaults,model:e.model,formConfig:{fields:s.fields||e.fields,...s,submitButton:!1,resetButton:!1}}),d=new F({title:t,body:c,size:i,centered:a,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:r,class:"btn-primary",action:"submit"}],...l}),f=document.querySelector(".table-fullscreen")||document.body;return await d.render(!0,f),d.show(),new Promise(m=>{let p=!1;d.on("action:submit",async()=>{if(!p){if(!c.validate()){c.focusFirstError();return}if(e.autoSave&&e.model){d.setLoading(!0);const g=await c.saveModel();if(!g.success){d.setLoading(!1),d.render(),d.getApp().toast.error(g.message);return}p=!0,d.hide(),m(g)}try{const g=await c.getFormData();p=!0,d.hide(),m(g)}catch(g){console.error("Error collecting form data:",g),c.showError("Error collecting form data")}}}),d.on("action:cancel",()=>{p||(p=!0,d.hide(),m(null))}),d.on("hidden",()=>{p||(p=!0,m(null)),setTimeout(()=>{c.destroy(),d.destroy()},100)})})}static async showModelView(e,t){const i=e.constructor.VIEW_CLASS,a=new i({model:e});return t=t||{},await F.showDialog({header:!1,body:a,size:"lg",centered:!1,...t})}static async showModelForm(e={}){const{title:t="Edit",formConfig:s={},size:i="md",centered:a=!0,submitText:r="Save",cancelText:n="Cancel",model:l,fields:o,...c}=e;if(!l)throw new Error("showModelForm requires a model");const d=(await Promise.resolve().then(()=>xe)).default,h=new d({fileHandling:e.fileHandling||"base64",model:l,data:e.data,defaults:e.defaults,formConfig:{fields:o||s.fields||[],...s,submitButton:!1,resetButton:!1}}),f=new F({title:t,body:h,size:i,centered:a,buttons:[{text:n,class:"btn-secondary",action:"cancel"},{text:r,class:"btn-primary",action:"submit"}],...c}),p=document.querySelector(".table-fullscreen")||document.body;return await f.render(!0,p),f.show(),new Promise(g=>{let y=!1;f.on("action:submit",async()=>{if(!y){f.setLoading(!0,"Saving...");try{const b=await h.handleSubmit();if(b.success)y=!0,f.hide(),g(b);else{f.setLoading(!1);let w=b.error;b.data&&b.data.error&&(w=b.data.error),f.getApp().toast.error(w)}}catch(b){console.error("Error saving form:",b),await f.setContent(h),h.showError(b.message||"An error occurred while saving")}}}),f.on("action:cancel",()=>{y||(y=!0,f.hide(),g(null))}),f.on("hidden",()=>{y||(y=!0,g(null)),setTimeout(()=>{h.destroy(),f.destroy()},100)})})}static async showData(e={}){const{title:t="Data View",data:s={},model:i=null,fields:a=[],columns:r=2,responsive:n=!0,showEmptyValues:l=!1,emptyValueText:o="—",size:c="lg",centered:d=!0,closeText:h="Close",...f}=e,m=(await Promise.resolve().then(()=>Ze)).default,p=new m({data:s,model:i,fields:a,columns:r,responsive:n,showEmptyValues:l,emptyValueText:o}),g=new F({title:t,body:p,size:c,centered:d,buttons:[{text:h,class:"btn-secondary",value:"close"}],...f}),b=document.querySelector(".table-fullscreen")||document.body;return await g.render(!0,b),g.show(),new Promise(w=>{let C=!1;const $=g.element.querySelector(".modal-footer button"),S=()=>{C||(C=!0,g.hide(),w(!0))};$?.addEventListener("click",S),g.on("hidden",()=>{C||(C=!0,w(!0)),setTimeout(()=>{p.destroy(),g.destroy(),g.element.remove()},100)}),p.on("field:click",M=>{g.emit("dataview:field:click",M)}),p.on("error",M=>{g.emit("dataview:error",M)})})}};k.showConfirm=k.confirm,k.showError=k.alert;const _=Object.freeze(Object.defineProperty({__proto__:null,default:k},Symbol.toStringTag,{value:"Module"}));class $e extends v{constructor(e={}){super({template:"progress-view-template",...e}),this.filename=e.filename||"Unknown file",this.filesize=e.filesize||0,this.filesizeFormatted=V.pipe(this.filesize,"filesize"),this.progress=0,this.percentage=0,this.loaded=0,this.total=this.filesize,this.loadedFormatted="0 B",this.totalFormatted=this.filesizeFormatted,this.status="Starting upload...",this.showCancel=e.showCancel!==!1,this.onCancel=e.onCancel||null,this.cancelled=!1,this.completed=!1}getTemplate(){return`
|
|
1215
1215
|
<div class="progress-view">
|
|
1216
1216
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
1217
1217
|
<div class="flex-grow-1 min-width-0">
|
|
@@ -1293,7 +1293,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1293
1293
|
</div>
|
|
1294
1294
|
</div>
|
|
1295
1295
|
</td>
|
|
1296
|
-
`),this.columns.forEach(t=>{const s=t.class||t.className||"",i=this.getResponsiveClasses(t.visibility),a=t.editable?"editable-cell":"",r=[s,i,a].filter(
|
|
1296
|
+
`),this.columns.forEach(t=>{const s=t.class||t.className||"",i=this.getResponsiveClasses(t.visibility),a=t.editable?"editable-cell":"",r=[s,i,a].filter(o=>o).join(" "),n=this.buildCellTemplate(t);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}">${n}</td>`:e+=`<td class="${r}" data-column="${t.key}">${n}</td>`}),this.actions?e+=this.buildActionsTemplate():this.contextMenu&&(e+=this.buildContextMenuTemplate()),e}buildCellTemplate(e){const t=`model.${e.key}`,s=e.formatter||e.format;if(s){if(typeof s=="string")return`{{{${t}|${s}}}}`;if(typeof s=="function")return`<span data-formatter="${e.key}">{{${t}}}</span>`}return e.template?e.template:e.editable?`<span class="cell-content" data-field="${e.key}">{{{${t}}}}</span>`:`{{{${t}}}}`}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`
|
|
1297
1297
|
<button class="btn btn-sm btn-outline-primary"
|
|
1298
1298
|
data-action="view"
|
|
1299
1299
|
title="View">
|
|
@@ -1344,7 +1344,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1344
1344
|
${e.label}
|
|
1345
1345
|
</a>
|
|
1346
1346
|
</li>
|
|
1347
|
-
`}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach(t=>{if(t.formatter&&typeof t.formatter=="function"){const s=this.element.querySelector(`[data-formatter="${t.key}"]`);if(s){const i=this.model.get?this.model.get(t.key):this.model[t.key],a={value:i,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{s.innerHTML=t.formatter(i,a)}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],r=this.createCellEditor(t,a),n=i.innerHTML;i.style.display="none";const
|
|
1347
|
+
`}).join("")}async onAfterRender(){await super.onAfterRender(),this.columns.forEach(t=>{if(t.formatter&&typeof t.formatter=="function"){const s=this.element.querySelector(`[data-formatter="${t.key}"]`);if(s){const i=this.model.get?this.model.get(t.key):this.model[t.key],a={value:i,row:this.model,model:this.model,column:t,table:this.tableView,index:this.index};try{s.innerHTML=t.formatter(i,a)}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],r=this.createCellEditor(t,a),n=i.innerHTML;i.style.display="none";const l=document.createElement("div");l.className="cell-editor",l.innerHTML=r,s.appendChild(l);const o=l.querySelector("input, select, .form-check-input");o&&(o.focus(),(o.type==="text"||o.type==="textarea")&&o.select()),l.dataset.originalContent=n,l.dataset.columnKey=e,this.setupEditorEvents(l,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`
|
|
1348
1348
|
<div class="d-flex gap-1 align-items-center">
|
|
1349
1349
|
<input type="${e.inputType||"text"}"
|
|
1350
1350
|
class="form-control form-control-sm cell-input"
|
|
@@ -1397,7 +1397,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1397
1397
|
</button>
|
|
1398
1398
|
</div>
|
|
1399
1399
|
</div>
|
|
1400
|
-
`}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(
|
|
1400
|
+
`}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(o=>o.key===t);let l=s;n&&n.formatter&&typeof n.formatter=="string"&&(l=V.pipe(s,n.formatter)),r.innerHTML=this.escapeHtml(l)}else i&&(r.innerHTML=i);r.style.display=""}e.remove(),this.editingCells.delete(t)}escapeHtml(e){if(e==null)return"";const t=document.createElement("div");return t.textContent=e,t.innerHTML}select(){super.select(),this.addClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.add("selected")}deselect(){super.deselect(),this.removeClass("selected");const e=this.element?.querySelector(".mojo-select-cell");e&&e.classList.remove("selected")}}const Se={exact:{display:"is",description:"Exact match"},in:{display:"in",description:"Match any of the values (comma-separated)"},not:{display:"is not",description:"Does not match"},not_in:{display:"not in",description:"Does not match any of the values"},gt:{display:">",description:"Greater than"},gte:{display:">=",description:"Greater than or equal to"},lt:{display:"<",description:"Less than"},lte:{display:"<=",description:"Less than or equal to"},contains:{display:"contains",description:"Contains substring (case-sensitive)"},icontains:{display:"contains",description:"Contains substring (case-insensitive)"},startswith:{display:"starts with",description:"Starts with substring (case-sensitive)"},istartswith:{display:"starts with",description:"Starts with substring (case-insensitive)"},endswith:{display:"ends with",description:"Ends with substring (case-sensitive)"},iendswith:{display:"ends with",description:"Ends with substring (case-insensitive)"},isnull:{display:u=>u==="true"||u===!0?"is null":"is not null",description:"Check if value is null or not"},range:{display:"between",description:"Between two values (comma-separated)"}};function G(u){if(!u||typeof u!="string")return{field:u,lookup:null};const e=u.split("__");if(e.length===1)return{field:u,lookup:null};const t=e[e.length-1];return Se[t]?{field:e.slice(0,-1).join("__"),lookup:t}:{field:u,lookup:null}}function Ye(u,e,t){if(!u||e===null||e===void 0)return"";const{field:s,lookup:i}=G(u),a=Se[i];if(e&&typeof e=="object"&&!Array.isArray(e)){const n=e.start!==void 0&&e.start!==null&&e.start!=="",l=e.end!==void 0&&e.end!==null&&e.end!=="";return n||l?n&&l?`${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(o=>o.trim()).filter(o=>o);if(n.length===0)return`${t} ${a.display}`;const l=n.map(o=>`'${o}'`).join(", ");return`${t} ${a.display} ${l}`}if(i==="range"){const n=r.split(",").map(l=>l.trim()).filter(l=>l);return n.length===2?`${t} between '${n[0]}' and '${n[1]}'`:`${t} ${a.display} '${r}'`}if(i==="isnull"){const n=typeof a.display=="function"?a.display(r):a.display;return`${t} ${n}`}return a?`${t} ${a.display} '${r}'`:`${t} is '${r}'`}class Fe extends he{constructor(e={}){const t={className:"table-view-component",itemClass:e.itemClass||ue,selectionMode:e.selectable?"multiple":"none",emptyMessage:e.emptyMessage||"No data available",addButtonIcon:e.addButtonIcon||"bi bi-plus-circle",...e};super(t),this.isFullscreen=!1,this.columns=e.columns||[],this.actions=e.actions||null,this.contextMenu=e.contextMenu||null,this.batchActions=e.batchActions||null,this.searchable=e.searchable!==!1,this.sortable=e.sortable!==!1,this.filterable=e.filterable!==!1,this.paginated=e.paginated!==!1,this.clickAction=e.clickAction||"view",this.itemView=e.itemView,this.addForm=e.addForm,this.editForm=e.editForm,this.deleteTemplate=e.deleteTemplate,this.formDialogConfig=e.formDialogConfig||{},this.viewDialogOptions=e.viewDialogOptions||{},this.exportOptions=e.exportOptions||null,this.options.showExport&&!this.exportOptions&&(this.exportOptions=[{format:"csv",label:"Export as CSV",icon:"bi bi-file-earmark-spreadsheet"},{format:"json",label:"Export as JSON",icon:"bi bi-file-earmark-code"}]),this.exportSource=e.exportSource||"remote",this.filters={},this.additionalFilters=e.filters||[],this.hideActivePills=e.hideActivePills||!1,this.hideActivePillNames=e.hideActivePillNames||[],this.rowAction=e.rowAction||"row-click",this.batchBarLocation=e.batchBarLocation||"bottom",this.options.addButtonLabel=e.addButtonLabel||"Add",this.toolbarButtons=e.toolbarButtons||[],this.tableOptions={striped:!0,bordered:!1,hover:!0,responsive:!1,size:null,...e.tableOptions},this.searchPlacement=e.searchPlacement||"toolbar",this.searchPlaceholder=e.searchPlaceholder||"Search...",this.initializeColumns(),this.extractColumnFilters(),this.footerTotalColumns=this.columns.filter(s=>s.footer_total===!0),this.hasFooterTotals=this.footerTotalColumns.length>0,this.template=this.buildTableTemplate(),this.setupCollectionListeners()}setupCollectionListeners(){this.hasFooterTotals&&this.collection&&this.collection.on("reset add remove change",()=>{this.updateFooterTotals()})}initializeColumns(){this.columns.forEach(e=>{!e.key&&e.name&&(e.key=e.name),!e.label&&!e.title&&(e.label=e.key.charAt(0).toUpperCase()+e.key.slice(1))})}getResponsiveClasses(e){if(!e)return"";const t=["sm","md","lg","xl","xxl"];if(typeof e=="string")return t.includes(e)?`d-none d-${e}-table-cell`:(console.warn(`Invalid visibility breakpoint: ${e}. Valid options are: ${t.join(", ")}`),"");if(typeof e=="object"){const s=[];if(e.hide){if(!t.includes(e.hide))return console.warn(`Invalid hide breakpoint: ${e.hide}. Valid options are: ${t.join(", ")}`),"";s.push(`d-table-cell d-${e.hide}-none`)}if(e.show){if(!t.includes(e.show))return console.warn(`Invalid show breakpoint: ${e.show}. Valid options are: ${t.join(", ")}`),"";e.hide?s.push(`d-${e.show}-table-cell`):s.push(`d-none d-${e.show}-table-cell`)}return s.join(" ")}return""}parseColumnKey(e){const t=e.split("|");return{fieldKey:t[0],formatter:t[1]||null}}updateFooterTotals(){if(!this.hasFooterTotals||!this.element)return;const e=this.calculateFooterTotals();console.log("Updating footer totals in DOM:",e);let t=0;this.columns.forEach(s=>{if(s.footer_total){const i=`col_${t}`,a=this.element.querySelector(`[data-total-column="${i}"]`);if(a&&e[i]){const r=this.parseColumnKey(s.key).formatter||s.formatter;let n;r&&typeof r=="string"?n=this.formatValue(e[i].value,r):n=e[i].value,a.textContent=n}t++}})}formatValue(e,t){try{return V.pipe(e,t)}catch(s){return console.warn("Error formatting value:",s),e}}calculateFooterTotals(){if(!this.hasFooterTotals||!this.collection||this.collection.length===0)return{};const e={};return this.footerTotalColumns.forEach((t,s)=>{const{fieldKey:i,formatter:a}=this.parseColumnKey(t.key);let r=0;this.collection.forEach(l=>{const o=l.get?l.get(i):l[i],c=parseFloat(o)||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`
|
|
1401
1401
|
<div class="mojo-table-wrapper">
|
|
1402
1402
|
${this.buildToolbarTemplate()}
|
|
1403
1403
|
${e}
|
|
@@ -1482,10 +1482,10 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1482
1482
|
<i class="bi bi-download me-1"></i>
|
|
1483
1483
|
<span class="d-none d-lg-inline">Export</span>
|
|
1484
1484
|
</button>
|
|
1485
|
-
`)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((t,s)=>{const{label:i="Button",icon:a="",action:r="",handler:n=null,variant:
|
|
1485
|
+
`)}return this.toolbarButtons&&this.toolbarButtons.length>0&&this.toolbarButtons.forEach((t,s)=>{const{label:i="Button",icon:a="",action:r="",handler:n=null,variant:l="outline-secondary",title:o=i,className:c="",permissions:d=null}=t;if(d&&!this.checkPermissions(d))return;const h=a?`<i class="${a} me-1"></i>`:"",f=`<span class="d-none d-lg-inline">${i}</span>`;let m="";n?m=`data-action="custom-toolbar-button" data-button-index="${s}"`:r&&(m=`data-action="${r}"`);const p=`btn btn-sm btn-${l} ${c}`.trim();e.push(`
|
|
1486
1486
|
<button class="${p}"
|
|
1487
1487
|
${m}
|
|
1488
|
-
title="${
|
|
1488
|
+
title="${o}">
|
|
1489
1489
|
${h}${f}
|
|
1490
1490
|
</button>
|
|
1491
1491
|
`)}),e.join("")}buildSearchTemplate(){return`
|
|
@@ -1537,7 +1537,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1537
1537
|
<i class="bi bi-x-circle me-2"></i>Clear All Filters
|
|
1538
1538
|
</button>
|
|
1539
1539
|
`:""}
|
|
1540
|
-
`}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,
|
|
1540
|
+
`}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,l])=>l&&l.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,l])=>{const{field:o}=G(n),c=this.getFilterLabel(o),d=Ye(n,l,c);return`
|
|
1541
1541
|
<span class="badge bg-primary me-1 mb-1 py-1 px-2 position-relative" style="font-size: 0.75rem;">
|
|
1542
1542
|
<i class="bi bi-filter me-1" style="font-size: 0.65rem;"></i>
|
|
1543
1543
|
|
|
@@ -1578,7 +1578,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1578
1578
|
</div>
|
|
1579
1579
|
</div>
|
|
1580
1580
|
</th>
|
|
1581
|
-
`),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,
|
|
1581
|
+
`),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,l=this.getResponsiveClasses(t.visibility),o=i?`
|
|
1582
1582
|
<div class="dropdown d-inline-block ms-2">
|
|
1583
1583
|
<button class="btn btn-sm btn-link p-0 text-decoration-none" type="button"
|
|
1584
1584
|
data-bs-toggle="dropdown" aria-expanded="false"
|
|
@@ -1601,10 +1601,10 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1601
1601
|
</ul>
|
|
1602
1602
|
</div>
|
|
1603
1603
|
`:"";e+=`
|
|
1604
|
-
<th class="${i?"sortable":""} ${
|
|
1604
|
+
<th class="${i?"sortable":""} ${l}">
|
|
1605
1605
|
<div class="d-flex align-items-center">
|
|
1606
1606
|
<span>${n}</span>
|
|
1607
|
-
${
|
|
1607
|
+
${o}
|
|
1608
1608
|
</div>
|
|
1609
1609
|
</th>
|
|
1610
1610
|
`}),this.actions?e+="<th>Actions</th>":this.contextMenu&&(e+='<th style="width: 1px;"></th>'),`
|
|
@@ -1613,7 +1613,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1613
1613
|
${e}
|
|
1614
1614
|
</tr>
|
|
1615
1615
|
</thead>
|
|
1616
|
-
`}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
|
|
1616
|
+
`}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 l;n&&typeof n=="string"?l=`{{{footerTotals.${r}.value|${n}}}}`:l=`{{footerTotals.${r}.value}}`,e+=`<td class="table-footer-total ${a}" data-total-column="${r}">${l}</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>"),`
|
|
1617
1617
|
<tfoot>
|
|
1618
1618
|
<tr class="table-totals-row">
|
|
1619
1619
|
${e}
|
|
@@ -1695,29 +1695,29 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1695
1695
|
</nav>
|
|
1696
1696
|
</div>
|
|
1697
1697
|
</div>
|
|
1698
|
-
`:""}_createItemView(e,t){const s=new this.itemClass({model:e,index:t,listView:this,tableView:this,template:this.itemTemplate,columns:this.columns,actions:this.actions,contextMenu:this.contextMenu,batchActions:this.batchActions,containerId:"items"});return this.itemViews.set(e.id,s),s.on("item:select",i=>{this._onItemSelect(i),this.updateBatchActionsPanel()}),s.on("item:deselect",i=>{this._onItemDeselect(i),this.updateBatchActionsPanel()}),s.on("row:click",this._onRowClick.bind(this)),s.on("row:view",this._onRowView.bind(this)),s.on("row:edit",this._onRowEdit.bind(this)),s.on("row:delete",this._onRowDelete.bind(this)),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onMounted(){await super.onMounted();const e=this.getActiveFilters();this.collection&&Object.keys(e).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){if(!this.element)return;this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",s=>{s.target.value===""&&this.getActiveFilters().search&&this.onActionClearSearch(s,s.target)})})}_onRowClick(e){if(this.emit("row:click",e),this.options.onRowClick)return this.options.onRowClick(e.model,e.event);this.clickAction==="view"?this._onRowView(e):this.clickAction==="edit"&&this._onRowEdit(e)}getModelClass(e){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:e?.constructor?e.constructor:null}getModelName(e){const t=this.getModelClass(e);return t&&(t.MODEL_NAME||t.name.replace(/Model$/,""))||"Item"}getItemViewClass(e){if(this.itemView)return this.itemView;const t=this.getModelClass(e);return t?.VIEW_CLASS?t.VIEW_CLASS:null}getAddFormConfig(e){return this.addForm||e?.ADD_FORM||this.editForm||e?.EDIT_FORM}getEditFormConfig(e){return this.editForm||e?.EDIT_FORM||this.addForm||e?.ADD_FORM}getFormDialogConfig(e){return{...e?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(e,t){return e?E.render(e,t):""}async _onRowView(e){if(this.emit("row:view",e),this.options.onItemView){await this.options.onItemView(e.model,e.event);return}const t=this.getItemViewClass(e.model);if(t){const s=new t({model:e.model});await k.showDialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await k.showData({title:`View ${this.getModelName(e.model)} #${e.model.id}`,model:e.model})}async _onRowEdit(e){if(this.emit("row:edit",e),this.options.onItemEdit){await this.options.onItemEdit(e.model,e.event);return}const t=this.getModelClass(e.model);let s=this.getEditFormConfig(t);if(s){s.fields||(s={title:`Edit ${this.getModelName(e.model)}`,fields:s});const i=await k.showModelForm({model:e.model,...s,...this.getFormDialogConfig(t)});if(!i)return;if(!i.success||!i?.result?.data.status){k.showError(i?.result?.data?.error||i?.result?.message||"An error occurred");return}}else{const i=await k.showDialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new H({model:e.model,fields:this.options.formFields||[]})});if(i){const a=await e.model.save(i);if(!a.data?.status){k.showError(a.data.error||"An error occurred");return}await this.refresh()}}}async _onRowDelete(e){if(this.emit("row:delete",e),this.options.onItemDelete){await this.options.onItemDelete(e.model,e.event);return}const t=this.getModelClass(e.model),s=this.deleteTemplate||t?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',i=this.renderTemplateString(s,e.model);await k.confirm({message:i||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await e.model.destroy(),this.collection.fetch())}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){this.isFullscreen?await this.exitFullscreen():await this.enterFullscreen()}async enterFullscreen(){try{this.element.requestFullscreen?await this.element.requestFullscreen():this.element.mozRequestFullScreen?await this.element.mozRequestFullScreen():this.element.webkitRequestFullscreen?await this.element.webkitRequestFullscreen():this.element.msRequestFullscreen&&await this.element.msRequestFullscreen(),this.isFullscreen=!0,this.element.classList.add("table-fullscreen"),this.updateFullscreenButton(),this.setupFullscreenListeners(),this.emit("table:fullscreen:enter")}catch(e){console.warn("Could not enter fullscreen:",e)}}async exitFullscreen(){try{document.exitFullscreen?await document.exitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.msExitFullscreen&&await document.msExitFullscreen(),this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit")}catch(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!!!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)&&this.isFullscreen&&(this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit"))};document.addEventListener("fullscreenchange",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}cleanupFullscreenListeners(){this._fullscreenHandler&&(document.removeEventListener("fullscreenchange",this._fullscreenHandler),document.removeEventListener("mozfullscreenchange",this._fullscreenHandler),document.removeEventListener("webkitfullscreenchange",this._fullscreenHandler),document.removeEventListener("msfullscreenchange",this._fullscreenHandler),this._fullscreenHandler=null)}destroy(){this.cleanupFullscreenListeners(),super.destroy()}async onActionRefresh(e,t){await this.refresh()}async onActionAdd(e,t){if(this.options.onAdd){this.emit("table:add",{event:e}),await this.options.onAdd(e);return}this.emit("table:add",{event:e});const s=this.getModelClass();if(!s){console.warn("Cannot determine Model class for add operation");return}let i=this.getAddFormConfig(s);if(i){const a=new s;i.fields||(i={title:`Add ${this.getModelName()}`,fields:i});const r=await k.showForm({model:a,...i,...this.getFormDialogConfig(s)});if(r){this.options.addRequiresActiveGroup&&(r.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(r.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(r,this.options.addFormDefaults);const n=await a.save(r);if(!n?.data.status){k.showError(n?.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}else{const a=new s,r=await k.showDialog({title:`Add ${this.getModelName()}`,body:new H({model:a,fields:this.options.formFields||[]})});if(r){const n=await a.save(r);if(!n?.data.status){k.showError(n.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";this.emit("table:export",{format:s,source:this.exportSource,event:e}),this.exportSource==="remote"?this.collection?await this.collection.download(s):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],s):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(e,t){const s=t.value.trim();this.collection&&(this.setFilter("search",s),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:s,event:e}),this.emit("params-changed")}async onActionClearSearch(e,t){this.setFilter("search",null),this.collection&&(this.collection.params.start=0,this.collection.restEnabled&&await this.collection.fetch()),await this.render(),this.updateFilterPills(),this.emit("table:search",{searchTerm:"",event:e}),this.emit("params-changed")}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return e==="asc"?'<i class="bi bi-sort-alpha-down text-primary"></i>':e==="desc"?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),i=t.getAttribute("data-direction");if(this.collection){let a;if(i==="none"?a=void 0:i==="desc"?a=`-${s}`:a=s,this.collection.setParams({...this.collection.params,sort:a,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(a){const r=a.startsWith("-"),n=r?a.slice(1):a;this.collection.sort((o,l)=>{const c=o.get(n),d=l.get(n);return c<d?r?1:-1:c>d?r?-1:1:0})}this.render()}}this.updateSortIcons(),this.emit("table:sort",{field:s,event:e}),this.emit("params-changed")}updateSortIcons(){if(!this.element)return;const e=this.getSortBy(),t=this.getSortDirection();this.columns.forEach(s=>{if(this.sortable&&s.sortable!==!1){const{fieldKey:i}=this.parseColumnKey(s.key),a=this.element.querySelector(`[data-bs-toggle="dropdown"][data-column="${i}"]`);if(a){const r=e===i,n=this.getSortIcon(r?t:null);a.innerHTML=n;const o=a.nextElementSibling;if(o){const l=o.querySelector(`[data-field="${i}"][data-direction="asc"]`),c=o.querySelector(`[data-field="${i}"][data-direction="desc"]`),d=o.querySelector(`[data-field="${i}"][data-direction="none"]`);l&&l.classList.toggle("active",r&&t==="asc"),c&&c.classList.toggle("active",r&&t==="desc"),d&&d.classList.toggle("active",!r||e!==i)}}}})}async onActionSelectAll(e,t){e.stopPropagation();const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(a=>a.selected);s?this.clearSelection():this.forEachItem(a=>{a.selected||a.select()});const i=this.element?.querySelector(".mojo-select-all-cell");i&&i.classList.toggle("selected",!s),this.updateBatchActionsPanel()}async onBeforeRender(){this.searchValue=this.getActiveFilters().search||"",this.footerTotals=this.calculateFooterTotals()}async onAfterRender(){if(await super.onAfterRender(),this.hasFooterTotals&&this.updateFooterTotals(),this.paginated&&this.collection){const e=this.collection.meta?.count||this.collection.length(),t=this.collection.params?.start||0,s=this.collection.params?.size||10,i=Math.min(t+s,e),a=this.element.querySelector('[data-value="start"]'),r=this.element.querySelector('[data-value="end"]'),n=this.element.querySelector('[data-value="total"]');a&&(a.textContent=t+1),r&&(r.textContent=i),n&&(n.textContent=e);const o=this.element.querySelector('[data-change-action="page-size"]');o&&(o.value=s),this.renderPagination()}this.updateSortIcons(),this.updateFilterPills(),this.setupSearchClearListener()}renderPagination(){const e=this.element.querySelector('[data-container="pagination"]');if(!e||!this.collection)return;const t=this.collection.meta?.count||this.collection.length(),s=this.collection.params?.size||10,i=this.collection.params?.start||0,a=Math.floor(i/s)+1,r=Math.ceil(t/s);if(r<=1){e.innerHTML="";return}const n=a>1?a-1:r,o=a<r?a+1:1,l=[];l.push(`
|
|
1698
|
+
`:""}_createItemView(e,t){const s=new this.itemClass({model:e,index:t,listView:this,tableView:this,template:this.itemTemplate,columns:this.columns,actions:this.actions,contextMenu:this.contextMenu,batchActions:this.batchActions,containerId:"items"});return this.itemViews.set(e.id,s),s.on("item:select",i=>{this._onItemSelect(i),this.updateBatchActionsPanel()}),s.on("item:deselect",i=>{this._onItemDeselect(i),this.updateBatchActionsPanel()}),s.on("row:click",this._onRowClick.bind(this)),s.on("row:view",this._onRowView.bind(this)),s.on("row:edit",this._onRowEdit.bind(this)),s.on("row:delete",this._onRowDelete.bind(this)),s.on("cell:edit",this._onCellEdit.bind(this)),s.on("cell:save",this._onCellSave.bind(this)),s.on("cell:cancel",this._onCellCancel.bind(this)),s}async onMounted(){await super.onMounted();const e=this.getActiveFilters();this.collection&&Object.keys(e).length>0&&this.updateFilterPills(),this.setupSearchClearListener()}setupSearchClearListener(){if(!this.element)return;this.element.querySelectorAll('input[type="search"][data-filter="search"]').forEach(t=>{t.addEventListener("input",s=>{s.target.value===""&&this.getActiveFilters().search&&this.onActionClearSearch(s,s.target)})})}_onRowClick(e){if(this.emit("row:click",e),this.options.onRowClick)return this.options.onRowClick(e.model,e.event);this.clickAction==="view"?this._onRowView(e):this.clickAction==="edit"&&this._onRowEdit(e)}getModelClass(e){return this.collection?.ModelClass?this.collection.ModelClass:this.collection?.model?this.collection.model:e?.constructor?e.constructor:null}getModelName(e){const t=this.getModelClass(e);return t&&(t.MODEL_NAME||t.name.replace(/Model$/,""))||"Item"}getItemViewClass(e){if(this.itemView)return this.itemView;const t=this.getModelClass(e);return t?.VIEW_CLASS?t.VIEW_CLASS:null}getAddFormConfig(e){return this.addForm||e?.ADD_FORM||this.editForm||e?.EDIT_FORM}getEditFormConfig(e){return this.editForm||e?.EDIT_FORM||this.addForm||e?.ADD_FORM}getFormDialogConfig(e){return{...e?.FORM_DIALOG_CONFIG,...this.formDialogConfig}}renderTemplateString(e,t){return e?E.render(e,t):""}async _onRowView(e){if(this.emit("row:view",e),this.options.onItemView){await this.options.onItemView(e.model,e.event);return}const t=this.getItemViewClass(e.model);if(t){const s=new t({model:e.model,collection:this.collection});await k.showDialog({header:!1,body:s,size:"lg",centered:!1,...this.getFormDialogConfig(this.getModelClass(e.model)),...this.viewDialogOptions})}else await k.showData({title:`View ${this.getModelName(e.model)} #${e.model.id}`,model:e.model})}async _onRowEdit(e){if(this.emit("row:edit",e),this.options.onItemEdit){await this.options.onItemEdit(e.model,e.event);return}const t=this.getModelClass(e.model);let s=this.getEditFormConfig(t);if(s){s.fields||(s={title:`Edit ${this.getModelName(e.model)}`,fields:s});const i=await k.showModelForm({model:e.model,...s,...this.getFormDialogConfig(t)});if(!i)return;if(!i.success||!i?.result?.data.status){k.showError(i?.result?.data?.error||i?.result?.message||"An error occurred");return}}else{const i=await k.showDialog({title:`Edit ${this.getModelName(e.model)} #${e.model.id}`,body:new H({model:e.model,fields:this.options.formFields||[]})});if(i){const a=await e.model.save(i);if(!a.data?.status){k.showError(a.data.error||"An error occurred");return}await this.refresh()}}}async _onRowDelete(e){if(this.emit("row:delete",e),this.options.onItemDelete){await this.options.onItemDelete(e.model,e.event);return}const t=this.getModelClass(e.model),s=this.deleteTemplate||t?.DELETE_TEMPLATE||'Are you sure you want to delete this {{name||"item"}}?',i=this.renderTemplateString(s,e.model);await k.confirm({message:i||"Are you sure you want to delete this item?",title:"Confirm Delete",confirmText:"Delete",confirmClass:"btn-danger"})&&(await e.model.destroy(),this.collection.fetch())}_onCellEdit(e){this.emit("cell:edit",e)}async _onCellSave(e){this.emit("cell:save",e)}_onCellCancel(e){this.emit("cell:cancel",e)}isFullscreenSupported(){return!!(document.fullscreenEnabled||document.mozFullScreenEnabled||document.webkitFullscreenEnabled||document.msFullscreenEnabled)}async onActionToggleFullscreen(e,t){this.isFullscreen?await this.exitFullscreen():await this.enterFullscreen()}async enterFullscreen(){try{this.element.requestFullscreen?await this.element.requestFullscreen():this.element.mozRequestFullScreen?await this.element.mozRequestFullScreen():this.element.webkitRequestFullscreen?await this.element.webkitRequestFullscreen():this.element.msRequestFullscreen&&await this.element.msRequestFullscreen(),this.isFullscreen=!0,this.element.classList.add("table-fullscreen"),this.updateFullscreenButton(),this.setupFullscreenListeners(),this.emit("table:fullscreen:enter")}catch(e){console.warn("Could not enter fullscreen:",e)}}async exitFullscreen(){try{document.exitFullscreen?await document.exitFullscreen():document.mozCancelFullScreen?await document.mozCancelFullScreen():document.webkitExitFullscreen?await document.webkitExitFullscreen():document.msExitFullscreen&&await document.msExitFullscreen(),this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit")}catch(e){console.warn("Could not exit fullscreen:",e)}}updateFullscreenButton(){const e=this.element?.querySelector(".btn-fullscreen"),t=e?.querySelector("i");e&&t&&(this.isFullscreen?(t.className="bi bi-fullscreen-exit",e.title="Exit Fullscreen"):(t.className="bi bi-fullscreen",e.title="Enter Fullscreen"))}setupFullscreenListeners(){if(this._fullscreenHandler)return;const e=()=>{!!!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)&&this.isFullscreen&&(this.isFullscreen=!1,this.element.classList.remove("table-fullscreen"),this.updateFullscreenButton(),this.emit("table:fullscreen:exit"))};document.addEventListener("fullscreenchange",e),document.addEventListener("mozfullscreenchange",e),document.addEventListener("webkitfullscreenchange",e),document.addEventListener("msfullscreenchange",e),this._fullscreenHandler=e}cleanupFullscreenListeners(){this._fullscreenHandler&&(document.removeEventListener("fullscreenchange",this._fullscreenHandler),document.removeEventListener("mozfullscreenchange",this._fullscreenHandler),document.removeEventListener("webkitfullscreenchange",this._fullscreenHandler),document.removeEventListener("msfullscreenchange",this._fullscreenHandler),this._fullscreenHandler=null)}destroy(){this.cleanupFullscreenListeners(),super.destroy()}async onActionRefresh(e,t){await this.refresh()}async onActionAdd(e,t){if(this.options.onAdd){this.emit("table:add",{event:e}),await this.options.onAdd(e);return}this.emit("table:add",{event:e});const s=this.getModelClass();if(!s){console.warn("Cannot determine Model class for add operation");return}let i=this.getAddFormConfig(s);if(i){const a=new s;i.fields||(i={title:`Add ${this.getModelName()}`,fields:i});const r=await k.showForm({model:a,...i,...this.getFormDialogConfig(s)});if(r){this.options.addRequiresActiveGroup&&(r.group=this.getApp().activeGroup.id),this.options.addRequiresActiveUser&&(r.user=this.getApp().activeUser.id),this.options.addFormDefaults&&Object.assign(r,this.options.addFormDefaults);const n=await a.save(r);if(!n?.data.status){k.showError(n?.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}else{const a=new s,r=await k.showDialog({title:`Add ${this.getModelName()}`,body:new H({model:a,fields:this.options.formFields||[]})});if(r){const n=await a.save(r);if(!n?.data.status){k.showError(n.data.error||"An error occurred");return}this.collection&&this.collection.add(a),await this.refresh()}}}async onActionExport(e,t){const s=t.getAttribute("data-format")||"json";this.emit("table:export",{format:s,source:this.exportSource,event:e}),this.exportSource==="remote"?this.collection?await this.collection.download(s):console.warn("TableView: Cannot export from remote without a collection."):this.options.onExport?await this.options.onExport(this.collection?.toJSON()||[],s):console.warn("TableView: onExport handler not implemented for local export.")}async onActionApplySearch(e,t){const s=t.value.trim();this.collection&&(this.setFilter("search",s),this.collection.params.start=0,this.collection.restEnabled?await this.collection.fetch():this.render()),this.updateFilterPills(),this.emit("table:search",{searchTerm:s,event:e}),this.emit("params-changed")}async onActionClearSearch(e,t){this.setFilter("search",null),this.collection&&(this.collection.params.start=0,this.collection.restEnabled&&await this.collection.fetch()),await this.render(),this.updateFilterPills(),this.emit("table:search",{searchTerm:"",event:e}),this.emit("params-changed")}getSortBy(){const e=this.collection?.params?.sort;return e?e.startsWith("-")?e.slice(1):e:null}getSortDirection(){const e=this.collection?.params?.sort;return e&&e.startsWith("-")?"desc":"asc"}getSortIcon(e){return e==="asc"?'<i class="bi bi-sort-alpha-down text-primary"></i>':e==="desc"?'<i class="bi bi-sort-alpha-down-alt text-primary"></i>':'<i class="bi bi-three-dots-vertical text-muted"></i>'}async onActionSort(e,t){e.preventDefault();const s=t.getAttribute("data-field"),i=t.getAttribute("data-direction");if(this.collection){let a;if(i==="none"?a=void 0:i==="desc"?a=`-${s}`:a=s,this.collection.setParams({...this.collection.params,sort:a,start:0}),this.collection.restEnabled)await this.collection.fetch();else{if(a){const r=a.startsWith("-"),n=r?a.slice(1):a;this.collection.sort((l,o)=>{const c=l.get(n),d=o.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 l=a.nextElementSibling;if(l){const o=l.querySelector(`[data-field="${i}"][data-direction="asc"]`),c=l.querySelector(`[data-field="${i}"][data-direction="desc"]`),d=l.querySelector(`[data-field="${i}"][data-direction="none"]`);o&&o.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 l=this.element.querySelector('[data-change-action="page-size"]');l&&(l.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,l=a<r?a+1:1,o=[];o.push(`
|
|
1699
1699
|
<li class="page-item">
|
|
1700
1700
|
<a class="page-link" href="#" data-action="page" data-page="${n}">
|
|
1701
1701
|
<i class="bi bi-chevron-left"></i>
|
|
1702
1702
|
</a>
|
|
1703
1703
|
</li>
|
|
1704
|
-
`);const c=1,d=new Set([1,r]);for(let m=a-c;m<=a+c;m++)m>=1&&m<=r&&d.add(m);const h=Array.from(d).sort((m,p)=>m-p);let f=0;for(const m of h)f&&m-f>1&&
|
|
1704
|
+
`);const c=1,d=new Set([1,r]);for(let m=a-c;m<=a+c;m++)m>=1&&m<=r&&d.add(m);const h=Array.from(d).sort((m,p)=>m-p);let f=0;for(const m of h)f&&m-f>1&&o.push(`
|
|
1705
1705
|
<li class="page-item disabled"><span class="page-link">…</span></li>
|
|
1706
|
-
`),
|
|
1706
|
+
`),o.push(`
|
|
1707
1707
|
<li class="page-item ${m===a?"active":""}">
|
|
1708
1708
|
<a class="page-link" href="#" data-action="page" data-page="${m}">${m}</a>
|
|
1709
1709
|
</li>
|
|
1710
|
-
`),f=m;
|
|
1710
|
+
`),f=m;o.push(`
|
|
1711
1711
|
<li class="page-item">
|
|
1712
|
-
<a class="page-link" href="#" data-action="page" data-page="${
|
|
1712
|
+
<a class="page-link" href="#" data-action="page" data-page="${l}">
|
|
1713
1713
|
<i class="bi bi-chevron-right"></i>
|
|
1714
1714
|
</a>
|
|
1715
1715
|
</li>
|
|
1716
|
-
`),e.innerHTML=
|
|
1716
|
+
`),e.innerHTML=o.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(l=>{if(l.config.type==="daterange"){const o=l.key,c=l.config.startName||"dr_start",d=l.config.endName||"dr_end",h=l.config.fieldName||"dr_field";i[h]===o&&(i[c]||i[d])&&(a[o]={start:i[c]||"",end:i[d]||""},r.add(c),r.add(d),r.add(h))}}),Object.keys(i).forEach(l=>{r.has(l)||(a[l]=i[l])}),Object.keys(a).forEach(l=>{if(a.hasOwnProperty(l)){const o=`${l}__in`;a.hasOwnProperty(o)&&(delete a[l],a[o]=a[o])}}),a}setFilter(e,t){if(!this.collection)return;const s=this.getFilterConfig(e);if(s&&s.type==="daterange"){const i=s.startName||"dr_start",a=s.endName||"dr_end",r=s.fieldName||"dr_field";delete this.collection.params[i],delete this.collection.params[a],delete this.collection.params[r],t&&typeof t=="object"&&(t.start||t.end)&&(t.start&&(this.collection.params[i]=t.start),t.end&&(this.collection.params[a]=t.end),this.collection.params[r]=e)}else{const{field:i,lookup:a}=G(e);if(delete this.collection.params[e],delete this.collection.params[i],delete this.collection.params[`${i}__in`],!t||Array.isArray(t)&&t.length===0)return;Array.isArray(t)?t.length===1?this.collection.params[i]=t[0]:this.collection.params[`${i}__in`]=t.join(","):this.collection.params[e]=t}}getAllAvailableFilters(){const e=[];return this.columns.forEach(t=>{if(t.filter){const{fieldKey:s}=this.parseColumnKey(t.key);e.push({key:s,label:t.filter.label||t.label||s,type:t.filter.type,config:t.filter})}}),this.additionalFilters&&Array.isArray(this.additionalFilters)&&this.additionalFilters.forEach(t=>{e.push({key:t.name||t.key,label:t.label,type:t.type,config:t})}),e}getFilterConfig(e){const t=this.columns.find(s=>{const{fieldKey:i}=this.parseColumnKey(s.key);return i===e});if(t&&t.filter)return t.filter;if(this.additionalFilters&&Array.isArray(this.additionalFilters)){const s=this.additionalFilters.find(i=>(i.name||i.key)===e);if(s)return s}return null}getFilterLabel(e){if(e==="search")return"Search";const t=this.filters[e];if(t&&t.label)return t.label;const s=this.additionalFilters.find(i=>(i.name||i.key)===e);return s&&s.label?s.label:e.charAt(0).toUpperCase()+e.slice(1)}getFilterDisplayValue(e,t){if(e==="search")return`"${t}"`;const s=this.filters[e]||this.additionalFilters.find(i=>(i.name||i.key)===e);if(s&&s.type==="daterange"&&typeof t=="object"){const i=t.start||"",a=t.end||"";return`${i} to ${a}`}if(s&&s.type==="select"&&s.options){if(typeof s.options[0]=="object"){const i=s.options.find(a=>a.value===t);return i?i.label:t}return t}return t}getFilterIcon(e){return{text:"search",select:"funnel",date:"calendar",daterange:"calendar-range",number:"123",boolean:"toggle-on"}[e]||"filter"}async onActionAddFilter(e,t){const s=t.getAttribute("data-filter-key"),i=this.getFilterConfig(s),a=this.getActiveFilters()[s];if(!i){console.warn("No filter config found for key:",s);return}const r=await k.showForm({title:`${a!==void 0&&a!==""?"Edit":"Add"} ${this.getFilterLabel(s)} Filter`,size:"md",fields:[this.buildFilterDialogField(i,a,s)]});if(r){const n=this.extractFilterValue(i,r);this.setFilter(s,n),await this.applyFilters()}}buildFilterDialogField(e,t,s){const i={name:"filter_value",label:e.label,value:t,...e,placeholder:e.placeholder||e.placeHolder};if(e.type==="daterange"){if(i.startName=i.startName||"dr_start",i.endName=i.endName||"dr_end",i.fieldName=i.fieldName||"dr_field",i.format=i.format||"YYYY-MM-DD",i.displayFormat=i.displayFormat||"MMM DD, YYYY",i.separator=i.separator||" to ",i.label=i.label||"Date Range",t&&typeof t=="object"){const a=r=>{if(!r&&r!==0)return"";if(r instanceof Date&&!isNaN(r))return r.toISOString().slice(0,10);const n=String(r).trim();if(!n)return"";if(/^-?\d+$/.test(n)){const o=Number(n),c=n.length<=10?o*1e3:o,d=new Date(c);if(!isNaN(d))return d.toISOString().slice(0,10)}const l=new Date(n);return isNaN(l)?n:l.toISOString().slice(0,10)};i.startDate=a(t.start||t.from||t.begin||""),i.endDate=a(t.end||t.to||t.finish||"")}}else if(e.type==="multiselect"){let a=[];t&&(Array.isArray(t)?a=t:typeof t=="string"&&(a=t.split(",").map(r=>r.trim()).filter(r=>r))),i.value=a,!i.placeholder&&!i.placeHolder&&(e.placeholder||e.placeHolder?i.placeholder=e.placeholder||e.placeHolder:e.label&&(i.placeholder=`Select ${e.label}...`))}return i}extractFilterValue(e,t){if(e.type==="daterange"){const s=e.startName||"dr_start",i=e.endName||"dr_end";return{start:t[s],end:t[i]}}return e.type==="multiselect",t.filter_value}async applyFilters(){if(this.collection&&(this.collection.params.start=0),this.collection?.restEnabled)try{await this.collection.fetch(),this.render()}catch(e){console.error("Failed to fetch filtered data:",e),this.render()}else this.render();this.updateFilterPills(),this.emit("params-changed")}async onActionEditFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=G(s);let a=this.getFilterConfig(i)||this.getFilterConfig(s);const r=this.getActiveFilters(),n=r[s]||r[i];if(!a){console.warn("No filter config found for key:",s,"or field:",i);return}const l={filter_value:n};if(a.type==="daterange"&&n&&typeof n=="object"){const c=a.startName||"dr_start",d=a.endName||"dr_end";l[c]=n.start||"",l[d]=n.end||""}const o=await k.showForm({title:`Edit ${this.getFilterLabel(i)} Filter`,size:"md",data:l,fields:[this.buildFilterDialogField(a,n,i)]});if(o){const c=this.extractFilterValue(a,o);this.setFilter(s,c),await this.applyFilters()}}async onActionRemoveFilter(e,t){const s=t.getAttribute("data-filter"),{field:i}=G(s);this.setFilter(s,null),s==="search"&&this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filter:remove",{key:s,field:i}),this.emit("params-changed")}async onActionClearAllFilters(e,t){if(!this.collection)return;const{start:s,size:i,sort:a}=this.collection.params;this.collection.params={start:s,size:i},a&&(this.collection.params.sort=a),this.updateSearchInputs(""),this.collection.restEnabled&&await this.collection.fetch(),this.render(),this.updateFilterPills(),this.emit("filters:clear"),this.emit("params-changed")}updateBatchActionsPanel(){if(!this.batchActions||this.batchActions.length===0)return;const e=this.getSelectedItems().length;if(this.batchBarLocation==="top"){const s=this.element?.querySelector(".batch-actions-panel-top"),i=this.element?.querySelector(".batch-select-count");s&&i&&(i.textContent=e,e>0?s.classList.remove("d-none"):s.classList.add("d-none"))}else{const s=this.element?.querySelector(".batch-actions-panel"),i=this.element?.querySelector(".batch-select-count");s&&i&&(i.textContent=e,s.style.display=e>0?"block":"none")}const t=this.element?.querySelector(".mojo-select-all-cell");if(t){const s=this.itemViews.size>0&&Array.from(this.itemViews.values()).every(r=>r.selected),i=Array.from(this.itemViews.values()).some(r=>r.selected);t.classList.toggle("selected",s),t.classList.toggle("indeterminate",!s&&i);const a=t.querySelector("i");a&&(a.className=!s&&i?"bi bi-dash":"bi bi-check")}}async onActionBatch(e,t){const s=t.getAttribute("data-action").replace("batch-",""),i=this.getSelectedItems();this.emit("batch:action",{action:s,items:i,event:e})}async onActionClearSelection(e,t){this.clearSelection(),this.updateBatchActionsPanel()}async onActionCustomToolbarButton(e,t){const s=parseInt(t.getAttribute("data-button-index"),10),i=this.toolbarButtons[s];i&&typeof i.handler=="function"&&await i.handler.call(this,e,t)}}function We(){return typeof window>"u"?null:((!window.MOJO||typeof window.MOJO!="object")&&(window.MOJO={}),window.MOJO)}function Je(u){return u.__lite&&u.__lite.version||(u.WebApp=K,u.View=v,u.Page=W,u.Router=te,u.Model=X,u.Collection=J,u.Rest=z,u.FormBuilder=Z,u.FormView=H,u.Dialog=k,u.ProgressView=$e,u.ListView=he,u.ListViewItem=ee,u.TableView=Fe,u.TableRow=ue,u.DataFormatter=V,u.MOJOUtils=x,u.__lite={version:"dev",build:"web-mojo.lite"},u.mount=async function(t,s){if(!t)throw new Error("MOJO.mount(view, container) requires a view");const i=typeof s=="string"?document.querySelector(s):s;if(!i)throw new Error("MOJO.mount(view, container) container not found");if(typeof t.render!="function"||typeof t.mount!="function")throw new Error("MOJO.mount expects a View instance with render() and mount() methods");return await t.render(),await t.mount(i),t}),u}const fe=We();fe&&Je(fe);class me extends v{constructor(e={}){const{data:t,model:s,fields:i,columns:a,responsive:r,showEmptyValues:n,emptyValueText:l,...o}=e;super({tagName:"div",className:"data-view",...o}),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:l||"—",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`
|
|
1717
1717
|
<div class="${this.dataViewOptions.rowClass}">
|
|
1718
1718
|
${e}
|
|
1719
1719
|
</div>
|
|
1720
|
-
`}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=V.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("|"),
|
|
1720
|
+
`}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=V.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("|"),l=n[0],o=n.slice(1).join("|");return this.model&&typeof this.model.get=="function"?r=this.model.get(l):r=l.split(".").reduce((c,d)=>c?c[d]:void 0,t),o&&(r=V.pipe(r,o)),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`
|
|
1721
1721
|
<div class="${this.getColumnClasses(e)}">
|
|
1722
1722
|
<div class="${this.dataViewOptions.itemClass} ${e.className}" data-field="${e.name}">
|
|
1723
1723
|
${this.buildLabelHTML(s,e)}
|
|
@@ -1725,7 +1725,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1725
1725
|
</div>
|
|
1726
1726
|
</div>
|
|
1727
1727
|
`}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(`
|
|
1728
|
-
`).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?"...":""),
|
|
1728
|
+
`).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?"...":""),l=this.escapeHtml(n);return`
|
|
1729
1729
|
<div class="json-container">
|
|
1730
1730
|
<div class="d-flex align-items-center justify-content-between mb-1">
|
|
1731
1731
|
<small class="text-muted">${Array.isArray(e)?"Array":"Object"} (${i} lines)</small>
|
|
@@ -1739,7 +1739,7 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1739
1739
|
</div>
|
|
1740
1740
|
</div>
|
|
1741
1741
|
<div class="json-preview bg-light p-2 rounded small border" style="font-family: 'Courier New', monospace;">
|
|
1742
|
-
<code class="text-muted">${
|
|
1742
|
+
<code class="text-muted">${l}</code>
|
|
1743
1743
|
</div>
|
|
1744
1744
|
<div class="collapse mt-2" id="${r}">
|
|
1745
1745
|
<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>
|
|
@@ -1761,5 +1761,5 @@ var MOJO=function(F){"use strict";class te{constructor(e={}){this.defaultRoute=e
|
|
|
1761
1761
|
${i}
|
|
1762
1762
|
</div>
|
|
1763
1763
|
</div>
|
|
1764
|
-
`}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&typeof this.model.set=="function"&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&typeof this.model.fetch=="function")try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?(s.format=t,s.formatter=t):this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t,formatter:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?(s.format+=`|${t}`,s.formatter=s.format):(s.format=t,s.formatter=t)),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData(),i=this.inferFormatter(s[e],e,t.type);t.format=i,t.formatter=i}return this}getFormattedValue(e,t=null){const s=this.getField(e);if(!s)return null;const i=t!==null?t:this.getData()[e],a=s.format||s.formatter;return a&&i!=null?V.pipe(i,a):i}setFieldFormats(e){return Object.entries(e).forEach(([t,s])=>{this.setFieldFormat(t,s)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{const s=t.format||t.formatter;s&&(e[t.name]=s)}),e}onInit(){super.onInit(),this.model&&typeof this.model.on=="function"&&this.model.on("change",()=>{this.render()})}static create(e={}){return new me(e)}}const Ze=Object.freeze(Object.defineProperty({__proto__:null,default:me},Symbol.toStringTag,{value:"Module"}));return
|
|
1764
|
+
`}catch(s){return console.error("Error creating nested DataView:",s),'<span class="text-danger">Error displaying nested data</span>'}}async updateData(e){return this.data=e,this.model&&typeof this.model.set=="function"&&this.model.set(e),this.fields.length>0&&!this.options.fields&&(this.fields=[]),await this.render(),this.emit("data:updated",{data:e}),this}async updateFields(e){return this.fields=e,await this.render(),this.emit("fields:updated",{fields:e}),this}async updateConfig(e){return this.dataViewOptions={...this.dataViewOptions,...e},await this.render(),this.emit("config:updated",{options:this.dataViewOptions}),this}async refresh(){if(this.model&&typeof this.model.fetch=="function")try{await this.model.fetch(),this.emit("data:refreshed",{model:this.model})}catch(e){throw this.emit("error",{error:e,message:"Failed to refresh data"}),e}return this}getCurrentData(){return this.getData()}getField(e){return this.fields.find(t=>t.name===e)||null}setFieldFormat(e,t){const s=this.getField(e);return s?(s.format=t,s.formatter=t):this.fields.push({name:e,label:this.formatLabel(e),type:this.inferFieldType(this.getData()[e],e),format:t,formatter:t}),this}addFormatPipe(e,t){const s=this.getField(e);return s&&(s.format?(s.format+=`|${t}`,s.formatter=s.format):(s.format=t,s.formatter=t)),this}clearFieldFormat(e){const t=this.getField(e);if(t){const s=this.getData(),i=this.inferFormatter(s[e],e,t.type);t.format=i,t.formatter=i}return this}getFormattedValue(e,t=null){const s=this.getField(e);if(!s)return null;const i=t!==null?t:this.getData()[e],a=s.format||s.formatter;return a&&i!=null?V.pipe(i,a):i}setFieldFormats(e){return Object.entries(e).forEach(([t,s])=>{this.setFieldFormat(t,s)}),this}getFieldFormats(){const e={};return this.fields.forEach(t=>{const s=t.format||t.formatter;s&&(e[t.name]=s)}),e}onInit(){super.onInit(),this.model&&typeof this.model.on=="function"&&this.model.on("change",()=>{this.render()})}static create(e={}){return new me(e)}}const Ze=Object.freeze(Object.defineProperty({__proto__:null,default:me},Symbol.toStringTag,{value:"Module"}));return D.Collection=J,D.DataFormatter=V,D.Dialog=k,D.FormBuilder=Z,D.FormPage=Ue,D.FormView=H,D.ListView=he,D.ListViewItem=ee,D.MOJOUtils=x,D.Model=X,D.Page=W,D.ProgressView=$e,D.Rest=z,D.Router=te,D.TableRow=ue,D.TableView=Fe,D.View=v,D.WebApp=K,D.default=fe,Object.defineProperties(D,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),D}({});
|
|
1765
1765
|
//# sourceMappingURL=web-mojo.lite.iife.min.js.map
|