web-mojo 2.2.47 → 2.2.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +18 -13
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.es.js +4 -4
  9. package/dist/chunks/{ChatView-BT_nD9K9.js → ChatView-BUfkeVnX.js} +14 -7
  10. package/dist/chunks/ChatView-BUfkeVnX.js.map +1 -0
  11. package/dist/chunks/ChatView-Tj1Kb9Hf.js +2 -0
  12. package/dist/chunks/ChatView-Tj1Kb9Hf.js.map +1 -0
  13. package/dist/chunks/{Collection-C_Ssejpi.js → Collection-D3K_bEJN.js} +2 -2
  14. package/dist/chunks/{Collection-C_Ssejpi.js.map → Collection-D3K_bEJN.js.map} +1 -1
  15. package/dist/chunks/{Collection-C5uDBFur.js → Collection-DpUI5_9f.js} +2 -2
  16. package/dist/chunks/{Collection-C5uDBFur.js.map → Collection-DpUI5_9f.js.map} +1 -1
  17. package/dist/chunks/{ContextMenu-leHTa126.js → ContextMenu-BSlbXHBy.js} +2 -2
  18. package/dist/chunks/{ContextMenu-leHTa126.js.map → ContextMenu-BSlbXHBy.js.map} +1 -1
  19. package/dist/chunks/{ContextMenu-4yi1YZwj.js → ContextMenu-sdxDECHa.js} +3 -3
  20. package/dist/chunks/{ContextMenu-4yi1YZwj.js.map → ContextMenu-sdxDECHa.js.map} +1 -1
  21. package/dist/chunks/{DataView-34YwqbHX.js → DataView-CACdHJV9.js} +2 -2
  22. package/dist/chunks/{DataView-34YwqbHX.js.map → DataView-CACdHJV9.js.map} +1 -1
  23. package/dist/chunks/{DataView-DNhn9WNt.js → DataView-DUOJRtqo.js} +2 -2
  24. package/dist/chunks/{DataView-DNhn9WNt.js.map → DataView-DUOJRtqo.js.map} +1 -1
  25. package/dist/chunks/{Dialog-G4K6rcmt.js → Dialog-BVDLaVFL.js} +5 -5
  26. package/dist/chunks/{Dialog-G4K6rcmt.js.map → Dialog-BVDLaVFL.js.map} +1 -1
  27. package/dist/chunks/{Dialog-DISGn8Ys.js → Dialog-BmkbDDol.js} +2 -2
  28. package/dist/chunks/{Dialog-DISGn8Ys.js.map → Dialog-BmkbDDol.js.map} +1 -1
  29. package/dist/chunks/{FormView-DezJ-BG_.js → FormView-CqiU2UkE.js} +3 -3
  30. package/dist/chunks/{FormView-DezJ-BG_.js.map → FormView-CqiU2UkE.js.map} +1 -1
  31. package/dist/chunks/{FormView-DTIezo2P.js → FormView-Cuqamyp7.js} +3 -3
  32. package/dist/chunks/{FormView-DTIezo2P.js.map → FormView-Cuqamyp7.js.map} +1 -1
  33. package/dist/chunks/{ListView-CCa2A8AN.js → ListView-B7_jgGeh.js} +2 -2
  34. package/dist/chunks/{ListView-CCa2A8AN.js.map → ListView-B7_jgGeh.js.map} +1 -1
  35. package/dist/chunks/{ListView-fAR_2dr2.js → ListView-ChN3NCAY.js} +3 -3
  36. package/dist/chunks/{ListView-fAR_2dr2.js.map → ListView-ChN3NCAY.js.map} +1 -1
  37. package/dist/chunks/{MetricsCountryMapView-B-f2p9Sa.js → MetricsCountryMapView-BDdS4s7L.js} +2 -2
  38. package/dist/chunks/{MetricsCountryMapView-B-f2p9Sa.js.map → MetricsCountryMapView-BDdS4s7L.js.map} +1 -1
  39. package/dist/chunks/{MetricsCountryMapView-CUOaeRfl.js → MetricsCountryMapView-Pu1VZIJT.js} +2 -2
  40. package/dist/chunks/{MetricsCountryMapView-CUOaeRfl.js.map → MetricsCountryMapView-Pu1VZIJT.js.map} +1 -1
  41. package/dist/chunks/{MetricsMiniChartWidget-DLg1F5tP.js → MetricsMiniChartWidget-BVS-pJks.js} +4 -4
  42. package/dist/chunks/{MetricsMiniChartWidget-DLg1F5tP.js.map → MetricsMiniChartWidget-BVS-pJks.js.map} +1 -1
  43. package/dist/chunks/{MetricsMiniChartWidget-B8KsG_h3.js → MetricsMiniChartWidget-ZqmLtt06.js} +2 -2
  44. package/dist/chunks/{MetricsMiniChartWidget-B8KsG_h3.js.map → MetricsMiniChartWidget-ZqmLtt06.js.map} +1 -1
  45. package/dist/chunks/{PDFViewer-cMv7WDvp.js → PDFViewer-Byo3kf7Z.js} +3 -3
  46. package/dist/chunks/{PDFViewer-cMv7WDvp.js.map → PDFViewer-Byo3kf7Z.js.map} +1 -1
  47. package/dist/chunks/{PDFViewer-D-NSWRwM.js → PDFViewer-CCW5ZvRb.js} +2 -2
  48. package/dist/chunks/{PDFViewer-D-NSWRwM.js.map → PDFViewer-CCW5ZvRb.js.map} +1 -1
  49. package/dist/chunks/Rest-BSz7rF0G.js +2 -0
  50. package/dist/chunks/Rest-BSz7rF0G.js.map +1 -0
  51. package/dist/chunks/{Rest-CtdC4cD9.js → Rest-DSzjaYYe.js} +77 -15
  52. package/dist/chunks/Rest-DSzjaYYe.js.map +1 -0
  53. package/dist/chunks/{TokenManager-D_WVmyKY.js → TokenManager-C07o8g1O.js} +2 -2
  54. package/dist/chunks/{TokenManager-D_WVmyKY.js.map → TokenManager-C07o8g1O.js.map} +1 -1
  55. package/dist/chunks/{TokenManager-DmqJ4qmb.js → TokenManager-DNwuLDw6.js} +5 -5
  56. package/dist/chunks/{TokenManager-DmqJ4qmb.js.map → TokenManager-DNwuLDw6.js.map} +1 -1
  57. package/dist/chunks/{WebApp-158Y9QSF.js → WebApp-1HetrsR0.js} +2 -2
  58. package/dist/chunks/{WebApp-158Y9QSF.js.map → WebApp-1HetrsR0.js.map} +1 -1
  59. package/dist/chunks/{WebApp-CiBl53Hl.js → WebApp-Do2IaAGx.js} +12 -12
  60. package/dist/chunks/{WebApp-CiBl53Hl.js.map → WebApp-Do2IaAGx.js.map} +1 -1
  61. package/dist/chunks/{WebSocketClient-BGnzEIjP.js → WebSocketClient-BlBSBvYP.js} +2 -2
  62. package/dist/chunks/{WebSocketClient-BGnzEIjP.js.map → WebSocketClient-BlBSBvYP.js.map} +1 -1
  63. package/dist/chunks/{WebSocketClient-iJiqRgNy.js → WebSocketClient-BvwLDqeR.js} +2 -2
  64. package/dist/chunks/{WebSocketClient-iJiqRgNy.js.map → WebSocketClient-BvwLDqeR.js.map} +1 -1
  65. package/dist/chunks/{version-7OD8J84F.js → version-DXeLxR_3.js} +4 -4
  66. package/dist/chunks/{version-7OD8J84F.js.map → version-DXeLxR_3.js.map} +1 -1
  67. package/dist/chunks/{version-Dra0sZ9N.js → version-e7n3DAiW.js} +2 -2
  68. package/dist/chunks/{version-Dra0sZ9N.js.map → version-e7n3DAiW.js.map} +1 -1
  69. package/dist/docit.cjs.js +1 -1
  70. package/dist/docit.es.js +6 -6
  71. package/dist/index.cjs.js +1 -1
  72. package/dist/index.cjs.js.map +1 -1
  73. package/dist/index.es.js +70 -17
  74. package/dist/index.es.js.map +1 -1
  75. package/dist/lightbox.cjs.js +1 -1
  76. package/dist/lightbox.es.js +5 -5
  77. package/dist/map.cjs.js +1 -1
  78. package/dist/map.es.js +5 -5
  79. package/dist/timeline.cjs.js +1 -1
  80. package/dist/timeline.es.js +4 -4
  81. package/dist/web-mojo.lite.css +154 -0
  82. package/dist/web-mojo.lite.iife.js +21760 -0
  83. package/dist/web-mojo.lite.iife.js.map +1 -0
  84. package/dist/web-mojo.lite.iife.min.js +1765 -0
  85. package/dist/web-mojo.lite.iife.min.js.map +1 -0
  86. package/package.json +4 -3
  87. package/dist/chunks/ChatView-BT_nD9K9.js.map +0 -1
  88. package/dist/chunks/ChatView-G_I8KNR_.js +0 -2
  89. package/dist/chunks/ChatView-G_I8KNR_.js.map +0 -1
  90. package/dist/chunks/Rest-Bd1ABsfh.js +0 -2
  91. package/dist/chunks/Rest-Bd1ABsfh.js.map +0 -1
  92. package/dist/chunks/Rest-CtdC4cD9.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-mojo.lite.iife.min.js","sources":["../src/core/Router.js","../src/core/utils/EventBus.js","../src/core/Rest.js","../src/core/WebApp.js","../src/core/utils/DataFormatter.js","../src/core/utils/MOJOUtils.js","../src/core/utils/mustache.js","../src/core/mixins/EventDelegate.js","../src/core/mixins/EventEmitter.js","../src/core/View.js","../src/core/Page.js","../src/core/Model.js","../src/core/Collection.js","../src/core/forms/FormPlugins.js","../src/core/forms/FormBuilder.js","../src/core/mixins/FileDropMixin.js","../src/core/forms/inputs/TagInput.js","../src/core/forms/inputs/CollectionSelect.js","../src/core/forms/inputs/CollectionMultiSelect.js","../src/core/forms/inputs/MultiSelectDropdown.js","../src/core/forms/inputs/DatePicker.js","../src/core/forms/inputs/DateRangePicker.js","../src/core/forms/inputs/ComboInput.js","../src/core/forms/inputs/ComboBox.js","../src/core/forms/FormView.js","../src/core/forms/FormPage.js","../src/core/views/feedback/Dialog.js","../src/core/views/feedback/ProgressView.js","../src/core/views/list/ListViewItem.js","../src/core/views/list/ListView.js","../src/core/views/table/TableRow.js","../src/core/utils/DjangoLookups.js","../src/core/views/table/TableView.js","../src/lite/index.js","../src/core/views/data/DataView.js"],"sourcesContent":["class Router {\n constructor(options = {}) {\n this.defaultRoute = options.defaultRoute || 'home';\n this.routes = [];\n this.currentRoute = null;\n this.eventEmitter = options.eventEmitter || null; // WebApp.events\n\n this.boundHandlePopState = this.handlePopState.bind(this);\n }\n\n start() {\n // Listen for browser navigation\n window.addEventListener('popstate', this.boundHandlePopState);\n\n // Handle current location\n this.handleCurrentLocation();\n }\n\n stop() {\n window.removeEventListener('popstate', this.boundHandlePopState);\n }\n\n addRoute(pattern, pageName) {\n this.routes.push({\n pattern: this.normalizePattern(pattern),\n regex: this.patternToRegex(pattern),\n pageName,\n paramNames: this.extractParamNames(pattern)\n });\n }\n\n async navigate(path, options = {}) {\n const { replace = false, state = null, trigger = true } = options;\n\n // Parse input to extract page name and query parameters\n const { pageName, queryParams } = this.parseInput(path);\n\n // Update browser URL\n // this.updateBrowserUrl(pageName, queryParams, replace, state);\n\n // Handle the route change\n if (trigger) {\n await this.handleRouteChange(pageName, queryParams);\n }\n }\n\n // Browser navigation\n back() {\n window.history.back();\n }\n\n forward() {\n window.history.forward();\n }\n\n // Get current route info\n getCurrentRoute() {\n return this.currentRoute;\n }\n\n getCurrentPath() {\n const { pageName, queryParams } = this.parseCurrentUrl();\n return this.buildPublicUrl(pageName, queryParams);\n }\n\n // Private methods\n handlePopState(_event) {\n if (this.allowPopState) {\n this.handleCurrentLocation();\n } else {\n console.warn('PopStateEvent is not allowed');\n }\n }\n\n async handleCurrentLocation() {\n const { pageName, queryParams } = this.parseCurrentUrl();\n await this.handleRouteChange(pageName, queryParams);\n }\n\n async handleRouteChange(pageName, queryParams) {\n // Convert page name to route path for matching\n const routePath = '/' + pageName;\n const route = this.matchRoute(routePath);\n\n // Build public URL for events\n const publicUrl = this.buildPublicUrl(pageName, queryParams);\n\n // If no route matched, emit 404\n if (!route) {\n console.log('No route matched for page:', pageName);\n if (this.eventEmitter) {\n this.eventEmitter.emit('route:notfound', { path: publicUrl });\n }\n return null;\n }\n\n // Route was found, process it\n this.currentRoute = route;\n\n // Emit route change event\n if (this.eventEmitter) {\n this.eventEmitter.emit('route:changed', {\n path: publicUrl,\n pageName: route.pageName,\n params: route.params,\n query: queryParams,\n route: route\n });\n }\n\n // Return route info for WebApp to handle\n return route;\n }\n\n matchRoute(path) {\n for (const route of this.routes) {\n const match = path.match(route.regex);\n if (match) {\n const params = {};\n route.paramNames.forEach((name, index) => {\n params[name] = match[index + 1];\n });\n\n return {\n ...route,\n params,\n path\n };\n }\n }\n return null;\n }\n\n // Parse any input format and extract page name + query params\n parseInput(input) {\n let pageName = this.defaultRoute;\n let queryParams = {};\n\n if (!input) {\n return { pageName, queryParams };\n }\n\n try {\n // First, check if input contains search params (regardless of format)\n if (input.includes('?')) {\n const [pathPart, queryPart] = input.split('?', 2);\n const urlParams = new URLSearchParams(queryPart);\n\n // If page parameter exists in search params, use it\n if (urlParams.has('page')) {\n pageName = urlParams.get('page') || this.defaultRoute;\n\n // Get all other parameters\n for (const [key, value] of urlParams) {\n if (key !== 'page') {\n queryParams[key] = value;\n }\n }\n } else {\n // No page parameter in search params, extract from path part\n if (pathPart.startsWith('/')) {\n pageName = pathPart.substring(1) || this.defaultRoute;\n } else {\n pageName = pathPart || this.defaultRoute;\n }\n\n // Still collect query parameters\n for (const [key, value] of urlParams) {\n queryParams[key] = value;\n }\n }\n } else if (input.startsWith('/')) {\n // Input: \"/admin\" (no query params)\n pageName = input.substring(1) || this.defaultRoute;\n } else {\n // Input: \"admin\" (plain page name)\n pageName = input;\n }\n } catch (error) {\n console.warn('Failed to parse input:', input, error);\n pageName = this.defaultRoute;\n queryParams = {};\n }\n\n return { pageName, queryParams };\n }\n\n // Parse current browser URL\n parseCurrentUrl() {\n const urlParams = new URLSearchParams(window.location.search);\n const pageName = urlParams.get('page') || this.defaultRoute;\n\n const queryParams = {};\n for (const [key, value] of urlParams) {\n if (key !== 'page') {\n queryParams[key] = value;\n }\n }\n\n return { pageName, queryParams };\n }\n\n // Build public URL format: ?page=admin&group=123\n buildPublicUrl(pageName, queryParams = {}) {\n const urlParams = new URLSearchParams();\n urlParams.set('page', pageName);\n\n Object.entries(queryParams).forEach(([key, value]) => {\n if (value !== null && value !== undefined && value !== '') {\n urlParams.set(key, String(value));\n }\n });\n\n return '?' + urlParams.toString();\n }\n\n // Update browser URL\n updateBrowserUrl(pageName, queryParams, replace, state) {\n const currentUrl = new URL(window.location.origin + window.location.pathname);\n currentUrl.searchParams.set('page', pageName);\n\n // Add all query parameters\n Object.entries(queryParams).forEach(([key, value]) => {\n if (value !== null && value !== undefined && value !== '') {\n currentUrl.searchParams.set(key, String(value));\n }\n });\n\n const url = currentUrl.toString();\n\n if (replace) {\n window.history.replaceState(state, '', url);\n } else {\n window.history.pushState(state, '', url);\n }\n }\n\n // Route pattern utilities\n patternToRegex(pattern) {\n // Convert /users/:id to /users/([^/]+)\n // Handle optional parameters like /:id? -> (?:/([^/]+))?\n let regexPattern = pattern\n .replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&') // Escape regex chars\n .replace(/\\/:([^/?]+)\\?/g, '(?:/([^/]+))?') // Optional params /:id? -> (?:/([^/]+))?\n .replace(/:([^/]+)/g, '([^/]+)'); // Required params :id\n\n return new RegExp(`^${regexPattern}$`);\n }\n\n extractParamNames(pattern) {\n const matches = pattern.match(/:([^/?]+)\\??/g) || [];\n return matches.map(match => match.replace(/[:?]/g, ''));\n }\n\n normalizePattern(pattern) {\n return pattern.startsWith('/') ? pattern : `/${pattern}`;\n }\n\n /**\n * Update URL parameters without triggering navigation\n * @param {object} params - Parameters to update in URL\n * @param {object} options - Options like { replace: true }\n */\n updateUrl(params = {}, options = {}) {\n const { replace = false } = options;\n\n const { pageName } = this.parseCurrentUrl();\n this.updateBrowserUrl(pageName, params, replace);\n }\n\n /**\n * Build URL for given page and query parameters\n */\n buildUrl(page, query = {}) {\n return this.buildPublicUrl(page, query);\n }\n\n /**\n * Check if two routes match (ignoring query parameters)\n * @param {string} route1 - First route in any format (\"/admin\", \"?page=admin\", \"admin\")\n * @param {string} route2 - Second route in any format\n * @returns {boolean} - True if routes point to the same page\n */\n doRoutesMatch(route1, route2) {\n if (!route1 || !route2) return false;\n\n // Parse both routes to extract page names\n const { pageName: pageName1 } = this.parseInput(route1);\n const { pageName: pageName2 } = this.parseInput(route2);\n\n // Compare normalized page names\n return pageName1 === pageName2;\n }\n}\n\nexport default Router;\n","/**\n * EventBus - Global event management utility for MOJO framework\n * Provides a centralized event system for communication between components\n */\n\nclass EventBus {\n constructor() {\n this.listeners = {};\n this.onceListeners = {};\n this.maxListeners = 100; // Prevent memory leaks\n this.debugMode = false; // Debug mode for development\n this.eventStats = {}; // Track event emission statistics\n }\n\n /**\n * Add event listener\n * @param {string|Array<string>} event - Event name or array of event names\n * @param {function} callback - Event callback\n * @returns {EventBus} This instance for chaining\n */\n on(event, callback) {\n if (typeof callback !== 'function') {\n throw new Error('Callback must be a function');\n }\n\n // Handle multiple events\n if (Array.isArray(event)) {\n event.forEach(eventName => this.on(eventName, callback));\n return this;\n }\n\n if (!this.listeners[event]) {\n this.listeners[event] = [];\n }\n\n // Check max listeners limit\n if (this.listeners[event].length >= this.maxListeners) {\n console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${event}`);\n }\n\n this.listeners[event].push(callback);\n return this;\n }\n\n /**\n * Add one-time event listener\n * @param {string|Array<string>} event - Event name or array of event names\n * @param {function} callback - Event callback\n * @returns {EventBus} This instance for chaining\n */\n once(event, callback) {\n if (typeof callback !== 'function') {\n throw new Error('Callback must be a function');\n }\n\n // Handle multiple events\n if (Array.isArray(event)) {\n event.forEach(eventName => this.once(eventName, callback));\n return this;\n }\n\n if (!this.onceListeners[event]) {\n this.onceListeners[event] = [];\n }\n\n this.onceListeners[event].push(callback);\n return this;\n }\n\n /**\n * Remove event listener\n * @param {string|Array<string>} event - Event name or array of event names\n * @param {function} callback - Event callback to remove\n * @returns {EventBus} This instance for chaining\n */\n off(event, callback) {\n // Handle multiple events\n if (Array.isArray(event)) {\n event.forEach(eventName => this.off(eventName, callback));\n return this;\n }\n\n if (!callback) {\n // Remove all listeners for event\n delete this.listeners[event];\n delete this.onceListeners[event];\n return this;\n }\n\n // Remove from regular listeners\n if (this.listeners[event]) {\n const index = this.listeners[event].indexOf(callback);\n if (index !== -1) {\n this.listeners[event].splice(index, 1);\n\n // Clean up empty arrays\n if (this.listeners[event].length === 0) {\n delete this.listeners[event];\n }\n }\n }\n\n // Remove from once listeners\n if (this.onceListeners[event]) {\n const index = this.onceListeners[event].indexOf(callback);\n if (index !== -1) {\n this.onceListeners[event].splice(index, 1);\n\n // Clean up empty arrays\n if (this.onceListeners[event].length === 0) {\n delete this.onceListeners[event];\n }\n }\n }\n\n return this;\n }\n\n /**\n * Emit event to all listeners\n * @param {string} event - Event name\n * @param {*} data - Event data\n * @returns {EventBus} This instance for chaining\n */\n emit(event, data) {\n // Update statistics\n this.updateEventStats(event);\n\n // Debug logging\n if (this.debugMode) {\n console.log(`[EventBus] Emitting: ${event}`, data);\n }\n\n const listeners = [];\n\n // Collect regular listeners\n if (this.listeners[event]) {\n listeners.push(...this.listeners[event]);\n }\n\n // Collect wildcard listeners\n if (this.listeners['*']) {\n listeners.push(...this.listeners['*']);\n }\n\n // Collect and remove once listeners\n if (this.onceListeners[event]) {\n listeners.push(...this.onceListeners[event]);\n delete this.onceListeners[event];\n }\n\n // Collect and remove wildcard once listeners\n if (this.onceListeners['*']) {\n listeners.push(...this.onceListeners['*']);\n delete this.onceListeners['*'];\n }\n\n // Debug listener count\n if (this.debugMode && listeners.length > 0) {\n console.log(`[EventBus] ${listeners.length} listener(s) for '${event}'`);\n }\n\n // Call all listeners\n listeners.forEach(callback => {\n try {\n if (callback(data, event)) {\n if (event.stopPropagation) {\n event.stopPropagation();\n }\n if (event.preventDefault) {\n event.preventDefault();\n }\n }\n } catch (error) {\n console.error(`Error in event listener for '${event}':`, error);\n\n // Emit error event\n this.emitError(error, event, callback);\n }\n });\n\n return this;\n }\n\n /**\n * Emit event asynchronously\n * @param {string} event - Event name\n * @param {*} data - Event data\n * @returns {Promise<EventBus>} Promise that resolves with this instance\n */\n async emitAsync(event, data) {\n const listeners = [];\n\n // Collect regular listeners\n if (this.listeners[event]) {\n listeners.push(...this.listeners[event]);\n }\n\n // Collect wildcard listeners\n if (this.listeners['*']) {\n listeners.push(...this.listeners['*']);\n }\n\n // Collect and remove once listeners\n if (this.onceListeners[event]) {\n listeners.push(...this.onceListeners[event]);\n delete this.onceListeners[event];\n }\n\n // Collect and remove wildcard once listeners\n if (this.onceListeners['*']) {\n listeners.push(...this.onceListeners['*']);\n delete this.onceListeners['*'];\n }\n\n // Call all listeners asynchronously\n const promises = listeners.map(callback => {\n return new Promise(resolve => {\n try {\n const result = callback(data, event);\n resolve(result);\n } catch (error) {\n console.error(`Error in async event listener for '${event}':`, error);\n this.emitError(error, event, callback);\n resolve();\n }\n });\n });\n\n await Promise.all(promises);\n return this;\n }\n\n /**\n * Remove all listeners for all events\n * @returns {EventBus} This instance for chaining\n */\n removeAllListeners() {\n this.listeners = {};\n this.onceListeners = {};\n return this;\n }\n\n /**\n * Get listener count for an event\n * @param {string} event - Event name\n * @returns {number} Number of listeners\n */\n listenerCount(event) {\n const regularCount = this.listeners[event] ? this.listeners[event].length : 0;\n const onceCount = this.onceListeners[event] ? this.onceListeners[event].length : 0;\n return regularCount + onceCount;\n }\n\n /**\n * Get all event names that have listeners\n * @returns {Array<string>} Array of event names\n */\n eventNames() {\n const regularEvents = Object.keys(this.listeners);\n const onceEvents = Object.keys(this.onceListeners);\n return [...new Set([...regularEvents, ...onceEvents])];\n }\n\n /**\n * Set maximum number of listeners per event\n * @param {number} max - Maximum listeners\n * @returns {EventBus} This instance for chaining\n */\n setMaxListeners(max) {\n if (typeof max !== 'number' || max < 0) {\n throw new Error('Max listeners must be a non-negative number');\n }\n this.maxListeners = max;\n return this;\n }\n\n /**\n * Create a namespaced event bus\n * @param {string} namespace - Namespace prefix\n * @returns {object} Namespaced event bus methods\n */\n namespace(namespace) {\n const prefixEvent = (event) => `${namespace}:${event}`;\n\n return {\n on: (event, callback) => this.on(prefixEvent(event), callback),\n once: (event, callback) => this.once(prefixEvent(event), callback),\n off: (event, callback) => this.off(prefixEvent(event), callback),\n emit: (event, data) => this.emit(prefixEvent(event), data),\n emitAsync: (event, data) => this.emitAsync(prefixEvent(event), data)\n };\n }\n\n /**\n * Add middleware to intercept events\n * @param {function} middleware - Middleware function\n * @returns {EventBus} This instance for chaining\n */\n use(middleware) {\n if (typeof middleware !== 'function') {\n throw new Error('Middleware must be a function');\n }\n\n const originalEmit = this.emit;\n\n this.emit = (event, data) => {\n try {\n const result = middleware(event, data);\n\n // If middleware returns false, cancel the event\n if (result === false) {\n return this;\n }\n\n // If middleware returns modified data, use it\n const finalData = result !== undefined ? result : data;\n return originalEmit.call(this, event, finalData);\n\n } catch (error) {\n console.error('Error in event middleware:', error);\n return originalEmit.call(this, event, data);\n }\n };\n\n return this;\n }\n\n /**\n * Emit error event for listener errors\n * @private\n * @param {Error} error - The error that occurred\n * @param {string} originalEvent - The event that caused the error\n * @param {function} callback - The callback that threw the error\n */\n emitError(error, originalEvent, callback) {\n // Don't emit error events for error events to prevent loops\n if (originalEvent === 'error') {\n return;\n }\n\n setTimeout(() => {\n this.emit('error', {\n error,\n originalEvent,\n callback: callback.toString()\n });\n }, 0);\n }\n\n /**\n * Create a promise that resolves when an event is emitted\n * @param {string} event - Event name\n * @param {number} timeout - Optional timeout in milliseconds\n * @returns {Promise} Promise that resolves with event data\n */\n waitFor(event, timeout = null) {\n return new Promise((resolve, reject) => {\n let timeoutId = null;\n\n const cleanup = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n\n const listener = (data) => {\n cleanup();\n resolve(data);\n };\n\n this.once(event, listener);\n\n if (timeout) {\n timeoutId = setTimeout(() => {\n this.off(event, listener);\n reject(new Error(`Timeout waiting for event: ${event}`));\n }, timeout);\n }\n });\n }\n\n /**\n * Enable/disable debug mode for detailed event logging\n * @param {boolean} enable - Whether to enable debug mode\n * @returns {EventBus} This instance for chaining\n */\n debug(enable = true) {\n this.debugMode = enable;\n if (enable) {\n console.log('[EventBus] Debug mode enabled');\n } else {\n console.log('[EventBus] Debug mode disabled');\n }\n return this;\n }\n\n /**\n * Get statistics about the event bus\n * @returns {object} Statistics object\n */\n getStats() {\n const eventNames = this.eventNames();\n const stats = {\n totalEvents: eventNames.length,\n totalListeners: 0,\n events: {},\n emissions: { ...this.eventStats }\n };\n\n eventNames.forEach(event => {\n const count = this.listenerCount(event);\n stats.events[event] = count;\n stats.totalListeners += count;\n });\n\n return stats;\n }\n\n /**\n * Update event emission statistics\n * @private\n * @param {string} event - Event name\n */\n updateEventStats(event) {\n if (!this.eventStats[event]) {\n this.eventStats[event] = {\n count: 0,\n firstEmission: Date.now(),\n lastEmission: null\n };\n }\n\n this.eventStats[event].count++;\n this.eventStats[event].lastEmission = Date.now();\n }\n\n /**\n * Get detailed statistics for a specific event\n * @param {string} event - Event name\n * @returns {Object} Event statistics\n */\n getEventStats(event) {\n const stats = this.eventStats[event];\n if (!stats) {\n return null;\n }\n\n return {\n ...stats,\n listenerCount: this.listenerCount(event),\n avgEmissionsPerMinute: this.calculateEmissionRate(stats)\n };\n }\n\n /**\n * Calculate emission rate for an event\n * @private\n * @param {Object} stats - Event statistics\n * @returns {number} Emissions per minute\n */\n calculateEmissionRate(stats) {\n if (!stats.firstEmission || !stats.lastEmission) {\n return 0;\n }\n\n const durationMs = stats.lastEmission - stats.firstEmission;\n if (durationMs === 0) {\n return 0;\n }\n\n const durationMinutes = durationMs / (1000 * 60);\n return Math.round((stats.count / durationMinutes) * 100) / 100;\n }\n\n /**\n * Reset event statistics\n * @returns {EventBus} This instance for chaining\n */\n resetStats() {\n this.eventStats = {};\n return this;\n }\n\n /**\n * Get a summary of the most active events\n * @param {number} limit - Number of events to return (default: 10)\n * @returns {Array} Array of event statistics sorted by emission count\n */\n getTopEvents(limit = 10) {\n return Object.entries(this.eventStats)\n .map(([event, stats]) => ({\n event,\n count: stats.count,\n rate: this.calculateEmissionRate(stats),\n listeners: this.listenerCount(event)\n }))\n .sort((a, b) => b.count - a.count)\n .slice(0, limit);\n }\n\n /**\n * Log comprehensive debug information\n * @returns {EventBus} This instance for chaining\n */\n debugInfo() {\n console.group('[EventBus] Debug Information');\n console.log('Debug Mode:', this.debugMode);\n console.log('Max Listeners:', this.maxListeners);\n\n const stats = this.getStats();\n console.log('Total Events:', stats.totalEvents);\n console.log('Total Listeners:', stats.totalListeners);\n\n if (Object.keys(this.eventStats).length > 0) {\n console.log('Top Events:', this.getTopEvents(5));\n }\n\n console.groupEnd();\n return this;\n }\n}\n\nexport default EventBus;\n","/**\n * Rest - HTTP client for API communication\n * Provides methods for making REST API calls with interceptors and error handling\n */\n\nclass Rest {\n constructor() {\n this.config = {\n baseURL: '',\n timeout: 30000,\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json'\n },\n trackDevice: true, // New setting to control DUID tracking\n duidHeader: 'X-Mojo-UID', // Header name for the DUID\n duidTransport: 'header' // How to send the DUID: 'payload' or 'header'\n };\n\n this.interceptors = {\n request: [],\n response: []\n };\n\n this.duid = null;\n if (this.config.trackDevice) {\n this._initializeDuid();\n }\n }\n\n /**\n * Initialize or generate the Device Unique ID (DUID)\n * @private\n */\n _initializeDuid() {\n const storageKey = 'mojo_device_uid';\n try {\n let storedDuid = localStorage.getItem(storageKey);\n if (storedDuid) {\n this.duid = storedDuid;\n } else {\n this.duid = this._generateDuid();\n localStorage.setItem(storageKey, this.duid);\n }\n } catch (e) {\n console.error(\"Could not access localStorage to get/set DUID.\", e);\n // Use a non-persistent DUID as a fallback\n this.duid = this._generateDuid();\n }\n }\n\n /**\n * Generate a new DUID (UUID v4)\n * @private\n * @returns {string} A new UUID\n */\n _generateDuid() {\n if (crypto && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older browsers\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n /**\n * Configure the REST client\n * @param {object} config - Configuration object\n */\n configure(config) {\n if (config.baseUrl) config.baseURL = config.baseUrl;\n const oldTrackDevice = this.config.trackDevice;\n\n this.config = {\n ...this.config,\n ...config,\n headers: {\n ...this.config.headers,\n ...config.headers\n }\n };\n\n // Initialize DUID if tracking is newly enabled\n if (this.config.trackDevice && !oldTrackDevice) {\n this._initializeDuid();\n }\n }\n\n /**\n * Add request or response interceptor\n * @param {string} type - 'request' or 'response'\n * @param {function} interceptor - Interceptor function\n */\n addInterceptor(type, interceptor) {\n if (this.interceptors[type]) {\n this.interceptors[type].push(interceptor);\n }\n }\n\n /**\n * Build complete URL\n * @param {string} url - Endpoint URL\n * @returns {string} Complete URL\n */\n buildUrl(url) {\n if (url.startsWith('http://') || url.startsWith('https://')) {\n return url;\n }\n\n\n const baseURL = this.config.baseURL.endsWith('/')\n ? this.config.baseURL.slice(0, -1)\n : this.config.baseURL;\n\n const endpoint = url.startsWith('/') ? url : `/${url}`;\n\n return `${baseURL}${endpoint}`;\n }\n\n /**\n * Categorize error into common reason codes\n * @param {Error} error - The error object\n * @param {number} status - HTTP status code (if available)\n * @returns {object} Object with reason code and user-friendly message\n */\n categorizeError(error, status = 0) {\n // Network/connection errors\n if (error.name === 'TypeError' && error.message.includes('fetch')) {\n return {\n reason: 'not_reachable',\n message: 'Service is not reachable - please check your connection'\n };\n }\n\n if (error.name === 'AbortError') {\n return {\n reason: 'cancelled',\n message: 'Request was cancelled'\n };\n }\n\n if (error.name === 'TimeoutError' || error.message.includes('timeout')) {\n return {\n reason: 'timed_out',\n message: 'Request timed out - please try again'\n };\n }\n\n // HTTP status-based categorization\n if (status >= 400) {\n if (status === 400) {\n return {\n reason: 'bad_request',\n message: 'Invalid request data'\n };\n }\n if (status === 401) {\n return {\n reason: 'unauthorized',\n message: 'Authentication required'\n };\n }\n if (status === 403) {\n return {\n reason: 'forbidden',\n message: 'Access denied'\n };\n }\n if (status === 404) {\n return {\n reason: 'not_found',\n message: 'Resource not found'\n };\n }\n if (status === 409) {\n return {\n reason: 'conflict',\n message: 'Resource conflict'\n };\n }\n if (status === 422) {\n return {\n reason: 'validation_error',\n message: 'Validation failed'\n };\n }\n if (status === 429) {\n return {\n reason: 'rate_limited',\n message: 'Too many requests - please wait'\n };\n }\n if (status >= 500) {\n return {\n reason: 'server_error',\n message: 'Server error - please try again later'\n };\n }\n if (status >= 400) {\n return {\n reason: 'client_error',\n message: 'Request error'\n };\n }\n }\n\n // Generic network errors\n if (error.message.includes('CORS')) {\n return {\n reason: 'cors_error',\n message: 'Cross-origin request blocked'\n };\n }\n\n if (error.message.includes('DNS') || error.message.includes('ENOTFOUND')) {\n return {\n reason: 'dns_error',\n message: 'Unable to resolve server address'\n };\n }\n\n // Default fallback\n return {\n reason: 'unknown_error',\n message: `Network error: ${error.message}`\n };\n }\n\n /**\n * Build query string from parameters\n * @param {object} params - Query parameters\n * @returns {string} Query string\n */\n buildQueryString(params = {}) {\n const searchParams = new URLSearchParams();\n\n Object.entries(params).forEach(([key, value]) => {\n if (value !== null && value !== undefined) {\n if (Array.isArray(value)) {\n value.forEach(v => searchParams.append(`${key}[]`, v));\n } else {\n searchParams.append(key, value);\n }\n }\n });\n\n const queryString = searchParams.toString();\n return queryString ? `?${queryString}` : '';\n }\n\n /**\n * Process request through interceptors\n * @param {object} request - Request configuration\n * @returns {object} Processed request configuration\n */\n async processRequestInterceptors(request) {\n let processedRequest = { ...request };\n\n for (const interceptor of this.interceptors.request) {\n try {\n processedRequest = await interceptor(processedRequest);\n } catch (error) {\n console.error('Request interceptor error:', error);\n throw error;\n }\n }\n\n return processedRequest;\n }\n\n /**\n * Process response through interceptors\n * @param {Response} response - Fetch response object\n * @param {object} request - Original request configuration\n * @returns {object} Processed response data\n */\n async processResponseInterceptors(response, request) {\n let responseData = {\n success: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n data: null,\n errors: null,\n message: null,\n reason: null\n };\n\n // Parse response body\n try {\n const contentType = response.headers.get('content-type');\n\n if (contentType && contentType.includes('application/json')) {\n const jsonData = await response.json();\n responseData.data = jsonData;\n\n // Handle API error responses\n if (!response.ok) {\n const errorInfo = this.categorizeError(new Error('HTTP Error'), response.status);\n responseData.errors = jsonData.errors || {};\n responseData.message = jsonData.message || errorInfo.message;\n responseData.reason = errorInfo.reason;\n }\n } else {\n responseData.data = await response.text();\n\n if (!response.ok) {\n const errorInfo = this.categorizeError(new Error('HTTP Error'), response.status);\n responseData.message = errorInfo.message;\n responseData.reason = errorInfo.reason;\n }\n }\n } catch (error) {\n responseData.errors = { parse: 'Failed to parse response' };\n responseData.message = 'Invalid response format';\n }\n\n // Process through response interceptors\n for (const interceptor of this.interceptors.response) {\n try {\n responseData = await interceptor(responseData, request);\n } catch (error) {\n console.error('Response interceptor error:', error);\n }\n }\n\n return responseData;\n }\n\n /**\n * Make HTTP request\n * @param {string} method - HTTP method\n * @param {string} url - Request URL\n * @param {object} data - Request body data\n * @param {object} params - Query parameters\n * @param {object} options - Additional request options\n * @returns {Promise} Promise that resolves with response data\n */\n async request(method, url, data = null, params = {}, options = {}) {\n // Build request configuration\n let request = {\n method: method.toUpperCase(),\n url: this.buildUrl(url) + this.buildQueryString(params),\n headers: {\n ...this.config.headers,\n ...options.headers\n },\n data,\n options: {\n timeout: this.config.timeout,\n ...options\n }\n };\n\n // Process request interceptors\n request = await this.processRequestInterceptors(request);\n\n // Add DUID if tracking is enabled\n if (this.config.trackDevice && this.duid) {\n if (this.config.duidTransport === 'header') {\n // Always add as a header\n request.headers[this.config.duidHeader] = this.duid;\n } else { // 'payload' transport (default)\n if (request.method === 'GET') {\n // For GET requests, add as a query parameter\n const url = new URL(request.url);\n url.searchParams.append('duid', this.duid);\n request.url = url.toString();\n } else if (request.data && typeof request.data === 'object' && !(request.data instanceof FormData)) {\n // For POST/PUT/PATCH with JSON body, add to the data payload\n request.data.duid = this.duid;\n }\n // Note: For other request types like FormData, the duid is not sent in 'payload' mode.\n }\n }\n\n // Prepare fetch options\n const fetchOptions = {\n method: request.method,\n headers: request.headers\n };\n\n // Handle abort signals - combine timeout and external signal if provided\n const signals = [];\n\n // Add timeout signal\n if (request.options.timeout) {\n signals.push(AbortSignal.timeout(request.options.timeout));\n }\n\n // Add external signal if provided\n if (request.options.signal) {\n signals.push(request.options.signal);\n }\n\n // Combine signals or use single signal\n if (signals.length > 1) {\n fetchOptions.signal = AbortSignal.any ? AbortSignal.any(signals) : signals[0];\n } else if (signals.length === 1) {\n fetchOptions.signal = signals[0];\n }\n\n // Add body for methods that support it\n if (request.data && ['POST', 'PUT', 'PATCH'].includes(request.method)) {\n if (request.data instanceof FormData) {\n fetchOptions.body = request.data;\n // Remove Content-Type header for FormData (browser sets it with boundary)\n delete fetchOptions.headers['Content-Type'];\n } else if (typeof request.data === 'object') {\n fetchOptions.body = JSON.stringify(request.data);\n } else {\n fetchOptions.body = request.data;\n }\n }\n\n try {\n // Make the request\n const response = await fetch(request.url, fetchOptions);\n\n // Process response through interceptors\n const responseData = await this.processResponseInterceptors(response, request);\n\n return responseData;\n\n } catch (error) {\n // Handle AbortError (cancellation) - re-throw to be handled by caller\n if (error.name === 'AbortError') {\n throw error;\n }\n\n // Categorize the error\n const errorInfo = this.categorizeError(error);\n\n // Handle network and timeout errors\n const errorResponse = {\n success: false,\n status: 0,\n statusText: 'Network Error',\n headers: {},\n data: null,\n errors: { network: error.message },\n message: errorInfo.message,\n reason: errorInfo.reason\n };\n\n // Create mock response for interceptor processing\n const mockResponse = {\n ok: false,\n status: 0,\n statusText: 'Network Error',\n headers: new Headers(),\n json: async () => ({}),\n text: async () => ''\n };\n\n // Process through interceptors and return the categorized error\n await this.processResponseInterceptors(mockResponse, request);\n return errorResponse;\n }\n }\n\n /**\n * GET request\n * @param {string} url - Request URL\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with response data\n */\n async GET(url, params = {}, options = {}) {\n return this.request('GET', url, null, params, options);\n }\n\n /**\n * POST request\n * @param {string} url - Request URL\n * @param {object} data - Request body data\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with response data\n */\n async POST(url, data = {}, params = {}, options = {}) {\n return this.request('POST', url, data, params, options);\n }\n\n /**\n * PUT request\n * @param {string} url - Request URL\n * @param {object} data - Request body data\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with response data\n */\n async PUT(url, data = {}, params = {}, options = {}) {\n return this.request('PUT', url, data, params, options);\n }\n\n /**\n * PATCH request\n * @param {string} url - Request URL\n * @param {object} data - Request body data\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with response data\n */\n async PATCH(url, data = {}, params = {}, options = {}) {\n return this.request('PATCH', url, data, params, options);\n }\n\n /**\n * DELETE request\n * @param {string} url - Request URL\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with response data\n */\n async DELETE(url, params = {}, options = {}) {\n return this.request('DELETE', url, null, params, options);\n }\n\n /**\n * Download a file from a URL\n * @param {string} url - Request URL\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves when download is initiated\n */\n async download(url, params = {}, options = {}) {\n const requestUrl = this.buildUrl(url) + this.buildQueryString(params);\n const request = {\n method: 'GET',\n url: requestUrl,\n headers: {\n ...this.config.headers,\n 'Accept': '*/*', // Default, can be overridden by options\n ...options.headers\n },\n options: {\n ...options\n }\n };\n // Remove content-type for GET request\n delete request.headers['Content-Type'];\n\n try {\n const response = await fetch(request.url, {\n method: request.method,\n headers: request.headers,\n signal: request.options.signal\n });\n\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n }\n\n const contentDisposition = response.headers.get('content-disposition');\n let filename = options.filename || 'download';\n\n if (contentDisposition) {\n const filenameMatch = contentDisposition.match(/filename=\"?(.+)\"?/);\n if (filenameMatch && filenameMatch.length > 1) {\n filename = filenameMatch[1];\n }\n }\n\n // Create download without loading entire file into memory\n const reader = response.body.getReader();\n const stream = new ReadableStream({\n start(controller) {\n function pump() {\n return reader.read().then(({ done, value }) => {\n if (done) {\n controller.close();\n return;\n }\n controller.enqueue(value);\n return pump();\n });\n }\n return pump();\n }\n });\n\n const blob = await new Response(stream).blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.style.display = 'none';\n a.href = downloadUrl;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n window.URL.revokeObjectURL(downloadUrl);\n a.remove();\n\n return { success: true, message: 'Download initiated' };\n\n } catch (error) {\n console.error('Download error:', error);\n return { success: false, message: error.message };\n }\n }\n\n /**\n * Download a file from a URL by fetching the entire content into a Blob.\n * @param {string} url - Request URL\n * @param {object} params - Query parameters\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves when download is initiated\n */\n async downloadBlob(url, params = {}, options = {}) {\n const requestUrl = this.buildUrl(url) + this.buildQueryString(params);\n const request = {\n method: 'GET',\n url: requestUrl,\n headers: {\n ...this.config.headers,\n 'Accept': '*/*', // Default, can be overridden by options\n ...options.headers\n },\n options: {\n ...options\n }\n };\n // Remove content-type for GET request\n delete request.headers['Content-Type'];\n\n try {\n const response = await fetch(request.url, {\n method: request.method,\n headers: request.headers,\n signal: request.options.signal\n });\n\n if (!response.ok) {\n throw new Error(`Download failed: ${response.status} ${response.statusText}`);\n }\n\n const blob = await response.blob();\n const contentDisposition = response.headers.get('content-disposition');\n let filename = options.filename || 'download';\n\n if (contentDisposition) {\n const filenameMatch = contentDisposition.match(/filename=\"?(.+)\"?/);\n if (filenameMatch && filenameMatch.length > 1) {\n filename = filenameMatch[1];\n }\n }\n\n const downloadUrl = window.URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.style.display = 'none';\n a.href = downloadUrl;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n window.URL.revokeObjectURL(downloadUrl);\n a.remove();\n\n return { success: true, message: 'Download initiated' };\n\n } catch (error) {\n console.error('Download error:', error);\n return { success: false, message: error.message };\n }\n }\n\n /**\n * Upload file with raw PUT request (compatible with legacy backend)\n * @param {string} url - Upload URL\n * @param {File} file - Single file to upload\n * @param {object} options - Request options\n * @param {function} options.onProgress - Progress callback function(event)\n * @returns {Promise} Promise that resolves with response data\n */\n async upload(url, file, options = {}) {\n return new Promise((resolve, reject) => {\n // Validate input - only accept single File objects\n if (!(file instanceof File)) {\n reject(new Error('Only single File objects are supported for legacy backend compatibility'));\n return;\n }\n\n const xhr = new XMLHttpRequest();\n\n // Set up progress tracking if callback provided\n if (options.onProgress && typeof options.onProgress === 'function') {\n xhr.upload.onprogress = options.onProgress;\n }\n\n // Set up response handlers\n xhr.onload = function() {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve({\n data: xhr.response,\n status: xhr.status,\n statusText: xhr.statusText,\n xhr: xhr\n });\n } else {\n reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));\n }\n };\n\n xhr.onerror = function() {\n reject(new Error('Upload failed: Network error'));\n };\n\n xhr.ontimeout = function() {\n reject(new Error('Upload failed: Timeout'));\n };\n\n // Configure request - use PUT method with raw file data\n xhr.open('PUT', url);\n xhr.setRequestHeader('Content-Type', file.type);\n\n // Set timeout if specified\n if (options.timeout) {\n xhr.timeout = options.timeout;\n }\n\n // Send the raw file data\n xhr.send(file);\n });\n }\n\n /**\n * Upload multiple files with multipart/form-data (for modern backends)\n * @param {string} url - Upload URL\n * @param {File|FileList|FormData} files - File(s) to upload\n * @param {object} additionalData - Additional form fields\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with response data\n */\n async uploadMultipart(url, files, additionalData = {}, options = {}) {\n const formData = new FormData();\n\n // Add files to form data\n if (files instanceof FileList) {\n Array.from(files).forEach((file, index) => {\n formData.append(`file_${index}`, file);\n });\n } else if (files instanceof File) {\n formData.append('file', files);\n } else if (files instanceof FormData) {\n // Use provided FormData directly\n return this.POST(url, files, {}, options);\n }\n\n // Add additional data\n Object.entries(additionalData).forEach(([key, value]) => {\n formData.append(key, value);\n });\n\n return this.POST(url, formData, {}, options);\n }\n\n /**\n * Set authentication token\n * @param {string} token - JWT or API token\n * @param {string} type - Token type ('Bearer', 'Token', etc.)\n */\n setAuthToken(token, type = 'Bearer') {\n if (token) {\n this.config.headers['Authorization'] = `${type} ${token}`;\n } else {\n delete this.config.headers['Authorization'];\n }\n }\n\n /**\n * Clear authentication\n */\n clearAuth() {\n delete this.config.headers['Authorization'];\n }\n\n /**\n * Check if an error is retryable (network issues that might resolve)\n * @param {object} response - Response object with reason field\n * @returns {boolean} True if error can be retried\n */\n isRetryableError(response) {\n const retryableReasons = [\n 'not_reachable',\n 'timed_out',\n 'server_error',\n 'dns_error'\n ];\n return retryableReasons.includes(response.reason);\n }\n\n /**\n * Check if error requires authentication\n * @param {object} response - Response object with reason field\n * @returns {boolean} True if authentication is required\n */\n requiresAuth(response) {\n return response.reason === 'unauthorized';\n }\n\n /**\n * Check if error is network-related\n * @param {object} response - Response object with reason field\n * @returns {boolean} True if it's a network error\n */\n isNetworkError(response) {\n const networkReasons = [\n 'not_reachable',\n 'timed_out',\n 'cancelled',\n 'cors_error',\n 'dns_error'\n ];\n return networkReasons.includes(response.reason);\n }\n\n /**\n * Get user-friendly error message based on reason\n * @param {object} response - Response object with reason field\n * @returns {string} User-friendly error message\n */\n getUserMessage(response) {\n if (response.message) {\n return response.message;\n }\n\n const messages = {\n 'not_reachable': 'Unable to connect to the server. Please check your internet connection.',\n 'timed_out': 'The request took too long. Please try again.',\n 'cancelled': 'The request was cancelled.',\n 'unauthorized': 'Please log in to continue.',\n 'forbidden': 'You don\\'t have permission to perform this action.',\n 'not_found': 'The requested resource was not found.',\n 'validation_error': 'Please check your input and try again.',\n 'rate_limited': 'Too many requests. Please wait a moment before trying again.',\n 'server_error': 'Server error. Please try again later.',\n 'cors_error': 'Access blocked by security policy.',\n 'dns_error': 'Unable to reach the server.',\n 'unknown_error': 'An unexpected error occurred.'\n };\n\n return messages[response.reason] || 'An error occurred. Please try again.';\n }\n}\n\n// Create singleton instance\nconst rest = new Rest();\n\nexport default rest;\n","/**\n * WebApp - Central application container for MOJO framework\n * Handles configuration, routing, pages, REST interface, layout management, and global events\n *\n * Global Event System:\n * Provides a global EventBus at this.events for cross-cutting application concerns\n * Emits framework-wide events that any component can subscribe to\n * Enables loose coupling between different parts of the application\n *\n * Standard Global Events Emitted:\n * - 'app:ready' - When application has fully started\n * - 'notification' - For all user notifications (error, success, info, warning)\n * - 'loading:show' - When loading indicator should be shown\n * - 'loading:hide' - When loading indicator should be hidden\n * - 'state:changed' - When application state changes\n * - 'browser:focus' - When browser/tab gains focus\n * - 'browser:blur' - When browser/tab loses focus\n * - Router events are also emitted via this.events (see Router documentation)\n */\n\nimport Router from '@core/Router.js';\nimport EventBus from '@core/utils/EventBus.js';\n// Dialog is intentionally not imported statically to avoid circular deps and reduce initial bundle size.\n// It will be loaded lazily inside helper methods when needed.\nimport rest from '@core/Rest.js';\n\nclass WebApp {\n constructor(config = {}) {\n // Initialize global MOJO plugin registry\n this.config = config;\n this.initPluginRegistry();\n\n // Core configuration\n this.name = config.name || 'MOJO App';\n this.version = config.version || '1.0.0';\n this.debug = config.debug || false;\n this.container = config.container || '#app';\n\n // Layout configuration\n this.layoutType = config.layout || 'portal'; // 'portal', 'single', 'custom', 'none'\n this.layoutConfig = config.layoutConfig || {};\n if (config.sidebar) {\n this.layoutConfig.sidebarConfig = config.sidebar;\n }\n if (config.topbar) {\n this.layoutConfig.topbarConfig = config.topbar;\n }\n this.layout = null;\n\n this.layoutConfig.containerId = this.container || this.containerId || '#app';\n this.pageContainer = config.pageContainer || '#page-container';\n this.basePath = config.basePath || '';\n\n // Router configuration\n this.routerMode = config.routerMode || config.router?.mode || 'param'; // 'param', 'hash', 'history'\n this.basePath = config.basePath || config.router?.base || '';\n this.defaultRoute = config.defaultRoute || 'home';\n\n // Store session config if provided\n this.session = config.session || {};\n\n // Initialize router placeholder\n this.router = null;\n\n // Navigation configuration - support both old and new structure\n this.navigation = config.navigation || {};\n\n // State management\n this.state = {\n currentPage: null,\n previousPage: null,\n loading: false\n };\n\n // Global event bus (singleton for the app)\n this.events = new EventBus();\n this.rest = rest;\n if (config.api) {\n this.rest.configure(config.api);\n }\n\n // Initialize router with event integration after EventBus is ready\n this.router = new Router({\n mode: this.routerMode === 'param' ? 'params' : this.routerMode,\n basePath: this.basePath,\n defaultRoute: this.defaultRoute,\n eventEmitter: this.events\n });\n\n // Listen for route changes - router already resolved everything\n this.events.on('route:changed', async (routeInfo) => {\n const { pageName, params, query } = routeInfo;\n await this.showPage(pageName, query, params, { fromRouter: true });\n });\n\n\n // Make router globally accessible\n if (typeof window !== 'undefined') {\n window.MOJO = window.MOJO || {};\n window.MOJO.router = this.router;\n }\n\n // Setup browser focus/blur tracking\n this.setupFocusTracking();\n\n // Component registries\n this.pageCache = new Map(); // pageName -> page instance (cached)\n this.pageClasses = new Map(); // pageName -> {PageClass, constructorOptions}\n this.componentClasses = new Map(); // componentName -> ComponentClass\n this.modelClasses = new Map(); // modelName -> ModelClass\n\n // Current page reference\n this.currentPage = null;\n\n // Runtime flags\n this.isStarted = false;\n\n if (window.matchUUID) {\n window[window.matchUUID] = this;\n } else if (window.MOJO) {\n window.MOJO.app = this;\n } else {\n window.__app__ = this;\n }\n }\n\n /**\n * Start the application\n */\n async start() {\n if (this.isStarted) {\n console.warn('WebApp already started');\n return;\n }\n\n try {\n // Setup global REST configuration\n // this.setupRest();\n // Setup page container\n this.setupPageContainer();\n\n // Validate default route before starting router\n this.validateDefaultRoute();\n\n // Setup router\n await this.setupRouter();\n\n // Mark as started\n this.isStarted = true;\n\n this.router.allowPopState = false;\n\n // Emit app ready event\n this.events.emit('app:ready', { app: this });\n } catch (error) {\n console.error(`Failed to start ${this.name}:`, error);\n this.showError('Failed to start application');\n throw error;\n }\n }\n\n /**\n * Setup router with configuration\n */\n async setupRouter() {\n if (!this.router) {\n console.error('Router not initialized');\n return;\n }\n\n this.events.on('route:notfound', async (info) => {\n console.warn(`Route not found: ${info.path}`);\n // RENDER 404 PAGE IN PLACE: Just render, don't navigate\n this._show404(info.path);\n });\n\n // Start the router to begin handling routes\n this.router.start();\n console.log(`Router started in ${this.routerMode} mode`);\n }\n\n /**\n * Setup layout based on configuration\n */\n setupPageContainer() {\n // Simple page container setup - no complex layouts\n const container = typeof this.container === 'string'\n ? document.querySelector(this.container)\n : this.container;\n\n if (container && !container.querySelector('#page-container')) {\n container.innerHTML = '<div id=\"page-container\"></div>';\n }\n\n this.pageContainer = '#page-container';\n }\n\n\n /**\n * Register a page with the app\n */\n registerPage(pageName, PageClass, options = {}) {\n // Validate inputs\n if (typeof pageName !== 'string' || !pageName) {\n console.error('registerPage: pageName must be a non-empty string');\n return this;\n }\n\n if (typeof PageClass !== 'function') {\n console.error('registerPage: PageClass must be a constructor function');\n return this;\n }\n\n if (!options.containerId) options.containerId = this.pageContainer;\n // Store the page class and options\n this.pageClasses.set(pageName, {\n PageClass,\n constructorOptions: options\n });\n\n // If router is initialized, add route\n if (this.router) {\n // Get route pattern from options or use default (/pageName)\n let route = options.route || `/${pageName}`;\n\n // Ensure route starts with / for pattern matching\n if (!route.startsWith('/')) {\n route = `/${route}`;\n }\n\n // Store the route pattern in options so page instances can access it\n options.route = route;\n\n // Register route with router\n this.router.addRoute(route, pageName);\n\n\n }\n\n return this;\n }\n\n /**\n * Get page instance (cached)\n */\n getPage(pageName) {\n return this.pageCache.get(pageName);\n }\n\n getPagePermissions(pageName) {\n if (this.pageCache.has(pageName)) {\n return this.pageCache.get(pageName).permissions;\n }\n const pageInfo = this.pageClasses.get(pageName);\n if (!pageInfo) return null;\n const { PageClass, constructorOptions } = pageInfo;\n if (!constructorOptions) return null;\n return constructorOptions.permissions;\n }\n\n /**\n * Get or create page instance (with caching)\n */\n getOrCreatePage(pageName) {\n // Check cache first\n if (this.pageCache.has(pageName)) {\n return this.pageCache.get(pageName);\n }\n\n // Check if page class is registered\n const pageInfo = this.pageClasses.get(pageName);\n if (!pageInfo) {\n console.error(`Page not registered: ${pageName}`);\n return null;\n }\n\n const { PageClass, constructorOptions } = pageInfo;\n\n try {\n // Create page instance with merged options\n const pageOptions = {\n pageName,\n ...constructorOptions,\n app: this\n };\n\n const page = new PageClass(pageOptions);\n\n // Store route information on page instance for easy access\n if (constructorOptions.route) {\n page.route = constructorOptions.route;\n }\n\n // Cache the instance\n this.pageCache.set(pageName, page);\n\n console.log(`Created page: ${pageName} with route: ${page.route}`);\n return page;\n\n } catch (error) {\n console.error(`Failed to create page ${pageName}:`, error);\n return null;\n }\n }\n\n /**\n * Show a page\n */\n /**\n * SIMPLIFIED UNIFIED PAGE SHOWING METHOD\n * @param {string|object} page - Page name or page instance\n * @param {object} query - URL query parameters (URL-safe)\n * @param {object} params - Any data to pass to page (can include objects)\n * @param {object} options - Options { fromRouter, replace, force }\n */\n async showPage(page, query = {}, params = {}, options = {}) {\n const { fromRouter = false, replace = false, force = false } = options;\n\n try {\n // 1. RESOLVE PAGE INSTANCE\n let pageInstance, pageName;\n if (typeof page === 'string') {\n pageName = page;\n pageInstance = this.getOrCreatePage(page);\n } else if (page && typeof page === 'object') {\n pageInstance = page;\n pageName = page.pageName;\n }\n\n const oldPage = this.currentPage;\n if (!pageInstance) {\n this._show404(pageName, params, query, fromRouter);\n return; // Keep current URL, don't update router/history\n }\n\n this.events.emit('page:showing', {\n page: pageInstance,\n pageName: pageInstance.pageName,\n params,\n query,\n fromRouter\n });\n\n // 2. PERMISSION CHECK\n if (!pageInstance.canEnter()) {\n this._showDeniedPage(pageInstance, params, query, fromRouter);\n return; // Keep current URL, don't update router/history\n }\n\n // 3. EXIT CURRENT PAGE\n if (oldPage && oldPage !== pageInstance) {\n await this._exitOldPage(oldPage);\n }\n\n\n // 4. UPDATE PAGE DATA\n await pageInstance.onParams(params, query);\n\n // 5. ENTER NEW PAGE\n if (oldPage !== pageInstance) {\n await pageInstance.onEnter();\n }\n\n pageInstance.syncUrl();\n\n // 8. EMIT SUCCESS EVENT\n this.events.emit('page:show', {\n page: pageInstance,\n pageName: pageInstance.pageName,\n params,\n query,\n fromRouter\n });\n\n // 6. RENDER PAGE (automatically replaces DOM content)\n await pageInstance.render();\n this.currentPage = pageInstance;\n\n console.log(`✅ Showing page: ${pageInstance.pageName}`, { query, params });\n\n } catch (error) {\n console.error('Error in showPage:', error);\n this.showError(`Failed to load page: ${error.message}`);\n\n // Fallback to error page\n if (page !== 'error') {\n await this.showPage('error', {}, { error, originalPage: page }, { fromRouter });\n }\n }\n }\n\n async _show404(pageName, params, query, fromRouter) {\n const notFoundPage = this.getOrCreatePage('404');\n if (!notFoundPage) return;\n if (notFoundPage.setInfo) {\n notFoundPage.setInfo(pageName);\n }\n await this._exitOldPage(this.currentPage);\n await notFoundPage.render(); // Render over current page\n this.currentPage = notFoundPage;\n this.events.emit('page:404', {\n page: null,\n pageName: pageName,\n params,\n query,\n fromRouter\n });\n }\n\n async _showDeniedPage(pageInstance, params, query, fromRouter) {\n const deniedPage = this.getOrCreatePage('denied');\n if (deniedPage.setDeniedPage) {\n deniedPage.setDeniedPage(pageInstance);\n }\n await this._exitOldPage(this.currentPage);\n await deniedPage.render(); // Render over current page\n this.currentPage = deniedPage;\n this.events.emit('page:denied', {\n page: pageInstance,\n pageName: pageInstance.pageName,\n params,\n query,\n fromRouter\n });\n }\n\n async _exitOldPage(oldPage) {\n if (!oldPage) return;\n try {\n await oldPage.onExit();\n await oldPage.unmount();\n this.events.emit('page:hide', { page: oldPage });\n } catch (error) {\n console.error(`Error exiting page ${oldPage.pageName}:`, error);\n }\n }\n\n /**\n * SIMPLIFIED NAVIGATION - delegates to router\n * @param {string} route - Route like \"/users\" or \"/users/123\"\n * @param {object} query - Query string params { filter: 'active' }\n * @param {object} options - Navigation options { replace, force }\n */\n async navigate(route, query = {}, options = {}) {\n if (!this.router) {\n console.error('Router not initialized');\n return;\n }\n\n\n\n // Build full path with query parameters\n let fullPath = route;\n if (Object.keys(query).length > 0) {\n const queryString = new URLSearchParams(query).toString();\n fullPath += (route.includes('?') ? '&' : '?') + queryString;\n }\n\n // Let router handle everything - it will emit route:changed back to us\n return await this.router.navigate(fullPath, options);\n }\n\n /**\n * Navigate to the default route\n */\n async navigateToDefault(options = {}) {\n return await this.showPage(this.defaultRoute, {}, {}, options);\n }\n\n\n\n\n\n /**\n * Navigate back\n */\n back() {\n if (this.router) {\n this.router.back();\n } else {\n console.warn('Router not initialized');\n }\n }\n\n /**\n * Navigate forward\n */\n forward() {\n if (this.router) {\n this.router.forward();\n } else {\n console.warn('Router not initialized');\n }\n }\n\n /**\n * Get current page\n */\n getCurrentPage() {\n return this.currentPage;\n }\n\n /**\n * Get page container element\n */\n getPageContainer() {\n // Try layout first\n if (this.layout && this.layout.getPageContainer) {\n return this.layout.getPageContainer();\n }\n\n // Fallback to configured container\n const container = typeof this.pageContainer === 'string'\n ? document.querySelector(this.pageContainer)\n : this.pageContainer;\n\n return container;\n }\n\n /**\n * Show error notification\n */\n async showError(message) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n await Dialog.alert(message, 'Error', { size: 'md', class: 'text-danger' });\n } catch (e) {\n this.events.emit('notification', { message, type: 'error' });\n if (typeof window !== 'undefined' && window?.console) {\n console.error('[WebApp] showError fallback:', e);\n }\n if (typeof window !== 'undefined') {\n alert(`Error: ${message}`);\n }\n }\n }\n\n /**\n * Show success notification\n */\n async showSuccess(message) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n await Dialog.alert(message, 'Success', { size: 'md', class: 'text-success' });\n } catch (e) {\n this.events.emit('notification', { message, type: 'success' });\n if (typeof window !== 'undefined' && window?.console) {\n console.warn('[WebApp] showSuccess fallback:', e);\n }\n if (typeof window !== 'undefined') {\n alert(`Success: ${message}`);\n }\n }\n }\n\n /**\n * Show info notification\n */\n async showInfo(message) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n await Dialog.alert(message, 'Information', { size: 'md', class: 'text-info' });\n } catch (e) {\n this.events.emit('notification', { message, type: 'info' });\n if (typeof window !== 'undefined' && window?.console) {\n console.info('[WebApp] showInfo fallback:', e);\n }\n if (typeof window !== 'undefined') {\n alert(`Info: ${message}`);\n }\n }\n }\n\n /**\n * Show warning notification\n */\n async showWarning(message) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n await Dialog.alert(message, 'Warning', { size: 'md', class: 'text-warning' });\n } catch (e) {\n this.events.emit('notification', { message, type: 'warning' });\n if (typeof window !== 'undefined' && window?.console) {\n console.warn('[WebApp] showWarning fallback:', e);\n }\n if (typeof window !== 'undefined') {\n alert(`Warning: ${message}`);\n }\n }\n }\n\n /**\n * Show generic notification\n */\n showNotification(message, type = 'info') {\n this.events.emit('notification', { message, type });\n }\n\n /**\n * Show loading indicator\n */\n async showLoading(opts = {}) {\n if (typeof opts === 'string') {\n opts = { message: opts };\n }\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n Dialog.showBusy(opts);\n } catch (e) {\n if (typeof window !== 'undefined' && window?.console) {\n console.warn('[WebApp] showLoading fallback:', e, opts);\n }\n // Minimal no-op fallback; consumers can listen to 'notification' if desired\n this.events.emit('notification', { message: opts.message || 'Loading...', type: 'info' });\n }\n }\n\n /**\n * Hide loading indicator\n */\n async hideLoading() {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n Dialog.hideBusy();\n } catch (e) {\n if (typeof window !== 'undefined' && window?.console) {\n console.warn('[WebApp] hideLoading fallback:', e);\n }\n // No visible fallback necessary\n }\n }\n\n async showModelForm(options = {}) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n return await Dialog.showModelForm(options);\n } catch (e) {\n if (typeof window !== 'undefined' && window?.console) {\n console.error('[WebApp] showModelForm failed:', e);\n }\n throw e;\n }\n }\n\n async showForm(options = {}) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n return await Dialog.showForm(options);\n } catch (e) {\n if (typeof window !== 'undefined' && window?.console) {\n console.error('[WebApp] showForm failed:', e);\n }\n throw e;\n }\n }\n\n async showDialog(options = {}) {\n try {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n return await Dialog.showDialog(options);\n } catch (e) {\n if (typeof window !== 'undefined' && window?.console) {\n console.error('[WebApp] showDialog failed:', e);\n }\n throw e;\n }\n }\n\n async confirm(message, title = 'Confirm', options = {}) {\n const Dialog = (await import('./views/feedback/Dialog.js')).default;\n return await Dialog.confirm(message, title, options);\n }\n\n /**\n * Setup browser focus/blur tracking\n */\n setupFocusTracking() {\n if (typeof window === 'undefined') return;\n\n this.isFocused = !document.hidden;\n\n // Handle visibility change (tab switching, minimizing, etc.)\n const handleVisibilityChange = () => {\n const wasFocused = this.isFocused;\n this.isFocused = !document.hidden;\n\n if (wasFocused !== this.isFocused) {\n if (this.isFocused) {\n // console.log('🔥 Browser gained focus');\n this.events.emit('browser:focus');\n } else {\n // console.log('💤 Browser lost focus');\n this.events.emit('browser:blur');\n }\n }\n };\n\n // Handle window focus/blur (for when user clicks in/out of window)\n const handleFocus = () => {\n if (!this.isFocused) {\n this.isFocused = true;\n // console.log('🔥 Browser gained focus');\n this.events.emit('browser:focus');\n }\n };\n\n const handleBlur = () => {\n if (this.isFocused) {\n this.isFocused = false;\n // console.log('💤 Browser lost focus');\n this.events.emit('browser:blur');\n }\n };\n\n // Listen for visibility changes\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n // Listen for window focus/blur\n window.addEventListener('focus', handleFocus);\n window.addEventListener('blur', handleBlur);\n\n // Store references for cleanup\n this._focusHandlers = {\n visibilitychange: handleVisibilityChange,\n focus: handleFocus,\n blur: handleBlur\n };\n }\n\n /**\n * Setup global error handling\n */\n setupErrorHandling() {\n window.addEventListener('error', (event) => {\n console.error('Global error:', event.error);\n if (this.debug) {\n this.showError(`Error: ${event.error?.message || 'Unknown error'}`);\n }\n });\n\n window.addEventListener('unhandledrejection', (event) => {\n console.error('Unhandled promise rejection:', event.reason);\n if (this.debug) {\n this.showError(`Promise rejected: ${event.reason?.message || 'Unknown error'}`);\n }\n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(unsafe) {\n return unsafe\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n }\n\n /**\n * Get application state\n */\n getState(key) {\n return key ? this.state[key] : this.state;\n }\n\n /**\n * Set application state\n */\n setState(updates) {\n const oldState = { ...this.state };\n Object.assign(this.state, updates);\n\n // Emit state change event\n this.events.emit('state:changed', {\n oldState,\n newState: this.state,\n updates\n });\n }\n\n /**\n * Register component class\n */\n registerComponent(name, ComponentClass) {\n this.componentClasses.set(name, ComponentClass);\n }\n\n /**\n * Get component class\n */\n getComponent(name) {\n return this.componentClasses.get(name);\n }\n\n /**\n * Register model class\n */\n registerModel(name, ModelClass) {\n this.modelClasses.set(name, ModelClass);\n }\n\n /**\n * Get model class\n */\n getModel(name) {\n return this.modelClasses.get(name);\n }\n\n /**\n * Setup REST configuration\n */\n setupRest() {\n this.rest = rest;\n rest.configure(this.api);\n }\n\n /**\n * Destroy the application\n */\n async destroy() {\n console.log('Destroying WebApp...');\n\n // Stop router\n if (this.router) {\n this.router.stop();\n }\n\n // Clean up focus tracking\n if (this._focusHandlers && typeof window !== 'undefined') {\n document.removeEventListener('visibilitychange', this._focusHandlers.visibilitychange);\n window.removeEventListener('focus', this._focusHandlers.focus);\n window.removeEventListener('blur', this._focusHandlers.blur);\n }\n\n // Destroy all cached pages\n const pages = Array.from(this.pageCache.values());\n await Promise.allSettled(\n pages.map(async (page) => {\n try {\n if (page.destroy) {\n await page.destroy();\n }\n } catch (error) {\n console.error('Error destroying page:', error);\n }\n })\n );\n\n // Destroy layout\n if (this.layout && this.layout.destroy) {\n try {\n await this.layout.destroy();\n } catch (error) {\n console.error('Error destroying layout:', error);\n }\n }\n\n // Clear all registries\n this.pageCache.clear();\n this.pageClasses.clear();\n this.componentClasses.clear();\n this.modelClasses.clear();\n\n // Remove global references\n if (typeof window !== 'undefined' && window.MOJO) {\n delete window.MOJO.router;\n }\n\n this.isStarted = false;\n console.log(`✨ ${this.name} destroyed`);\n }\n\n /**\n * Build page path with params and query\n */\n buildPagePath(page, params, query) {\n let path = page.route || `/${page.pageName.toLowerCase()}`;\n\n // Replace :param tokens with actual values\n Object.keys(params).forEach(key => {\n if (typeof params[key] === 'string' || typeof params[key] === 'number') {\n path = path.replace(`:${key}`, params[key]);\n }\n });\n\n // Add query parameters\n if (query && Object.keys(query).length > 0) {\n const queryString = new URLSearchParams(query).toString();\n path += (path.includes('?') ? '&' : '?') + queryString;\n }\n\n return path;\n }\n\n /**\n * Static factory method\n */\n /**\n * Validate that default route has a registered page\n */\n validateDefaultRoute() {\n if (!this.pageClasses.has(this.defaultRoute)) {\n console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`);\n console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`);\n console.warn(` Or change default route: new WebApp({ defaultRoute: 'your-page' });`);\n } else {\n console.log(`✅ Default route '${this.defaultRoute}' is registered`);\n }\n }\n\n /**\n * Find any registered page to use as emergency fallback\n */\n findFallbackPage() {\n // Skip system pages\n const systemPages = ['404', 'error', 'denied'];\n\n for (const [pageName] of this.pageClasses.entries()) {\n if (!systemPages.includes(pageName)) {\n return pageName;\n }\n }\n\n return null; // No pages available\n }\n\n static create(config = {}) {\n return new WebApp(config);\n }\n\n /**\n * Initialize global plugin registry for extensions\n * Allows extensions to register components that core can use optionally\n */\n initPluginRegistry() {\n if (typeof window !== 'undefined') {\n // Create global MOJO object if it doesn't exist\n if (!window.MOJO) {\n window.MOJO = {};\n }\n\n // Create plugins registry\n if (!window.MOJO.plugins) {\n window.MOJO.plugins = {};\n }\n\n // Store reference to this app instance\n window.MOJO.app = this;\n }\n }\n\n /**\n * Register a plugin component\n * @param {string} name - Plugin component name (e.g., 'ImageCropView')\n * @param {*} component - The component/class to register\n */\n static registerPlugin(name, component) {\n if (typeof window !== 'undefined') {\n if (!window.MOJO) {\n window.MOJO = {};\n }\n if (!window.MOJO.plugins) {\n window.MOJO.plugins = {};\n }\n\n window.MOJO.plugins[name] = component;\n console.debug(`MOJO Plugin registered: ${name}`);\n }\n }\n}\n\nexport default WebApp;\n","/**\n * DataFormatter - Universal data formatting utility for MOJO Framework\n * Provides consistent formatting with clean APIs and pipe syntax support\n * @version 1.0.0\n */\n\n// A generic, gray, person icon SVG, encoded as a Base64 data URI.\n// This is used as a fallback for the avatar formatter when no image URL is provided.\nconst GENERIC_AVATAR_SVG = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==';\n\nclass DataFormatter {\n constructor() {\n this.formatters = new Map();\n this.registerBuiltInFormatters();\n }\n\n escapeHtml(str) {\n if (str === null || str === undefined) {\n return '';\n }\n const map = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#039;'\n };\n return String(str).replace(/[&<>\"']/g, (m) => map[m]);\n }\n\n /**\n * Register all built-in formatters\n */\n registerBuiltInFormatters() {\n // Date/Time formatters\n this.register('date', this.date.bind(this));\n this.register('time', this.time.bind(this));\n this.register('datetime', this.datetime.bind(this));\n this.register('datetime_tz', this.datetime_tz.bind(this));\n this.register('datatime_tz', this.datetime_tz.bind(this)); // Alias for common typo\n this.register('date_range', this.date_range.bind(this));\n this.register('datetime_range', this.datetime_range.bind(this));\n this.register('relative', this.relative.bind(this));\n this.register('fromNow', this.relative.bind(this)); // Alias\n this.register('relative_short', this.relative_short.bind(this)); // Alias for short relative time\n this.register('iso', this.iso.bind(this));\n this.register('epoch', (v) => {\n if (v === null || v === undefined || v === '') return v;\n const num = parseFloat(v);\n if (isNaN(num)) return v;\n // Convert seconds to milliseconds\n return num * 1000;\n });\n\n // Number formatters\n this.register('number', this.number.bind(this));\n this.register('currency', this.currency.bind(this));\n this.register('percent', this.percent.bind(this));\n this.register('filesize', this.filesize.bind(this));\n this.register('ordinal', this.ordinal.bind(this));\n this.register('compact', this.compact.bind(this));\n\n // Math formatters\n this.register('add', this.add.bind(this));\n this.register('subtract', this.subtract.bind(this));\n this.register('multiply', this.multiply.bind(this));\n this.register('divide', this.divide.bind(this));\n this.register('sub', this.subtract.bind(this)); // Alias\n this.register('mult', this.multiply.bind(this)); // Alias\n this.register('div', this.divide.bind(this)); // Alias\n\n // String formatters\n this.register('uppercase', (v) => String(v).toUpperCase());\n this.register('lowercase', (v) => String(v).toLowerCase());\n this.register('upper', (v) => String(v).toUpperCase());\n this.register('lower', (v) => String(v).toLowerCase());\n this.register('capitalize', this.capitalize.bind(this));\n this.register('caps', this.capitalize.bind(this));\n this.register('replace', this.replace.bind(this));\n this.register('truncate', this.truncate.bind(this));\n this.register('truncate_middle', this.truncate_middle.bind(this));\n this.register('truncate_front', this.truncate_front.bind(this));\n this.register('slug', this.slug.bind(this));\n this.register('initials', this.initials.bind(this));\n this.register('mask', this.mask.bind(this));\n this.register('hex', this.hex.bind(this));\n this.register('tohex', this.hex.bind(this));\n this.register('unhex', this.unhex.bind(this));\n this.register('fromhex', this.unhex.bind(this));\n\n // HTML/Web formatters\n this.register('email', this.email.bind(this));\n this.register('phone', this.phone.bind(this));\n this.register('url', this.url.bind(this));\n this.register('badge', this.badge.bind(this));\n this.register('badgeClass', this.badgeClass.bind(this));\n this.register('status', this.status.bind(this));\n this.register('status_text', this.status_text.bind(this));\n this.register('status_icon', this.status_icon.bind(this));\n this.register('boolean', this.boolean.bind(this));\n this.register('bool', this.boolean.bind(this));\n this.register('yesno', (v) => this.boolean(v, 'Yes', 'No'));\n this.register('yesnoicon', this.yesnoicon.bind(this));\n this.register('icon', this.icon.bind(this));\n this.register('avatar', this.avatar.bind(this));\n this.register('image', this.image.bind(this));\n this.register('tooltip', this.tooltip.bind(this));\n this.register('linkify', this.linkify.bind(this));\n this.register('clipboard', this.clipboard.bind(this));\n\n // Utility formatters\n this.register('default', this.default.bind(this));\n this.register('equals', this.equals.bind(this));\n this.register('json', this.json.bind(this));\n this.register('raw', (v) => v);\n this.register('custom', (v, fn) => typeof fn === 'function' ? fn(v) : v);\n this.register('iter', this.iter.bind(this));\n this.register('keys', (v) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n return Object.keys(v);\n }\n return null;\n });\n this.register('values', (v) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n return Object.values(v);\n }\n return null;\n });\n\n // Text/Content formatters\n this.register('plural', this.plural.bind(this));\n this.register('list', this.formatList.bind(this));\n this.register('duration', this.duration.bind(this));\n this.register('hash', this.hash.bind(this));\n this.register('stripHtml', this.stripHtml.bind(this));\n this.register('highlight', this.highlight.bind(this));\n this.register('nl2br', this.nl2br.bind(this));\n this.register('code', this.code.bind(this));\n this.register('pre', (v) => `<pre class=\"bg-light p-2 rounded border\">${this.escapeHtml(String(v))}</pre>`);\n }\n\n relative_short(value) {\n return this.relative(value, true);\n }\n\n linkify(value, options = {}) {\n if (value === null || value === undefined) return '';\n const text = String(value);\n const escaped = this.escapeHtml(text);\n const defaults = { urls: true, emails: true, target: '_blank', rel: 'noopener noreferrer' };\n const opts = (options && typeof options === 'object') ? { ...defaults, ...options } : defaults;\n\n let result = escaped;\n\n // URLs: http(s):// and www.\n if (opts.urls !== false) {\n const urlRegex = /(^|\\s)((?:https?:\\/\\/|www\\.)[^\\s<]+)/gi;\n result = result.replace(urlRegex, (match, prefix, url) => {\n const href = url.startsWith('www.') ? `https://${url}` : url;\n return `${prefix}<a href=\"${href}\" target=\"${opts.target}\" rel=\"${opts.rel}\">${url}</a>`;\n });\n }\n\n // Emails\n if (opts.emails !== false) {\n const emailRegex = /\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/gi;\n result = result.replace(emailRegex, (email) => `<a href=\"mailto:${email}\">${email}</a>`);\n }\n\n return result;\n }\n\n clipboard(value, mode = 'text') {\n if (value === null || value === undefined) return '';\n const text = String(value);\n const escapedText = this.escapeHtml(text);\n const showText = mode !== 'icon-only';\n\n const buttonHtml = `\n <button type=\"button\"\n class=\"btn btn-sm btn-outline-secondary ms-1 p-0 border-0 bg-transparent\"\n title=\"Copy\"\n data-bs-toggle=\"tooltip\"\n data-action=\"copy-to-clipboard\"\n data-clipboard=\"${escapedText}\">\n <i class=\"bi bi-clipboard\"></i>\n </button>`.trim();\n\n return `\n <span class=\"mojo-clipboard d-inline-flex align-items-center\">\n ${showText ? `<span class=\"font-monospace\">${escapedText}</span>` : ''}\n ${buttonHtml}\n </span>\n `;\n }\n\n nl2br(value) {\n if (value === null || value === undefined) return '';\n return this.escapeHtml(String(value)).replace(/\\r\\n|\\r|\\n/g, '<br>');\n }\n\n code(value, lang = '') {\n if (value === null || value === undefined) return '';\n const language = lang ? `language-${this.escapeHtml(String(lang))}` : '';\n const content = this.escapeHtml(String(value));\n return `<pre class=\"bg-light p-2 rounded border\"><code class=\"${language}\">${content}</code></pre>`;\n }\n\n /**\n * Register a custom formatter\n * @param {string} name - Formatter name\n * @param {Function} formatter - Formatter function\n * @returns {DataFormatter} This instance for chaining\n */\n register(name, formatter) {\n if (typeof formatter !== 'function') {\n throw new Error(`Formatter must be a function, got ${typeof formatter}`);\n }\n this.formatters.set(name.toLowerCase(), formatter);\n return this;\n }\n\n /**\n * Apply a formatter\n * @param {string} name - Formatter name\n * @param {*} value - Value to format\n * @param {...*} args - Additional arguments\n * @returns {*} Formatted value\n */\n apply(name, value, ...args) {\n try {\n const formatter = this.formatters.get(name.toLowerCase());\n if (!formatter) {\n console.warn(`Formatter '${name}' not found`);\n return value;\n }\n return formatter(value, ...args);\n } catch (error) {\n console.error(`Error in formatter '${name}':`, error);\n return value;\n }\n }\n\n /**\n * Process pipe string\n * @param {*} value - Value to format\n * @param {string} pipeString - Pipe string (e.g., \"date('YYYY-MM-DD')|uppercase\")\n * @param {object} context - Optional context for resolving variables in formatter arguments\n * @returns {*} Formatted value\n */\n pipe(value, pipeString, context = null) {\n if (!pipeString) return value;\n\n // Split by pipe and process each formatter\n const pipes = this.parsePipeString(pipeString, context);\n\n return pipes.reduce((currentValue, pipe) => {\n return this.apply(pipe.name, currentValue, ...pipe.args);\n }, value);\n }\n\n /**\n * Parse pipe string into formatter calls\n * @param {string} pipeString - Pipe string\n * @param {object} context - Optional context for resolving variables\n * @returns {Array} Array of {name, args} objects\n */\n parsePipeString(pipeString, context = null) {\n const pipes = [];\n const tokens = pipeString.split('|').map(s => s.trim());\n\n for (const token of tokens) {\n const parsed = this.parseFormatter(token, context);\n if (parsed) {\n pipes.push(parsed);\n }\n }\n\n return pipes;\n }\n\n /**\n * Parse individual formatter with arguments\n * Supports both syntaxes:\n * - Parentheses: formatter('arg1', 'arg2', 3)\n * - Colon: formatter:'arg1':'arg2':3\n *\n * @param {string} token - Formatter token\n * @param {object} context - Optional context for resolving variables\n * @returns {Object} {name, args} object\n */\n parseFormatter(token, context = null) {\n // Try parentheses syntax first: formatter(arg1, arg2)\n const parenMatch = token.match(/^([a-zA-Z_]\\w*)\\s*\\((.*)\\)$/);\n if (parenMatch) {\n const [, name, argsString] = parenMatch;\n const args = argsString ? this.parseArguments(argsString, context) : [];\n return { name, args };\n }\n\n // Try colon syntax: formatter:arg1:arg2\n const colonMatch = token.match(/^([a-zA-Z_]\\w*)(?::(.+))?$/);\n if (colonMatch) {\n const [, name, argsString] = colonMatch;\n const args = argsString ? this.parseColonArguments(argsString, context) : [];\n return { name, args };\n }\n\n return null;\n }\n\n /**\n * Parse formatter arguments (comma-separated, parentheses syntax)\n * @param {string} argsString - Arguments string\n * @param {object} context - Optional context for resolving variables\n * @returns {Array} Parsed arguments\n */\n parseArguments(argsString, context = null) {\n const args = [];\n let current = '';\n let inQuotes = false;\n let quoteChar = null;\n let depth = 0;\n\n for (let i = 0; i < argsString.length; i++) {\n const char = argsString[i];\n\n if (!inQuotes && (char === '\"' || char === \"'\")) {\n inQuotes = true;\n quoteChar = char;\n current += char;\n } else if (inQuotes && char === quoteChar && argsString[i - 1] !== '\\\\') {\n inQuotes = false;\n quoteChar = null;\n current += char;\n } else if (!inQuotes && char === '{') {\n depth++;\n current += char;\n } else if (!inQuotes && char === '}') {\n depth--;\n current += char;\n } else if (!inQuotes && depth === 0 && char === ',') {\n args.push(this.parseValue(current.trim(), context));\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n args.push(this.parseValue(current.trim(), context));\n }\n\n return args;\n }\n\n /**\n * Parse formatter arguments (colon-separated syntax)\n * Handles quoted strings with colons inside them\n * @param {string} argsString - Arguments string\n * @param {object} context - Optional context for resolving variables\n * @returns {Array} Parsed arguments\n */\n parseColonArguments(argsString, context = null) {\n const args = [];\n let current = '';\n let inQuotes = false;\n let quoteChar = null;\n\n for (let i = 0; i < argsString.length; i++) {\n const char = argsString[i];\n\n if (!inQuotes && (char === '\"' || char === \"'\")) {\n inQuotes = true;\n quoteChar = char;\n current += char;\n } else if (inQuotes && char === quoteChar && argsString[i - 1] !== '\\\\') {\n inQuotes = false;\n quoteChar = null;\n current += char;\n } else if (!inQuotes && char === ':') {\n args.push(this.parseValue(current.trim(), context));\n current = '';\n } else {\n current += char;\n }\n }\n\n if (current.trim()) {\n args.push(this.parseValue(current.trim(), context));\n }\n\n return args;\n }\n\n /**\n * Parse a single value\n * @param {string} value - Value string\n * @param {object} context - Optional context for resolving variables\n * @returns {*} Parsed value\n */\n parseValue(value, context = null) {\n // Remove quotes if present - quoted strings are always literals\n if ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n return value.slice(1, -1);\n }\n\n // Boolean values\n if (value === 'true') return true;\n if (value === 'false') return false;\n if (value === 'null') return null;\n if (value === 'undefined') return undefined;\n\n // Numbers\n if (!isNaN(value) && value !== '') {\n return Number(value);\n }\n\n // Objects\n if (value.startsWith('{') && value.endsWith('}')) {\n try {\n return JSON.parse(value);\n } catch (e) {\n // Not valid JSON, continue\n }\n }\n\n // Try to resolve from context if available\n // This allows: {{value|tooltip:title:'top'}} where title is a context variable\n // Also supports dot notation: {{value|tooltip:model.email}}\n if (context && this.isIdentifier(value)) {\n // For simple identifiers, try direct property access\n if (!value.includes('.')) {\n if (Object.prototype.hasOwnProperty.call(context, value)) {\n return context[value];\n }\n }\n\n // Try get() method (for Models/Views) - handles dot notation\n if (context.get && typeof context.get === 'function') {\n const contextValue = context.get(value);\n if (contextValue !== undefined) {\n return contextValue;\n }\n }\n // Try getContextValue() method - handles dot notation\n if (context.getContextValue && typeof context.getContextValue === 'function') {\n const contextValue = context.getContextValue(value);\n if (contextValue !== undefined) {\n return contextValue;\n }\n }\n\n // For dot notation on plain objects, use MOJOUtils\n if (value.includes('.')) {\n // Import MOJOUtils if needed for nested property access\n const MOJOUtils = window.MOJOUtils || (typeof require !== 'undefined' ? require('./MOJOUtils.js').default : null);\n if (MOJOUtils) {\n const contextValue = MOJOUtils.getNestedValue(context, value);\n if (contextValue !== undefined) {\n return contextValue;\n }\n }\n }\n }\n\n // Fall back to treating as literal string\n return value;\n }\n\n /**\n * Check if a value is a valid identifier (variable name or dot-notation path)\n * @param {string} value - Value to check\n * @returns {boolean} True if valid identifier or path\n */\n isIdentifier(value) {\n // Valid JavaScript identifier or dot notation path\n // Examples: \"email\", \"model.email\", \"user.profile.name\"\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(value);\n }\n\n // ============= Date/Time Formatters =============\n\n /**\n * Format date\n * @param {*} value - Date value\n * @param {string} format - Date format pattern\n * @returns {string} Formatted date\n */\n date(value, format = 'MM/DD/YYYY') {\n if (!value) return '';\n value = this.normalizeEpoch(value);\n let date;\n if (value instanceof Date) {\n date = value;\n } else if (typeof value === 'string') {\n // Handle date strings more carefully to avoid timezone issues\n // If it's a date-only string (YYYY-MM-DD), parse as local time\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const [year, month, day] = value.split('-').map(Number);\n date = new Date(year, month - 1, day);\n } else {\n date = new Date(value);\n }\n } else {\n date = new Date(value);\n }\n\n if (isNaN(date.getTime())) return String(value);\n\n // Build replacements with placeholders to avoid corruption\n const tokens = {\n 'YYYY': date.getFullYear(),\n 'YY': String(date.getFullYear()).slice(-2),\n 'MMMM': date.toLocaleDateString('en-US', { month: 'long' }),\n 'MMM': date.toLocaleDateString('en-US', { month: 'short' }),\n 'MM': String(date.getMonth() + 1).padStart(2, '0'),\n 'M': date.getMonth() + 1,\n 'dddd': date.toLocaleDateString('en-US', { weekday: 'long' }),\n 'ddd': date.toLocaleDateString('en-US', { weekday: 'short' }),\n 'DD': String(date.getDate()).padStart(2, '0'),\n 'D': date.getDate()\n };\n\n // Replace tokens using a single pass with placeholders\n let result = format;\n const tokenPattern = new RegExp(`(${Object.keys(tokens).join('|')})`, 'g');\n result = result.replace(tokenPattern, (match) => tokens[match] || match);\n\n return result;\n }\n\n /**\n * Format time\n * @param {*} value - Time value\n * @param {string} format - Time format pattern\n * @returns {string} Formatted time\n */\n time(value, format = 'HH:mm:ss') {\n if (!value) return '';\n value = this.normalizeEpoch(value);\n const date = value instanceof Date ? value : new Date(value);\n if (isNaN(date.getTime())) return String(value);\n\n const hours = date.getHours();\n const replacements = {\n 'HH': String(hours).padStart(2, '0'),\n 'H': hours,\n 'hh': String(hours % 12 || 12).padStart(2, '0'),\n 'h': hours % 12 || 12,\n 'mm': String(date.getMinutes()).padStart(2, '0'),\n 'm': date.getMinutes(),\n 'ss': String(date.getSeconds()).padStart(2, '0'),\n 's': date.getSeconds(),\n 'A': hours >= 12 ? 'PM' : 'AM',\n 'a': hours >= 12 ? 'pm' : 'am'\n };\n\n let result = format;\n const sortedKeys = Object.keys(replacements).sort((a, b) => b.length - a.length);\n for (const key of sortedKeys) {\n result = result.replace(new RegExp(key, 'g'), replacements[key]);\n }\n\n return result;\n }\n\n /**\n * Format date and time\n * @param {*} value - DateTime value\n * @param {string} dateFormat - Date format\n * @param {string} timeFormat - Time format\n * @returns {string} Formatted datetime\n */\n datetime(value, dateFormat = 'MM/DD/YYYY', timeFormat = 'HH:mm:ss') {\n value = this.normalizeEpoch(value);\n const dateStr = this.date(value, dateFormat);\n const timeStr = this.time(value, timeFormat);\n return dateStr && timeStr ? `${dateStr} ${timeStr}` : '';\n }\n\n /**\n * Format date and time with short timezone abbreviation (e.g., EST, PDT)\n * @param {*} value - DateTime value\n * @param {string} dateFormat - Date format\n * @param {string} timeFormat - Time format\n * @param {Object} options - Options: { timeZone?: string, locale?: string }\n * @returns {string} Formatted datetime with timezone abbreviation\n */\n datetime_tz(value, dateFormat = 'MM/DD/YYYY', timeFormat = 'HH:mm:ss', options = {}) {\n if (!value) return '';\n value = this.normalizeEpoch(value);\n const date = value instanceof Date ? value : new Date(value);\n if (isNaN(date.getTime())) return String(value);\n\n const locale = (options && options.locale) || 'en-US';\n const timeZone = options && options.timeZone ? options.timeZone : undefined;\n\n // Helper: build short TZ abbreviation in the requested zone\n const getTzAbbr = () => {\n let abbr = '';\n try {\n const parts = new Intl.DateTimeFormat(locale, {\n hour: '2-digit',\n minute: '2-digit',\n timeZoneName: 'short',\n ...(timeZone ? { timeZone } : {})\n }).formatToParts(date);\n const tzPart = parts.find(p => p.type === 'timeZoneName');\n abbr = tzPart ? tzPart.value : '';\n\n // Try to avoid GMT offsets if browser returns them\n if (abbr && /^GMT[+-]/i.test(abbr)) {\n try {\n const parts2 = new Intl.DateTimeFormat(locale, {\n timeStyle: 'short',\n timeZoneName: 'short',\n ...(timeZone ? { timeZone } : {})\n }).formatToParts(date);\n const tz2 = parts2.find(p => p.type === 'timeZoneName');\n if (tz2 && tz2.value && !/^GMT[+-]/i.test(tz2.value)) {\n abbr = tz2.value;\n }\n } catch (e) { void 0; }\n }\n // Collapse long names like \"Eastern Daylight Time\" to initials \"EDT\"\n if (abbr && /\\s/.test(abbr)) {\n const initials = abbr.split(/\\s+/).map(w => w[0]).join('').toUpperCase();\n if (initials.length >= 2 && initials.length <= 4) {\n abbr = initials;\n }\n }\n } catch (e) {\n abbr = '';\n }\n return abbr;\n };\n\n // If no specific timeZone requested, fall back to existing date/time logic\n if (!timeZone) {\n const dateStr = this.date(date, dateFormat);\n const timeStr = this.time(date, timeFormat);\n const abbr = getTzAbbr();\n return dateStr && timeStr ? `${dateStr} ${timeStr} ${abbr}`.trim() : '';\n }\n\n // With a specific timeZone, generate tokens from Intl parts in that zone\n const parts = new Intl.DateTimeFormat(locale, {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hourCycle: 'h23'\n }).formatToParts(date);\n const get = (type) => {\n const p = parts.find(pt => pt.type === type);\n return p ? p.value : '';\n };\n\n const y4 = get('year');\n const M2 = get('month'); // '01'..'12'\n const D2 = get('day'); // '01'..'31'\n const H2 = get('hour'); // '00'..'23'\n const m2 = get('minute');\n const s2 = get('second');\n\n const M = M2 ? String(parseInt(M2, 10)) : '';\n const D = D2 ? String(parseInt(D2, 10)) : '';\n const H = H2 ? String(parseInt(H2, 10)) : '';\n const hNum = H2 ? ((parseInt(H2, 10) % 12) || 12) : '';\n const A = H2 ? (parseInt(H2, 10) >= 12 ? 'PM' : 'AM') : '';\n const a = A ? A.toLowerCase() : '';\n\n const monthLong = new Intl.DateTimeFormat(locale, { timeZone, month: 'long' }).format(date);\n const monthShort = new Intl.DateTimeFormat(locale, { timeZone, month: 'short' }).format(date);\n const weekdayLong = new Intl.DateTimeFormat(locale, { timeZone, weekday: 'long' }).format(date);\n const weekdayShort = new Intl.DateTimeFormat(locale, { timeZone, weekday: 'short' }).format(date);\n\n const dateTokens = {\n 'YYYY': y4,\n 'YY': y4 ? y4.slice(-2) : '',\n 'MMMM': monthLong,\n 'MMM': monthShort,\n 'MM': M2,\n 'M': M,\n 'dddd': weekdayLong,\n 'ddd': weekdayShort,\n 'DD': D2,\n 'D': D\n };\n const timeTokens = {\n 'HH': H2,\n 'H': H,\n 'hh': hNum !== '' ? String(hNum).padStart(2, '0') : '',\n 'h': hNum !== '' ? String(hNum) : '',\n 'mm': m2,\n 'm': m2 ? String(parseInt(m2, 10)) : '',\n 'ss': s2,\n 's': s2 ? String(parseInt(s2, 10)) : '',\n 'A': A,\n 'a': a\n };\n\n const replaceTokens = (fmt, tokens) => {\n if (!fmt) return '';\n const pattern = new RegExp(`(${Object.keys(tokens).sort((a, b) => b.length - a.length).join('|')})`, 'g');\n return fmt.replace(pattern, (match) => tokens[match] ?? match);\n };\n\n const dateStr = replaceTokens(dateFormat, dateTokens);\n const timeStr = replaceTokens(timeFormat, timeTokens);\n const abbr = getTzAbbr();\n\n return dateStr && timeStr ? `${dateStr} ${timeStr} ${abbr}`.trim() : '';\n }\n\n normalizeEpoch(value) {\n if (typeof value !== \"number\") value = Number(value);\n\n // Check if the number is valid\n if (isNaN(value)) return '';\n\n // treat anything smaller than year 2000 in ms as seconds\n if (value < 1e11) { // less than ~Sat Mar 03 1973 09:46:40 GMT\n return value * 1000; // seconds → ms\n } else if (value > 1e12 && value < 1e13) {\n return value; // already ms\n } else {\n throw new Error(\"Value doesn't look like epoch seconds or ms\");\n }\n }\n\n /**\n * Format date range\n * @param {*} startValue - Start date (required)\n * @param {*} endValue - End date (defaults to now)\n * @param {string} format - Date format (defaults to 'MM/DD/YYYY')\n * @returns {string} Formatted date range (e.g., \"01/01/2025 - 01/31/2025\")\n */\n date_range(startValue, endValue = null, format = 'MM/DD/YYYY') {\n if (!startValue) return '';\n\n const endVal = endValue || new Date();\n const startStr = this.date(startValue, format);\n const endStr = this.date(endVal, format);\n\n if (!startStr || !endStr) return '';\n return `${startStr} - ${endStr}`;\n }\n\n /**\n * Format datetime range\n * @param {*} startValue - Start datetime (required)\n * @param {*} endValue - End datetime (defaults to now)\n * @param {string} dateFormat - Date format (defaults to 'MM/DD/YYYY')\n * @param {string} timeFormat - Time format (defaults to 'HH:mm')\n * @returns {string} Formatted datetime range (e.g., \"01/01/2025 14:30 - 01/31/2025 16:45\")\n */\n datetime_range(startValue, endValue = null, dateFormat = 'MM/DD/YYYY', timeFormat = 'HH:mm') {\n if (!startValue) return '';\n\n const endVal = endValue || new Date();\n const startStr = this.datetime(startValue, dateFormat, timeFormat);\n const endStr = this.datetime(endVal, dateFormat, timeFormat);\n\n if (!startStr || !endStr) return '';\n return `${startStr} - ${endStr}`;\n }\n\n /**\n * Format relative time\n * @param {*} value - Date value\n * @param {boolean} short - Use short format\n * @returns {string} Relative time string\n */\n relative(value, short = false) {\n if (!value) return '';\n value = this.normalizeEpoch(value);\n const date = value instanceof Date ? value : new Date(value);\n if (isNaN(date.getTime())) return String(value);\n\n const now = new Date();\n const diffMs = date - now; // Changed to support future dates\n const absDiffMs = Math.abs(diffMs);\n const diffSecs = Math.floor(absDiffMs / 1000);\n const diffMins = Math.floor(diffSecs / 60);\n const diffHours = Math.floor(diffMins / 60);\n const diffDays = Math.floor(diffHours / 24);\n const isFuture = diffMs > 0;\n\n if (short) {\n if (diffDays > 365) return Math.floor(diffDays / 365) + 'y';\n if (diffDays > 30) return Math.floor(diffDays / 30) + 'mo';\n if (diffDays > 7) return Math.floor(diffDays / 7) + 'w';\n if (diffDays > 0) return diffDays + 'd';\n if (diffHours > 0) return diffHours + 'h';\n if (diffMins > 0) return diffMins + 'm';\n return 'now';\n }\n\n if (diffDays > 365) {\n const years = Math.floor(diffDays / 365);\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + years + ' year' + (years > 1 ? 's' : '') + suffix;\n }\n if (diffDays > 30) {\n const months = Math.floor(diffDays / 30);\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + months + ' month' + (months > 1 ? 's' : '') + suffix;\n }\n if (diffDays > 7) {\n const weeks = Math.floor(diffDays / 7);\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + weeks + ' week' + (weeks > 1 ? 's' : '') + suffix;\n }\n if (diffDays === 1) return isFuture ? 'tomorrow' : 'yesterday';\n if (diffDays > 0) {\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + diffDays + ' days' + suffix;\n }\n if (diffHours > 0) {\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + diffHours + ' hour' + (diffHours > 1 ? 's' : '') + suffix;\n }\n if (diffMins > 0) {\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + diffMins + ' minute' + (diffMins > 1 ? 's' : '') + suffix;\n }\n if (diffSecs > 30) {\n const prefix = isFuture ? 'in ' : '';\n const suffix = isFuture ? '' : ' ago';\n return prefix + diffSecs + ' seconds' + suffix;\n }\n\n return 'just now';\n }\n\n /**\n * Format ISO date\n * @param {*} value - Date value\n * @param {boolean} dateOnly - Return date only\n * @returns {string} ISO date string\n */\n iso(value, dateOnly = false) {\n if (!value) return '';\n value = this.normalizeEpoch(value);\n const date = value instanceof Date ? value : new Date(value);\n if (isNaN(date.getTime())) return String(value);\n\n if (dateOnly) {\n return date.toISOString().split('T')[0];\n }\n return date.toISOString();\n }\n\n // ============= Number Formatters =============\n\n /**\n * Format number\n * @param {*} value - Number value\n * @param {number} decimals - Decimal places\n * @param {string} locale - Locale string\n * @returns {string} Formatted number\n */\n number(value, decimals = 2, locale = 'en-US') {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n\n return num.toLocaleString(locale, {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals\n });\n }\n\n /**\n * Format currency\n * @param {*} value - Number value in cents\n * @param {string} symbol - Currency symbol\n * @param {number} decimals - Decimal places\n * @returns {string} Formatted currency\n */\n currency(value, symbol = '$', decimals = 2) {\n const num = parseInt(value);\n if (isNaN(num)) return String(value);\n\n // Convert cents to dollars using string manipulation to avoid floating point issues\n const centsStr = Math.abs(num).toString();\n const sign = num < 0 ? '-' : '';\n\n let dollars, cents;\n if (centsStr.length <= 2) {\n dollars = '0';\n cents = centsStr.padStart(2, '0');\n } else {\n dollars = centsStr.slice(0, -2);\n cents = centsStr.slice(-2);\n }\n\n // Add thousands separators to dollars part\n dollars = dollars.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n\n // Format based on requested decimals\n let formatted;\n if (decimals === 0) {\n // Round to nearest dollar\n const totalCents = parseInt(cents);\n if (totalCents >= 50) {\n dollars = (parseInt(dollars.replace(/,/g, '')) + 1).toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n }\n formatted = dollars;\n } else if (decimals === 2) {\n formatted = `${dollars}.${cents}`;\n } else {\n // For other decimal places, truncate or pad cents as needed\n const adjustedCents = cents.slice(0, decimals).padEnd(decimals, '0');\n formatted = `${dollars}.${adjustedCents}`;\n }\n\n return sign + symbol + formatted;\n }\n\n /**\n * Format percentage\n * @param {*} value - Number value\n * @param {number} decimals - Decimal places\n * @param {boolean} multiply - Multiply by 100\n * @returns {string} Formatted percentage\n */\n percent(value, decimals = 0, multiply = true) {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n\n const percent = multiply ? num * 100 : num;\n return this.number(percent, decimals) + '%';\n }\n\n /**\n * Format file size\n * @param {*} value - Size in bytes\n * @param {boolean} binary - Use binary units (1024)\n * @param {number} decimals - Decimal places\n * @returns {string} Formatted file size\n */\n filesize(value, binary = false, decimals = 1) {\n const bytes = parseInt(value);\n if (isNaN(bytes)) return String(value);\n\n const units = binary ?\n ['B', 'KiB', 'MiB', 'GiB', 'TiB'] :\n ['B', 'KB', 'MB', 'GB', 'TB'];\n const divisor = binary ? 1024 : 1000;\n\n let size = bytes;\n let unitIndex = 0;\n\n while (size >= divisor && unitIndex < units.length - 1) {\n size /= divisor;\n unitIndex++;\n }\n\n const decimalPlaces = unitIndex === 0 ? 0 : decimals;\n return `${size.toFixed(decimalPlaces)} ${units[unitIndex]}`;\n }\n\n /**\n * Format ordinal number\n * @param {*} value - Number value\n * @param {boolean} suffixOnly - Return suffix only\n * @returns {string} Ordinal number\n */\n ordinal(value, suffixOnly = false) {\n const num = parseInt(value);\n if (isNaN(num)) return String(value);\n\n const j = num % 10;\n const k = num % 100;\n\n let suffix = 'th';\n if (j === 1 && k !== 11) suffix = 'st';\n else if (j === 2 && k !== 12) suffix = 'nd';\n else if (j === 3 && k !== 13) suffix = 'rd';\n\n return suffixOnly ? suffix : num + suffix;\n }\n\n /**\n * Format compact number\n * @param {*} value - Number value\n * @param {number} decimals - Decimal places\n * @returns {string} Compact number\n */\n compact(value, decimals = 1) {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n\n const abs = Math.abs(num);\n const sign = num < 0 ? '-' : '';\n\n if (abs >= 1e9) {\n return sign + (abs / 1e9).toFixed(decimals) + 'B';\n }\n if (abs >= 1e6) {\n return sign + (abs / 1e6).toFixed(decimals) + 'M';\n }\n if (abs >= 1e3) {\n return sign + (abs / 1e3).toFixed(decimals) + 'K';\n }\n\n return String(num);\n }\n\n /**\n * Add numbers\n * @param {*} value - First number\n * @param {*} addend - Number to add\n * @returns {number} Sum\n */\n add(value, addend) {\n const num1 = parseFloat(value);\n const num2 = parseFloat(addend);\n if (isNaN(num1) || isNaN(num2)) return value;\n return num1 + num2;\n }\n\n /**\n * Subtract numbers\n * @param {*} value - First number\n * @param {*} subtrahend - Number to subtract\n * @returns {number} Difference\n */\n subtract(value, subtrahend) {\n const num1 = parseFloat(value);\n const num2 = parseFloat(subtrahend);\n if (isNaN(num1) || isNaN(num2)) return value;\n return num1 - num2;\n }\n\n /**\n * Multiply numbers\n * @param {*} value - First number\n * @param {*} multiplier - Number to multiply by\n * @returns {number} Product\n */\n multiply(value, multiplier) {\n const num1 = parseFloat(value);\n const num2 = parseFloat(multiplier);\n if (isNaN(num1) || isNaN(num2)) return value;\n return num1 * num2;\n }\n\n /**\n * Divide numbers\n * @param {*} value - Dividend\n * @param {*} divisor - Divisor\n * @returns {number} Quotient\n */\n divide(value, divisor) {\n const num1 = parseFloat(value);\n const num2 = parseFloat(divisor);\n if (isNaN(num1) || isNaN(num2) || num2 === 0) return value;\n return num1 / num2;\n }\n\n // ============= String Formatters =============\n\n /**\n * Capitalize string\n * @param {*} value - String value\n * @param {boolean} all - Capitalize all words (default: true). If false, only capitalizes first letter\n * @returns {string} Capitalized string\n */\n capitalize(value, all = true) {\n const str = String(value);\n if (!str) return '';\n\n if (all) {\n return str.replace(/\\b\\w/g, c => c.toUpperCase());\n }\n return str.charAt(0).toUpperCase() + str.slice(1);\n }\n\n /**\n * Replace occurrences in a string\n * @param {*} value - String value\n * @param {*} search - Search value (string or RegExp-ish string like \"/_/g\")\n * @param {*} replacement - Replacement string\n * @param {string} flags - Optional RegExp flags when search is a plain string\n * @returns {string} Updated string\n *\n * Examples:\n * - {{model.name|replace:'_':''}} // underscores removed (all occurrences)\n * - {{model.name|replace('_', '')}} // parentheses syntax\n * - {{model.name|replace:'_':' ':'g'}} // replace all underscores with spaces\n * - {{model.name|replace:'/[_-]+/g':' '}} // regex form in a string\n */\n replace(value, search, replacement = '', flags = 'g') {\n if (value === null || value === undefined) return '';\n const str = String(value);\n\n if (search === null || search === undefined || search === '') {\n return str;\n }\n\n // If a real RegExp was passed in, use it directly.\n if (search instanceof RegExp) {\n return str.replace(search, String(replacement));\n }\n\n const searchStr = String(search);\n\n // Support \"/pattern/flags\" style passed as a string.\n // Note: this is intentionally simple and doesn't attempt to parse escaped slashes.\n const regexLike = searchStr.match(/^\\/(.+)\\/([a-z]*)$/i);\n if (regexLike) {\n const [, pattern, rxFlags] = regexLike;\n try {\n return str.replace(new RegExp(pattern, rxFlags), String(replacement));\n } catch (e) {\n // Fall back to string replace below if regex construction fails\n }\n }\n\n // Default: string replace. If flags includes 'g', replace all occurrences.\n if (String(flags).includes('g')) {\n return str.split(searchStr).join(String(replacement));\n }\n\n return str.replace(searchStr, String(replacement));\n }\n\n /**\n * Truncate string\n * @param {*} value - String value\n * @param {number} length - Max length\n * @param {string} suffix - Suffix to append\n * @returns {string} Truncated string\n */\n truncate(value, length = 50, suffix = '...') {\n const str = String(value);\n if (str.length <= length) return str;\n return str.substring(0, length) + suffix;\n }\n\n /**\n * Truncate keeping only the end of the string\n * @param {*} value - String value\n * @param {number} length - Characters to keep at the end\n * @param {string} prefix - Text to prepend when truncating\n * @returns {string} Truncated string\n */\n truncate_front(value, length = 8, prefix = '...') {\n const str = String(value);\n if (str.length <= length) {\n return str;\n }\n return `${prefix}${str.slice(-length)}`;\n }\n\n /**\n * Truncate string in the middle\n * @param {*} value - String value\n * @param {number} size - The total number of characters to keep (half for the start, half for the end).\n * @param {string} replace - The character(s) to use for the middle part.\n * @returns {string} Truncated string\n */\n truncate_middle(value, size = 8, replace = '***') {\n const str = String(value);\n if (str.length <= size) {\n return str;\n }\n\n const halfSize = Math.floor(size / 2);\n const front = str.substring(0, halfSize);\n const back = str.substring(str.length - halfSize);\n\n return `${front}${replace}${back}`;\n }\n\n /**\n * Create slug from string\n * @param {*} value - String value\n * @param {string} separator - Word separator\n * @returns {string} Slug\n */\n slug(value, separator = '-') {\n const str = String(value);\n return str\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, separator)\n .replace(new RegExp(`${separator}+`, 'g'), separator)\n .replace(new RegExp(`^${separator}|${separator}$`, 'g'), '');\n }\n\n /**\n * Get initials from string\n * @param {*} value - String value\n * @param {number} count - Number of initials\n * @returns {string} Initials\n */\n initials(value, count = 2) {\n const str = String(value);\n const words = str.split(/\\s+/).filter(w => w.length > 0);\n return words\n .slice(0, count)\n .map(word => word.charAt(0).toUpperCase())\n .join('');\n }\n\n /**\n * Mask string\n * @param {*} value - String value\n * @param {string} char - Mask character\n * @param {number} showLast - Number of chars to show at end\n * @returns {string} Masked string\n */\n mask(value, char = '*', showLast = 4) {\n const str = String(value);\n if (str.length <= showLast) return str;\n\n const masked = char.repeat(Math.max(0, str.length - showLast));\n const visible = str.slice(-showLast);\n return masked + visible;\n }\n\n // ============= HTML/Web Formatters =============\n\n /**\n * Format email\n * @param {*} value - Email value\n * @param {Object} options - Options\n * @returns {string} Formatted email\n */\n email(value, options = {}) {\n const email = String(value).trim();\n if (!email) return '';\n\n if (options.link === false) {\n return email;\n }\n\n const subject = options.subject ? `?subject=${encodeURIComponent(options.subject)}` : '';\n const body = options.body ? `&body=${encodeURIComponent(options.body)}` : '';\n const className = options.class ? ` class=\"${options.class}\"` : '';\n\n return `<a href=\"mailto:${email}${subject}${body}\"${className}>${email}</a>`;\n }\n\n /**\n * Format phone number\n * @param {*} value - Phone value\n * @param {string} format - Format type\n * @param {boolean} link - Create tel link\n * @returns {string} Formatted phone\n */\n phone(value, format = 'US', link = true) {\n let phone = String(value).replace(/\\D/g, '');\n\n let formatted = phone;\n if (format === 'US') {\n if (phone.length === 10) {\n formatted = `(${phone.slice(0, 3)}) ${phone.slice(3, 6)}-${phone.slice(6)}`;\n } else if (phone.length === 11 && phone[0] === '1') {\n formatted = `+1 (${phone.slice(1, 4)}) ${phone.slice(4, 7)}-${phone.slice(7)}`;\n }\n }\n\n if (!link) {\n return formatted;\n }\n\n return `<a href=\"tel:${phone}\">${formatted}</a>`;\n }\n\n /**\n * Format URL\n * @param {*} value - URL value\n * @param {string} text - Link text\n * @param {boolean} newWindow - Open in new window\n * @returns {string} Formatted URL\n */\n url(value, text = null, newWindow = true) {\n let url = String(value).trim();\n if (!url) return '';\n\n // Add protocol if missing\n if (!/^https?:\\/\\//.test(url)) {\n url = 'https://' + url;\n }\n\n const linkText = text || url;\n const target = newWindow ? ' target=\"_blank\"' : '';\n const rel = newWindow ? ' rel=\"noopener noreferrer\"' : '';\n\n return `<a href=\"${url}\"${target}${rel}>${linkText}</a>`;\n }\n\n /**\n * Format as badge\n * @param {*} value - Badge text\n * @param {string} type - Badge type\n * @returns {string} Badge HTML\n */\n badge(value, type = 'auto') {\n // If the value is an array, map over it and create a badge for each item.\n if (Array.isArray(value)) {\n return value.map(item => this.badge(item, type)).join(' ');\n }\n\n const text = String(value);\n const badgeType = type === 'auto' ? this.inferBadgeType(text) : type;\n const className = badgeType ? `bg-${badgeType}` : 'bg-secondary';\n\n return `<span class=\"badge ${className}\">${text}</span>`;\n }\n\n /**\n * Get badge CSS class for a value\n * @param {*} value - Value to get badge class for\n * @param {string} type - Badge type (optional, auto-detected if not specified)\n * @returns {string} Badge CSS class\n */\n badgeClass(value, type = 'auto') {\n const text = String(value);\n const badgeType = type === 'auto' ? this.inferBadgeType(text) : type;\n return badgeType ? `bg-${badgeType}` : 'bg-secondary';\n }\n\n /**\n * Infer badge type from text\n * @param {string} text - Badge text\n * @returns {string} Badge type\n */\n inferBadgeType(text) {\n const lowered = text.toLowerCase();\n if (['active', 'success', 'complete', 'completed', 'approved', 'done', 'true', 'on', 'yes'].includes(lowered)) return 'success';\n if (['error', 'failed', 'rejected', 'deleted', 'cancelled', 'false', 'off', 'no', 'declined'].includes(lowered)) return 'danger';\n if (['warning', 'pending', 'review', 'processing', 'uploading'].includes(lowered)) return 'warning';\n if (['info', 'new', 'draft'].includes(lowered)) return 'info';\n if (['inactive', 'disabled', 'archived', 'suspended'].includes(lowered)) return 'secondary';\n return 'secondary';\n }\n\n status(value) {\n return this._status(value);\n }\n\n status_icon(value) {\n return this._status(value, {}, {}, false, true);\n }\n\n status_text(value) {\n return this._status(value, {}, {}, true, false);\n }\n\n /**\n * Format status\n * @param {*} value - Status value\n * @param {Object} icons - Icon mapping\n * @param {Object} colors - Color mapping\n * @param {boolean} noIcons - Whether to include icons\n * @param {boolean} noText - Whether to include text\n * @returns {string} Status HTML\n */\n _status(value, icons = {}, colors = {}, noIcons = false, noText = false) {\n const status = String(value).toLowerCase();\n\n const defaultIcons = {\n 'active': 'bi bi-check-circle-fill',\n 'approved': 'bi bi-check-circle-fill',\n 'declined': 'bi bi-x-circle-fill',\n 'inactive': 'bi bi-pause-circle-fill',\n 'pending': 'bi bi-clock-fill',\n 'success': 'bi bi-check-circle-fill',\n 'error': 'bi bi-exclamation-triangle-fill',\n 'warning': 'bi bi-exclamation-triangle-fill'\n };\n\n const defaultColors = {\n 'active': 'success',\n \"approved\": \"success\",\n \"declined\": \"danger\",\n 'inactive': 'secondary',\n 'pending': 'warning',\n 'success': 'success',\n 'error': 'danger',\n 'warning': 'warning'\n };\n\n const iconClass = icons[status] || defaultIcons[status] || '';\n const color = colors[status] || defaultColors[status] || 'secondary';\n\n let icon = '';\n if (!noIcons && iconClass) {\n icon = `<i class=\"${iconClass}\"></i>`;\n }\n let text = '';\n if (!noText) {\n text = value;\n }\n\n return `<span class=\"text-${color}\">${icon}${icon ? ' ' : ''}${text}</span>`;\n }\n\n /**\n * Format boolean\n * @param {*} value - Boolean value\n * @param {string} trueText - Text for true\n * @param {string} falseText - Text for false\n * @returns {string} Boolean text\n */\n boolean(value, trueText = 'True', falseText = 'False', colored = false) {\n const text = value ? trueText : falseText;\n return colored ? `<span class=\"text-${value ? 'success' : 'danger'}\">${text}</span>` : text;\n }\n\n /**\n * Format icon\n * @param {*} value - Icon key\n * @param {Object} mapping - Icon mapping\n * @returns {string} Icon HTML\n */\n icon(value, mapping = {}) {\n const key = String(value).toLowerCase();\n const icon = mapping[key] || '';\n return icon ? `<i class=\"${icon}\"></i>` : '';\n }\n\n /**\n * Format boolean as a yes/no icon\n * @param {*} value - Boolean value\n * @returns {string} Icon HTML\n */\n yesnoicon(value, yesIcon = 'bi bi-check-circle-fill text-success', noIcon = 'bi bi-x-circle-fill text-danger') {\n if (value) { // Handles true, 1, \"true\", \"on\", etc.\n return `<i class=\"${yesIcon}\"></i>`;\n }\n // Handles false, 0, \"\", null, undefined\n return `<i class=\"${noIcon}\"></i>`;\n }\n\n /**\n * Format value as Bootstrap 5 image with optional rendition support\n * @param {string|object} value - URL string or file object with renditions\n * @param {string} rendition - Desired rendition (thumbnail, thumbnail_sm, etc.)\n * @param {string} classes - Additional CSS classes\n * @param {string} alt - Alt text for the image\n * @returns {string} Bootstrap image HTML\n */\n image(value, rendition = 'thumbnail', classes = 'img-fluid', alt = '') {\n const url = this._extractImageUrl(value, rendition);\n if (!url) return '';\n\n return `<img src=\"${url}\" class=\"${classes}\" alt=\"${alt}\" />`;\n }\n\n /**\n * Format value as Bootstrap 5 avatar (circular image)\n * @param {string|object} value - URL string or file object with renditions\n * @param {string} size - Avatar size (xs, sm, md, lg, xl)\n * @param {string} classes - Additional CSS classes\n * @param {string} alt - Alt text for the avatar\n * @returns {string} Bootstrap avatar HTML\n */\n avatar(value, size = 'md', classes = 'rounded-circle', alt = '') {\n const url = this._extractImageUrl(value, 'square_sm') || GENERIC_AVATAR_SVG;\n\n // Bootstrap avatar sizing\n const sizeClasses = {\n 'xs': 'width: 1.5rem; height: 1.5rem;',\n 'sm': 'width: 2rem; height: 2rem;',\n 'md': 'width: 3rem; height: 3rem;',\n 'lg': 'width: 4rem; height: 4rem;',\n 'xl': 'width: 5rem; height: 5rem;'\n };\n\n const sizeStyle = sizeClasses[size] || sizeClasses['md'];\n const baseClasses = 'object-fit-cover';\n const allClasses = `${baseClasses} ${classes}`.trim();\n\n return `<img src=\"${url}\" class=\"${allClasses}\" style=\"${sizeStyle}\" alt=\"${alt}\" />`;\n }\n\n /**\n * Tooltip formatter - wraps value with Bootstrap tooltip\n * Usage:\n * {{value|tooltip:'Tooltip text'}}\n * {{value|tooltip:'Help text':top}}\n * {{value|tooltip:'Info':bottom:html}}\n *\n * @param {*} value - Value to display (not escaped, works with formatter chains)\n * @param {string} text - Tooltip text content\n * @param {string} placement - Tooltip placement: top, bottom, left, right (default: top)\n * @param {string} html - 'html' to allow HTML in tooltip (default: text only)\n * @returns {string} HTML with tooltip\n */\n tooltip(value, text = '', placement = 'top', html = '') {\n if (value === null || value === undefined) return '';\n\n // Don't escape value - it may be HTML from previous formatters in the chain\n const displayValue = String(value);\n const tooltipText = html === 'html' ? text : this.escapeHtml(text);\n const dataAttr = html === 'html' ? 'data-bs-html=\"true\"' : '';\n\n return `<span data-bs-toggle=\"tooltip\" data-bs-placement=\"${placement}\" ${dataAttr} data-bs-title=\"${tooltipText}\">${displayValue}</span>`;\n }\n\n /**\n * Helper method to extract image URL from string or file object\n * @param {string|object} value - URL string or file object with renditions\n * @param {string} preferredRendition - Preferred rendition name\n * @returns {string|null} Image URL or null if not found\n */\n _extractImageUrl(value, preferredRendition = 'thumbnail') {\n // Handle null/undefined\n if (!value) return null;\n\n // Handle string URL directly\n if (typeof value === 'string') {\n return value;\n }\n\n // Handle file object with renditions\n if (typeof value === 'object') {\n\n if (value.attributes) value = value.attributes;\n if (preferredRendition === \"thumbnail\" && value.thumbnail && typeof value.thumbnail === 'string') {\n return value.thumbnail;\n }\n // Check if it has renditions\n if (value.renditions && typeof value.renditions === 'object') {\n // Try to get preferred rendition\n const rendition = value.renditions[preferredRendition];\n if (rendition && rendition.url) {\n return rendition.url;\n }\n\n // Fallback to any available rendition\n const availableRenditions = Object.values(value.renditions);\n if (availableRenditions.length > 0 && availableRenditions[0].url) {\n return availableRenditions[0].url;\n }\n }\n\n // Fallback to original file URL\n if (value.url) {\n return value.url;\n }\n }\n\n return null;\n }\n\n // ============= Utility Formatters =============\n\n /**\n * Apply default value\n * @param {*} value - Value\n * @param {*} defaultValue - Default value\n * @returns {*} Value or default\n */\n default(value, defaultValue = '') {\n return value === null || value === undefined || value === '' ? defaultValue : value;\n }\n\n /**\n * Compare value and return one of two results based on equality\n * Useful for conditional CSS classes, text, or any conditional output\n *\n * @param {*} value - Value to compare\n * @param {*} compareValue - Value to compare against\n * @param {*} trueResult - Result if values are equal\n * @param {*} falseResult - Result if values are not equal (optional, defaults to empty string)\n * @returns {*} trueResult or falseResult\n *\n * @example\n * // CSS classes\n * {{status|equals:1:'text-success':'text-secondary'}}\n * {{model.state|equals:'active':'badge-success':'badge-secondary'}}\n *\n * // Text output\n * {{role|equals:'admin':'Administrator':'User'}}\n *\n * // Numbers\n * {{count|equals:0:'No items':'Has items'}}\n */\n equals(value, compareValue, trueResult, falseResult = '') {\n // Handle loose equality for common cases (1 == '1', true == 'true', etc.)\n // eslint-disable-next-line eqeqeq\n return value == compareValue ? trueResult : falseResult;\n }\n\n /**\n * Format as JSON\n * @param {*} value - Value to stringify\n * @param {number} indent - Indentation\n * @returns {string} JSON string\n */\n /**\n * Format pluralization based on count\n * @param {number} count - The count value\n * @param {string} singular - Singular form of the word\n * @param {string|null} plural - Plural form (defaults to singular + 's')\n * @param {boolean} includeCount - Whether to include the count in output\n * @returns {string} Formatted plural string\n */\n plural(count, singular, plural = null, includeCount = true) {\n if (count === null || count === undefined || singular === null || singular === undefined) {\n return includeCount ? `${count} ${singular}` : (singular || '');\n }\n\n const num = parseInt(count);\n if (isNaN(num)) {\n return includeCount ? `${count} ${singular}` : (singular || '');\n }\n\n const word = Math.abs(num) === 1 ? singular : (plural || singular + 's');\n return includeCount ? `${num} ${word}` : word;\n }\n\n /**\n * Format array as a human-readable list\n * @param {Array} array - Array to format\n * @param {Object} options - Formatting options\n * @returns {string} Formatted list string\n */\n formatList(array, options = {}) {\n if (!Array.isArray(array)) {\n return String(array);\n }\n\n const { conjunction = 'and', limit = null, moreText = 'others' } = options;\n\n if (array.length === 0) return '';\n if (array.length === 1) return String(array[0]);\n\n let items = array.slice();\n let hasMore = false;\n\n if (limit && array.length > limit) {\n items = array.slice(0, limit);\n hasMore = true;\n }\n\n if (hasMore) {\n const remaining = array.length - limit;\n return `${items.join(', ')}, ${conjunction} ${remaining} ${moreText}`;\n }\n\n if (items.length === 2) {\n return `${items[0]} ${conjunction} ${items[1]}`;\n }\n\n return `${items.slice(0, -1).join(', ')}, ${conjunction} ${items[items.length - 1]}`;\n }\n\n /**\n * Format duration to human-readable format\n * @param {number} value - Duration value\n * @param {string} unit - Input unit: 'ms', 's', 'm', 'h', 'd' (defaults to 'ms')\n * @param {boolean} short - Use short format (e.g., '1h30m' vs '1 hour 30 minutes')\n * @param {number} precision - Max number of units to show (defaults to 2)\n * @returns {string} Formatted duration string\n */\n duration(value, unit = 'ms', short = false, precision = 2) {\n if (value === null || value === undefined) return '';\n\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n\n // Convert input to milliseconds\n let ms;\n switch (unit) {\n case 's':\n case 'sec':\n case 'seconds':\n ms = num * 1000;\n break;\n case 'm':\n case 'min':\n case 'minutes':\n ms = num * 60000;\n break;\n case 'h':\n case 'hr':\n case 'hours':\n ms = num * 3600000;\n break;\n case 'd':\n case 'day':\n case 'days':\n ms = num * 86400000;\n break;\n case 'ms':\n case 'milliseconds':\n default:\n ms = num;\n }\n\n const units = [\n { name: 'day', short: 'd', value: 86400000 },\n { name: 'hour', short: 'h', value: 3600000 },\n { name: 'minute', short: 'm', value: 60000 },\n { name: 'second', short: 's', value: 1000 }\n ];\n\n if (ms === 0) return short ? '0s' : '0 seconds';\n\n const absMs = Math.abs(ms);\n const sign = ms < 0 ? '-' : '';\n const parts = [];\n let remaining = absMs;\n\n for (const u of units) {\n if (remaining >= u.value) {\n const count = Math.floor(remaining / u.value);\n remaining = remaining % u.value;\n\n const unitName = short ? u.short : (count === 1 ? u.name : u.name + 's');\n parts.push(short ? `${count}${unitName}` : `${count} ${unitName}`);\n\n if (parts.length >= precision) break;\n }\n }\n\n if (parts.length === 0) {\n return short ? `${Math.round(absMs)}ms` : `${Math.round(absMs)} milliseconds`;\n }\n\n return sign + (short ? parts.join('') : parts.join(' '));\n }\n\n /**\n * Format long strings/IDs with truncation\n * @param {string} value - Value to format\n * @param {number} length - Maximum length before truncation\n * @param {string} prefix - Prefix to add\n * @param {string} suffix - Suffix for truncated strings\n * @returns {string} Formatted hash string\n */\n hash(value, length = 8, prefix = '', suffix = '...') {\n if (value === null || value === undefined) return '';\n\n const str = String(value);\n if (str.length <= length) return prefix + str;\n return prefix + str.substring(0, length) + suffix;\n }\n\n /**\n * Strip HTML tags from text\n * @param {string} html - HTML string to strip\n * @returns {string} Plain text without HTML tags\n */\n stripHtml(html) {\n if (html === null || html === undefined) return '';\n return String(html).replace(/<[^>]*>/g, '');\n }\n\n /**\n * Highlight search terms in text\n * @param {string} text - Text to search in\n * @param {string} searchTerm - Term to highlight\n * @param {string} className - CSS class for highlighting\n * @returns {string} Text with highlighted terms\n */\n highlight(text, searchTerm, className = 'highlight') {\n if (text === null || text === undefined || !searchTerm) {\n return String(text || '');\n }\n\n const escapedTerm = String(searchTerm).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const regex = new RegExp(`(${escapedTerm})`, 'gi');\n return String(text).replace(regex, `<mark class=\"${className}\">$1</mark>`);\n }\n\n /**\n * Encode a value as a hex string.\n * - Strings are encoded as UTF-8 bytes, then hex-encoded\n * - Numbers are converted to base-16 (padded to even length)\n * - Uint8Array/ArrayBuffer/number[] are treated as bytes\n *\n * @param {*} value - The value to encode\n * @param {boolean} uppercase - Uppercase hex letters (A-F)\n * @param {boolean} withPrefix - Prefix with '0x'\n * @returns {string} Hex string\n */\n hex(value, uppercase = false, withPrefix = false) {\n if (value === null || value === undefined) return '';\n\n let hexStr = '';\n\n const toHexFromBytes = (bytes) =>\n Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');\n\n if (typeof value === 'number') {\n let hex = Math.abs(Math.trunc(value)).toString(16);\n if (hex.length % 2) hex = '0' + hex;\n hexStr = hex;\n } else if (value instanceof Uint8Array) {\n hexStr = toHexFromBytes(value);\n } else if (value instanceof ArrayBuffer) {\n hexStr = toHexFromBytes(new Uint8Array(value));\n } else if (Array.isArray(value) && value.every(n => typeof n === 'number')) {\n hexStr = toHexFromBytes(Uint8Array.from(value.map(n => n & 0xFF)));\n } else {\n // Treat everything else as string and encode to UTF-8\n const enc = new TextEncoder();\n const bytes = enc.encode(String(value));\n hexStr = toHexFromBytes(bytes);\n }\n\n if (uppercase) hexStr = hexStr.toUpperCase();\n return (withPrefix ? '0x' : '') + hexStr;\n }\n\n /**\n * Decode a hex string into UTF-8 text.\n * Accepts optional '0x' prefix and ignores whitespace.\n *\n * @param {string} value - Hex string\n * @returns {string} Decoded UTF-8 string (or original value on parse error)\n */\n unhex(value) {\n if (value === null || value === undefined) return '';\n\n let str = String(value).trim();\n if (str.startsWith('0x') || str.startsWith('0X')) str = str.slice(2);\n str = str.replace(/\\s+/g, '');\n\n if (str.length === 0) return '';\n\n // If odd length, pad with leading zero\n if (str.length % 2 !== 0) str = '0' + str;\n\n const bytes = new Uint8Array(str.length / 2);\n for (let i = 0; i < str.length; i += 2) {\n const byte = parseInt(str.slice(i, i + 2), 16);\n if (Number.isNaN(byte)) {\n return String(value);\n }\n bytes[i / 2] = byte;\n }\n\n try {\n const dec = new TextDecoder();\n return dec.decode(bytes);\n } catch (e) {\n // Fallback if TextDecoder is unavailable\n let text = '';\n for (const b of bytes) text += String.fromCharCode(b);\n return text;\n }\n }\n\n json(value, indent = 2) {\n try {\n return JSON.stringify(value, null, indent);\n } catch (e) {\n return String(value);\n }\n }\n\n /**\n * Check if formatter exists\n * @param {string} name - Formatter name\n * @returns {boolean} True if exists\n */\n has(name) {\n return this.formatters.has(name.toLowerCase());\n }\n\n /**\n * Remove a formatter\n * @param {string} name - Formatter name\n * @returns {boolean} True if removed\n */\n unregister(name) {\n return this.formatters.delete(name.toLowerCase());\n }\n\n /**\n * Get all formatter names\n * @returns {Array} Formatter names\n */\n listFormatters() {\n return Array.from(this.formatters.keys()).sort();\n }\n\n iter(v) {\n if (v === null || v === undefined) {\n return [];\n }\n\n // If it's already an array, return as-is\n if (Array.isArray(v)) {\n return v;\n }\n\n // If it's an object, convert to key-value pairs\n if (typeof v === 'object') {\n return Object.entries(v).map(([key, value]) => ({\n key: key,\n value: value\n }));\n }\n\n // For primitive values, wrap in array with single item\n return [{ key: '0', value: v }];\n }\n}\n\n// Create singleton instance\nconst dataFormatter = new DataFormatter();\nwindow.dataFormatter = dataFormatter;\n\n// Export both class and instance\nexport { DataFormatter };\nexport default dataFormatter;\n","/**\n * MOJOUtils - Core utility functions for MOJO Framework\n * Provides centralized data access and formatting utilities\n */\n\nimport dataFormatter from './DataFormatter.js';\n\nclass MOJOUtils {\n /**\n * Get data from context with support for:\n * - Dot notation (e.g., \"user.name\")\n * - Pipe formatting (e.g., \"name|uppercase\")\n * - Combined (e.g., \"user.name|uppercase|truncate(10)\")\n *\n * @param {object} context - The data context to search in\n * @param {string} key - The key path with optional pipes\n * @returns {*} The value, possibly formatted\n */\n static getContextData(context, key) {\n if (!key || context == null) {\n return undefined;\n }\n\n // Check for pipe syntax - split on first pipe outside of parentheses\n let field = key;\n let pipes = '';\n\n // Find the first pipe that's not inside parentheses\n let parenDepth = 0;\n let pipeIndex = -1;\n\n for (let i = 0; i < key.length; i++) {\n const char = key[i];\n if (char === '(') parenDepth++;\n else if (char === ')') parenDepth--;\n else if (char === '|' && parenDepth === 0) {\n pipeIndex = i;\n break;\n }\n }\n\n if (pipeIndex > -1) {\n field = key.substring(0, pipeIndex).trim();\n pipes = key.substring(pipeIndex + 1).trim();\n }\n\n // Get the raw value\n const value = this.getNestedValue(context, field);\n\n // Apply pipes if present, passing context for variable resolution\n if (pipes) {\n return dataFormatter.pipe(value, pipes, context);\n }\n\n return value;\n }\n\n /**\n * Get nested value from object using dot notation\n * IMPORTANT: Never calls get() on the top-level context to avoid recursion\n * But DOES call get() on nested objects if they have that method\n *\n * @param {object} context - The object to search in\n * @param {string} path - Dot notation path\n * @returns {*} The value at the path\n */\n static getNestedValue(context, path) {\n if (!path || context == null) {\n return undefined;\n }\n\n // If no dots, simple property lookup\n if (!path.includes('.')) {\n // Direct property access - never call get() on top level\n // Check if property exists (including prototype chain for methods)\n if (path in context) {\n const value = context[path];\n // Check if it's a method (like getStatus, getButtonClass)\n if (typeof value === 'function') {\n return value.call(context);\n }\n return value;\n }\n\n return undefined;\n }\n\n // Handle dot notation\n const keys = path.split('.');\n let current = context;\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n\n if (current == null) {\n return undefined;\n }\n\n // For the first key, never use get() (it's the top-level context)\n if (i === 0) {\n // Direct property access\n if (current.hasOwnProperty(key)) {\n const value = current[key];\n // Check if it's a method and call it\n if (typeof value === 'function') {\n current = value.call(context);\n } else {\n current = value;\n }\n } else {\n return undefined;\n }\n } else {\n // For nested objects, check if they have a get() method\n if (current && typeof current.getContextValue === 'function') {\n // Use get() for the remaining path\n const remainingPath = keys.slice(i).join('.');\n return current.getContextValue(remainingPath);\n }\n\n // Standard property access\n if (Array.isArray(current) && !isNaN(key)) {\n // Array index access\n current = current[parseInt(key)];\n } else if (current.hasOwnProperty(key)) {\n current = current[key];\n } else if (typeof current[key] === 'function') {\n current = current[key].call(current);\n } else {\n return undefined;\n }\n }\n }\n\n return current;\n }\n\n /**\n * Check if a value is null or undefined\n * @param {*} value - Value to check\n * @returns {boolean} True if null or undefined\n */\n static isNullOrUndefined(value) {\n return value === null || value === undefined;\n }\n\n /**\n * Deep clone an object\n * @param {*} obj - Object to clone\n * @returns {*} Cloned object\n */\n static deepClone(obj) {\n if (obj === null || typeof obj !== 'object') return obj;\n if (obj instanceof Date) return new Date(obj.getTime());\n if (obj instanceof Array) return obj.map(item => this.deepClone(item));\n if (obj instanceof Object) {\n const clonedObj = {};\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n clonedObj[key] = this.deepClone(obj[key]);\n }\n }\n return clonedObj;\n }\n }\n\n /**\n * Merge objects deeply\n * @param {object} target - Target object\n * @param {...object} sources - Source objects to merge\n * @returns {object} Merged object\n */\n static deepMerge(target, ...sources) {\n if (!sources.length) return target;\n const source = sources.shift();\n\n if (this.isObject(target) && this.isObject(source)) {\n for (const key in source) {\n if (this.isObject(source[key])) {\n if (!target[key]) Object.assign(target, { [key]: {} });\n this.deepMerge(target[key], source[key]);\n } else {\n Object.assign(target, { [key]: source[key] });\n }\n }\n }\n\n return this.deepMerge(target, ...sources);\n }\n\n /**\n * Check if value is a plain object\n * @param {*} item - Value to check\n * @returns {boolean} True if plain object\n */\n static isObject(item) {\n return item && typeof item === 'object' && !Array.isArray(item);\n }\n\n /**\n * Debounce function calls\n * @param {function} func - Function to debounce\n * @param {number} wait - Wait time in milliseconds\n * @returns {function} Debounced function\n */\n static debounce(func, wait) {\n let timeout;\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n }\n\n /**\n * Throttle function calls\n * @param {function} func - Function to throttle\n * @param {number} limit - Time limit in milliseconds\n * @returns {function} Throttled function\n */\n static throttle(func, limit) {\n let inThrottle;\n return function(...args) {\n if (!inThrottle) {\n func.apply(this, args);\n inThrottle = true;\n setTimeout(() => inThrottle = false, limit);\n }\n };\n }\n\n /**\n * Generate a unique ID\n * @param {string} prefix - Optional prefix for the ID\n * @returns {string} Unique ID\n */\n static generateId(prefix = '') {\n const timestamp = Date.now().toString(36);\n const randomStr = Math.random().toString(36).substr(2, 9);\n return prefix ? `${prefix}_${timestamp}_${randomStr}` : `${timestamp}_${randomStr}`;\n }\n\n /**\n * Escape HTML special characters\n * @param {string} str - String to escape\n * @returns {string} Escaped string\n */\n static escapeHtml(str) {\n const entityMap = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n '/': '&#x2F;',\n '`': '&#x60;',\n '=': '&#x3D;'\n };\n\n return String(str).replace(/[&<>\"'`=\\/]/g, s => entityMap[s]);\n }\n\n /**\n * Check password strength and provide detailed feedback\n * @param {string} password - Password to check\n * @returns {object} Password strength analysis\n */\n static checkPasswordStrength(password) {\n if (!password || typeof password !== 'string') {\n return {\n score: 0,\n strength: 'invalid',\n feedback: ['Password must be a non-empty string'],\n details: {\n length: 0,\n hasLowercase: false,\n hasUppercase: false,\n hasNumbers: false,\n hasSpecialChars: false,\n hasCommonPatterns: false,\n isCommonPassword: false\n }\n };\n }\n\n const feedback = [];\n const details = {\n length: password.length,\n hasLowercase: /[a-z]/.test(password),\n hasUppercase: /[A-Z]/.test(password),\n hasNumbers: /[0-9]/.test(password),\n hasSpecialChars: /[^a-zA-Z0-9]/.test(password),\n hasCommonPatterns: false,\n isCommonPassword: false\n };\n\n let score = 0;\n\n // Length scoring\n if (password.length < 6) {\n feedback.push('Password should be at least 6 characters long');\n } else if (password.length < 8) {\n score += 1;\n feedback.push('Consider using at least 8 characters for better security');\n } else if (password.length < 12) {\n score += 3;\n } else {\n score += 4;\n }\n\n // Character variety scoring\n if (details.hasLowercase) score += 1;\n else feedback.push('Include lowercase letters');\n\n if (details.hasUppercase) score += 1;\n else feedback.push('Include uppercase letters');\n\n if (details.hasNumbers) score += 1;\n else feedback.push('Include numbers');\n\n if (details.hasSpecialChars) score += 2;\n else feedback.push('Include special characters (!@#$%^&* etc.)');\n\n // Check for common patterns\n const commonPatterns = [\n /123/, // Sequential numbers\n /abc/i, // Sequential letters\n /qwerty/i, // Keyboard patterns\n /asdf/i, // Keyboard patterns\n /(.)\\1{2,}/, // Repeated characters (aaa, 111)\n /password/i, // Contains \"password\"\n /admin/i, // Contains \"admin\"\n /user/i, // Contains \"user\"\n /login/i // Contains \"login\"\n ];\n\n for (const pattern of commonPatterns) {\n if (pattern.test(password)) {\n details.hasCommonPatterns = true;\n score -= 1;\n feedback.push('Avoid common patterns and dictionary words');\n break;\n }\n }\n\n // Check against very common passwords\n const commonPasswords = [\n '123456', 'password', '123456789', '12345678', '12345',\n '1234567', '1234567890', 'qwerty', 'abc123', '111111',\n '123123', 'admin', 'letmein', 'welcome', 'monkey',\n 'password123', '123qwe', 'qwerty123', '000000', 'dragon',\n 'sunshine', 'princess', 'azerty', '1234', 'iloveyou',\n 'trustno1', 'superman', 'shadow', 'master', 'jennifer'\n ];\n\n if (commonPasswords.includes(password.toLowerCase())) {\n details.isCommonPassword = true;\n score = Math.max(0, score - 3);\n feedback.push('This password is too common and easily guessed');\n }\n\n // Calculate final strength\n let strength;\n if (score < 2) {\n strength = 'very-weak';\n } else if (score < 4) {\n strength = 'weak';\n } else if (score < 6) {\n strength = 'fair';\n } else if (score < 8) {\n strength = 'good';\n } else {\n strength = 'strong';\n }\n\n // Add positive feedback for strong passwords\n if (score >= 7 && feedback.length === 0) {\n feedback.push('Strong password! Consider using a password manager.');\n } else if (score >= 5 && feedback.length <= 1) {\n feedback.push('Good password strength. Consider adding more variety.');\n }\n\n return {\n score: Math.max(0, score),\n strength,\n feedback,\n details\n };\n }\n\n /**\n * Generate a secure password with customizable options\n * @param {object} options - Password generation options\n * @param {number} options.length - Password length (default: 12)\n * @param {boolean} options.includeLowercase - Include lowercase letters (default: true)\n * @param {boolean} options.includeUppercase - Include uppercase letters (default: true)\n * @param {boolean} options.includeNumbers - Include numbers (default: true)\n * @param {boolean} options.includeSpecialChars - Include special characters (default: true)\n * @param {string} options.customChars - Custom character set to use\n * @param {boolean} options.excludeAmbiguous - Exclude ambiguous characters like 0, O, l, I (default: false)\n * @returns {string} Generated password\n */\n static generatePassword(options = {}) {\n const defaults = {\n length: 12,\n includeLowercase: true,\n includeUppercase: true,\n includeNumbers: true,\n includeSpecialChars: true,\n customChars: '',\n excludeAmbiguous: false\n };\n\n const config = { ...defaults, ...options };\n\n if (config.length < 4) {\n throw new Error('Password length must be at least 4 characters');\n }\n\n // Build character sets\n let lowercase = 'abcdefghijklmnopqrstuvwxyz';\n let uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n let numbers = '0123456789';\n let specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?';\n\n // Remove ambiguous characters if requested\n if (config.excludeAmbiguous) {\n lowercase = lowercase.replace(/[il]/g, '');\n uppercase = uppercase.replace(/[IOL]/g, '');\n numbers = numbers.replace(/[01]/g, '');\n specialChars = specialChars.replace(/[|]/g, '');\n }\n\n // Build character pool\n let charPool = '';\n const requiredChars = [];\n\n if (config.customChars) {\n charPool = config.customChars;\n } else {\n if (config.includeLowercase) {\n charPool += lowercase;\n requiredChars.push(lowercase[Math.floor(Math.random() * lowercase.length)]);\n }\n if (config.includeUppercase) {\n charPool += uppercase;\n requiredChars.push(uppercase[Math.floor(Math.random() * uppercase.length)]);\n }\n if (config.includeNumbers) {\n charPool += numbers;\n requiredChars.push(numbers[Math.floor(Math.random() * numbers.length)]);\n }\n if (config.includeSpecialChars) {\n charPool += specialChars;\n requiredChars.push(specialChars[Math.floor(Math.random() * specialChars.length)]);\n }\n }\n\n if (!charPool) {\n throw new Error('No character types selected for password generation');\n }\n\n // Generate password\n let password = '';\n\n // Add required characters first to ensure variety\n for (const char of requiredChars) {\n password += char;\n }\n\n // Fill remaining length with random characters\n for (let i = password.length; i < config.length; i++) {\n password += charPool[Math.floor(Math.random() * charPool.length)];\n }\n\n // Shuffle the password to avoid predictable patterns\n return password.split('').sort(() => Math.random() - 0.5).join('');\n }\n\n /**\n * Parse query string into object\n * @param {string} queryString - Query string to parse\n * @returns {object} Parsed query parameters\n */\n static parseQueryString(queryString) {\n const params = {};\n const searchParams = new URLSearchParams(queryString);\n for (const [key, value] of searchParams.entries()) {\n params[key] = value;\n }\n return params;\n }\n\n /**\n * Convert object to query string\n * @param {object} params - Parameters object\n * @returns {string} Query string\n */\n static toQueryString(params) {\n return new URLSearchParams(params).toString();\n }\n\n /**\n * Wrap data objects to provide get() method support\n * This ensures pipe formatting works in all contexts\n * @param {*} data - Data to wrap\n * @param {object} rootContext - Optional root context for nested access\n * @returns {*} Wrapped data if object/array, otherwise original\n */\n static wrapData(data, rootContext = null, depth = 3) {\n if (!data || typeof data !== 'object') {\n return data;\n }\n\n // Don't wrap built-in types (Date, RegExp, etc.)\n if (data instanceof Date || data instanceof RegExp || data instanceof Error) {\n return data;\n }\n\n // Stop wrapping at max depth to prevent infinite recursion\n if (depth <= 0) {\n return data;\n }\n\n // Don't wrap if already has get method\n if (typeof data.getContextValue === 'function') {\n return data;\n }\n\n // Handle arrays specially - wrap each element but keep as array\n if (Array.isArray(data)) {\n return data.map(item => {\n if (item && typeof item === 'object' && !item.getContextValue) {\n return new DataWrapper(item, rootContext);\n }\n return item;\n });\n }\n\n // Use DataWrapper for objects\n return new DataWrapper(data, rootContext);\n }\n}\n\n/**\n * DataWrapper - Wraps plain objects to provide get() method with pipe support\n * Used internally by View to ensure all data objects support formatting\n */\nclass DataWrapper {\n constructor(data, rootContext = null) {\n // Store the wrapped data as non-enumerable to avoid JSDOM serialization issues\n Object.defineProperty(this, '_data', {\n value: data,\n writable: false,\n enumerable: false,\n configurable: false\n });\n\n Object.defineProperty(this, '_rootContext', {\n value: rootContext,\n writable: false,\n enumerable: false,\n configurable: false\n });\n\n // Copy all properties from data to this wrapper\n // This allows direct property access\n if (data && typeof data === 'object') {\n for (const key in data) {\n if (data.hasOwnProperty(key)) {\n const value = data[key];\n // Wrap nested values using wrapData for consistency\n this[key] = MOJOUtils.wrapData(value, rootContext);\n }\n }\n }\n }\n\n /**\n * Get value with pipe support\n * @param {string} key - Key with optional pipes\n * @returns {*} Value, possibly formatted\n */\n getContextValue(key) {\n // Check if key has pipes\n let field = key;\n let pipes = '';\n\n // Find the first pipe that's not inside parentheses\n let parenDepth = 0;\n let pipeIndex = -1;\n\n for (let i = 0; i < key.length; i++) {\n const char = key[i];\n if (char === '(') parenDepth++;\n else if (char === ')') parenDepth--;\n else if (char === '|' && parenDepth === 0) {\n pipeIndex = i;\n break;\n }\n }\n\n if (pipeIndex > -1) {\n field = key.substring(0, pipeIndex).trim();\n pipes = key.substring(pipeIndex + 1).trim();\n }\n\n // Get value - supports both direct properties and dot-notation paths\n let value;\n \n // First check if it's a direct property on the wrapper (already wrapped)\n if (field in this && field !== '_data' && field !== '_rootContext') {\n value = this[field];\n } else {\n // Try to get nested value using dot-notation path\n value = MOJOUtils.getNestedValue(this._data, field);\n }\n\n // Apply pipes if present, passing root context for variable resolution\n if (pipes && value !== undefined) {\n return dataFormatter.pipe(value, pipes, this._rootContext || this._data);\n }\n\n return value;\n }\n\n /**\n * Check if wrapper has a property\n * @param {string} key - Property key\n * @returns {boolean} True if property exists\n */\n has(key) {\n return this._data && this._data.hasOwnProperty(key);\n }\n\n /**\n * Get the raw wrapped data\n * @returns {object} The original data object\n */\n toJSON() {\n return this._data;\n }\n}\n\n// Attach DataWrapper to MOJOUtils for easy access\nMOJOUtils.DataWrapper = DataWrapper;\n\n// Export as both class and singleton for flexibility\nexport default MOJOUtils;\nexport { MOJOUtils, DataWrapper };\n\n// Also attach to window for global access if needed\nif (typeof window !== 'undefined') {\n // window.MOJO = window.MOJO || {};\n // window.MOJO.Utils = MOJOUtils;\n // window.MOJO.DataWrapper = DataWrapper;\n window.utils = MOJOUtils;\n}\n","/**\n * MOJO Mustache - Clean ES6 Template Engine\n * Simplified mustache implementation for MOJO Framework\n * Based on mustache.js logic-less templates\n */\n\nimport MOJOUtils from './MOJOUtils.js';\n\n// Utility functions\nconst objectToString = Object.prototype.toString;\nconst isArray = Array.isArray || function(obj) {\n return objectToString.call(obj) === '[object Array]';\n};\n\nconst isFunction = function(obj) {\n return typeof obj === 'function';\n};\n\nconst isObject = function(obj) {\n return obj !== null && typeof obj === 'object';\n};\n\n// Access DataFormatter without async imports so Mustache remains synchronous.\nfunction getDataFormatter() {\n if (typeof window === 'undefined') return null;\n if (window.MOJO?.dataFormatter) return window.MOJO.dataFormatter;\n if (window.dataFormatter) return window.dataFormatter;\n return null;\n}\n\nconst escapeHtml = function(string) {\n const entityMap = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n '/': '&#x2F;',\n '`': '&#x60;',\n '=': '&#x3D;'\n };\n\n return String(string).replace(/[&<>\"'`=\\/]/g, function(s) {\n return entityMap[s];\n });\n};\n\n// Scanner class for parsing templates\nclass Scanner {\n constructor(string) {\n this.string = string;\n this.tail = string;\n this.pos = 0;\n }\n\n eos() {\n return this.tail === '';\n }\n\n scan(re) {\n const match = this.tail.match(re);\n if (!match || match.index !== 0) {\n return '';\n }\n\n const string = match[0];\n this.tail = this.tail.substring(string.length);\n this.pos += string.length;\n return string;\n }\n\n scanUntil(re) {\n const index = this.tail.search(re);\n let match;\n\n switch (index) {\n case -1:\n match = this.tail;\n this.tail = '';\n break;\n case 0:\n match = '';\n break;\n default:\n match = this.tail.substring(0, index);\n this.tail = this.tail.substring(index);\n }\n\n this.pos += match.length;\n return match;\n }\n}\n\n// Context class for variable resolution\nclass Context {\n constructor(view, parentContext) {\n this.view = view;\n this.cache = { '.': this.view };\n this.parent = parentContext;\n\n // Generate unique context ID for render caching\n if (!this.view?._cacheId) {\n if (this.view && typeof this.view === 'object') {\n this.view._cacheId = Math.random().toString(36).substring(2);\n }\n }\n }\n\n push(view) {\n return new Context(view, this);\n }\n\n lookup(name) {\n // Check render-level cache first\n if (this.renderCache && this.view?._cacheId) {\n const cacheKey = `${this.view._cacheId}:${name}`;\n if (this.renderCache.has(cacheKey)) {\n return this.renderCache.get(cacheKey);\n }\n }\n\n // Special case: '.' refers to the current context value itself\n if (name === '.') {\n return this.view;\n }\n\n // Handle dot-prefix to prevent context chain walking\n // If name starts with '.', only look in current context\n if (name && name.startsWith('.')) {\n let actualName = name.substring(1);\n let shouldIterate = false;\n\n // Support pipe formatting on dot-prefixed lookups, e.g. \".title|upper\"\n // We split pipes here so the lookup remains \"current context only\" while still\n // allowing DataFormatter-style pipe transforms in templates.\n let pipeString = null;\n const pipeIndex = actualName.indexOf('|');\n if (pipeIndex !== -1) {\n pipeString = actualName.substring(pipeIndex + 1).trim();\n actualName = actualName.substring(0, pipeIndex).trim();\n }\n\n // Check for |iter suffix for array iteration (only applies to the base name)\n if (actualName.endsWith('|iter')) {\n actualName = actualName.substring(0, actualName.length - 5); // Remove '|iter'\n shouldIterate = true;\n }\n\n // Only check current view, not parents\n if (this.view && typeof this.view === 'object') {\n let value;\n\n // Check if view has a get method for unified access\n if (typeof this.view.getContextValue === 'function') {\n try {\n value = this.view.getContextValue(actualName);\n if (value !== undefined) {\n if (isFunction(value)) {\n value = value.call(this.view);\n }\n }\n } catch (e) {\n // Fall back to direct property access\n value = undefined;\n }\n }\n\n // Direct property access if get didn't work\n if (value === undefined && actualName in this.view) {\n value = this.view[actualName];\n if (isFunction(value)) {\n value = value.call(this.view);\n }\n }\n\n // If pipes were provided, apply them before any boolean \"existence check\"\n // conversions for arrays/objects. This keeps \".items|iter\" behavior intact\n // while allowing \".title|upper\" style formatting.\n if (pipeString && value !== undefined) {\n try {\n const formatter = getDataFormatter();\n if (formatter && typeof formatter.pipe === 'function') {\n value = formatter.pipe(value, pipeString, this);\n }\n } catch (e) {\n // If formatting fails, fall back to the raw value\n }\n }\n\n // Handle array values based on shouldIterate flag\n if (isArray(value)) {\n if (shouldIterate) {\n // Return array for iteration\n return value;\n } else {\n // Return boolean for existence check\n return value.length > 0;\n }\n }\n\n if (isObject(value)) {\n if (shouldIterate) {\n // Return array of key-value pairs for iteration\n return Object.entries(value).map(([key, val]) => ({\n key: key,\n value: val\n }));\n } else {\n // Return boolean for existence check\n return Object.keys(value).length > 0;\n }\n }\n\n return value;\n }\n\n return undefined; // Don't walk up the chain\n }\n\n // Original lookup logic for non-dot-prefixed names\n const cache = this.cache;\n\n let value;\n if (cache.hasOwnProperty(name)) {\n value = cache[name];\n } else {\n let context = this;\n let intermediateValue;\n let names;\n let index;\n let lookupHit = false;\n\n while (context) {\n // Check if view has a get method for unified access\n if (context.view && typeof context.view.getContextValue === 'function') {\n try {\n intermediateValue = context.view.getContextValue(name);\n if (intermediateValue !== undefined) {\n lookupHit = true;\n }\n } catch (e) {\n // If get throws, fall back to standard lookup\n lookupHit = false;\n }\n }\n\n // Fall back to standard property lookup if get method didn't work\n if (!lookupHit) {\n if (name.indexOf('.') > 0) {\n intermediateValue = context.view;\n names = name.split('.');\n index = 0;\n\n while (intermediateValue != null && index < names.length) {\n // Check if intermediate value has a get method\n if (intermediateValue && typeof intermediateValue.getContextValue === 'function' && index < names.length) {\n try {\n const remainingPath = names.slice(index).join('.');\n intermediateValue = intermediateValue.getContextValue(remainingPath);\n index = names.length; // Skip to end\n if (intermediateValue !== undefined) {\n lookupHit = true;\n }\n } catch (e) {\n // Fall back to property access\n if (index === names.length - 1) {\n lookupHit = (\n hasProperty(intermediateValue, names[index]) ||\n primitiveHasOwnProperty(intermediateValue, names[index])\n );\n }\n intermediateValue = intermediateValue[names[index++]];\n }\n } else {\n if (index === names.length - 1) {\n lookupHit = (\n hasProperty(intermediateValue, names[index]) ||\n primitiveHasOwnProperty(intermediateValue, names[index])\n );\n }\n intermediateValue = intermediateValue[names[index++]];\n }\n }\n } else {\n intermediateValue = context.view[name];\n lookupHit = hasProperty(context.view, name);\n }\n }\n\n if (lookupHit) {\n value = intermediateValue;\n break;\n }\n\n context = context.parent;\n }\n\n cache[name] = value;\n }\n\n if (isFunction(value)) value = value.call(this.view);\n\n // Store in render-level cache after function evaluation\n if (this.renderCache && this.view?._cacheId) {\n const cacheKey = `${this.view._cacheId}:${name}`;\n this.renderCache.set(cacheKey, value);\n }\n\n return value;\n }\n}\n\n// Helper functions\nfunction hasProperty(obj, propName) {\n return obj != null && typeof obj === 'object' && (propName in obj);\n}\n\nfunction primitiveHasOwnProperty(primitive, propName) {\n return (\n primitive != null &&\n typeof primitive !== 'object' &&\n primitive.hasOwnProperty &&\n primitive.hasOwnProperty(propName)\n );\n}\n\n// Writer class for rendering\nclass Writer {\n constructor() {\n this.templateCache = new Map();\n }\n\n clearCache() {\n this.templateCache.clear();\n }\n\n parse(template, tags) {\n tags = tags || ['{{', '}}'];\n\n const cacheKey = template + ':' + tags.join(':');\n let tokens = this.templateCache.get(cacheKey);\n\n if (tokens == null) {\n tokens = this.parseTemplate(template, tags);\n this.templateCache.set(cacheKey, tokens);\n }\n\n return tokens;\n }\n\n parseTemplate(template, tags) {\n if (!template) return [];\n\n const openingTag = tags[0];\n const closingTag = tags[1];\n const scanner = new Scanner(template);\n const tokens = [];\n let start, type, value, chr, token;\n\n const openingTagRe = new RegExp(escapeRegExp(openingTag) + '\\\\s*');\n const closingTagRe = new RegExp('\\\\s*' + escapeRegExp(closingTag));\n const closingCurlyRe = new RegExp('\\\\s*' + escapeRegExp('}' + closingTag));\n\n while (!scanner.eos()) {\n start = scanner.pos;\n\n // Match text before tags\n value = scanner.scanUntil(openingTagRe);\n\n if (value) {\n for (let i = 0; i < value.length; ++i) {\n chr = value.charAt(i);\n\n if (chr === '\\n') {\n tokens.push(['text', chr]);\n } else {\n tokens.push(['text', chr]);\n }\n }\n }\n\n if (!scanner.scan(openingTagRe)) break;\n\n type = scanner.scan(/[#^\\/>{&=!]/);\n if (!type) type = 'name';\n\n scanner.scan(/\\s*/);\n\n if (type === '=') {\n value = scanner.scanUntil(/\\s*=/);\n scanner.scan(/\\s*=/);\n scanner.scanUntil(closingTagRe);\n } else if (type === '{') {\n value = scanner.scanUntil(closingCurlyRe);\n scanner.scan(closingCurlyRe);\n type = '&';\n } else {\n value = scanner.scanUntil(closingTagRe);\n }\n\n scanner.scan(closingTagRe);\n\n if (type === '#' || type === '^') {\n token = [type, value, start, scanner.pos];\n tokens.push(token);\n } else if (type === '/') {\n // Find matching opening section\n let openSection;\n for (let i = tokens.length - 1; i >= 0; --i) {\n if (tokens[i][0] === '#' || tokens[i][0] === '^') {\n if (tokens[i][1] === value) {\n openSection = tokens[i];\n break;\n }\n }\n }\n\n if (openSection) {\n // Add closing position if token doesn't have it yet\n if (openSection.length === 4) {\n openSection.push(scanner.pos);\n }\n }\n // Add closing token for nestSections processing\n tokens.push([type, value, start, scanner.pos]);\n } else {\n tokens.push([type, value, start, scanner.pos]);\n }\n }\n\n return this.nestSections(this.squashTokens(tokens));\n }\n\n squashTokens(tokens) {\n const squashedTokens = [];\n let token, lastToken;\n\n for (let i = 0; i < tokens.length; ++i) {\n token = tokens[i];\n\n if (token) {\n if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {\n lastToken[1] += token[1];\n lastToken[3] = token[3];\n } else {\n squashedTokens.push(token);\n lastToken = token;\n }\n }\n }\n\n return squashedTokens;\n }\n\n nestSections(tokens) {\n const nestedTokens = [];\n let collector = nestedTokens;\n const sections = [];\n\n for (let i = 0; i < tokens.length; ++i) {\n const token = tokens[i];\n\n switch (token[0]) {\n case '#':\n case '^':\n // Create section token with proper structure: [type, name, start, end, children, closing]\n const sectionToken = [\n token[0], // type ('#' or '^')\n token[1], // section name\n token[2], // start position\n token[3], // end position after opening tag\n [], // children array\n token[4] || null // closing position (if set during parsing)\n ];\n\n collector.push(sectionToken);\n sections.push(sectionToken);\n collector = sectionToken[4]; // children array\n break;\n case '/':\n const section = sections.pop();\n if (section) {\n // Set closing position\n section[5] = token[2];\n // Return to parent collector\n collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;\n }\n break;\n default:\n collector.push(token);\n }\n }\n\n return nestedTokens;\n }\n\n render(template, view, partials, config) {\n const tags = this.getConfigTags(config) || ['{{', '}}'];\n const tokens = this.parse(template, tags);\n\n // Create render-level cache for this render operation\n const renderCache = new Map();\n\n return this.renderTokens(tokens, new Context(view), partials, template, config, renderCache);\n }\n\n renderTokens(tokens, context, partials, originalTemplate, config, renderCache) {\n // Attach render cache to context if provided\n if (renderCache && !context.renderCache) {\n context.renderCache = renderCache;\n }\n let buffer = '';\n\n for (let i = 0; i < tokens.length; ++i) {\n const token = tokens[i];\n let value;\n\n switch (token[0]) {\n case '#':\n value = context.lookup(token[1]);\n if (!value) continue;\n\n // Ensure we have child tokens\n const childTokens = token[4];\n if (!childTokens || !isArray(childTokens)) {\n console.warn(`MUSTACHE WARNING - Section ${token[1]} has no child tokens:`, token);\n continue;\n }\n\n if (isArray(value)) {\n // Process each array item\n for (let j = 0; j < value.length; ++j) {\n const itemContext = context.push(value[j]);\n // Pass render cache to child context\n if (context.renderCache) {\n itemContext.renderCache = context.renderCache;\n }\n const itemResult = this.renderTokens(childTokens, itemContext, partials, originalTemplate, config, renderCache);\n buffer += itemResult;\n }\n } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {\n const pushedContext = context.push(value);\n // Pass render cache to child context\n if (context.renderCache) {\n pushedContext.renderCache = context.renderCache;\n }\n buffer += this.renderTokens(childTokens, pushedContext, partials, originalTemplate, config, renderCache);\n } else if (isFunction(value)) {\n const text = originalTemplate == null ? null : originalTemplate.slice(token[3], token[5]);\n value = value.call(context.view, text, (template) => this.render(template, context.view, partials, config));\n if (value != null) buffer += value;\n } else if (value) {\n // Handle boolean true and other truthy values\n buffer += this.renderTokens(childTokens, context, partials, originalTemplate, config, renderCache);\n }\n break;\n\n case '^':\n value = context.lookup(token[1]);\n if (!value || (isArray(value) && value.length === 0)) {\n // Ensure we have child tokens for inverted sections too\n const childTokens = token[4];\n if (childTokens && isArray(childTokens)) {\n buffer += this.renderTokens(childTokens, context, partials, originalTemplate, config, renderCache);\n }\n }\n break;\n\n case '>':\n if (!partials) continue;\n value = isFunction(partials) ? partials(token[1]) : partials[token[1]];\n if (value != null) {\n buffer += this.render(value, context.view, partials, config);\n }\n break;\n\n case '&':\n value = context.lookup(token[1]);\n if (value != null) buffer += value;\n break;\n\n case 'name':\n value = context.lookup(token[1]);\n if (value != null) buffer += escapeHtml(value);\n break;\n\n case 'text':\n buffer += token[1];\n break;\n }\n }\n\n return buffer;\n }\n\n getConfigTags(config) {\n if (isObject(config) && isArray(config.tags)) {\n return config.tags;\n }\n return null;\n }\n}\n\nfunction escapeRegExp(string) {\n return string.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, '\\\\$&');\n}\n\n// Default writer instance\nconst defaultWriter = new Writer();\n\n// Main Mustache object\nconst Mustache = {\n name: 'MOJO Mustache',\n version: '1.0.0',\n tags: ['{{', '}}'],\n\n Scanner,\n Context,\n Writer,\n\n escape: escapeHtml,\n\n clearCache() {\n return defaultWriter.clearCache();\n },\n\n parse(template, tags) {\n return defaultWriter.parse(template, tags);\n },\n\n render(template, view, partials, config) {\n if (typeof template !== 'string') {\n throw new TypeError('Invalid template! Template should be a \"string\"');\n }\n\n // Auto-wrap context to enable pipe formatters if not already wrapped\n // This ensures pipes work everywhere without requiring manual wrapping\n if (view && typeof view === 'object' && !view.getContextValue && typeof view.toJSON !== 'function') {\n view = MOJOUtils.wrapData(view);\n }\n\n return defaultWriter.render(template, view, partials, config);\n }\n};\n\n// ES6 Module Export\nexport default Mustache;\n","// EventDelegate.js\nexport class EventDelegate {\n constructor(view) {\n this.view = view;\n this.domListeners = [];\n this.debounceTimers = new Map();\n }\n\n bind(rootEl) {\n this.unbind();\n if (!rootEl) return;\n\n const onClick = async (event) => {\n const actionEl = event.target.closest('[data-action]');\n if (actionEl && this.shouldHandle(actionEl, event)) {\n const action = actionEl.getAttribute('data-action');\n\n // Hide any tooltips on the clicked element\n this.hideTooltip(actionEl);\n\n const handled = await this.dispatch(action, event, actionEl);\n if (handled) {\n event.preventDefault();\n event.stopPropagation();\n event.handledByChild = true;\n return;\n }\n }\n const navEl = event.target.closest('a[href], [data-page]');\n if (navEl && !navEl.hasAttribute('data-action') && this.shouldHandle(navEl, event)) {\n if (event.ctrlKey || event.metaKey || event.shiftKey || event.button === 1) return;\n if (navEl.tagName === 'A') {\n const href = navEl.getAttribute('href');\n if (href && href !== '#' && !href.startsWith('#') &&\n (this.view.isExternalLink(href) || navEl.hasAttribute('data-external'))) return;\n }\n\n // Hide any tooltips on the clicked element before navigation\n this.hideTooltip(navEl);\n\n event.preventDefault();\n event.stopPropagation();\n event.handledByChild = true;\n if (navEl.hasAttribute('data-page')) await this.view.handlePageNavigation(navEl);\n else await this.view.handleHrefNavigation(navEl);\n }\n };\n\n const onChange = (event) => {\n const el = event.target.closest('[data-change-action]');\n if (!el || !this.shouldHandle(el, event)) return;\n const action = el.getAttribute('data-change-action');\n this.dispatchChange(action, event, el).then((handled) => {\n if (handled) {\n event.stopPropagation();\n event.handledByChild = true;\n }\n });\n };\n\n const onInput = (event) => {\n const el = event.target.closest('[data-change-action]');\n if (!el || !this.shouldHandle(el, event)) return;\n\n const liveSearch = event.target.matches('[data-filter=\"live-search\"]');\n if (!liveSearch) return;\n\n const action = el.getAttribute('data-change-action');\n const debounceMs = parseInt(el.getAttribute('data-filter-debounce')) || 300;\n const timerId = `${action}-${el.getAttribute('data-container') || 'default'}`;\n\n // Clear existing timer\n if (this.debounceTimers.has(timerId)) {\n clearTimeout(this.debounceTimers.get(timerId));\n }\n\n // Set new debounced timer\n const timer = setTimeout(() => {\n this.debounceTimers.delete(timerId);\n this.dispatchChange(action, event, el).then((handled) => {\n if (handled) {\n event.stopPropagation();\n event.handledByChild = true;\n }\n });\n }, debounceMs);\n\n this.debounceTimers.set(timerId, timer);\n };\n\n const onKeyDown = (event) => {\n if (event.target.matches('[data-filter=\"search\"]')) return;\n const el = event.target.closest('[data-keydown-action]') || event.target.closest('[data-change-action]');\n if (!el || !this.shouldHandle(el, event)) return;\n let changeKeys = [\"Enter\"];\n if (el.getAttribute('data-change-keys')) {\n changeKeys = el.getAttribute('data-change-keys').split(',').map(key => key.trim());\n }\n if (changeKeys.includes('*') || changeKeys.includes(event.key)) {\n const action = el.getAttribute('data-keydown-action') || el.getAttribute('data-change-action');\n this.dispatch(action, event, el).then((handled) => {\n if (handled) {\n event.preventDefault();\n event.stopPropagation();\n event.handledByChild = true;\n }\n });\n }\n };\n\n const onSubmit = (event) => {\n const form = event.target.closest('form[data-action]');\n if (!form || !this.shouldHandle(form, event)) return;\n event.preventDefault();\n const action = form.getAttribute('data-action');\n this.dispatch(action, event, form);\n };\n\n rootEl.addEventListener('click', onClick);\n rootEl.addEventListener('change', onChange);\n rootEl.addEventListener('input', onInput);\n rootEl.addEventListener('keydown', onKeyDown);\n rootEl.addEventListener('submit', onSubmit);\n\n this.domListeners.push(\n { el: rootEl, type: 'click', fn: onClick },\n { el: rootEl, type: 'change', fn: onChange },\n { el: rootEl, type: 'input', fn: onInput },\n { el: rootEl, type: 'keydown', fn: onKeyDown },\n { el: rootEl, type: 'submit', fn: onSubmit },\n );\n }\n\n unbind() {\n for (const { el, type, fn } of this.domListeners) el.removeEventListener(type, fn);\n this.domListeners = [];\n\n // Clear any pending debounce timers\n for (const timer of this.debounceTimers.values()) {\n clearTimeout(timer);\n }\n this.debounceTimers.clear();\n }\n\n hideDropdown(element) {\n const dropdownMenu = element.closest('.dropdown-menu');\n const dropdown = dropdownMenu.closest('.dropdown');\n if (!dropdown) {\n return;\n }\n\n const dropdownBtn = dropdown.querySelector('[data-bs-toggle=\"dropdown\"]');\n if (dropdownBtn && window.bootstrap?.Dropdown) {\n const dropdownInstance = window.bootstrap.Dropdown.getInstance(dropdownBtn);\n dropdownInstance?.hide();\n }\n }\n\n hideTooltip(element) {\n // Hide tooltip on the clicked element\n if (element && window.bootstrap?.Tooltip) {\n const tooltip = window.bootstrap.Tooltip.getInstance(element);\n if (tooltip) {\n tooltip.dispose();\n // tooltip.hide();\n }\n }\n }\n\n hideAllTooltips() {\n // Hide all visible tooltips in the document\n if (window.bootstrap?.Tooltip) {\n const tooltips = document.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n tooltips.forEach(el => {\n const tooltip = window.bootstrap.Tooltip.getInstance(el);\n if (tooltip) {\n tooltip.hide();\n }\n });\n }\n }\n\n async dispatch(action, event, el) {\n const v = this.view;\n const cap = (s) => (s.includes('-') ? s.split('-').map(w => w[0].toUpperCase()+w.slice(1)).join('') : s[0].toUpperCase()+s.slice(1));\n\n const specific = `handleAction${cap(action)}`;\n if (typeof v[specific] === 'function') {\n try { event.preventDefault(); await v[specific](event, el); return true; }\n catch (e) { console.error(`Error in action ${action}:`, e); v.handleActionError(action, e, event, el); return true; }\n }\n\n const generic = `onAction${cap(action)}`;\n if (typeof v[generic] === 'function') {\n try {\n if (await v[generic](event, el)) {\n const isInDropdown = !!el.closest('.dropdown-menu');\n if (isInDropdown) this.hideDropdown(el);\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n return false;\n }\n catch (e) { console.error(`Error in action ${action}:`, e); v.handleActionError(action, e, event, el); return true; }\n }\n\n const passThru = `onPassThruAction${cap(action)}`;\n if (typeof v[passThru] === 'function') {\n try { await v[passThru](event, el); return false; }\n catch (e) { console.error(`Error in action ${action}:`, e); v.handleActionError(action, e, event, el); return true; }\n }\n\n if (typeof v.onActionDefault === 'function') {\n try { return await v.onActionDefault(action, event, el); }\n catch (e) { console.error(`Error in default action handler for ${action}:`, e); v.handleActionError(action, e, event, el); return true; }\n }\n\n v.emit?.(`action:${action}`, { action, event, element: el });\n return false;\n }\n\n async dispatchChange(action, event, el) {\n const v = this.view;\n const cap = (s) => (s.includes('-') ? s.split('-').map(w => w[0].toUpperCase()+w.slice(1)).join('') : s[0].toUpperCase()+s.slice(1));\n\n const changeHandler = `onChange${cap(action)}`;\n if (typeof v[changeHandler] === 'function') {\n try {\n await v[changeHandler](event, el);\n return true;\n }\n catch (e) {\n console.error(`Error in onChange ${action}:`, e);\n v.handleActionError?.(action, e, event, el);\n return true;\n }\n }\n\n // Fall back to regular dispatch if no onChange handler exists\n return await this.dispatch(action, event, el);\n }\n\n shouldHandle(el, event) {\n if (this.owns(el)) return true;\n if (this.contains(el) && !event.handledByChild) return true;\n return false;\n }\n\n owns(el) {\n const root = this.view.element;\n if (!root || !root.contains(el)) return false;\n for (const child of Object.values(this.view.children)) {\n if (child.element && child.element.contains(el)) return false;\n }\n return true;\n }\n\n contains(el) { return !!this.view.element && this.view.element.contains(el); }\n}\n\nexport default EventDelegate;\n","/**\n * EventEmitter - Lightweight event system for instance-level events.\n *\n * Provides a simple, consistent event API for Models, Collections, Views, and Pages.\n * Events are scoped to individual instances - use the global EventBus for cross-component communication.\n *\n * Usage as a mixin:\n * Object.assign(MyClass.prototype, EventEmitter);\n *\n * API:\n * on(event, callback, context) - Add event listener with optional context\n * off(event, callback, context) - Remove event listener\n * once(event, callback, context) - Add one-time event listener with optional context\n * emit(event, ...args) - Emit event to all listeners\n *\n * @example\n * // Clean context binding\n * model.on('change', this.handleChange, this);\n * model.off('change', this.handleChange, this); // Easy cleanup!\n * \n * // Traditional usage still works\n * model.on('change', (data) => console.log(data));\n */\n\nconst EventEmitter = {\n /**\n * Add an event listener\n * @param {string} event - Event name to listen for\n * @param {Function} callback - Function to call when event is emitted\n * @param {Object} [context] - Context to bind the callback to (optional)\n * @returns {Object} This instance for method chaining\n *\n * @example\n * // With context binding\n * model.on('change', this.handleChange, this);\n * \n * // Without context (traditional)\n * model.on('change', (data) => console.log(data));\n */\n on(event, callback, context) {\n if (!this._listeners) this._listeners = {};\n if (!this._listeners[event]) this._listeners[event] = [];\n \n const listener = {\n callback,\n context,\n fn: context ? callback.bind(context) : callback\n };\n \n this._listeners[event].push(listener);\n return this;\n },\n\n /**\n * Remove an event listener\n * @param {string} event - Event name\n * @param {Function} [callback] - Specific callback to remove. If omitted, removes all listeners for the event\n * @param {Object} [context] - Context that was used when adding the listener\n * @returns {Object} This instance for method chaining\n *\n * @example\n * // Remove specific listener with context\n * model.off('change', this.handleChange, this);\n *\n * // Remove specific callback (any context)\n * model.off('change', myHandler);\n *\n * // Remove all listeners for an event\n * model.off('change');\n */\n off(event, callback, context) {\n if (!this._listeners || !this._listeners[event]) return this;\n\n if (!callback) {\n // Remove all listeners for event\n delete this._listeners[event];\n } else {\n // Remove specific listener(s)\n this._listeners[event] = this._listeners[event].filter(listener => {\n // Match callback\n if (listener.callback !== callback) return true;\n \n // If context specified, must match context too\n if (arguments.length === 3 && listener.context !== context) return true;\n \n // This listener should be removed\n return false;\n });\n \n if (this._listeners[event].length === 0) {\n delete this._listeners[event];\n }\n }\n return this;\n },\n\n /**\n * Add a one-time event listener that automatically removes itself after being called\n * @param {string} event - Event name to listen for\n * @param {Function} callback - Function to call when event is emitted (called only once)\n * @param {Object} [context] - Context to bind the callback to (optional)\n * @returns {Object} This instance for method chaining\n *\n * @example\n * model.once('ready', this.handleReady, this);\n */\n once(event, callback, context) {\n const onceWrapper = (...args) => {\n this.off(event, onceWrapper);\n const fn = context ? callback.bind(context) : callback;\n fn.apply(context || this, args);\n };\n \n this.on(event, onceWrapper);\n return this;\n },\n\n /**\n * Emit an event to all registered listeners\n * @param {string} event - Event name to emit\n * @param {...*} args - Arguments to pass to event listeners\n * @returns {Object} This instance for method chaining\n *\n * @example\n * // Emit with single argument\n * model.emit('change', { field: 'name', value: 'John' });\n *\n * // Emit with multiple arguments\n * model.emit('update', oldValue, newValue, timestamp);\n *\n * // Emit without arguments\n * model.emit('ready');\n */\n emit(event, ...args) {\n if (!this._listeners || !this._listeners[event]) return this;\n \n // Copy the listeners in case one removes itself during emit\n const listeners = this._listeners[event].slice();\n \n for (const listener of listeners) {\n try {\n listener.fn.apply(listener.context || this, args);\n } catch (error) {\n // Don't allow one bad handler to block other listeners\n if (console && console.error) {\n console.error(`Error in ${event} event handler:`, error);\n }\n }\n }\n return this;\n }\n};\n\nexport default EventEmitter;","// -----------------------------------------------\n// Imports\n// -----------------------------------------------\nimport Mustache from '@core/utils/mustache.js';\nif (typeof window !== 'undefined') {\n window.Mustache = Mustache;\n}\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport EventDelegate from '@core/mixins/EventDelegate.js';\n\n// -----------------------------------------------\n// View\n// -----------------------------------------------\nexport class View {\n // ---------------------------------------------\n // Construction & defaults\n // ---------------------------------------------\n constructor(opts = {}) {\n this.tagName = opts.tagName ?? \"div\";\n this.className = opts.className ?? \"mojo-view\";\n this.style = opts.style ?? null; // inline css text\n this.id = opts.id ?? View._genId(); // element id\n this.containerId = opts.containerId ?? null; // container to render into\n this.container = opts.container ?? null; // container element\n\n // If container is a string, treat it as containerId and set container to null\n if (typeof this.container === 'string') {\n this.containerId = this.container;\n this.container = null;\n }\n this.parent = opts.parent ?? null; // parent view\n this.children = opts.children ?? {}; // dict of id -> child view\n this.template = opts.template || opts.templateUrl || \"\"; // string or function(model) -> string\n this.data = opts.data ?? {}; // data for Mustache or basic templating\n this.isRendering = false;\n this.lastRenderTime = 0;\n this.mounted = false;\n this.debug = opts.debug ?? false;\n this.app = opts.app ?? null;\n this.cacheTemplate = opts.cacheTemplate ?? true;\n this.enableTooltips = opts.enableTooltips ?? false; // Auto-initialize Bootstrap tooltips after render\n\n // keep original options\n this.options = { ...opts };\n\n // internal DOM element\n this.element = this._ensureElement();\n\n // in constructor\n this.events = new EventDelegate(this);\n\n if (opts.model) this.setModel(opts.model);\n\n }\n\n // ---------------------------------------------\n // Lifecycle hooks (overridable)\n // ---------------------------------------------\n async onInit() {\n\n }\n async onInitView() {\n if (this.initialized) return;\n this.initialized = true;\n await this.onInit();\n }\n async onBeforeRender() {}\n async onAfterRender() {}\n async onBeforeMount() {}\n async onAfterMount() {}\n async onBeforeUnmount() {}\n async onAfterUnmount() {}\n async onBeforeDestroy() {}\n async onAfterDestroy() {}\n\n // ---------------------------------------------\n // Public API\n // ---------------------------------------------\n setModel(model = {}) {\n let isDiff = model !== this.model;\n if (!isDiff) return this;\n if (this.model && this.model.off) {\n this.model.off(\"change\", this._onModelChange, this);\n }\n this.model = model;\n if (this.model && this.model.on) {\n this.model.on(\"change\", this._onModelChange, this);\n }\n\n // Set model on all children\n for (const id in this.children) {\n const child = this.children[id];\n if (child && typeof child.setModel === 'function') {\n child.setModel(model);\n }\n }\n\n if (isDiff ) {\n this._onModelChange();\n }\n return this;\n }\n\n _onModelChange() {\n if (this.isMounted()) {\n this.render();\n }\n }\n\n setTemplate(tpl) { this.template = tpl ?? \"\"; return this; }\n\n addChild(childView, options) {\n try {\n if (!childView || typeof childView !== \"object\") return this;\n \n // Allow overriding containerId and id via options\n if (options) {\n if (options.containerId || options.container) {\n childView.containerId = options.containerId || options.container;\n }\n if (options.id) {\n childView.id = options.id;\n }\n }\n \n childView.parent = this;\n if (this.getApp()) childView.app = this.app;\n this.children[childView.id] = childView;\n } catch (e) { View._warn(\"addChild error\", e); }\n return childView;\n }\n\n removeChild(idOrView) {\n try {\n const id = typeof idOrView === \"string\" ? idOrView : (idOrView && idOrView.id);\n if (!id) return this;\n const child = this.children[id];\n if (child) {\n // allow for async or sync destroy\n Promise.resolve(child.destroy()).catch(err => View._warn(\"removeChild destroy error\", err));\n delete this.children[id];\n }\n } catch (e) { View._warn(\"removeChild error\", e); }\n return this;\n }\n\n getChild(id) {\n return this.children[id];\n }\n\n async updateData(newData, rerender = false) {\n Object.assign(this.data, newData);\n\n if (rerender && this.isMounted()) {\n await this.render();\n }\n\n return this;\n }\n\n toggleClass(className, force) {\n if (force === undefined) {\n force = !this.element.classList.contains(className);\n }\n this.element.classList.toggle(className, force);\n return this;\n }\n\n addClass(className) {\n this.element.classList.add(className);\n return this;\n }\n\n setClass(className) {\n this.element.className = className;\n return this;\n }\n\n removeClass(className) {\n this.element.classList.remove(className);\n return this;\n }\n\n canRender() {\n if (this.isRendering) return false;\n // Optional render throttling\n const now = Date.now();\n if (this.options.renderCooldown > 0 && now - this.lastRenderTime < this.options.renderCooldown) {\n View._warn(`View ${this.id}: Render called too quickly, cooldown active`);\n return false;\n }\n if (this.options.noAppend) {\n if (this.parent) {\n if (!this.parent.contains(this.containerId || this.container)) {\n return false;\n } else if (this.containerId && !document.getElementById(this.containerId)) {\n return false;\n } else if (this.container && !document.contains(this.container)) {\n return false;\n }\n }\n }\n return true;\n }\n\n // ---------------------------------------------\n // Render flow\n // ---------------------------------------------\n async render(allowMount = true, container = null) {\n const now = Date.now();\n\n if (!this.canRender()) {\n return this;\n }\n\n this.isRendering = true;\n this.lastRenderTime = now;\n\n try {\n if (!this.initialized) await this.onInitView();\n this.unbindEvents();\n\n await this.onBeforeRender();\n if (this.getViewData) {\n this.data = await this.getViewData();\n }\n // 1) render own HTML (FIX #5: await the async template render)\n const html = await this.renderTemplate();\n this.element.innerHTML = html;\n\n if (allowMount && !this.isMounted()) {\n await this.mount(container)\n }\n\n // 3) render children\n await this._renderChildren();\n await this.onAfterRender();\n this.bindEvents();\n\n } catch (e) {\n View._warn(`Render error in ${this.id}`, e);\n } finally {\n // FIX #4: always reset isRendering\n this.isRendering = false;\n }\n\n return this;\n }\n\n async _renderChildren() {\n for (const id in this.children) {\n const child = this.children[id];\n if (!child) continue;\n child.parent = this;\n await Promise.resolve(child.render()).catch(err => View._warn(`Child render error (${id})`, err));\n }\n }\n\n async _unmountChildren() {\n for (const id in this.children) {\n const child = this.children[id];\n if (!child) continue;\n child.unbindEvents()\n }\n }\n\n\n isMounted() {\n return this.element?.isConnected;\n }\n\n getChildElementById(id, root = null) {\n const cleanId = id.startsWith('#') ? id.substring(1) : id;\n if (root) {\n return root.querySelector(`#${cleanId}`);\n }\n return this.element.querySelector(`#${cleanId}`);\n }\n\n getChildElement(id) {\n if (id.startsWith(\"#\")) {\n return this.getChildElementById(id);\n }\n let el = this.element?.querySelector(`[data-container=\"${id}\"]`);\n if (!el) {\n return this.getChildElementById(id);\n }\n return el;\n }\n\n getContainer() {\n if (this.replaceById) {\n // this means we want to place the element in the dom that matches our id\n if (this.parent) {\n return this.parent.getChildElementById(this.id);\n }\n // return this.getChildElementById(this.id, document.body);\n return null;\n }\n if (!this.containerId) return null;\n if (this.parent) {\n return this.parent.getChildElement(this.containerId);\n }\n return this.getChildElementById(this.containerId, document.body);\n }\n\n async mount(container = null) {\n await this.onBeforeMount();\n if (!container) {\n container = this.getContainer();\n }\n\n if (this.containerId && !container) {\n // throw new Error(`Container not found for ${this.containerId}`);\n console.error(`Container not found for ${this.containerId}`);\n return;\n }\n\n if (container && this.replaceById) {\n container.replaceWith(this.element);\n } else if (container) {\n // if we have a container just replace its children with our element\n container.replaceChildren(this.element);\n } else if (!this.containerId && this.parent) {\n // append to parent\n this.parent.element.appendChild(this.element);\n } else if (!this.containerId && !this.parent && this.options.allowAppendToBody) {\n // append to body\n console.log(\"APPENDING TO BODY!!!!\");\n document.body.appendChild(this.element);\n } else {\n // there is a containerId but no container\n console.error(`Container not found for ${this.containerId}`);\n }\n\n await this.onAfterMount();\n this.mounted = true;\n }\n\n async unmount() {\n if (!this.element || !this.element.parentNode) return;\n await this.onBeforeUnmount();\n // unbind all children\n await this._unmountChildren();\n if (this.element.parentNode) this.element.parentNode.removeChild(this.element);\n this.events.unbind();\n await this.onAfterUnmount();\n this.mounted = false;\n }\n\n // FIX #1: make destroy async (it already awaited hooks)\n async destroy() {\n try {\n this.events.unbind();\n // destroy children first (support async or sync destroy)\n for (const id in this.children) {\n const ch = this.children[id];\n if (ch) {\n await Promise.resolve(ch.destroy()).catch(err => View._warn(`Child destroy error (${id})`, err));\n }\n }\n this.mounted = false;\n if (this.element && this.element.parentNode) {\n await this.onBeforeDestroy();\n if (this.element.parentNode) this.element.parentNode.removeChild(this.element);\n await this.onAfterDestroy();\n }\n } catch (e) {\n View._warn(`Destroy error in ${this.id}`, e);\n }\n }\n\n // ---------------------------------------------\n // DOM helpers\n // ---------------------------------------------\n _ensureElement() {\n try {\n if (this.element && this.element.tagName?.toLowerCase() === this.tagName) {\n this._syncAttrs();\n return this.element;\n }\n const el = document.createElement(this.tagName);\n this.element = el;\n this.el = el;\n this._syncAttrs();\n return el;\n } catch (e) {\n View._warn(\"ensureElement error\", e);\n // last resort: create a DIV to avoid throwing\n const el = document.createElement(\"div\");\n // FIX #2: use View._genId() (not SimpleView)\n el.id = this.id || View._genId();\n return el;\n }\n }\n\n _syncAttrs() {\n try {\n if (!this.element) return;\n if (this.id) this.element.id = this.id;\n this.element.className = this.className || \"\";\n if (this.style == null) {\n this.element.removeAttribute(\"style\");\n } else {\n this.element.style.cssText = String(this.style);\n }\n } catch (e) { View._warn(\"_syncAttrs error\", e); }\n }\n\n bindEvents() {\n this.events.bind(this.element);\n\n // Initialize tooltips if enabled\n if (this.enableTooltips) {\n this.initializeTooltips();\n }\n }\n\n unbindEvents() {\n this.events.unbind();\n\n // Cleanup tooltips if enabled\n if (this.enableTooltips) {\n this.disposeTooltips();\n }\n }\n\n // ---------------------------------------------\n // Template helpers\n // ---------------------------------------------\n async renderTemplate() {\n const templateContent = await this.getTemplate();\n if (!templateContent) return '';\n const partials = this.getPartials();\n return Mustache.render(templateContent, this, partials);\n }\n\n renderTemplateString(template, context, partials) {\n // Mustache.render now auto-wraps context to enable pipe formatters\n return Mustache.render(template, context, partials);\n }\n\n getPartials() { return {}; }\n\n async getTemplate() {\n if (this._templateCache && this.cacheTemplate) {\n return this._templateCache;\n }\n const template = this.template || this.templateUrl;\n if (!template) {\n throw new Error('Template not found');\n }\n\n let templateContent = '';\n\n if (typeof template === 'string') {\n if (template.includes('<') || template.includes('{')) {\n templateContent = template;\n } else {\n try {\n let templatePath = template;\n if (!this.app) this.app = this.getApp();\n if (this.app && this.app.basePath) {\n if (!templatePath.startsWith('/') &&\n !templatePath.startsWith('http://') &&\n !templatePath.startsWith('https://')) {\n const base = this.app.basePath.endsWith('/')\n ? this.app.basePath.slice(0, -1)\n : this.app.basePath;\n templatePath = `${base}/${templatePath}`;\n }\n }\n\n const response = await fetch(templatePath);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n templateContent = await response.text();\n } catch (error) {\n View._warn(`Failed to load template from ${template}: ${error}`);\n // NOTE: showError may be provided by host app\n this.showError?.(`Failed to load template from ${template}: ${error.message}`);\n }\n }\n } else if (typeof template === 'function') {\n templateContent = await this.template(this.data, this.state);\n }\n\n if (this.cacheTemplate && templateContent) {\n this._templateCache = templateContent;\n }\n\n return templateContent;\n }\n\n getContextValue(path) {\n const value = MOJOUtils.getContextData(this, path);\n\n if (path && path.startsWith('data.') && value && typeof value === 'object') {\n return MOJOUtils.wrapData(value, this);\n }\n\n if (path && path.startsWith('model.') &&\n path !== 'model' &&\n value && typeof value === 'object' &&\n typeof value.getContextValue !== 'function') {\n return MOJOUtils.wrapData(value, null);\n }\n\n return value;\n }\n\n // 9) Navigation Helpers ------------------------------------------------------\n async handlePageNavigation(element) {\n const pageName = element.getAttribute('data-page');\n const paramsAttr = element.getAttribute('data-params');\n let params = {};\n if (paramsAttr) {\n try { params = JSON.parse(paramsAttr); }\n catch (error) { console.warn('Invalid JSON in data-params:', paramsAttr); }\n }\n\n const app = this.getApp();\n if (app) { app.showPage(pageName, params); return; }\n\n const router = this.findRouter();\n if (router && typeof router.navigateToPage === 'function') {\n await router.navigateToPage(pageName, params);\n } else {\n console.error(`No router found for page navigation to '${pageName}'`);\n }\n }\n\n async handleHrefNavigation(element) {\n const href = element.getAttribute('href');\n if (this.isExternalLink(href) || element.hasAttribute('data-external')) return;\n\n const router = this.findRouter();\n if (router) {\n if (router.options && router.options.mode === 'param' && href.startsWith('?')) {\n const fullPath = '/' + href;\n await router.navigate(fullPath);\n return;\n }\n if (router.options && router.options.mode === 'hash' && href.startsWith('#')) {\n await router.navigate(href);\n return;\n }\n const routePath = this.hrefToRoutePath(href);\n await router.navigate(routePath);\n } else {\n console.warn('No router found for navigation, using default behavior');\n window.location.href = href;\n }\n }\n\n isExternalLink(href) {\n if (!href) return true;\n if (href.startsWith(\"/\") && this.getApp()) {\n if (href.startsWith(this.findRouter().basePath)) return false;\n return true;\n }\n return href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:') || href.startsWith('http://') || href.startsWith('https://') || href.startsWith('//');\n }\n\n hrefToRoutePath(href) {\n if (href.startsWith('/')) {\n const router = this.findRouter();\n if (router && router.options && router.options.base) {\n const base = router.options.base;\n if (href.startsWith(base)) return href.substring(base.length) || '/';\n }\n return href;\n }\n return href.startsWith('./') ? href.substring(2) : href;\n }\n\n findRouter() {\n this.getApp();\n return this.app?.router || null;\n }\n\n getApp() {\n if (this.app) return this.app;\n const apps = [\n window.__app__,\n window.MOJO?.app,\n window.APP,\n window.app,\n window.WebApp,\n window.matchUUID ? window[window.matchUUID]() : window[window.matchUUID]\n ];\n this.app = apps.find(app => app && typeof app.showPage === 'function') || null;\n return this.app;\n }\n\n handleActionError(action, err, evt, el) {\n this.showError(`Action '${action}' failed: ${err}`, evt, el);\n }\n\n // ---------------------------------------------\n // Utilities\n // ---------------------------------------------\n /**\n * Escape HTML characters\n * @param {string} str - String to escape\n * @returns {string} Escaped string\n */\n escapeHtml(str) {\n if (typeof str !== 'string') return str;\n\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n contains(el) {\n if (typeof el === 'string') {\n if (!this.element) return false; // no parent element yet\n el = document.getElementById(el);\n }\n if (!el) return false; // no element with that id\n return this.element.contains(el);\n }\n\n /**\n * Initialize Bootstrap tooltips in this view's element\n * Called automatically in bindEvents() if enableTooltips is true\n */\n initializeTooltips() {\n if (!this.element || !window.bootstrap?.Tooltip) return;\n this.disposeTooltips();\n const tooltipTriggerList = this.element.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n [...tooltipTriggerList].map(tooltipTriggerEl => {\n // Extract custom options from data attributes\n const theme = tooltipTriggerEl.getAttribute('data-tooltip-theme');\n const size = tooltipTriggerEl.getAttribute('data-tooltip-size');\n\n // Build custom class list\n let customClass = '';\n if (theme) customClass += `tooltip-${theme} `;\n if (size) customClass += `tooltip-${size}`;\n\n // Build options object - only include customClass if it has a value\n const options = {};\n const trimmedClass = customClass.trim();\n if (trimmedClass) {\n options.customClass = trimmedClass;\n }\n\n // Initialize tooltip with custom options\n return new window.bootstrap.Tooltip(tooltipTriggerEl, options);\n });\n }\n\n /**\n * Dispose all Bootstrap tooltips in this view's element\n * Called automatically in unbindEvents() if enableTooltips is true\n */\n disposeTooltips() {\n if (!this.element || !window.bootstrap?.Tooltip) return;\n\n const tooltipElements = this.element.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n tooltipElements.forEach(element => {\n const tooltip = window.bootstrap.Tooltip.getInstance(element);\n if (tooltip) {\n tooltip.dispose();\n }\n });\n }\n\n /**\n * Show error message\n * @param {string} message - Error message\n */\n async showError(message) {\n console.error(`View ${this.id} error:`, message);\n const app = this.getApp ? this.getApp() : (this.app || null);\n if (app && typeof app.showError === 'function') {\n await app.showError(message);\n return;\n }\n // Fallback to browser alert if no app or helper available\n alert(`Error: ${message}`);\n }\n\n /**\n * Show success message\n * @param {string} message - Success message\n */\n async showSuccess(message) {\n if (this.debug) {\n console.log(`View ${this.id} success:`, message);\n }\n const app = this.getApp ? this.getApp() : (this.app || null);\n if (app && typeof app.showSuccess === 'function') {\n await app.showSuccess(message);\n return;\n }\n // Fallback to browser alert if no app or helper available\n alert(`Success: ${message}`);\n }\n\n /**\n * Show info message\n * @param {string} message - Info message\n */\n async showInfo(message) {\n console.info(`View ${this.id} info:`, message);\n const app = this.getApp ? this.getApp() : (this.app || null);\n if (app && typeof app.showInfo === 'function') {\n await app.showInfo(message);\n return;\n }\n // Fallback to browser alert if no app or helper available\n alert(`Info: ${message}`);\n }\n\n /**\n * Show warning message\n * @param {string} message - Warning message\n */\n async showWarning(message) {\n console.warn(`View ${this.id} warning:`, message);\n const app = this.getApp ? this.getApp() : (this.app || null);\n if (app && typeof app.showWarning === 'function') {\n await app.showWarning(message);\n return;\n }\n // Fallback to browser alert if no app or helper available\n alert(`Warning: ${message}`);\n }\n\n // Default action: copy value from data-clipboard to clipboard\n async onActionCopyToClipboard(event, element) {\n try {\n const carrier = element?.closest('[data-clipboard]') || element;\n const text = carrier?.getAttribute('data-clipboard') || '';\n // Consider handled even if empty to avoid bubbling\n if (!text) return true;\n\n if (navigator.clipboard && window.isSecureContext) {\n await navigator.clipboard.writeText(text);\n } else {\n const textarea = document.createElement('textarea');\n textarea.value = text;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n }\n\n // Visual feedback: temporarily swap icon to a checkmark\n const icon = element.querySelector('i');\n const originalClass = icon && icon.className;\n if (icon) {\n icon.className = 'bi bi-check';\n setTimeout(() => { icon.className = originalClass; }, 1000);\n }\n return true;\n } catch (err) {\n console.warn('Copy to clipboard failed:', err);\n return true;\n }\n }\n\n static _genId() { return `view_${Math.random().toString(36).substr(2, 9)}`; }\n\n static _warn(msg, err) {\n try {\n if (err) console.warn(`[View] ${msg}:`, err);\n else console.warn(`[View] ${msg}`);\n } catch { /* never throw on logging */ }\n }\n}\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\nObject.assign(View.prototype, EventEmitter);\n\nexport default View;\n","/**\n * Page - Extends View with routing capabilities for MOJO framework\n * Handles URL routing, parameters, and page-specific actions\n *\n * Event Emitter notes:\n * - Uses EventEmitter via View base class.\n * - Use .emit/.on/.off/.once for all custom events.\n */\n\nimport View from '@core/View.js';\n\nclass Page extends View {\n constructor(options = {}) {\n // Set default tag name for pages\n options.tagName = options.tagName || 'main';\n options.className = options.className || 'mojo-page';\n\n // Set page ID based on page name\n const pageName = options.pageName || '';\n if (pageName && !options.id) {\n options.id = 'page_' + pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n super(options);\n\n // Core page properties from design doc\n this.pageName = options.pageName || this.constructor.pageName || '';\n this.route = options.route || this.constructor.route || '';\n this.title = options.title || this.pageName || '';\n\n // Set page ID if not already set and we have a page_name from constructor\n if (!this.id && this.constructor.pageName && !options.pageName) {\n this.id = 'page_' + this.constructor.pageName.toLowerCase().replace(/\\s+/g, '_');\n }\n\n // Page metadata for event system\n this.pageIcon = options.icon || options.pageIcon || this.constructor.pageIcon || 'bi bi-file-text';\n this.displayName = options.displayName || this.constructor.displayName || this.pageName || '';\n this.pageDescription = options.pageDescription || this.constructor.pageDescription || '';\n\n // Routing state\n this.params = {};\n this.query = {};\n this.matched = false;\n this.isActive = false;\n\n // Page-specific options\n this.pageOptions = {\n title: options.title || this.pageName || 'Untitled Page',\n description: options.description || '',\n requiresAuth: options.requiresAuth || false,\n ...options.pageOptions\n };\n\n // State preservation\n this.savedState = null;\n\n console.log(`Page ${this.pageName} constructed with route: ${this.route}`);\n }\n\n /**\n * Handle route parameters - from design doc\n * @param {object} params - Route parameters\n * @param {object} query - Query string parameters\n */\n async onParams(params = {}, query = {}) {\n // const paramsChanged = JSON.stringify(params) !== JSON.stringify(this.params);\n // const queryChanged = JSON.stringify(query) !== JSON.stringify(this.query);\n\n this.params = params;\n this.query = query;\n\n // Only re-render if params actually changed and page is active\n // if (this.isActive && (paramsChanged || queryChanged)) {\n // console.log(`Page ${this.pageName} params changed, re-rendering`);\n // await this.render();\n // }\n }\n\n canEnter() {\n if (this.options.permissions) {\n const user = this.getApp().activeUser;\n if (!user || !user.hasPermission(this.options.permissions)) {\n return false;\n }\n }\n if (this.options.requiresGroup && !this.getApp().activeGroup) {\n return false;\n }\n return true;\n }\n\n /**\n * Called when entering this page (before render)\n * Override this method for initialization logic\n */\n async onEnter() {\n this.isActive = true;\n await this.onInitView();\n\n // Restore saved state if exists\n if (this.savedState) {\n this.restoreState(this.savedState);\n this.savedState = null;\n }\n\n // Set page title if provided\n if (this.pageOptions && this.pageOptions.title && typeof document !== 'undefined') {\n document.title = this.pageOptions.title;\n }\n\n // Emit activation event\n this.emit('activated', {\n page: this.getMetadata()\n });\n\n console.log(`Page ${this.pageName} entered`);\n }\n\n /**\n * Called when leaving this page (before cleanup)\n * Override this method for cleanup logic like removing listeners, clearing timers, etc.\n */\n async onExit() {\n // Save state before exit\n this.savedState = this.captureState();\n this.isActive = false;\n\n // Emit deactivation event\n this.emit('deactivated', {\n page: this.getMetadata()\n });\n console.log(`Page ${this.pageName} exiting`);\n }\n\n /**\n * Get page metadata for display and events\n * @returns {object} Page metadata\n */\n getMetadata() {\n return {\n name: this.pageName,\n displayName: this.displayName || this.pageName,\n icon: this.pageIcon,\n description: this.pageDescription,\n route: this.route,\n isActive: this.isActive\n };\n }\n\n /**\n * Handle default action - fallback from design doc\n */\n async onActionDefault(action) {\n console.log(`Default action '${action}' triggered on page: ${this.pageName}`);\n }\n\n async makeActive() {\n this.getApp().showPage(this);\n }\n\n async onActionNavigate(event, element) {\n event.preventDefault();\n const page = element.dataset.page;\n this.getApp().showPage(page);\n }\n\n /**\n * Capture current page state for preservation\n * @returns {object|null} Captured state\n */\n captureState() {\n if (!this.element) return null;\n\n return {\n scrollTop: this.element.scrollTop,\n formData: this.captureFormData(),\n custom: this.captureCustomState()\n };\n }\n\n /**\n * Restore saved state\n * @param {object} state - State to restore\n */\n restoreState(state) {\n if (!state || !this.element) return;\n\n this.element.scrollTop = state.scrollTop || 0;\n this.restoreFormData(state.formData);\n if (state.custom) {\n this.restoreCustomState(state.custom);\n }\n }\n\n /**\n * Capture form data from page\n * @returns {object} Form data\n */\n captureFormData() {\n const data = {};\n if (!this.element) return data;\n\n this.element.querySelectorAll('input, select, textarea').forEach(field => {\n if (field.name) {\n if (field.type === 'checkbox') {\n data[field.name] = field.checked;\n } else if (field.type === 'radio') {\n if (field.checked) {\n data[field.name] = field.value;\n }\n } else {\n data[field.name] = field.value;\n }\n }\n });\n\n return data;\n }\n\n /**\n * Restore form data to page\n * @param {object} formData - Form data to restore\n */\n restoreFormData(formData) {\n if (!formData || !this.element) return;\n\n Object.entries(formData).forEach(([name, value]) => {\n const field = this.element.querySelector(`[name=\"${name}\"]`);\n if (field) {\n if (field.type === 'checkbox') {\n field.checked = value;\n } else if (field.type === 'radio') {\n const radio = this.element.querySelector(`[name=\"${name}\"][value=\"${value}\"]`);\n if (radio) radio.checked = true;\n } else {\n field.value = value;\n }\n }\n });\n }\n\n /**\n * Capture custom state - override in subclasses\n * @returns {object} Custom state\n */\n captureCustomState() {\n return {};\n }\n\n /**\n * Restore custom state - override in subclasses\n * @param {object} state - Custom state to restore\n */\n restoreCustomState(state) {\n // Override in subclasses\n }\n\n\n\n /**\n * Set page metadata\n * @param {object} meta - Metadata object\n */\n setMeta(meta = {}) {\n if (typeof document === 'undefined') {\n return;\n }\n\n // Set title\n if (meta.title) {\n document.title = meta.title;\n this.pageOptions.title = meta.title;\n }\n\n // Set description\n if (meta.description) {\n let descMeta = document.querySelector('meta[name=\"description\"]');\n if (!descMeta) {\n descMeta = document.createElement('meta');\n descMeta.name = 'description';\n document.head.appendChild(descMeta);\n }\n descMeta.content = meta.description;\n this.pageOptions.description = meta.description;\n }\n\n // Set other meta tags\n Object.entries(meta).forEach(([key, value]) => {\n if (key !== 'title' && key !== 'description') {\n let metaEl = document.querySelector(`meta[name=\"${key}\"]`);\n if (!metaEl) {\n metaEl = document.createElement('meta');\n metaEl.name = key;\n document.head.appendChild(metaEl);\n }\n metaEl.content = value;\n }\n });\n }\n\n\n /**\n * Show error message with page context\n * @param {string} message - Error message\n */\n showError(message) {\n super.showError(message);\n\n // Page-specific error display can be implemented here\n if (this.element) {\n // Example: Add error to page\n const errorDiv = document.createElement('div');\n errorDiv.className = 'alert alert-danger alert-dismissible fade show';\n errorDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(errorDiv, this.element.firstChild);\n\n // Auto-remove after 5 seconds\n setTimeout(() => {\n if (errorDiv.parentNode) {\n errorDiv.parentNode.removeChild(errorDiv);\n }\n }, 5000);\n }\n }\n\n /**\n * Show success message with page context\n * @param {string} message - Success message\n */\n showSuccess(message) {\n super.showSuccess(message);\n\n // Page-specific success display\n if (this.element) {\n const successDiv = document.createElement('div');\n successDiv.className = 'alert alert-success alert-dismissible fade show';\n successDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n // Insert at top of page\n this.element.insertBefore(successDiv, this.element.firstChild);\n\n // Auto-remove after 3 seconds\n setTimeout(() => {\n if (successDiv.parentNode) {\n successDiv.parentNode.removeChild(successDiv);\n }\n }, 3000);\n }\n }\n\n /**\n * Page-specific before render hook\n */\n async onBeforeRender() {\n await super.onBeforeRender();\n\n // Set page metadata before rendering\n this.setMeta({\n title: this.pageOptions.title,\n description: this.pageOptions.description\n });\n }\n\n /**\n * Page-specific after mount hook\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n // Add page-specific class to body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Page-specific before destroy hook\n */\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n\n // Remove page-specific class from body\n if (typeof document !== 'undefined' && this.pageName) {\n document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\\s+/g, '-')}`);\n }\n }\n\n /**\n * Navigate to another page using the app's router\n * @param {string} route - Route to navigate to\n * @param {object} params - Route parameters\n * @param {object} options - Navigation options\n */\n navigate(route, params = {}, options = {}) {\n // Delegate to app's router\n if (this.app && this.app.router) {\n return this.app.router.navigate(route, options);\n }\n\n // Fallback to MOJO global router\n if (typeof window !== 'undefined' && window.MOJO?.router) {\n return window.MOJO.router.navigate(route, options);\n }\n\n console.error('No router available for navigation');\n }\n\n getRoute() {\n if (this.route) {\n let route = this.route;\n if (typeof route === 'string' && route.startsWith('/')) {\n route = route.substring(1);\n }\n return route;\n }\n return this.pageName;\n }\n\n syncUrl(force = true) {\n this.updateBrowserUrl(this.query, false, false);\n }\n\n updateBrowserUrl(query = null, replace = false, trigger = false) {\n this.getApp();\n // we need to do this to normalize the URL\n // const targetPath = this.app.buildPagePath(this, this.params, query);\n // const { pageName, queryParams } = this.app.router.parseInput(targetPath);\n this.app.router.updateBrowserUrl(this.getRoute(), query, replace, trigger);\n }\n\n /**\n * Static method to define a page class with metadata\n * @param {object} definition - Page class definition\n * @returns {class} Page class\n */\n static define(definition) {\n class DefinedPage extends Page {\n constructor(options = {}) {\n super({\n ...definition,\n ...options\n });\n }\n }\n\n // Copy static properties\n DefinedPage.template = definition.template;\n DefinedPage.pageName = definition.pageName;\n DefinedPage.route = definition.route;\n\n return DefinedPage;\n }\n}\n\nexport default Page;\n","/**\n * Model - Base class for models with REST API support\n * Provides CRUD operations for API resources with built-in event system\n *\n * Event System:\n * Uses EventEmitter mixin for instance-level events (emit, on, off, once)\n * Automatically emits 'change' events when data is modified via set()\n * Emits 'change:attributeName' for specific attribute changes\n *\n * Standard Events:\n * - 'change' - Emitted when any model data changes\n * - 'change:fieldName' - Emitted when specific field changes\n *\n * @example\n * const user = new User({ name: 'John', email: 'john@example.com' });\n *\n * // Listen for any changes\n * user.on('change', (model) => {\n * console.log('User model changed');\n * view.render();\n * });\n *\n * // Listen for specific field changes\n * user.on('change:name', (newName, model) => {\n * console.log('Name changed to:', newName);\n * });\n *\n * // Trigger events by changing data\n * user.set('name', 'Jane'); // Emits 'change' and 'change:name'\n * user.set({ name: 'Bob', email: 'bob@example.com' }); // Emits 'change' and individual field events\n */\n\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport EventEmitter from '@core/mixins/EventEmitter.js';\nimport rest from '@core/Rest.js';\n\nclass Model {\n constructor(data = {}, options = {}) {\n this.endpoint = options.endpoint || this.constructor.endpoint || '';\n this.id = data.id || null;\n this.attributes = { ...data };\n this._ = this.attributes;\n this.originalAttributes = { ...data };\n this.errors = {};\n this.loading = false;\n this.rest = rest;\n\n // Event system via EventEmitter mixin (applied to prototype)\n\n // Configuration options\n this.options = {\n idAttribute: 'id',\n timestamps: true,\n ...options\n };\n }\n\n getContextValue(key) {\n return this.get(key);\n }\n\n /**\n * Get attribute value with support for dot notation and pipe formatting\n * @param {string} key - Attribute key with optional pipes (e.g., \"name|uppercase\")\n * @returns {*} Attribute value, possibly formatted\n */\n get(key) {\n // Check if key exists as an instance field first (for 'id', 'endpoint', etc.)\n if (!key.includes('.') && !key.includes('|') && this[key] !== undefined) {\n // If it's a function, call it and return the result\n if (typeof this[key] === 'function') {\n return this[key]();\n }\n return this[key];\n }\n\n // Use MOJOUtils for all attribute access with pipes and dot notation\n return MOJOUtils.getContextData(this.attributes, key);\n }\n\n /**\n * Set attribute value(s)\n * @param {string|object} key - Attribute key or object of key-value pairs\n * @param {*} value - Attribute value (if key is string)\n * @param {object} options - Options (silent: true to not trigger change event)\n */\n set(key, value, options = {}) {\n const previousAttributes = JSON.parse(JSON.stringify(this.attributes)); // Deep copy\n let hasChanged = false;\n if (key === undefined || key === null) return;\n\n if (typeof key === 'object') {\n // Set multiple attributes\n for (const [attrKey, attrValue] of Object.entries(key)) {\n hasChanged = this._setNestedAttribute(attrKey, attrValue) || hasChanged;\n }\n if (key.id !== undefined) {\n this.id = key.id;\n }\n } else {\n // Set single attribute\n if (key === 'id') {\n this.id = value;\n hasChanged = true;\n } else {\n hasChanged = this._setNestedAttribute(key, value);\n }\n }\n\n // Trigger change event if data changed and not silent\n if (hasChanged && !options.silent) {\n this.emit('change', this);\n\n // Trigger specific attribute change events\n if (typeof key === 'string') {\n this.emit(`change:${key}`, value, this);\n } else {\n for (const [attr, val] of Object.entries(key)) {\n // Get the final value that was actually set (after nested expansion)\n const finalValue = this._getNestedValue(attr);\n if (JSON.stringify(this._getNestedValue(attr, previousAttributes)) !== JSON.stringify(finalValue)) {\n this.emit(`change:${attr}`, finalValue, this);\n }\n }\n }\n }\n }\n\n /**\n * Set a nested attribute using dot notation\n * @param {string} key - Attribute key (may contain dots)\n * @param {*} value - Value to set\n * @returns {boolean} - Whether the value changed\n */\n _setNestedAttribute(key, value) {\n if (!key.includes('.')) {\n // Simple attribute\n const oldValue = this.attributes[key];\n this.attributes[key] = value;\n this[key] = value;\n return oldValue !== value;\n }\n\n // Nested attribute with dot notation\n const keys = key.split('.');\n const topLevelKey = keys[0];\n\n // Ensure the top-level object exists\n if (!this.attributes[topLevelKey] || typeof this.attributes[topLevelKey] !== 'object') {\n this.attributes[topLevelKey] = {};\n }\n if (!this[topLevelKey] || typeof this[topLevelKey] !== 'object') {\n this[topLevelKey] = {};\n }\n\n // Get the old value for comparison\n const oldValue = this._getNestedValue(key);\n\n // Navigate to the nested location and set the value\n let attrTarget = this.attributes[topLevelKey];\n let instanceTarget = this[topLevelKey];\n\n for (let i = 1; i < keys.length - 1; i++) {\n const currentKey = keys[i];\n\n if (!attrTarget[currentKey] || typeof attrTarget[currentKey] !== 'object') {\n attrTarget[currentKey] = {};\n }\n if (!instanceTarget[currentKey] || typeof instanceTarget[currentKey] !== 'object') {\n instanceTarget[currentKey] = {};\n }\n\n attrTarget = attrTarget[currentKey];\n instanceTarget = instanceTarget[currentKey];\n }\n\n // Set the final value\n const finalKey = keys[keys.length - 1];\n attrTarget[finalKey] = value;\n instanceTarget[finalKey] = value;\n\n return JSON.stringify(oldValue) !== JSON.stringify(value);\n }\n\n /**\n * Get a nested value using dot notation\n * @param {string} key - Attribute key (may contain dots)\n * @param {object} source - Source object (defaults to this.attributes)\n * @returns {*} - The nested value\n */\n _getNestedValue(key, source = this.attributes) {\n if (!key.includes('.')) {\n return source[key];\n }\n\n const keys = key.split('.');\n let current = source;\n\n for (const k of keys) {\n if (current == null || typeof current !== 'object') {\n return undefined;\n }\n current = current[k];\n }\n\n return current;\n }\n\n getData() {\n return this.attributes;\n }\n\n getId() {\n return this.id;\n }\n\n /**\n * Fetch model data from API with request deduplication and cancellation\n * @param {object} options - Request options\n * @param {number} options.debounceMs - Optional debounce delay in milliseconds\n * @returns {Promise} Promise that resolves with REST response\n */\n async fetch(options = {}) {\n let url = options.url;\n if (!url) {\n const id = options.id || this.getId();\n if (!id && this.options.requiresId !== false) {\n throw new Error('Model: ID is required for fetching');\n }\n url = this.buildUrl(id);\n }\n const requestKey = JSON.stringify({url, params: options.params});\n\n // Handle debounced fetch\n if (options.debounceMs && options.debounceMs > 0) {\n return this._debouncedFetch(requestKey, options);\n }\n\n // CANCEL PREVIOUS REQUEST if it's different from current request\n if (this.currentRequest && this.currentRequestKey !== requestKey) {\n console.info('Model: Cancelling previous request for new parameters');\n this.abortController?.abort();\n this.currentRequest = null;\n }\n\n // REQUEST DEDUPLICATION - Return existing promise if identical request\n if (this.currentRequest && this.currentRequestKey === requestKey) {\n console.info('Model: Duplicate request in progress, returning existing promise');\n return this.currentRequest;\n }\n\n // RATE LIMITING - Prevent requests within 100ms of last request\n const now = Date.now();\n const minInterval = 100; // ms\n\n if (this.lastFetchTime && (now - this.lastFetchTime) < minInterval) {\n console.info('Model: Rate limited, skipping fetch');\n return this;\n }\n\n this.loading = true;\n this.errors = {};\n this.lastFetchTime = now;\n this.currentRequestKey = requestKey;\n\n // Create new AbortController for this request\n this.abortController = new AbortController();\n\n // Store the promise for deduplication\n this.currentRequest = this._performFetch(url, options, this.abortController);\n\n try {\n const result = await this.currentRequest;\n return result;\n } catch (error) {\n // Don't throw if request was cancelled\n if (error.name === 'AbortError') {\n console.info('Model: Request was cancelled');\n return this;\n }\n throw error;\n } finally {\n this.currentRequest = null;\n this.currentRequestKey = null;\n this.abortController = null;\n }\n }\n\n /**\n * Handle debounced fetch requests\n * @param {string} requestKey - Unique key for this request\n * @param {object} options - Fetch options\n * @returns {Promise} Promise that resolves with REST response\n */\n async _debouncedFetch(requestKey, options) {\n // Clear existing debounced fetch\n if (this.debouncedFetchTimeout) {\n clearTimeout(this.debouncedFetchTimeout);\n }\n\n // Cancel any active request since we're about to start a new one\n this.cancel();\n\n return new Promise((resolve, reject) => {\n this.debouncedFetchTimeout = setTimeout(async () => {\n try {\n const result = await this.fetch({ ...options, debounceMs: 0 });\n resolve(result);\n } catch (error) {\n reject(error);\n }\n }, options.debounceMs);\n });\n }\n\n /**\n * Internal method to perform the actual fetch\n * @param {string} url - API endpoint URL\n * @param {object} options - Request options\n * @param {AbortController} abortController - Controller for request cancellation\n * @returns {Promise} Promise that resolves with REST response\n */\n async _performFetch(url, options, abortController) {\n try {\n if (options.graph && (!options.params || !options.params.graph)) {\n if (!options.params) options.params = {};\n options.params.graph = options.graph;\n }\n const response = await this.rest.GET(url, options.params, {\n signal: abortController.signal\n });\n\n if (response.success) {\n if (response.data.status) {\n this.originalAttributes = { ...this.attributes };\n if (response.data.data) this.set(response.data.data);\n this.errors = {};\n } else {\n this.errors = response.data;\n }\n } else {\n this.errors = response.errors || {};\n }\n\n return response;\n } catch (error) {\n // Handle cancellation gracefully\n if (error.name === 'AbortError') {\n console.info('Model: Fetch was cancelled');\n throw error;\n }\n\n this.errors = { fetch: error.message };\n\n // Return error response for network/other errors\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n }\n }\n\n /**\n * Save model to API (create or update)\n * @param {object} data - Data to save to the model\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with REST response\n */\n async save(data, options = {}) {\n const isNew = !this.id;\n const method = isNew ? 'POST' : 'PUT';\n const url = isNew ? this.buildUrl() : this.buildUrl(this.id);\n\n this.loading = true;\n this.errors = {};\n\n try {\n const response = await this.rest[method](url, data, options.params);\n\n if (response.success) {\n if (response.data.status) {\n // Update model on success\n this.originalAttributes = { ...this.attributes };\n this.set(response.data.data);\n this.errors = {};\n } else {\n this.errors = response.data;\n }\n } else {\n this.errors = response.errors || {};\n }\n\n return response; // Always return the full response\n\n } catch (error) {\n // Return error response for network/other errors\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n }\n }\n\n\n /**\n * Delete model from API\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with REST response\n */\n async destroy(options = {}) {\n if (!this.id) {\n this.errors = { destroy: 'Cannot destroy model without ID' };\n return {\n success: false,\n error: 'Cannot destroy model without ID',\n status: 400\n };\n }\n\n const url = this.buildUrl(this.id);\n this.loading = true;\n this.errors = {};\n\n try {\n const response = await this.rest.DELETE(url, options.params);\n\n if (response.success) {\n // Clear model data on success\n this.attributes = {};\n this.originalAttributes = {};\n this.id = null;\n this.errors = {};\n } else {\n this.errors = response.errors || {};\n }\n\n return response;\n\n } catch (error) {\n this.errors = { destroy: error.message };\n\n // Return error response for network/other errors\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n }\n }\n\n /**\n * Check if model has been modified\n * @returns {boolean} True if model has unsaved changes\n */\n isDirty() {\n return JSON.stringify(this.attributes) !== JSON.stringify(this.originalAttributes);\n }\n\n /**\n * Get attributes that have changed since last save\n * @returns {object} Object containing only changed attributes\n */\n getChangedAttributes() {\n const changed = {};\n\n for (const [key, value] of Object.entries(this.attributes)) {\n if (this.originalAttributes[key] !== value) {\n changed[key] = value;\n }\n }\n\n return changed;\n }\n\n /**\n * Reset model to original state\n */\n reset() {\n this.attributes = { ...this.originalAttributes };\n this._ = this.attributes;\n this.errors = {};\n }\n\n /**\n * Build URL for API requests\n * @param {string|number} id - Optional ID to append to URL\n * @returns {string} Complete API URL\n */\n buildUrl(id = null) {\n let url = this.endpoint;\n if (id) {\n url = url.endsWith('/') ? `${url}${id}` : `${url}/${id}`;\n }\n return url;\n }\n\n /**\n * Convert model to JSON\n * @returns {object} Model attributes as plain object\n */\n toJSON() {\n return {\n id: this.id,\n ...this.attributes\n };\n }\n\n /**\n * Validate model attributes\n * @returns {boolean} True if valid, false if validation errors exist\n */\n validate() {\n this.errors = {};\n\n // Override in subclasses for custom validation\n if (this.constructor.validations) {\n for (const [field, rules] of Object.entries(this.constructor.validations)) {\n this.validateField(field, rules);\n }\n }\n\n return Object.keys(this.errors).length === 0;\n }\n\n /**\n * Validate a single field\n * @param {string} field - Field name\n * @param {object|array} rules - Validation rules\n */\n validateField(field, rules) {\n const value = this.get(field);\n const rulesArray = Array.isArray(rules) ? rules : [rules];\n\n for (const rule of rulesArray) {\n if (typeof rule === 'function') {\n const result = rule(value, this);\n if (result !== true) {\n this.errors[field] = result || `${field} is invalid`;\n break;\n }\n } else if (typeof rule === 'object') {\n if (rule.required && (value === undefined || value === null || value === '')) {\n this.errors[field] = rule.message || `${field} is required`;\n break;\n }\n if (rule.minLength && value && value.length < rule.minLength) {\n this.errors[field] = rule.message || `${field} must be at least ${rule.minLength} characters`;\n break;\n }\n if (rule.maxLength && value && value.length > rule.maxLength) {\n this.errors[field] = rule.message || `${field} must be no more than ${rule.maxLength} characters`;\n break;\n }\n if (rule.pattern && value && !rule.pattern.test(value)) {\n this.errors[field] = rule.message || `${field} format is invalid`;\n break;\n }\n }\n }\n }\n\n // EventEmitter API: on, off, once, emit (from mixin).\n\n /**\n * Static method to create and fetch a model by ID\n * @param {string|number} id - Model ID\n * @param {object} options - Options\n * @returns {Promise<RestModel>} Promise that resolves with fetched model\n */\n static async find(id, options = {}) {\n const model = new this({}, options);\n await model.fetch({ id, ...options });\n return model;\n }\n\n /**\n * Static method to create a new model with data\n * @param {object} data - Model data\n * @param {object} options - Options\n * @returns {RestModel} New model instance\n */\n static create(data = {}, options = {}) {\n return new this(data, options);\n }\n\n /**\n * Cancel any active fetch request\n * @returns {boolean} True if a request was cancelled, false if no active request\n */\n cancel() {\n if (this.currentRequest && this.abortController) {\n console.info('Model: Manually cancelling active request');\n this.abortController.abort();\n return true;\n }\n\n // Cancel debounced fetch if exists\n if (this.debouncedFetchTimeout) {\n clearTimeout(this.debouncedFetchTimeout);\n this.debouncedFetchTimeout = null;\n return true;\n }\n\n return false;\n }\n\n /**\n * Check if model has an active fetch request\n * @returns {boolean} True if fetch is in progress\n */\n isFetching() {\n return !!this.currentRequest;\n }\n\n async showError(message) {\n await Dialog.alert(message, 'Error', {\n size: 'md',\n class: 'text-danger'\n });\n }\n}\n\nObject.assign(Model.prototype, EventEmitter);\n\nexport default Model;\n","/**\n * Collection - Class for managing arrays of Model instances\n * Provides methods for fetching and managing collections of models with built-in event system\n *\n * Event System:\n * Uses EventEmitter mixin for instance-level events (emit, on, off, once)\n * Automatically emits events when collection is modified\n *\n * Standard Events:\n * - 'add' - Emitted when models are added to the collection\n * - 'remove' - Emitted when models are removed from the collection\n * - 'update' - Emitted when collection is modified (after add/remove)\n * - 'reset' - Emitted when collection is reset (all models replaced)\n *\n * @example\n * const users = new UserCollection();\n *\n * // Listen for collection changes\n * users.on('add', ({ models, collection }) => {\n * console.log('Added', models.length, 'users');\n * updateUI();\n * });\n *\n * users.on('remove', ({ models, collection }) => {\n * console.log('Removed', models.length, 'users');\n * updateUI();\n * });\n *\n * // Add models - triggers 'add' and 'update' events\n * users.add([\n * new User({ name: 'John' }),\n * new User({ name: 'Jane' })\n * ]);\n *\n * Usage Examples:\n *\n * // Preloaded Data (no REST fetching)\n * const collection = new MyCollection({ preloaded: true });\n * collection.add(new MyModel({...}));\n * // collection.fetch() will be skipped if data already exists\n *\n * // REST Data (fetch from API)\n * const collection = new MyCollection({ preloaded: false }); // default\n * await collection.fetch(); // Will make API call\n */\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\nimport Model from '@core/Model.js';\nimport rest from '@core/Rest.js';\n\nclass Collection {\n constructor(options = {}, data = null) {\n // Handle case where first argument is data instead of ModelClass\n if (Array.isArray(options)) {\n // First argument is an array, treat it as data\n data = options;\n options = data || {};\n } else {\n data = data || options.data || [];\n }\n this.ModelClass = options.ModelClass || Model;\n this.models = [];\n this.loading = false;\n this.errors = {};\n this.meta = {};\n this.rest = rest;\n if (data) {\n this.add(data);\n }\n\n // Initialize params with defaults - single source of truth for query state\n this.params = {\n start: 0,\n size: options.size || 10,\n ...options.params\n };\n\n // Set up endpoint\n this.endpoint = options.endpoint || this.ModelClass.endpoint || '';\n if (!this.endpoint) {\n let tmp = new this.ModelClass();\n this.endpoint = tmp.endpoint;\n }\n\n // Automatic REST detection based on endpoint\n this.restEnabled = this.endpoint ? true : false;\n\n // Allow explicit override\n if (options.restEnabled !== undefined) {\n this.restEnabled = options.restEnabled;\n }\n\n // Configuration\n this.options = {\n parse: true,\n reset: true,\n preloaded: false,\n ...options\n };\n\n // Event system via EventEmitter mixin (applied to prototype)\n }\n\n getModelName() {\n return this.ModelClass.name;\n }\n\n /**\n * Fetch collection data from API\n * @param {object} additionalParams - Additional parameters to merge for this fetch only\n * @returns {Promise} Promise that resolves with REST response\n */\n async fetch(additionalParams = {}) {\n const requestKey = JSON.stringify({ ...this.params, ...additionalParams });\n\n // CANCEL PREVIOUS REQUEST if it's different from current request\n if (this.currentRequest && this.currentRequestKey !== requestKey) {\n console.info('Collection: Cancelling previous request for new parameters');\n this.abortController?.abort();\n this.currentRequest = null;\n }\n\n // REQUEST DEDUPLICATION - Return existing promise if identical request\n if (this.currentRequest && this.currentRequestKey === requestKey) {\n console.info('Collection: Duplicate request in progress, returning existing promise');\n return this.currentRequest;\n }\n\n // RATE LIMITING - Prevent requests within 100ms of last request\n const now = Date.now();\n const minInterval = 100; // ms\n\n if (this.options.rateLimiting && this.lastFetchTime && (now - this.lastFetchTime) < minInterval) {\n console.info('Collection: Rate limited, skipping fetch');\n return { success: true, message: 'Rate limited, skipping fetch', data: { data: this.toJSON() } };\n }\n\n // Skip fetching if not REST enabled\n if (!this.restEnabled) {\n console.info('Collection: REST disabled, skipping fetch');\n return { success: true, message: 'REST disabled, skipping fetch', data: { data: this.toJSON() } };\n }\n\n // Skip fetching if preloaded is true and we already have data\n if (this.options.preloaded && this.models.length > 0) {\n console.info('Collection: Using preloaded data, skipping fetch');\n return { success: true, message: 'Using preloaded data, skipping fetch', data: { data: this.toJSON() } };\n }\n\n const url = this.buildUrl();\n this.loading = true;\n this.errors = {};\n this.lastFetchTime = now;\n this.currentRequestKey = requestKey;\n\n // Create new AbortController for this request\n this.abortController = new AbortController();\n\n // Store the promise for deduplication\n this.currentRequest = this._performFetch(url, additionalParams, this.abortController);\n\n try {\n const result = await this.currentRequest;\n return result;\n } catch (error) {\n // Don't throw if request was cancelled\n if (error.name === 'AbortError') {\n console.info('Collection: Request was cancelled');\n return { success: false, error: 'Request cancelled', status: 0 };\n }\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.currentRequest = null;\n this.currentRequestKey = null;\n this.abortController = null;\n }\n }\n\n /**\n * Internal method to perform the actual fetch\n * @param {string} url - API endpoint URL\n * @param {object} additionalParams - Additional parameters\n * @param {AbortController} abortController - Controller for request cancellation\n * @returns {Promise} Promise that resolves with REST response\n */\n async _performFetch(url, additionalParams, abortController) {\n const fetchParams = { ...this.params, ...additionalParams };\n console.log('Fetching collection data from', url, fetchParams);\n try {\n this.emit(\"fetch:start\");\n const response = await this.rest.GET(url, fetchParams, {\n signal: abortController.signal\n });\n\n if (response.success && response.data.status) {\n const data = this.options.parse ? this.parse(response) : response.data;\n\n if (this.options.reset || additionalParams.reset !== false) {\n this.reset();\n }\n\n this.add(data, { silent: additionalParams.silent });\n this.errors = {};\n this.emit(\"fetch:success\");\n } else {\n if (response.data && response.data.error) {\n this.errors = response.data;\n this.emit(\"fetch:error\", { message: response.data.error, error: response.data });\n } else {\n this.errors = response.errors || {};\n this.emit(\"fetch:error\", { error: response.errors });\n }\n }\n\n return response;\n } catch (error) {\n // Handle cancellation gracefully\n if (error.name === 'AbortError') {\n console.info('Collection: Fetch was cancelled');\n return { success: false, error: 'Request cancelled', status: 0 };\n }\n\n this.errors = { fetch: error.message };\n this.emit(\"fetch:error\", { message: error.message, error: error });\n\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n this.emit(\"fetch:end\");\n }\n }\n\n /**\n * Update collection parameters and optionally fetch new data\n * @param {object} newParams - Parameters to update\n * @param {boolean} autoFetch - Whether to automatically fetch after updating params\n * @param {number} debounceMs - Optional debounce delay in milliseconds\n * @returns {Promise} Promise that resolves with REST response if autoFetch=true, or collection if autoFetch=false\n */\n async updateParams(newParams, autoFetch = false, debounceMs = 0) {\n return await this.setParams({ ...this.params, ...newParams }, autoFetch, debounceMs);\n }\n\n async setParams(newParams, autoFetch = false, debounceMs = 0) {\n this.params = newParams;\n if (autoFetch && this.restEnabled) {\n if (debounceMs > 0) {\n // Clear existing debounced fetch\n if (this.debouncedFetchTimeout) {\n clearTimeout(this.debouncedFetchTimeout);\n }\n\n // Cancel any active request since we're about to start a new one\n this.cancel();\n\n return new Promise((resolve, reject) => {\n this.debouncedFetchTimeout = setTimeout(async () => {\n try {\n const result = await this.fetch();\n resolve(result);\n } catch (error) {\n reject(error);\n }\n }, debounceMs);\n });\n } else {\n // For immediate fetches, the fetch method will handle cancellation\n return this.fetch();\n }\n }\n\n return Promise.resolve(this);\n }\n\n /**\n * Fetch a single model by ID\n * @param {string|number} id - Model ID to fetch\n * @param {object} options - Additional fetch options\n * @returns {Promise<Model|null>} Promise that resolves with model instance or null if not found\n */\n async fetchOne(id, options = {}) {\n if (!id) {\n console.warn('Collection: fetchOne requires an ID');\n return null;\n }\n\n if (!this.restEnabled) {\n console.info('Collection: REST disabled, cannot fetch single item');\n return null;\n }\n\n try {\n // Create model instance with the ID and use its fetch method\n const model = new this.ModelClass({ id }, {\n endpoint: this.endpoint,\n collection: this\n });\n\n const response = await model.fetch(options);\n\n if (response.success) {\n // Optionally add to collection if not already present\n if (options.addToCollection === true) {\n const existingModel = this.get(model.id);\n if (!existingModel) {\n this.add(model, { silent: options.silent });\n } else if (options.merge !== false) {\n existingModel.set(model.attributes);\n }\n }\n\n return model;\n } else {\n console.warn('Collection: fetchOne failed -', response.error || 'Unknown error');\n return null;\n }\n } catch (error) {\n console.error('Collection: fetchOne error -', error.message);\n return null;\n }\n }\n\n /**\n * Download collection data in a specified format\n * @param {string} format - The format for the download (e.g., 'csv', 'json')\n * @param {object} options - Download options\n * @returns {Promise} Promise that resolves with the download result\n */\n async download(format = 'json', options = {}) {\n if (!this.restEnabled) {\n console.warn('Collection: REST is not enabled, cannot download from remote.');\n // Here we could implement local data export in the future.\n return { success: false, message: 'Remote downloads are not enabled for this collection.' };\n }\n\n const url = this.buildUrl();\n const downloadParams = { ...this.params };\n\n // Remove pagination params for full export\n delete downloadParams.start;\n delete downloadParams.size;\n\n // Add format param\n downloadParams.download_format = format;\n\n // Provide a default filename and content type\n const baseName = `export-${this.getModelName().toLowerCase()}`;\n const rangeSuffix = this._buildDateRangeSuffix(downloadParams);\n const filename = `${baseName}${rangeSuffix}.${format}`;\n const contentTypes = {\n json: 'application/json',\n csv: 'text/csv'\n };\n const acceptHeader = contentTypes[format] || '*/*';\n downloadParams.filename = filename;\n\n return this.rest.download(url, downloadParams, {\n ...options,\n filename,\n headers: { 'Accept': acceptHeader }\n });\n }\n\n _buildDateRangeSuffix(params = {}) {\n const hasStart = params.dr_start;\n const hasEnd = params.dr_end;\n\n if (!hasStart && !hasEnd) {\n return '';\n }\n\n const sanitize = (value) => {\n if (!value) return '';\n return String(value).replace(/[^\\dA-Za-z_-]/g, '-');\n };\n\n const parts = [];\n const field = params.dr_field || 'daterange';\n parts.push(sanitize(field));\n\n if (hasStart) {\n parts.push(`from-${sanitize(params.dr_start)}`);\n }\n if (hasEnd) {\n parts.push(`to-${sanitize(params.dr_end)}`);\n }\n\n return `-${parts.filter(Boolean).join('-')}`;\n }\n\n /**\n * Parse response data - override in subclasses for custom parsing\n * @param {object} response - API response\n * @returns {array} Array of model data objects\n */\n parse(response) {\n // Handle standard paginated responses with size/start/count\n if (response.data && Array.isArray(response.data.data)) {\n this.meta = {\n size: response.data.size || 10,\n start: response.data.start || 0,\n count: response.data.count || 0,\n status: response.data.status,\n graph: response.data.graph,\n ...response.meta\n };\n return response.data.data;\n }\n\n // Handle direct array responses\n if (Array.isArray(response.data)) {\n return response.data;\n }\n\n // Fallback - assume response itself is the data array\n return Array.isArray(response) ? response : [response];\n }\n\n /**\n * Add model(s) to the collection\n * @param {object|array} data - Model data or array of model data\n * @param {object} options - Options for adding models\n */\n add(data, options = {}) {\n const modelsData = Array.isArray(data) ? data : [data];\n const addedModels = [];\n\n for (const modelData of modelsData) {\n let model;\n\n if (modelData instanceof this.ModelClass) {\n model = modelData;\n } else {\n model = new this.ModelClass(modelData, {\n endpoint: this.endpoint,\n collection: this\n });\n }\n\n // Check for duplicates\n const existingIndex = this.models.findIndex(m => m.id === model.id);\n if (existingIndex !== -1) {\n if (options.merge !== false) {\n // Update existing model\n this.models[existingIndex].set(model.attributes);\n }\n } else {\n // Add new model\n this.models.push(model);\n addedModels.push(model);\n }\n }\n\n // Emit events if not silent\n if (!options.silent && addedModels.length > 0) {\n this.emit('add', { models: addedModels, collection: this });\n this.emit('update', { collection: this });\n }\n\n return addedModels;\n }\n\n /**\n * Remove model(s) from the collection\n * @param {Model|array|string|number} models - Model(s) to remove or ID(s)\n * @param {object} options - Options\n */\n remove(models, options = {}) {\n const modelsToRemove = Array.isArray(models) ? models : [models];\n const removedModels = [];\n\n for (const model of modelsToRemove) {\n let index = -1;\n\n if (typeof model === 'string' || typeof model === 'number') {\n // Remove by ID\n index = this.models.findIndex(m => m.id == model);\n } else {\n // Remove by model instance\n index = this.models.indexOf(model);\n }\n\n if (index !== -1) {\n const removedModel = this.models.splice(index, 1)[0];\n removedModels.push(removedModel);\n }\n }\n\n // Emit events if not silent\n if (!options.silent && removedModels.length > 0) {\n this.emit('remove', { models: removedModels, collection: this });\n this.emit('update', { collection: this });\n }\n\n return removedModels;\n }\n\n /**\n * Reset the collection (remove all models)\n * @param {array} models - Optional new models to set\n * @param {object} options - Options\n */\n reset(models = null, options = {}) {\n const previousModels = [...this.models];\n this.models = [];\n\n if (models) {\n this.add(models, { silent: true, ...options });\n }\n\n if (!options.silent) {\n this.emit('reset', {\n collection: this,\n previousModels\n });\n }\n\n return this;\n }\n\n /**\n * Get model by ID\n * @param {string|number} id - Model ID\n * @returns {Model|undefined} Model instance or undefined\n */\n get(id) {\n return this.models.find(model => model.id == id);\n }\n\n /**\n * Get model by index\n * @param {number} index - Model index\n * @returns {Model|undefined} Model instance or undefined\n */\n at(index) {\n return this.models[index];\n }\n\n /**\n * Get collection length\n * @returns {number} Number of models in collection\n */\n length() {\n return this.models.length;\n }\n\n /**\n * Check if collection is empty\n * @returns {boolean} True if collection has no models\n */\n isEmpty() {\n return this.models.length === 0;\n }\n\n /**\n * Find models matching criteria\n * @param {function|object} criteria - Filter function or object with key-value pairs\n * @returns {array} Array of matching models\n */\n where(criteria) {\n if (typeof criteria === 'function') {\n return this.models.filter(criteria);\n }\n\n if (typeof criteria === 'object') {\n return this.models.filter(model => {\n return Object.entries(criteria).every(([key, value]) => {\n return model.get(key) === value;\n });\n });\n }\n\n return [];\n }\n\n /**\n * Find first model matching criteria\n * @param {function|object} criteria - Filter function or object with key-value pairs\n * @returns {Model|undefined} First matching model or undefined\n */\n findWhere(criteria) {\n const results = this.where(criteria);\n return results.length > 0 ? results[0] : undefined;\n }\n\n /**\n * Iterate over each model in the collection\n * @param {function} callback - Function to execute for each model (model, index, collection)\n * @param {object} thisArg - Optional value to use as this when executing callback\n * @returns {Collection} Returns the collection for chaining\n */\n forEach(callback, thisArg) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n\n this.models.forEach((model, index) => {\n callback.call(thisArg, model, index, this);\n });\n\n return this;\n }\n\n /**\n * Sort collection by comparator function\n * @param {function|string} comparator - Comparison function or attribute name\n * @param {object} options - Sort options\n */\n sort(comparator, options = {}) {\n if (typeof comparator === 'string') {\n const attr = comparator;\n comparator = (a, b) => {\n const aVal = a.get(attr);\n const bVal = b.get(attr);\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n };\n }\n\n this.models.sort(comparator);\n\n if (!options.silent) {\n this.trigger('sort', { collection: this });\n }\n\n return this;\n }\n\n /**\n * Convert collection to JSON array\n * @returns {array} Array of model JSON representations\n */\n toJSON() {\n return this.models.map(model => model.toJSON());\n }\n\n /**\n * Cancel any active fetch request\n * @returns {boolean} True if a request was cancelled, false if no active request\n */\n cancel() {\n if (this.currentRequest && this.abortController) {\n console.info('Collection: Manually cancelling active request');\n this.abortController.abort();\n return true;\n }\n return false;\n }\n\n /**\n * Check if collection has an active fetch request\n * @returns {boolean} True if fetch is in progress\n */\n isFetching() {\n return !!this.currentRequest;\n }\n\n /**\n * Build URL for collection endpoint\n * @returns {string} Collection API URL\n */\n buildUrl() {\n return this.endpoint;\n }\n\n // EventEmitter API: on, off, once, emit (from mixin).\n\n /**\n * Iterator support for for...of loops\n */\n *[Symbol.iterator]() {\n for (const model of this.models) {\n yield model;\n }\n }\n\n /**\n * Static method to create collection from array data\n * @param {function} ModelClass - Model class constructor\n * @param {array} data - Array of model data\n * @param {object} options - Collection options\n * @returns {Collection} New collection instance\n */\n static fromArray(ModelClass, data = [], options = {}) {\n const collection = new this(ModelClass, options);\n collection.add(data, { silent: true });\n return collection;\n }\n}\n\nObject.assign(Collection.prototype, EventEmitter);\n\nexport default Collection;\n","/**\n * FormPlugins - Lightweight, pluggable registry for FormView/FormBuilder extensions\n *\n * Goals:\n * - Zero hard dependency on any single extension; core remains stable\n * - Extensions opt-in by calling FormPlugins.register(plugin)\n * - If no plugin is loaded, nothing changes\n *\n * A plugin is a plain object:\n * {\n * id: 'location', // required, unique\n * // Optional: custom field renderers by type\n * fieldTypes: {\n * 'address': (builder, fieldConfig) => stringHTML\n * },\n * // Optional lifecycle hooks\n * onFormBuilderInit(builder) {},\n * onFormViewInit(formView) {},\n * onAfterRender(formView) {},\n * onFieldInit(formView, fieldEl, fieldConfig) {},\n * onFieldChange(formView, fieldName, value) {}\n * }\n *\n * Usage (in an extension):\n * import { FormPlugins } from '@core/forms/FormPlugins.js';\n * const unregister = FormPlugins.register(myPlugin);\n * // later (optional)\n * unregister();\n */\n\nexport class FormPlugins {\n // Registered plugins\n static _plugins = [];\n // Map of field type => { renderer: Function, pluginId: string }\n static _renderers = new Map();\n\n /**\n * Register a plugin. If a plugin with the same id exists, it's replaced.\n * @param {object} plugin - Plugin definition (see header)\n * @returns {Function} unregister function\n */\n static register(plugin) {\n if (!plugin || typeof plugin !== 'object') {\n console.warn('[FormPlugins] register called with invalid plugin:', plugin);\n return () => {};\n }\n if (!plugin.id || typeof plugin.id !== 'string') {\n console.warn('[FormPlugins] plugin must have a unique string \"id\"', plugin);\n return () => {};\n }\n\n // Remove any previous plugin with same id\n this.unregister(plugin.id);\n\n // Register field renderers (if any)\n if (plugin.fieldTypes && typeof plugin.fieldTypes === 'object') {\n Object.entries(plugin.fieldTypes).forEach(([type, renderer]) => {\n if (typeof renderer === 'function') {\n this._renderers.set(type, { renderer, pluginId: plugin.id });\n } else {\n console.warn(`[FormPlugins] renderer for type \"${type}\" is not a function`);\n }\n });\n }\n\n // Store plugin\n this._plugins.push(plugin);\n\n // Return disposer\n return () => this.unregister(plugin.id);\n }\n\n /**\n * Unregister a plugin by id. Removes its field renderers as well.\n * @param {string} id - Plugin id\n */\n static unregister(id) {\n if (!id) return;\n\n // Remove from plugin list\n this._plugins = this._plugins.filter(p => p.id !== id);\n\n // Remove its renderers\n for (const [type, meta] of this._renderers.entries()) {\n if (meta?.pluginId === id) {\n this._renderers.delete(type);\n }\n }\n }\n\n /**\n * Get a custom field renderer by type (if registered).\n * @param {string} type - Field type\n * @returns {(builder: any, fieldConfig: object) => string | null}\n */\n static getRenderer(type) {\n const meta = this._renderers.get(type);\n return meta?.renderer || null;\n }\n\n /**\n * Returns true if a renderer exists for a field type\n */\n static hasRenderer(type) {\n return this._renderers.has(type);\n }\n\n /**\n * Get a shallow copy of registered plugins (read-only use).\n */\n static getPlugins() {\n return [...this._plugins];\n }\n\n // -----------------------------\n // Safe dispatch to plugin hooks\n // -----------------------------\n\n static _invoke(plugin, hook, ...args) {\n const fn = plugin?.[hook];\n if (typeof fn !== 'function') return;\n try {\n return fn.apply(plugin, args);\n } catch (err) {\n console.error(`[FormPlugins] ${hook} error from plugin \"${plugin.id}\":`, err);\n }\n }\n\n /**\n * Called by FormBuilder (e.g., in ctor or initializeTemplates)\n * @param {any} builder\n */\n static onFormBuilderInit(builder) {\n this._plugins.forEach(p => this._invoke(p, 'onFormBuilderInit', builder));\n }\n\n /**\n * Called by FormView constructor or early init\n * @param {any} formView\n */\n static onFormViewInit(formView) {\n this._plugins.forEach(p => this._invoke(p, 'onFormViewInit', formView));\n }\n\n /**\n * Called by FormView.onAfterRender\n * @param {any} formView\n */\n static onFormViewAfterRender(formView) {\n this._plugins.forEach(p => this._invoke(p, 'onAfterRender', formView));\n }\n\n /**\n * Called when a specific field element is initialized (after DOM exists)\n * @param {any} formView\n * @param {HTMLElement} fieldEl\n * @param {object} fieldConfig\n */\n static onFieldInit(formView, fieldEl, fieldConfig) {\n this._plugins.forEach(p => this._invoke(p, 'onFieldInit', formView, fieldEl, fieldConfig));\n }\n\n /**\n * Called when a field value changes (normalized point)\n * @param {any} formView\n * @param {string} fieldName\n * @param {any} value\n */\n static onFieldChange(formView, fieldName, value) {\n this._plugins.forEach(p => this._invoke(p, 'onFieldChange', formView, fieldName, value));\n }\n}\n\nexport default FormPlugins;","/**\n * FormBuilder - Pure HTML form generator for MOJO framework\n * Generates form HTML from field definitions with Bootstrap 5 grid layout support\n * NO event handling, lifecycle, or mounting - pure HTML generation only\n */\n\nimport Mustache from '@core/utils/mustache.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport { FormPlugins } from '@core/forms/FormPlugins.js';\n\nclass FormBuilder {\n constructor(config = {}) {\n this.fields = config.fields || [];\n this.structureOnly = config.structureOnly || false; // New option\n\n // Convert cols to columns for all fields\n this.fields.forEach(field => {\n if (field.cols && !field.columns) {\n field.columns = field.cols;\n delete field.cols;\n } else if (!field.columns) {\n field.columns = 12;\n }\n\n // Handle nested fields in groups\n if (field.type === 'group' && field.fields) {\n field.fields.forEach(groupField => {\n if (groupField.cols && !groupField.columns) {\n groupField.columns = groupField.cols;\n delete groupField.cols;\n } else if (!groupField.columns) {\n groupField.columns = 12;\n }\n });\n }\n });\n\n this.options = {\n formClass: 'needs-validation',\n formMethod: 'POST',\n formAction: '',\n groupClass: 'row mb-3',\n fieldWrapper: '',\n labelClass: 'form-label',\n inputClass: 'form-control',\n errorClass: 'invalid-feedback',\n helpClass: 'form-text',\n submitButton: false,\n resetButton: false,\n ...config.options\n };\n this.buttons = config.buttons || [];\n this.data = config.data || {};\n this.errors = config.errors || {};\n this.initializeTemplates();\n }\n\n /**\n * Initialize field templates\n */\n initializeTemplates() {\n FormPlugins.onFormBuilderInit?.(this);\n this.templates = {\n input: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n {{#showCopy}}\n <div class=\"input-group\">\n <input type=\"{{type}}\" id=\"{{fieldId}}\" name=\"{{name}}\"\n class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n value=\"{{fieldValue}}\" {{#placeholder}}placeholder=\"{{placeholder}}\"{{/placeholder}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}} {{{attrs}}}>\n <button class=\"btn btn-outline-secondary\" type=\"button\" data-action=\"copy-to-clipboard\" data-target=\"{{fieldId}}\" title=\"Copy to clipboard\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n {{/showCopy}}\n {{^showCopy}}\n <input type=\"{{type}}\" id=\"{{fieldId}}\" name=\"{{name}}\"\n class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n value=\"{{fieldValue}}\" {{#placeholder}}placeholder=\"{{placeholder}}\"{{/placeholder}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}} {{{attrs}}}>\n {{/showCopy}}\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n password: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n <div class=\"input-group\">\n <input type=\"password\" id=\"{{fieldId}}\" name=\"{{name}}\"\n class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n value=\"{{fieldValue}}\" {{#placeholder}}placeholder=\"{{placeholder}}\"{{/placeholder}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}}\n data-field-type=\"password\" {{{attrs}}}>\n {{#showToggle}}\n <button type=\"button\" class=\"btn btn-outline-secondary\"\n data-action=\"toggle-password\"\n data-target=\"{{fieldId}}\"\n aria-label=\"Show password\" aria-pressed=\"false\">\n <i class=\"bi bi-eye\"></i>\n </button>\n {{/showToggle}}\n </div>\n {{#strengthMeter}}\n <div class=\"mt-2\">\n <div class=\"progress\" style=\"height: 4px;\">\n <div class=\"progress-bar bg-secondary\" role=\"progressbar\"\n style=\"width: 0%;\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\"\n id=\"{{fieldId}}_strength_bar\"></div>\n </div>\n <small class=\"{{helpClass}}\" id=\"{{fieldId}}_strength_text\">Strength</small>\n </div>\n {{/strengthMeter}}\n {{#capsLockWarning}}\n <div class=\"{{helpClass}} text-warning d-none\" id=\"{{fieldId}}_caps_warning\">\n Caps Lock is on\n </div>\n {{/capsLockWarning}}\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n\n\n textarea: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n {{#showCopy}}\n <div class=\"position-relative\">\n <textarea id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n rows=\"{{rows}}\" {{#placeholder}}placeholder=\"{{placeholder}}\"{{/placeholder}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}} {{{attrs}}}>{{fieldValue}}</textarea>\n <button class=\"btn btn-sm btn-outline-secondary position-absolute top-0 end-0 m-2\" type=\"button\" data-action=\"copy-to-clipboard\" data-target=\"{{fieldId}}\" title=\"Copy to clipboard\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n {{/showCopy}}\n {{^showCopy}}\n <textarea id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n rows=\"{{rows}}\" {{#placeholder}}placeholder=\"{{placeholder}}\"{{/placeholder}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}} {{{attrs}}}>{{fieldValue}}</textarea>\n {{/showCopy}}\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n htmlpreview: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n <div class=\"position-relative\">\n <textarea id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n rows=\"{{rows}}\" {{#placeholder}}placeholder=\"{{placeholder}}\"{{/placeholder}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}}\n data-field-type=\"htmlpreview\" {{{attrs}}}>{{fieldValue}}</textarea>\n <div class=\"position-absolute d-flex gap-1\" style=\"top: 8px; right: 8px; z-index: 10;\">\n {{#showCopy}}\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\"\n data-action=\"copy-to-clipboard\"\n data-target=\"{{fieldId}}\"\n title=\"Copy to clipboard\"\n aria-label=\"Copy to clipboard\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n {{/showCopy}}\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\"\n data-action=\"preview-html\"\n data-target=\"{{fieldId}}\"\n title=\"Preview HTML\"\n aria-label=\"Preview HTML\">\n <i class=\"bi bi-eye\"></i>\n </button>\n </div>\n </div>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n select: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n {{#searchInput}}{{{searchInput}}}{{/searchInput}}\n <select id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#multiple}}multiple{{/multiple}} {{{attrs}}}>\n {{{optionsHTML}}}\n </select>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n checkbox: `\n <div class=\"mojo-form-control\">\n <div class=\"form-check {{fieldClass}}\">\n <input type=\"checkbox\" id=\"{{fieldId}}\" name=\"{{name}}\"\n class=\"form-check-input{{#error}} is-invalid{{/error}}\" value=\"{{value}}\"\n {{#checked}}checked{{/checked}} {{#required}}required{{/required}}\n {{#disabled}}disabled{{/disabled}} {{{attrs}}}>\n <label class=\"form-check-label\" for=\"{{fieldId}}\">{{label}}</label>\n </div>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n </div>\n `,\n\n switch: `\n <div class=\"mojo-form-control\">\n <div class=\"form-check form-switch {{sizeClass}} {{fieldClass}}\">\n <input type=\"checkbox\" id=\"{{fieldId}}\" name=\"{{name}}\" role=\"switch\"\n class=\"form-check-input{{#error}} is-invalid{{/error}}\" value=\"{{value}}\"\n {{#checked}}checked{{/checked}} {{#required}}required{{/required}}\n {{#disabled}}disabled{{/disabled}} data-change-action=\"toggle-switch\"\n data-field=\"{{name}}\" {{{attrs}}}>\n <label class=\"form-check-label\" for=\"{{fieldId}}\">{{label}}</label>\n </div>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n image: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n <div class=\"image-field-container {{containerClass}}\" id=\"{{dropZoneId}}\" style=\"width: {{width}}px; height: {{height}}px;\">\n <input type=\"file\" id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}} d-none{{#error}} is-invalid{{/error}}\"\n accept=\"{{accept}}\" {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n data-change-action=\"image-selected\" data-field=\"{{name}}\" {{{attrs}}}>\n <div class=\"image-drop-zone{{#allowDrop}} droppable{{/allowDrop}}\" data-action=\"click-image-upload\"\n data-field-id=\"{{fieldId}}\" data-field=\"{{name}}\" style=\"width: 100%; height: 100%; cursor: {{cursor}};\">\n {{#imageUrl}}\n <img id=\"{{previewId}}\" src=\"{{imageUrl}}\" alt=\"Preview\" class=\"img-thumbnail w-100 h-100\" style=\"object-fit: cover;\">\n {{#showRemove}}\n <button type=\"button\" class=\"btn btn-sm btn-danger position-absolute top-0 end-0 m-1\"\n data-action=\"remove-image\" data-field-id=\"{{fieldId}}\" data-field=\"{{name}}\" style=\"opacity: 0.8;\">\n <i class=\"bi bi-x\"></i>\n </button>\n {{/showRemove}}\n {{/imageUrl}}\n {{^imageUrl}}\n <div class=\"d-flex flex-column align-items-center justify-content-center h-100 text-muted border border-2 border-dashed\">\n <i class=\"bi bi-image fs-1 mb-2\"></i>\n <small class=\"text-center px-2\">{{placeholderText}}</small>\n </div>\n {{/imageUrl}}\n </div>\n </div>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n range: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}: <span id=\"{{fieldId}}_value\">{{fieldValue}}</span>\n </label>\n {{/label}}\n <input type=\"range\" id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n min=\"{{min}}\" max=\"{{max}}\" step=\"{{step}}\" value=\"{{fieldValue}}\" {{#disabled}}disabled{{/disabled}}\n data-change-action=\"range-changed\" data-target=\"{{fieldId}}_value\" {{{attrs}}}>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n file: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n <input type=\"file\" id=\"{{fieldId}}\" name=\"{{name}}\" class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n accept=\"{{accept}}\" {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#multiple}}multiple{{/multiple}} data-change-action=\"file-selected\" {{{attrs}}}>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n radio: `\n <div class=\"mojo-form-control\">\n {{#label}}<div class=\"{{labelClass}}\">{{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}</div>{{/label}}\n {{#options}}\n <div class=\"form-check\">\n <input type=\"radio\" id=\"{{fieldId}}_{{value}}\" name=\"{{name}}\" value=\"{{value}}\"\n class=\"form-check-input{{#error}} is-invalid{{/error}}\" {{#checked}}checked{{/checked}}\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{{attrs}}}>\n <label class=\"form-check-label\" for=\"{{fieldId}}_{{value}}\">{{text}}</label>\n </div>\n {{/options}}\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `,\n\n button: `\n <button type=\"button\" {{#name}}name=\"{{name}}\"{{/name}} class=\"btn {{fieldClass}}\"\n {{#action}}data-action=\"{{action}}\"{{/action}} {{#disabled}}disabled{{/disabled}} {{{attrs}}}>\n {{label}}\n </button>\n `,\n\n divider: `\n <hr class=\"{{dividerClass}}\">\n `,\n\n html: `\n <div class=\"form-html {{fieldClass}}\">{{{html}}}</div>\n `,\n\n header: `\n <h{{level}} {{#id}}id=\"{{id}}\"{{/id}} class=\"text-primary mb-3 {{fieldClass}}\">{{text}}</h{{level}}>\n `,\n\n hidden: `\n <input type=\"hidden\" id=\"{{fieldId}}\" name=\"{{name}}\" value=\"{{fieldValue}}\">\n `,\n\n checklistdropdown: `\n <div class=\"dropdown\">\n <button class=\"{{buttonClass}}\" type=\"button\" data-bs-toggle=\"dropdown\">\n <i class=\"{{buttonIcon}} me-1\"></i> {{buttonText}}\n </button>\n <div class=\"{{dropdownClass}}\" style=\"min-width: {{minWidth}};\">\n {{#options}}\n <div class=\"form-check\">\n <input class=\"form-check-input\" type=\"checkbox\"\n value=\"{{value}}\"\n id=\"{{id}}\"\n {{#checked}}checked{{/checked}}\n data-change-action=\"update-checklist\"\n data-field=\"{{fieldName}}\">\n <label class=\"form-check-label\" for=\"{{id}}\">\n {{label}}\n </label>\n </div>\n {{/options}}\n <hr class=\"my-2\">\n <button class=\"btn btn-sm btn-primary w-100\" data-action=\"apply-filter\">\n Apply\n </button>\n </div>\n </div>\n `,\n\n buttongroup: `\n <div class=\"btn-group btn-group-{{size}}\" role=\"group\">\n {{#options}}\n <button type=\"button\" class=\"{{buttonClass}} {{#active}}active{{/active}}\"\n {{^action}}\n data-action=\"select-button-option\"\n {{/action}}\n {{#action}}\n data-action='{{action}}'\n {{/action}}\n data-field=\"{{fieldName}}\"\n data-value=\"{{value}}\">\n {{#icon}}<i class=\"{{icon}} me-1\"></i> {{/icon}} {{label}}\n </button>\n {{/options}}\n </div>\n `,\n\n toolbarform: `\n <div class=\"mojo-toolbar-form\">\n <div class=\"row g-2 align-items-center\">\n {{#fields}}\n <div class=\"{{containerClass}}\">\n {{#label}}<label class=\"form-label-sm mb-1\">{{label}}</label>{{/label}}\n {{{fieldHtml}}}\n </div>\n {{/fields}}\n </div>\n </div>\n `,\n\n color: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label for=\"{{fieldId}}\" class=\"{{labelClass}}\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n <div class=\"d-flex align-items-center gap-2\">\n <input type=\"color\" id=\"{{fieldId}}\" name=\"{{name}}\"\n class=\"{{inputClass}}{{#error}} is-invalid{{/error}}\"\n value=\"{{fieldValue}}\"\n {{#required}}required{{/required}} {{#disabled}}disabled{{/disabled}}\n {{#readonly}}readonly{{/readonly}} {{{attrs}}}>\n <button type=\"button\" class=\"btn-sm text-muted border-0 bg-transparent p-1\"\n data-action=\"clear-color\" data-field=\"{{name}}\"\n title=\"Clear color\">\n <i class=\"bi bi-x fs-5\"></i>\n </button>\n </div>\n {{#help}}<div class=\"{{helpClass}}\">{{help}}</div>{{/help}}\n {{#error}}<div class=\"{{errorClass}}\">{{error}}</div>{{/error}}\n </div>\n `\n };\n }\n\n /**\n * Generate complete form HTML\n * @returns {string} Complete form HTML\n */\n buildFormHTML() {\n const fieldsHTML = this.buildFieldsHTML();\n const buttonsHTML = this.buildButtonsHTML();\n\n return `\n <form class=\"${this.options.formClass}\" novalidate>\n ${fieldsHTML}\n ${buttonsHTML}\n </form>\n `;\n }\n\n /**\n * Generate HTML for all fields\n * @returns {string} Fields HTML\n */\n /**\n * Check if field should use auto-sizing columns (class=\"col\")\n * @param {object} field - Field configuration\n * @returns {boolean}\n */\n isAutoSizingField(field) {\n return !field.columns || field.columns === 'auto' || field.columns === '';\n }\n\n buildFieldsHTML() {\n const result = [];\n let i = 0;\n\n while (i < this.fields.length) {\n const field = this.fields[i];\n field.columns = field.columns || field.cols;\n\n if (field.type === 'group') {\n // Collect consecutive groups that should be in a row\n const groupsInRow = [field];\n let totalColumns = field.columns || 12;\n\n // Handle responsive columns - use md breakpoint for calculation, fallback to largest available\n if (typeof totalColumns === 'object' && totalColumns !== null) {\n totalColumns = totalColumns.md || totalColumns.sm || totalColumns.xs || 12;\n }\n\n let j = i + 1;\n\n // Look ahead for more groups that can fit in the same row\n while (j < this.fields.length &&\n this.fields[j].type === 'group' &&\n totalColumns < 12) {\n const nextGroup = this.fields[j];\n let nextColumns = nextGroup.columns || 12;\n\n // Handle responsive columns - use md breakpoint for calculation, fallback to largest available\n if (typeof nextColumns === 'object' && nextColumns !== null) {\n nextColumns = nextColumns.md || nextColumns.sm || nextColumns.xs || 12;\n }\n\n if (totalColumns + nextColumns <= 12) {\n groupsInRow.push(nextGroup);\n totalColumns += nextColumns;\n j++;\n } else {\n break;\n }\n }\n\n // If we have multiple groups or a single group with specific columns, wrap in row\n let shouldWrap = groupsInRow.length > 1;\n\n // Check if single group has specific columns (not full width)\n if (groupsInRow.length === 1 && field.columns) {\n let cols = field.columns;\n if (typeof cols === 'object' && cols !== null) {\n cols = cols.md || cols.sm || cols.xs || 12;\n }\n shouldWrap = shouldWrap || cols < 12;\n }\n\n if (shouldWrap) {\n const groupsHTML = groupsInRow.map(group => this.buildGroupHTML(group)).join('');\n result.push(`<div class=\"row\">${groupsHTML}</div>`);\n } else {\n result.push(this.buildGroupHTML(field));\n }\n\n i = j;\n } else if (field.columns && field.columns < 12) {\n // Collect consecutive fields with columns that should be in a row\n const fieldsInRow = [field];\n let totalColumns = field.columns || 12;\n\n // Handle responsive columns - use md breakpoint for calculation, fallback to largest available\n if (typeof totalColumns === 'object' && totalColumns !== null) {\n totalColumns = totalColumns.md || totalColumns.sm || totalColumns.xs || 12;\n }\n\n let j = i + 1;\n\n // Look ahead for more fields that can fit in the same row\n while (j < this.fields.length &&\n this.fields[j].columns &&\n totalColumns < 12) {\n const nextField = this.fields[j];\n let nextColumns = nextField.columns || 12;\n\n // Handle responsive columns - use md breakpoint for calculation, fallback to largest available\n if (typeof nextColumns === 'object' && nextColumns !== null) {\n nextColumns = nextColumns.md || nextColumns.sm || nextColumns.xs || 12;\n }\n\n if (totalColumns + nextColumns <= 12) {\n fieldsInRow.push(nextField);\n totalColumns += nextColumns;\n j++;\n } else {\n break;\n }\n }\n\n // Wrap fields in a row\n const fieldsHTML = fieldsInRow.map(f => this.buildFieldHTML(f)).join('');\n result.push(`<div class=\"row\">${fieldsHTML}</div>`);\n\n i = j;\n } else if (this.isAutoSizingField(field)) {\n // Collect consecutive auto-sizing fields that should be in a row\n const fieldsInRow = [field];\n let j = i + 1;\n\n // Look ahead for more auto-sizing fields\n while (j < this.fields.length) {\n const nextField = this.fields[j];\n if (this.isAutoSizingField(nextField)) {\n fieldsInRow.push(nextField);\n j++;\n } else {\n break;\n }\n }\n\n // If we have multiple auto-sizing fields, wrap them in a row\n if (fieldsInRow.length > 1) {\n const fieldsHTML = fieldsInRow.map(f => this.buildFieldHTML(f)).join('');\n result.push(`<div class=\"row\">${fieldsHTML}</div>`);\n } else {\n // Single auto-sizing field, still wrap in row for consistency\n result.push(`<div class=\"row\">${this.buildFieldHTML(field)}</div>`);\n }\n\n i = j;\n } else {\n result.push(this.buildFieldHTML(field));\n i++;\n }\n }\n\n return result.join('');\n }\n\n /**\n * Generate HTML for a field group with column layout\n * @param {Object} group - Group configuration\n * @returns {string} Group HTML\n */\n buildGroupHTML(group) {\n const {\n columns = 12,\n title,\n fields = [],\n class: groupClass = '',\n titleClass = 'h6 mb-3',\n responsive = {}\n } = group;\n\n // Build responsive column classes\n let colClasses = [];\n\n // Handle columns as object (responsive) or number (legacy)\n if (typeof columns === 'object' && columns !== null) {\n // New responsive syntax: columns: { xs: 12, sm: 6, md: 4, lg: 3 }\n if (columns.xs) colClasses.push(`col-${columns.xs}`);\n if (columns.sm) colClasses.push(`col-sm-${columns.sm}`);\n if (columns.md) colClasses.push(`col-md-${columns.md}`);\n if (columns.lg) colClasses.push(`col-lg-${columns.lg}`);\n if (columns.xl) colClasses.push(`col-xl-${columns.xl}`);\n if (columns.xxl) colClasses.push(`col-xxl-${columns.xxl}`);\n\n // If no md is specified but xs/sm are, use xs or sm as fallback for md\n if (!columns.md && (columns.xs || columns.sm)) {\n const fallback = columns.sm || columns.xs;\n colClasses.push(`col-md-${fallback}`);\n }\n\n // If no breakpoints specified at all, default to col-md-12\n if (colClasses.length === 0) {\n colClasses.push('col-md-12');\n }\n } else {\n // Legacy syntax: columns: 6\n colClasses.push(`col-md-${columns}`);\n }\n\n // Support legacy responsive property (for backward compatibility)\n if (responsive.xs) colClasses.push(`col-${responsive.xs}`);\n if (responsive.sm) colClasses.push(`col-sm-${responsive.sm}`);\n if (responsive.lg) colClasses.push(`col-lg-${responsive.lg}`);\n if (responsive.xl) colClasses.push(`col-xl-${responsive.xl}`);\n\n const colClass = colClasses.join(' ');\n const fieldsHTML = fields.map(field => {\n if (field.type === 'group') {\n // Nested groups (limited depth)\n return this.buildGroupHTML(field);\n }\n return this.buildFieldHTML(field);\n }).join('');\n\n return `\n <div class=\"${colClass}\">\n <div class=\"mojo-form-group ${groupClass}\">\n ${title ? `<div class=\"${titleClass}\">${this.escapeHtml(title)}</div>` : ''}\n <div class=\"row\">\n ${fieldsHTML}\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Generate HTML for a single field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n buildFieldHTML(field) {\n const { type, columns, class: fieldClass = '' } = field;\n\n let fieldHTML = '';\n // If a plugin registered a custom renderer for this field type, use it\n const customRenderer = (FormPlugins && typeof FormPlugins.getRenderer === 'function')\n ? FormPlugins.getRenderer(type)\n : null;\n if (typeof customRenderer === 'function') {\n try {\n const custom = customRenderer(this, field);\n if (custom !== undefined && custom !== null) {\n fieldHTML = String(custom);\n }\n } catch (err) {\n console.error('FormPlugins custom renderer error:', err);\n }\n }\n if (!fieldHTML) {\n switch (type) {\n case 'text':\n fieldHTML = this.renderTextField(field);\n break;\n case 'email':\n fieldHTML = this.renderEmailField(field);\n break;\n case 'password':\n fieldHTML = this.renderPasswordField(field);\n break;\n case 'number':\n fieldHTML = this.renderNumberField(field);\n break;\n case 'tel':\n fieldHTML = this.renderTelField(field);\n break;\n case 'url':\n fieldHTML = this.renderUrlField(field);\n break;\n case 'search':\n fieldHTML = this.renderSearchField(field);\n break;\n case 'hex':\n fieldHTML = this.renderHexField(field);\n break;\n case 'textarea':\n fieldHTML = this.renderTextareaField(field);\n break;\n case 'htmlpreview':\n fieldHTML = this.renderHtmlPreviewField(field);\n break;\n case 'json':\n fieldHTML = this.renderJsonField(field);\n break;\n case 'select':\n fieldHTML = this.renderSelectField(field);\n break;\n case 'multiselect':\n fieldHTML = this.renderMultiSelectField(field);\n break;\n case 'checkbox':\n fieldHTML = this.renderCheckboxField(field);\n break;\n case 'toggle':\n case 'switch':\n fieldHTML = this.renderSwitchField(field);\n break;\n case 'radio':\n fieldHTML = this.renderRadioField(field);\n break;\n case 'date':\n fieldHTML = this.renderDateField(field);\n break;\n case 'datetime':\n fieldHTML = this.renderDateTimeField(field);\n break;\n case 'time':\n fieldHTML = this.renderTimeField(field);\n break;\n case 'file':\n fieldHTML = this.renderFileField(field);\n break;\n case 'image':\n fieldHTML = this.renderImageField(field);\n break;\n case 'color':\n fieldHTML = this.renderColorField(field);\n break;\n case 'range':\n fieldHTML = this.renderRangeField(field);\n break;\n case 'hidden':\n fieldHTML = this.renderHiddenField(field);\n break;\n case 'button':\n fieldHTML = this.renderButton(field);\n break;\n case 'divider':\n fieldHTML = this.renderDivider(field);\n break;\n case 'html':\n fieldHTML = this.renderHtmlField(field);\n break;\n case 'heading':\n case 'header':\n fieldHTML = this.renderHeaderField(field);\n break;\n case 'tag':\n case 'tags':\n fieldHTML = this.renderTagField(field);\n break;\n case 'collection':\n fieldHTML = this.renderCollectionField(field);\n break;\n case 'collectionmultiselect':\n case 'collection-multiselect':\n fieldHTML = this.renderCollectionMultiSelectField(field);\n break;\n case 'datepicker':\n fieldHTML = this.renderDatePickerField(field);\n break;\n case 'daterange':\n fieldHTML = this.renderDateRangeField(field);\n break;\n case 'checklistdropdown':\n fieldHTML = this.renderChecklistDropdownField(field);\n break;\n case 'buttongroup':\n fieldHTML = this.renderButtonGroupField(field);\n break;\n case 'combo':\n case 'combobox':\n case 'autocomplete':\n fieldHTML = this.renderComboField(field);\n break;\n case 'tabset':\n fieldHTML = this.renderTabsetField(field);\n break;\n default:\n console.warn(`Unknown field type: ${type}`);\n fieldHTML = this.renderTextField(field);\n }\n }\n\n // Wrap field in column - handle auto-sizing columns\n let colClass;\n if (this.isAutoSizingField(field)) {\n colClass = `col ${fieldClass}`.trim();\n } else {\n colClass = `col-${columns} ${fieldClass}`.trim();\n }\n return `<div class=\"${colClass}\">${fieldHTML}</div>`;\n }\n\n /**\n * Generate field ID\n * @param {string} name - Field name\n * @returns {string} Unique field ID\n */\n getFieldId(name) {\n // Replace dots, spaces, and special characters with underscores for valid HTML IDs\n if (!name) {\n return `field_${Math.random().toString(36).substr(2, 9)}`;\n }\n const safeName = name.replace(/[.\\s\\[\\]]/g, '_');\n return `field_${safeName}`;\n }\n\n /**\n * Render text input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderTextField(field) {\n return this.renderInputField(field, 'text');\n }\n\n /**\n * Render email input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderEmailField(field) {\n return this.renderInputField(field, 'email');\n }\n\n /**\n * Render password input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderPasswordField(field) {\n const passwordUsage = field.passwordUsage || 'current';\n const inferredAutocomplete =\n passwordUsage === 'new' || passwordUsage === 'new-password'\n ? 'new-password'\n : 'current-password';\n\n const attributes = {\n ...(field.attributes || {}),\n autocomplete: (field.attributes && field.attributes.autocomplete) || inferredAutocomplete\n };\n\n return this.renderInputField(\n {\n ...field,\n showToggle: field.showToggle !== false, // default to showing the toggle unless explicitly disabled\n attributes\n },\n 'password'\n );\n }\n\n /**\n * Render number input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderNumberField(field) {\n const {\n min,\n max,\n step = 1,\n ...baseField\n } = field;\n\n const attrs = [];\n if (min !== undefined) attrs.push(`min=\"${min}\"`);\n if (max !== undefined) attrs.push(`max=\"${max}\"`);\n if (step !== undefined) attrs.push(`step=\"${step}\"`);\n\n return this.renderInputField({\n ...baseField,\n attributes: { ...baseField.attributes, ...attrs.reduce((obj, attr) => {\n const [key, value] = attr.split('=');\n obj[key] = value.replace(/\"/g, '');\n return obj;\n }, {}) }\n }, 'number');\n }\n\n /**\n * Render tel input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderTelField(field) {\n return this.renderInputField(field, 'tel');\n }\n\n /**\n * Render URL input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderUrlField(field) {\n return this.renderInputField(field, 'url');\n }\n\n /**\n * Render search input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderSearchField(field) {\n const searchField = {\n ...field,\n attributes: {\n 'data-filter': 'live-search',\n 'data-change-action': 'filter-search',\n 'data-filter-debounce': field.debounce || '300',\n ...field.attributes\n }\n };\n return this.renderInputField(searchField, 'search');\n }\n\n /**\n * Render hex input field with validation\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderHexField(field) {\n const {\n hexType = 'color', // 'color', 'string', 'any'\n allowPrefix = true,\n minLength: originalMinLength,\n maxLength: originalMaxLength,\n ...baseField\n } = field;\n\n let pattern, minLength, maxLength, placeholder, help;\n\n switch (hexType) {\n case 'color':\n // Hex color: #RRGGBB or RRGGBB\n pattern = allowPrefix ? '^#?[0-9A-Fa-f]{6}$' : '^[0-9A-Fa-f]{6}$';\n minLength = allowPrefix ? 6 : 6;\n maxLength = allowPrefix ? 7 : 6;\n placeholder = allowPrefix ? '#FF0000' : 'FF0000';\n help = help || 'Enter a valid hex color (e.g., ' + placeholder + ')';\n break;\n\n case 'color-short':\n // Hex color: #RGB or RGB, #RRGGBB or RRGGBB\n pattern = allowPrefix ? '^#?[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$' : '^[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$';\n minLength = allowPrefix ? 4 : 3;\n maxLength = allowPrefix ? 7 : 6;\n placeholder = allowPrefix ? '#F00 or #FF0000' : 'F00 or FF0000';\n help = help || 'Enter a valid hex color (3 or 6 digits)';\n break;\n\n case 'string':\n // General hex string\n pattern = '^[0-9A-Fa-f]+$';\n minLength = originalMinLength || 1;\n maxLength = originalMaxLength || 64;\n placeholder = 'ABCDEF123456';\n help = help || 'Only hexadecimal characters (0-9, A-F) allowed';\n break;\n\n default:\n // Any hex format\n pattern = allowPrefix ? '^#?[0-9A-Fa-f]+$' : '^[0-9A-Fa-f]+$';\n minLength = originalMinLength || 1;\n maxLength = originalMaxLength || 64;\n placeholder = allowPrefix ? '#ABCDEF or ABCDEF' : 'ABCDEF';\n help = help || 'Enter hexadecimal characters only';\n }\n\n const hexField = {\n ...baseField,\n pattern,\n minLength,\n maxLength,\n placeholder: baseField.placeholder || placeholder,\n help: baseField.help || help,\n attributes: {\n 'data-hex-type': hexType,\n 'data-allow-prefix': allowPrefix,\n 'style': 'text-transform: uppercase;',\n ...baseField.attributes\n }\n };\n\n return this.renderInputField(hexField, 'text');\n }\n\n /**\n * Render generic input field\n * @param {Object} field - Field configuration\n * @param {string} type - Input type\n * @returns {string} Field HTML\n */\n renderInputField(field, type = 'text') {\n const {\n name,\n label,\n value = '',\n placeholder = '',\n required = false,\n disabled = false,\n readonly = false,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n type,\n fieldValue: this.escapeHtml(fieldValue),\n label: label ? this.escapeHtml(label) : null,\n placeholder: placeholder ? this.escapeHtml(placeholder) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n required,\n disabled,\n readonly,\n attrs,\n showCopy: !!field.showCopy\n };\n\n if (type === 'password' && (field.showToggle || field.strengthMeter || field.capsLockWarning)) {\n const enhancedContext = {\n ...context,\n showToggle: !!field.showToggle,\n strengthMeter: !!field.strengthMeter,\n capsLockWarning: !!field.capsLockWarning\n };\n return Mustache.render(this.templates.password, enhancedContext);\n }\n if (type === 'password') {\n const enhancedContext = {\n ...context,\n showToggle: field.showToggle !== false,\n strengthMeter: !!field.strengthMeter,\n capsLockWarning: !!field.capsLockWarning\n };\n return Mustache.render(this.templates.password, enhancedContext);\n }\n return Mustache.render(this.templates.input, context);\n }\n\n /**\n * Render textarea field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderTextareaField(field) {\n const {\n name,\n label,\n value = '',\n placeholder = '',\n required = false,\n disabled = false,\n readonly = false,\n rows = 3,\n cols: _cols,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n fieldValue: fieldValue, // Pass raw value to template for correct rendering\n label: label ? this.escapeHtml(label) : null,\n placeholder: placeholder ? this.escapeHtml(placeholder) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n rows: rows || 3,\n required,\n disabled,\n readonly,\n showCopy: !!field.showCopy,\n attrs\n };\n\n return Mustache.render(this.templates.textarea, context);\n }\n\n /**\n * Render HTML preview field (textarea with preview button)\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderHtmlPreviewField(field) {\n const {\n name,\n label,\n value = '',\n placeholder = '',\n required = false,\n disabled = false,\n readonly = false,\n rows = 5,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n fieldValue: fieldValue, // Pass raw value to template for correct rendering\n label: label ? this.escapeHtml(label) : null,\n placeholder: placeholder ? this.escapeHtml(placeholder) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n rows: rows || 5,\n required,\n disabled,\n readonly,\n attrs,\n showCopy: !!field.showCopy\n };\n\n return Mustache.render(this.templates.htmlpreview, context);\n }\n\n /**\n * Render JSON field as a textarea\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderJsonField(field) {\n const {\n name,\n label,\n placeholder = '',\n required = false,\n disabled = false,\n readonly = false,\n rows = 3,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const rawValue = this.getFieldValue(field.name) ?? field.value ?? {};\n let formattedValue = rawValue;\n\n if (typeof rawValue === 'object' && rawValue !== null) {\n try {\n formattedValue = JSON.stringify(rawValue, null, 2);\n } catch (e) {\n formattedValue = \"{}\";\n }\n } else if (typeof rawValue !== 'string') {\n formattedValue = String(rawValue);\n }\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldId = this.getFieldId(name);\n\n const attrs = Object.entries({\n ...attributes,\n 'data-field-type': 'json'\n }).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n fieldValue: formattedValue, // Use the formatted JSON string directly\n label: label ? this.escapeHtml(label) : null,\n placeholder: placeholder ? this.escapeHtml(placeholder) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n rows: rows || 3,\n required,\n disabled,\n readonly,\n attrs\n };\n\n return Mustache.render(this.templates.textarea, context);\n }\n\n\n\n /**\n * Render select field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderSelectField(field) {\n const {\n name,\n label,\n options = [],\n value = '',\n required = false,\n disabled = false,\n multiple = false,\n searchable = false,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || '',\n // Auto-generation parameters\n start = field.start,\n end = field.end,\n step = field.step,\n format = field.format,\n prefix = field.prefix,\n suffix = field.suffix\n } = field;\n\n const inputClass = `form-select ${fieldClass}`.trim();\n const error = this.errors[name];\n const dataValue = this.getFieldValue(name);\n const fieldValue = (dataValue ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n // Generate options automatically if start/end are provided\n let generatedOptions = [...options];\n if (start !== undefined && end !== undefined) {\n const stepValue = step !== undefined ? step : 1;\n const autoOptions = this.generateSelectOptions(start, end, stepValue, { format, prefix, suffix });\n // Append auto-generated options after any manually specified ones\n generatedOptions = [...generatedOptions, ...autoOptions];\n }\n\n let optionsHTML = '';\n if (Array.isArray(generatedOptions)) {\n optionsHTML = generatedOptions.map(option => {\n if (typeof option === 'string') {\n const selected = option === fieldValue ? 'selected' : '';\n return `<option value=\"${this.escapeHtml(option)}\" ${selected}>${this.escapeHtml(option)}</option>`;\n } else if (option && typeof option === 'object') {\n // Use loose equality (==) to handle string/number type coercion\n const selected = option.value == fieldValue ? 'selected' : '';\n return `<option value=\"${this.escapeHtml(option.value)}\" ${selected}>${this.escapeHtml(option.label || option.text || option.value)}</option>`;\n }\n return '';\n }).join('');\n }\n\n const searchInput = searchable ? `\n <input type=\"text\"\n class=\"form-control form-control-sm mb-2\"\n placeholder=\"Search options...\"\n data-filter=\"live-search\"\n data-change-action=\"filter-select-options\"\n data-target=\"${fieldId}\">\n ` : '';\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n label: label ? this.escapeHtml(label) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n searchInput: searchable ? searchInput : null,\n optionsHTML,\n required,\n disabled,\n multiple,\n attrs\n };\n\n return Mustache.render(this.templates.select, context);\n }\n\n /**\n * Render multiselect dropdown field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderMultiSelectField(field) {\n const {\n name,\n label,\n options = [],\n value = [],\n required = false,\n disabled = false,\n maxHeight = 300,\n help = field.helpText || field.help || ''\n } = field;\n \n // Support both placeholder and placeHolder (capital H)\n const placeholder = field.placeholder || field.placeHolder || 'Select...';\n\n const fieldId = this.getFieldId(name);\n const error = this.errors[name];\n // Prioritize field.value (which may be an array set by buildFilterDialogField) over data lookup\n const fieldValue = field.value ?? this.getFieldValue(name) ?? value;\n\n // Create placeholder div that will be replaced with MultiSelectDropdown component\n // Note: MultiSelectDropdown renders its own mojo-form-control wrapper and label,\n // so we don't need to wrap it here like other field types\n return `\n <div class=\"multiselect-placeholder\"\n data-field-name=\"${name}\"\n data-field-type=\"multiselect\"\n data-field-config='${JSON.stringify({\n name,\n value: fieldValue,\n placeholder,\n maxHeight,\n disabled,\n required\n })}'>\n <input type=\"hidden\" name=\"${name}\" value=\"${this.escapeHtml(JSON.stringify(fieldValue))}\">\n <select class=\"form-select${error ? ' is-invalid' : ''}\" \n multiple \n ${disabled ? 'disabled' : ''}\n ${required ? 'required' : ''}>\n ${options.map(opt => {\n const optValue = typeof opt === 'string' ? opt : opt.value;\n const optLabel = typeof opt === 'string' ? opt : (opt.label || opt.value);\n const selected = Array.isArray(fieldValue) && fieldValue.includes(optValue) ? 'selected' : '';\n return `<option value=\"${this.escapeHtml(optValue)}\" ${selected}>${this.escapeHtml(optLabel)}</option>`;\n }).join('')}\n </select>\n <small class=\"form-text text-muted\">This will be enhanced with MultiSelectDropdown component</small>\n </div>\n `;\n }\n\n /**\n * Render checkbox field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderCheckboxField(field) {\n const {\n name,\n label,\n value = false,\n required = false,\n disabled = false,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n const checked = fieldValue === true || fieldValue === 'true' || fieldValue === '1';\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n label: this.escapeHtml(label),\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n value: this.escapeHtml(value),\n fieldClass,\n checked,\n required,\n disabled,\n attrs\n };\n\n return Mustache.render(this.templates.checkbox, context);\n }\n\n /**\n * Render switch field (enhanced checkbox)\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderSwitchField(field) {\n const {\n name,\n label,\n value = false,\n required = false,\n disabled = false,\n size = 'md',\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n const checked = fieldValue === true || fieldValue === 'true' || fieldValue === '1';\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n const sizeClass = size !== 'md' ? `form-switch-${size}` : '';\n\n const context = {\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n label: this.escapeHtml(label),\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n value: this.escapeHtml(value),\n sizeClass,\n fieldClass,\n checked,\n required,\n disabled,\n attrs\n };\n\n return Mustache.render(this.templates.switch, context);\n }\n\n /**\n * Render radio field group\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderRadioField(field) {\n const {\n name,\n label,\n options = [],\n value = '',\n disabled = false,\n inline = false,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n\n let optionsHTML = '';\n if (Array.isArray(options)) {\n optionsHTML = options.map((option, index) => {\n const radioId = `${name}_${index}`;\n const radioValue = typeof option === 'string' ? option : option.value;\n const radioLabel = typeof option === 'string' ? option : option.label || option.text || option.value;\n const checked = radioValue === fieldValue ? 'checked' : '';\n const inlineClass = inline ? 'form-check-inline' : '';\n\n return `\n <div class=\"form-check ${inlineClass}\">\n <input\n type=\"radio\"\n id=\"${radioId}\"\n name=\"${name}\"\n class=\"form-check-input ${error ? 'is-invalid' : ''}\"\n value=\"${this.escapeHtml(radioValue)}\"\n ${checked}\n ${disabled ? 'disabled' : ''}\n\n ${attrs}\n >\n <label class=\"form-check-label\" for=\"${radioId}\">\n ${this.escapeHtml(radioLabel)}\n </label>\n </div>\n `;\n }).join('');\n }\n\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<fieldset>\n <legend class=\"${this.options.labelClass}\">${this.escapeHtml(label)}</legend>\n <div class=\"${fieldClass}\">\n ${optionsHTML}\n </div>\n </fieldset>` : `<div class=\"${fieldClass}\">${optionsHTML}</div>`}\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render date field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderDateField(field) {\n return this.renderInputField(field, 'date');\n }\n\n /**\n * Render datetime field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderDateTimeField(field) {\n return this.renderInputField(field, 'datetime-local');\n }\n\n /**\n * Render time field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderTimeField(field) {\n return this.renderInputField(field, 'time');\n }\n\n /**\n * Render file field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderFileField(field) {\n const {\n name,\n label,\n required = false,\n disabled = false,\n multiple = false,\n accept = '*/*',\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n label: label ? this.escapeHtml(label) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n accept,\n required,\n disabled,\n multiple,\n attrs\n };\n\n return Mustache.render(this.templates.file, context);\n }\n\n /**\n * Render enhanced image field with drag & drop support\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderImageField(field) {\n const {\n name,\n label,\n required = false,\n disabled = false,\n accept = 'image/*',\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || '',\n size = 'md',\n allowDrop = true,\n placeholder = 'Drop image here or click to upload'\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldId = this.getFieldId(name);\n const dropZoneId = `${fieldId}_dropzone`;\n const previewId = `${fieldId}_preview`;\n\n // Size configurations\n const sizeMap = {\n xs: { width: 48, height: 48, containerClass: 'image-field-xs' },\n sm: { width: 96, height: 96, containerClass: 'image-field-sm' },\n md: { width: 150, height: 150, containerClass: 'image-field-md' },\n lg: { width: 200, height: 200, containerClass: 'image-field-lg' },\n xl: { width: 300, height: 300, containerClass: 'image-field-xl' }\n };\n\n const sizeConfig = sizeMap[size] || sizeMap.md;\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n\n // Get current value (could be URL string or file object)\n const currentValue = this.getFieldValue(name);\n const imageUrl = this.extractImageUrl(currentValue, size);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n label: label ? this.escapeHtml(label) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n dropZoneId,\n previewId,\n containerClass: sizeConfig.containerClass,\n width: sizeConfig.width,\n height: sizeConfig.height,\n accept,\n imageUrl,\n placeholderText: disabled ? 'No image' : this.escapeHtml(placeholder),\n cursor: disabled ? 'default' : 'pointer',\n allowDrop,\n showRemove: !disabled,\n required,\n disabled,\n attrs\n };\n\n return Mustache.render(this.templates.image, context);\n }\n\n /**\n * Extract image URL from field value (handles both URL strings and file objects)\n * @param {string|Object} value - Field value\n * @param {string} size - Requested size (xs, sm, md, lg, xl)\n * @returns {string|null} Image URL or null\n */\n extractImageUrl(value, size = 'md') {\n if (!value) return null;\n\n // If it's already a URL string\n if (typeof value === 'string') {\n return value;\n }\n\n // If it's a file object with renditions\n if (typeof value === 'object' && value.url) {\n // Try to get appropriate rendition based on size\n if (value.renditions) {\n const sizeMap = {\n xs: ['thumbnail_sm', 'thumbnail', 'square_sm'],\n sm: ['thumbnail', 'thumbnail_sm', 'square_sm'],\n md: ['thumbnail_md', 'thumbnail', 'thumbnail_lg'],\n lg: ['thumbnail_lg', 'thumbnail_md', 'thumbnail'],\n xl: ['original', 'thumbnail_lg']\n };\n\n const preferredSizes = sizeMap[size] || sizeMap.md;\n\n for (const renditionName of preferredSizes) {\n if (value.renditions[renditionName] && value.renditions[renditionName].url) {\n return value.renditions[renditionName].url;\n }\n }\n }\n\n // Fall back to original URL\n return value.url;\n }\n\n return null;\n }\n\n /**\n * Render color field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderColorField(field) {\n const {\n name,\n label,\n value = '',\n placeholder = '',\n required = false,\n disabled = false,\n readonly = false,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n fieldValue: this.escapeHtml(fieldValue),\n label: label ? this.escapeHtml(label) : null,\n placeholder: placeholder ? this.escapeHtml(placeholder) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n required,\n disabled,\n readonly,\n attrs\n };\n\n return Mustache.render(this.templates.color, context);\n }\n\n /**\n * Render range field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderRangeField(field) {\n const {\n name,\n label,\n min = 0,\n max = 100,\n step = 1,\n value = min,\n disabled = false,\n class: fieldClass = '',\n attributes = {},\n help = field.helpText || field.help || ''\n } = field;\n\n const inputClass = `${this.options.inputClass} ${fieldClass}`.trim();\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n const fieldId = this.getFieldId(name);\n\n const context = {\n labelClass: this.options.labelClass,\n inputClass: inputClass,\n helpClass: this.options.helpClass,\n errorClass: this.options.errorClass,\n fieldId,\n name,\n label: label ? this.escapeHtml(label) : null,\n help: help ? this.escapeHtml(help) : null,\n error: error ? this.escapeHtml(error) : null,\n min,\n max,\n step,\n fieldValue,\n disabled,\n attrs\n };\n\n return Mustache.render(this.templates.range, context);\n }\n\n /**\n * Render hidden field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderHiddenField(field) {\n const { name, value = '' } = field;\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n return `<input type=\"hidden\" name=\"${name}\" value=\"${this.escapeHtml(fieldValue)}\">`;\n }\n\n /**\n * Render button field\n * @param {Object} field - Field configuration\n * @returns {string} Button HTML\n */\n renderButton(field) {\n const {\n name = '',\n label = 'Button',\n type = 'button',\n action = '',\n class: fieldClass = 'btn-secondary',\n disabled = false,\n attributes = {}\n } = field;\n\n // Auto-assign actions for submit/reset types\n let buttonAction = action;\n if (!buttonAction) {\n if (type === 'submit') {\n buttonAction = 'submit-form';\n } else if (type === 'reset') {\n buttonAction = 'reset-form';\n }\n }\n\n const attrs = Object.entries(attributes).map(([key, val]) => `${key}=\"${this.escapeHtml(val)}\"`).join(' ');\n\n return `\n <button\n type=\"button\"\n ${name ? `name=\"${name}\"` : ''}\n class=\"btn ${fieldClass}\"\n ${buttonAction ? `data-action=\"${buttonAction}\"` : ''}\n ${disabled ? 'disabled' : ''}\n ${attrs}\n >\n ${this.escapeHtml(label)}\n </button>\n `;\n }\n\n /**\n * Render divider field\n * @param {Object} field - Field configuration\n * @returns {string} Divider HTML\n */\n renderDivider(field) {\n const { label = '', class: fieldClass = '' } = field;\n\n return `\n <div class=\"form-divider ${fieldClass}\">\n <hr>\n ${label ? `<div class=\"form-divider-label\">${this.escapeHtml(label)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render HTML field\n * @param {Object} field - Field configuration\n * @returns {string} HTML content\n */\n renderHtmlField(field) {\n const { html = '', class: fieldClass = '' } = field;\n\n return `\n <div class=\"form-html ${fieldClass}\">\n ${html}\n </div>\n `;\n }\n\n /**\n * Render header field\n * @param {Object} field - Field configuration\n * @returns {string} Header HTML\n */\n renderHeaderField(field) {\n const {\n text = '',\n level = 3,\n class: fieldClass = '',\n id = ''\n } = field;\n\n const headerLevel = Math.max(1, Math.min(6, parseInt(level)));\n const headerId = id ? ` id=\"${this.escapeHtml(id)}\"` : '';\n const headerClass = fieldClass ? ` class=\"${this.escapeHtml(fieldClass)}\"` : '';\n\n return `<h${headerLevel}${headerId}${headerClass}>${this.escapeHtml(text)}</h${headerLevel}>`;\n }\n\n /**\n * Generate buttons HTML\n * @returns {string} Buttons HTML\n */\n buildButtonsHTML() {\n if (!this.options.submitButton && !this.options.resetButton && !this.buttons.length) {\n return '';\n }\n\n let buttonsHTML = '';\n\n // Custom buttons\n this.buttons.forEach(button => {\n buttonsHTML += this.renderButton(button) + ' ';\n });\n\n // Default submit button\n if (this.options.submitButton) {\n let submitLabel = 'Submit';\n if (typeof this.options.submitButton === 'string') {\n submitLabel = this.options.submitButton;\n } else if (this.options.submitButton === true) {\n submitLabel = 'Submit';\n }\n buttonsHTML += `<button type=\"submit\" class=\"btn btn-primary me-2\" data-action=\"submit-form\">${submitLabel}</button>`;\n }\n\n // Default reset button\n if (this.options.resetButton) {\n let resetLabel = 'Reset';\n if (typeof this.options.resetButton === 'string') {\n resetLabel = this.options.resetButton;\n } else if (this.options.resetButton === true) {\n resetLabel = 'Reset';\n }\n buttonsHTML += `<button type=\"button\" class=\"btn btn-secondary\" data-action=\"reset-form\">${resetLabel}</button>`;\n }\n\n return buttonsHTML ? `\n <div class=\"form-actions mt-3\">\n ${buttonsHTML}\n </div>\n ` : '';\n }\n\n /**\n * Get field value from data\n * @param {string} name - Field name\n * @returns {*} Field value\n */\n getFieldValue(name) {\n // If in structure-only mode, return empty value\n if (this.structureOnly) {\n return '';\n }\n const value = MOJOUtils.getContextData(this.data, name);\n return value;\n }\n\n /**\n * Render tag input field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderTagField(field) {\n const {\n name,\n label,\n value = '',\n placeholder = 'Add tags...',\n required = false,\n disabled = false,\n readonly = false,\n maxTags = 50,\n allowDuplicates = false,\n separator = ',',\n help = field.helpText || field.help || ''\n } = field;\n\n const fieldId = this.getFieldId(name);\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<label for=\"${fieldId}\" class=\"${this.options.labelClass}\">${this.escapeHtml(label)}${required ? '<span class=\"text-danger\">*</span>' : ''}</label>` : ''}\n <div class=\"tag-input-placeholder\"\n data-field-name=\"${name}\"\n data-field-type=\"tag\"\n data-field-config='${JSON.stringify({\n name,\n value: fieldValue,\n placeholder,\n maxTags,\n allowDuplicates,\n separator,\n disabled,\n readonly,\n required\n })}'>\n <input type=\"text\"\n id=\"${fieldId}\"\n name=\"${name}_display\"\n class=\"${this.options.inputClass}${error ? ' is-invalid' : ''}\"\n placeholder=\"${this.escapeHtml(placeholder)}\"\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n\n <input type=\"hidden\" name=\"${name}\" value=\"${this.escapeHtml(fieldValue)}\">\n <small class=\"form-text text-muted\">This will be enhanced with TagInput component</small>\n </div>\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render collection select field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderCollectionField(field) {\n const {\n name,\n label,\n value = '',\n placeholder = 'Search...',\n required = false,\n disabled = false,\n readonly = false,\n Collection: _Collection,\n labelField = 'name',\n valueField = 'id',\n maxItems = 10,\n emptyFetch = false,\n debounceMs = 300,\n requiresActiveGroup = false,\n help = field.helpText || field.help || ''\n } = field;\n\n const fieldId = this.getFieldId(name);\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<label for=\"${fieldId}\" class=\"${this.options.labelClass}\">${this.escapeHtml(label)}${required ? '<span class=\"text-danger\">*</span>' : ''}</label>` : ''}\n <div class=\"collection-select-placeholder\"\n data-field-name=\"${name}\"\n data-field-type=\"collection\"\n data-field-config='${JSON.stringify({\n name,\n value: fieldValue,\n placeholder,\n labelField,\n valueField,\n maxItems,\n emptyFetch,\n debounceMs,\n disabled,\n readonly,\n required,\n requiresActiveGroup\n })}'>\n <input type=\"text\"\n id=\"${fieldId}\"\n name=\"${name}_display\"\n class=\"${this.options.inputClass}${error ? ' is-invalid' : ''}\"\n placeholder=\"${this.escapeHtml(placeholder)}\"\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n\n <input type=\"hidden\" name=\"${name}\" value=\"${this.escapeHtml(fieldValue)}\">\n <small class=\"form-text text-muted\">This will be enhanced with CollectionSelect component</small>\n </div>\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render collection multiselect field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderCollectionMultiSelectField(field) {\n const {\n name,\n label,\n value = [],\n required = false,\n disabled = false,\n Collection: _Collection,\n collectionParams = {},\n labelField = 'name',\n valueField = 'id',\n excludeIds = [],\n ignoreIds = [],\n size = 8,\n maxHeight = null,\n showSelectAll = true,\n enableSearch = false,\n searchPlaceholder = 'Search...',\n searchDebounce = 400,\n requiresActiveGroup = false,\n help = field.helpText || field.help || ''\n } = field;\n\n const fieldId = this.getFieldId(name);\n const error = this.errors[name];\n const fieldValue = this.getFieldValue(name) ?? value;\n\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<label class=\"${this.options.labelClass}\">${this.escapeHtml(label)}${required ? '<span class=\"text-danger\">*</span>' : ''}</label>` : ''}\n <div class=\"collection-multiselect-placeholder\"\n data-field-name=\"${name}\"\n data-field-type=\"collectionmultiselect\"\n data-field-config='${JSON.stringify({\n name,\n value: fieldValue,\n labelField,\n valueField,\n excludeIds,\n ignoreIds,\n size,\n maxHeight,\n showSelectAll,\n enableSearch,\n searchPlaceholder,\n searchDebounce,\n disabled,\n required,\n requiresActiveGroup\n })}'>\n <input type=\"hidden\" name=\"${name}\" value=\"${this.escapeHtml(JSON.stringify(fieldValue))}\">\n <small class=\"form-text text-muted\">This will be enhanced with CollectionMultiSelect component</small>\n </div>\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render enhanced date picker field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderDatePickerField(field) {\n const {\n name,\n label,\n value = '',\n placeholder = 'Select date...',\n required = false,\n disabled = false,\n readonly = false,\n min = null,\n max = null,\n format = 'YYYY-MM-DD',\n displayFormat = 'MMM DD, YYYY',\n help = field.helpText || field.help || ''\n } = field;\n\n const fieldId = this.getFieldId(name);\n const error = this.errors[name];\n const fieldValue = (this.getFieldValue(name) ?? value);\n\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<label for=\"${fieldId}\" class=\"${this.options.labelClass}\">${this.escapeHtml(label)}${required ? '<span class=\"text-danger\">*</span>' : ''}</label>` : ''}\n <div class=\"date-picker-placeholder\"\n data-field-name=\"${name}\"\n data-field-type=\"datepicker\"\n data-field-config='${JSON.stringify({\n name,\n value: fieldValue,\n placeholder,\n min,\n max,\n format,\n displayFormat,\n disabled,\n readonly,\n required\n })}'>\n <input type=\"date\"\n id=\"${fieldId}\"\n name=\"${name}\"\n class=\"${this.options.inputClass}${error ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(fieldValue)}\"\n placeholder=\"${this.escapeHtml(placeholder)}\"\n ${min ? `min=\"${min}\"` : ''}\n ${max ? `max=\"${max}\"` : ''}\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n ${required ? 'required' : ''}\n\n <small class=\"form-text text-muted\">This will be enhanced with Easepick DatePicker</small>\n </div>\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render date range picker field\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderDateRangeField(field) {\n const {\n name,\n startName,\n endName,\n fieldName,\n label,\n startDate = '',\n endDate = '',\n placeholder = 'Select date range...',\n required = false,\n disabled = false,\n readonly = false,\n min = null,\n max = null,\n format = 'YYYY-MM-DD',\n displayFormat = 'MMM DD, YYYY',\n outputFormat = 'date',\n separator = ' - ',\n help = field.helpText || field.help || ''\n } = field;\n\n const fieldId = this.getFieldId(name || startName || 'daterange');\n const error = this.errors[name];\n const startFieldName = startName || (name ? name + '_start' : '');\n const endFieldName = endName || (name ? name + '_end' : '');\n const startValue = this.getFieldValue(startFieldName) || startDate;\n const endValue = this.getFieldValue(endFieldName) || endDate;\n\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<label for=\"${fieldId}\" class=\"${this.options.labelClass}\">${this.escapeHtml(label)}${required ? '<span class=\"text-danger\">*</span>' : ''}</label>` : ''}\n <div class=\"date-range-picker-placeholder\"\n data-field-name=\"${name || startName || 'daterange'}\"\n data-field-type=\"daterange\"\n data-field-config='${JSON.stringify({\n name,\n startName,\n endName,\n fieldName,\n startDate: startValue,\n endDate: endValue,\n placeholder,\n min,\n max,\n format,\n displayFormat,\n outputFormat,\n separator,\n disabled,\n readonly,\n required\n })}'>\n <div class=\"row g-2\">\n <div class=\"col\">\n <input type=\"date\"\n id=\"${fieldId}_start\"\n name=\"${name}_start\"\n class=\"${this.options.inputClass}${error ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(startValue)}\"\n placeholder=\"Start date...\"\n ${min ? `min=\"${min}\"` : ''}\n ${max ? `max=\"${max}\"` : ''}\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n ${required ? 'required' : ''}\n\n </div>\n <div class=\"col-auto d-flex align-items-center\">\n <span class=\"text-muted\">${this.escapeHtml(separator.trim())}</span>\n </div>\n <div class=\"col\">\n <input type=\"date\"\n id=\"${fieldId}_end\"\n name=\"${name}_end\"\n class=\"${this.options.inputClass}${error ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(endValue)}\"\n placeholder=\"End date...\"\n ${min ? `min=\"${min}\"` : ''}\n ${max ? `max=\"${max}\"` : ''}\n ${disabled ? 'disabled' : ''}\n ${readonly ? 'readonly' : ''}\n ${required ? 'required' : ''}\n\n </div>\n </div>\n <small class=\"form-text text-muted\">This will be enhanced with Easepick DateRangePicker</small>\n </div>\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render checklistdropdown field\n * @param {Object} field - Field configuration\n * @returns {string} Rendered HTML\n */\n renderChecklistDropdownField(field) {\n const fieldId = this.getFieldId(field.name);\n const selectedValues = (this.getFieldValue(field.name) ?? []);\n\n // Prepare data for Mustache template\n const templateData = {\n fieldId,\n fieldName: field.name,\n buttonText: field.buttonText || 'Select Options',\n buttonIcon: field.buttonIcon || 'bi-chevron-down',\n buttonClass: field.buttonClass || 'btn btn-outline-secondary btn-sm dropdown-toggle',\n dropdownClass: field.dropdownClass || 'dropdown-menu p-2',\n minWidth: field.minWidth || '200px',\n options: field.options.map(option => ({\n value: option.value,\n label: option.label,\n id: `${field.name}-${option.value}`,\n checked: selectedValues.includes(option.value)\n }))\n };\n\n return Mustache.render(this.templates.checklistdropdown, templateData);\n }\n\n /**\n * Render buttongroup field\n * @param {Object} field - Field configuration\n * @returns {string} Rendered HTML\n */\n renderButtonGroupField(field) {\n const fieldId = this.getFieldId(field.name);\n const selectedValue = (this.getFieldValue(field.name) ?? field.value);\n\n // Prepare data for Mustache template\n const templateData = {\n fieldId,\n fieldName: field.name,\n size: field.size || 'sm',\n variant: field.variant || 'outline-primary',\n options: field.options.map(option => ({\n value: option.value,\n label: option.label,\n action: option.action,\n active: option.value === selectedValue,\n buttonClass: this.getButtonClass(option.value === selectedValue, field.variant)\n }))\n };\n\n return Mustache.render(this.templates.buttongroup, templateData);\n }\n\n /**\n * Get button class based on active state and variant\n * @param {boolean} isActive - Whether button is active\n * @param {string} variant - Button variant\n * @returns {string} Button class\n */\n getButtonClass(isActive, variant = 'outline-primary') {\n if (isActive) {\n // Remove 'outline-' prefix for active buttons\n const activeVariant = variant.replace('outline-', '');\n return `btn btn-${activeVariant}`;\n }\n return `btn btn-${variant}`;\n }\n\n /**\n * Render combo input field (editable select/autocomplete)\n * @param {Object} field - Field configuration\n * @returns {string} Field HTML\n */\n renderComboField(field) {\n const {\n name,\n label,\n value = '',\n required = false,\n disabled = false,\n maxHeight = 300,\n help = field.helpText || field.help || ''\n } = field;\n \n // Support both placeholder and placeHolder (capital H)\n const placeholder = field.placeholder || field.placeHolder || 'Type or select...';\n const allowCustom = field.allowCustom !== false; // Default: true\n\n const fieldId = this.getFieldId(name);\n const error = this.errors[name];\n // Prioritize field.value over data lookup\n const fieldValue = field.value ?? this.getFieldValue(name) ?? value;\n\n // Create placeholder div that will be replaced with ComboBox component\n return `\n <div class=\"mojo-form-control\">\n ${label ? `<label class=\"${this.options.labelClass}\">${this.escapeHtml(label)}${required ? '<span class=\"text-danger\">*</span>' : ''}</label>` : ''}\n <div class=\"combobox-placeholder\"\n data-field-name=\"${name}\"\n data-field-type=\"combobox\"\n data-field-config='${JSON.stringify({\n name,\n value: fieldValue,\n placeholder,\n maxHeight,\n allowCustom,\n disabled,\n required\n })}'>\n <input type=\"text\" \n class=\"form-control${error ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(fieldValue)}\"\n placeholder=\"${this.escapeHtml(placeholder)}\"\n ${disabled ? 'disabled' : ''}\n ${required ? 'required' : ''}>\n <small class=\"form-text text-muted\">This will be enhanced with ComboBox component</small>\n </div>\n ${help ? `<div class=\"${this.options.helpClass}\">${this.escapeHtml(help)}</div>` : ''}\n ${error ? `<div class=\"${this.options.errorClass}\">${this.escapeHtml(error)}</div>` : ''}\n </div>\n `;\n }\n\n /**\n * Render a tabset field consisting of Bootstrap nav tabs and tab panes.\n * Each pane's fields are rendered using existing field rendering,\n * wrapped in a row so column layouts work as expected.\n * @param {Object} field - Tabset configuration\n * @param {Array} field.tabs - Array of tabs: [{ label, fields }]\n * @param {string} [field.name] - Optional name used to generate stable IDs\n * @param {string} [field.navClass] - CSS classes for the tabs nav\n * @param {string} [field.contentClass] - CSS classes for the tab content container\n * @returns {string} HTML\n */\n renderTabsetField(field) {\n const {\n tabs = [],\n name = `tabset-${Date.now()}`,\n navClass = 'nav nav-tabs mb-3',\n contentClass = 'tab-content'\n } = field;\n\n const safe = String(name).toLowerCase().replace(/[^a-z0-9]/g, '-');\n\n const nav = tabs.map((t, i) => {\n const id = `${safe}-pane-${i}`;\n const isActive = i === 0;\n return `\n <li class=\"nav-item\" role=\"presentation\">\n <button class=\"nav-link ${isActive ? 'active' : ''}\"\n id=\"${id}-tab\"\n data-bs-toggle=\"tab\"\n data-bs-target=\"#${id}\"\n type=\"button\"\n role=\"tab\"\n aria-controls=\"${id}\"\n aria-selected=\"${isActive}\">\n ${this.escapeHtml(t.label || `Tab ${i + 1}`)}\n </button>\n </li>\n `;\n }).join('');\n\n const panes = tabs.map((t, i) => {\n const id = `${safe}-pane-${i}`;\n const isActive = i === 0;\n \n // Handle groups and regular fields properly\n const fieldsHTML = (t.fields || []).map(f => {\n if (f.type === 'group') {\n return this.buildGroupHTML(f);\n }\n return this.buildFieldHTML(f);\n }).join('');\n \n return `\n <div class=\"tab-pane fade ${isActive ? 'show active' : ''}\"\n id=\"${id}\"\n role=\"tabpanel\"\n aria-labelledby=\"${id}-tab\"\n data-tab-index=\"${i}\">\n <div class=\"row\">\n ${fieldsHTML}\n </div>\n </div>\n `;\n }).join('');\n\n return `\n <div class=\"mojo-form-tabset\">\n <ul class=\"${navClass}\" role=\"tablist\">\n ${nav}\n </ul>\n <div class=\"${contentClass}\">\n ${panes}\n </div>\n </div>\n `;\n }\n\n /**\n * Generate select options automatically from numeric range or patterns\n * Supports multiple generation modes:\n * - Numeric ranges: start, end, step\n * - Formatting: format function, prefix, suffix\n *\n * @param {number} start - Start value (inclusive)\n * @param {number} end - End value (inclusive)\n * @param {number} step - Step increment (default: 1)\n * @param {Object} options - Additional options\n * @param {Function|string} options.format - Format function or preset ('padded', 'ordinal')\n * @param {string} options.prefix - Prefix for label\n * @param {string} options.suffix - Suffix for label\n * @returns {Array} Array of option objects {value, label}\n *\n * @example\n * // Hours 1-24\n * generateSelectOptions(1, 24, 1)\n *\n * // Minutes in 15-min increments with padding\n * generateSelectOptions(0, 45, 15, { format: 'padded' })\n *\n * // Days with ordinal suffix\n * generateSelectOptions(1, 31, 1, { format: 'ordinal' })\n *\n * // Years with prefix\n * generateSelectOptions(2020, 2030, 1, { prefix: 'Year ' })\n *\n * // Percentages\n * generateSelectOptions(0, 100, 10, { suffix: '%' })\n *\n * // Custom formatter\n * generateSelectOptions(1, 12, 1, {\n * format: (v) => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n * 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][v-1]\n * })\n */\n generateSelectOptions(start, end, step = 1, options = {}) {\n const { format, prefix = '', suffix = '' } = options;\n const results = [];\n\n // Determine direction (ascending or descending)\n const increment = start <= end ? Math.abs(step) : -Math.abs(step);\n\n // Generate options\n for (let i = start; start <= end ? i <= end : i >= end; i += increment) {\n let label = String(i);\n\n // Apply formatting\n if (typeof format === 'function') {\n label = format(i);\n } else if (format === 'padded' || format === 'pad') {\n // Pad with zeros to match end value length\n const maxLength = String(Math.max(Math.abs(start), Math.abs(end))).length;\n label = String(i).padStart(maxLength, '0');\n } else if (format === 'ordinal') {\n label = this.formatOrdinal(i);\n }\n\n // Apply prefix/suffix\n label = `${prefix}${label}${suffix}`;\n\n results.push({\n value: i,\n label: label\n });\n }\n\n return results;\n }\n\n /**\n * Format number with ordinal suffix (1st, 2nd, 3rd, etc.)\n * @param {number} num - Number to format\n * @returns {string} Formatted ordinal string\n */\n formatOrdinal(num) {\n const j = num % 10;\n const k = num % 100;\n\n if (j === 1 && k !== 11) return num + 'st';\n if (j === 2 && k !== 12) return num + 'nd';\n if (j === 3 && k !== 13) return num + 'rd';\n return num + 'th';\n }\n\n /**\n * Escape HTML characters to prevent XSS\n * @param {string} str - String to escape\n * @returns {string} Escaped string\n */\n escapeHtml(str) {\n if (str == null) return '';\n const div = document.createElement('div');\n div.textContent = String(str);\n return div.innerHTML;\n }\n}\n\n// Export for use in MOJO framework\nexport { FormBuilder };\nexport default FormBuilder;\n","/**\n * FileDropMixin - Adds drag and drop file functionality to Views\n * Provides configurable file drop handling with validation and callbacks\n */\n\nconst FileDropMixin = {\n /**\n * Enable drag and drop file functionality on this view\n * @param {Object} config - Configuration options\n */\n enableFileDrop(config = {}) {\n // Store configuration\n this._fileDropConfig = {\n acceptedTypes: config.acceptedTypes || ['*/*'],\n maxFileSize: config.maxFileSize || 10 * 1024 * 1024, // 10MB\n dropZoneSelector: config.dropZoneSelector || null, // defaults to view element\n visualFeedback: config.visualFeedback !== false, // default true\n multiple: config.multiple || false,\n validateOnDrop: config.validateOnDrop !== false, // default true\n dragOverClass: config.dragOverClass || 'drag-over',\n dragActiveClass: config.dragActiveClass || 'drag-active'\n };\n\n // Initialize drag/drop state\n this._fileDropState = {\n isDragActive: false,\n dragCounter: 0\n };\n\n // Bind event handlers\n this._boundFileDropHandlers = {\n dragEnter: this._onFileDropDragEnter.bind(this),\n dragOver: this._onFileDropDragOver.bind(this),\n dragLeave: this._onFileDropDragLeave.bind(this),\n drop: this._onFileDropDrop.bind(this),\n preventDefault: this._onFileDropPreventDefault.bind(this)\n };\n\n // Set up event listeners after render\n if (this.element) {\n this._setupFileDropListeners();\n } else {\n // If not rendered yet, set up in onAfterRender\n const originalOnAfterRender = this.onAfterRender.bind(this);\n this.onAfterRender = async () => {\n await originalOnAfterRender();\n this._setupFileDropListeners();\n };\n }\n\n // Hook into cleanup\n const originalOnBeforeDestroy = this.onBeforeDestroy.bind(this);\n this.onBeforeDestroy = async () => {\n this._cleanupFileDropListeners();\n await originalOnBeforeDestroy();\n };\n },\n\n /**\n * Set up drag and drop event listeners\n * @private\n */\n _setupFileDropListeners() {\n if (!this._fileDropConfig) return;\n\n // Get drop zone element\n const dropZone = this._getFileDropZone();\n if (!dropZone) {\n console.warn('FileDropMixin: Drop zone not found');\n return;\n }\n\n this._fileDropZone = dropZone;\n\n // Add event listeners to drop zone\n dropZone.addEventListener('dragenter', this._boundFileDropHandlers.dragEnter);\n dropZone.addEventListener('dragover', this._boundFileDropHandlers.dragOver);\n dropZone.addEventListener('dragleave', this._boundFileDropHandlers.dragLeave);\n dropZone.addEventListener('drop', this._boundFileDropHandlers.drop);\n\n // Prevent default drag behaviors on document to avoid browser's default file handling\n ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {\n document.addEventListener(eventName, this._boundFileDropHandlers.preventDefault);\n });\n },\n\n /**\n * Clean up drag and drop event listeners\n * @private\n */\n _cleanupFileDropListeners() {\n if (!this._boundFileDropHandlers) return;\n\n // Remove from drop zone\n if (this._fileDropZone) {\n this._fileDropZone.removeEventListener('dragenter', this._boundFileDropHandlers.dragEnter);\n this._fileDropZone.removeEventListener('dragover', this._boundFileDropHandlers.dragOver);\n this._fileDropZone.removeEventListener('dragleave', this._boundFileDropHandlers.dragLeave);\n this._fileDropZone.removeEventListener('drop', this._boundFileDropHandlers.drop);\n }\n\n // Remove from document\n ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {\n document.removeEventListener(eventName, this._boundFileDropHandlers.preventDefault);\n });\n\n // Clear references\n this._fileDropZone = null;\n this._boundFileDropHandlers = null;\n this._fileDropConfig = null;\n this._fileDropState = null;\n },\n\n /**\n * Get the drop zone element\n * @returns {Element} The drop zone element\n * @private\n */\n _getFileDropZone() {\n if (this._fileDropConfig.dropZoneSelector) {\n return this.element.querySelector(this._fileDropConfig.dropZoneSelector);\n }\n return this.element;\n },\n\n /**\n * Prevent default drag behaviors\n * @param {Event} e - Drag event\n * @private\n */\n _onFileDropPreventDefault(e) {\n e.preventDefault();\n e.stopPropagation();\n },\n\n /**\n * Handle drag enter event\n * @param {DragEvent} e - Drag event\n * @private\n */\n _onFileDropDragEnter(e) {\n this._onFileDropPreventDefault(e);\n \n this._fileDropState.dragCounter++;\n \n if (!this._fileDropState.isDragActive) {\n this._fileDropState.isDragActive = true;\n this._applyFileDropVisualFeedback(true);\n }\n },\n\n /**\n * Handle drag over event\n * @param {DragEvent} e - Drag event\n * @private\n */\n _onFileDropDragOver(e) {\n this._onFileDropPreventDefault(e);\n e.dataTransfer.dropEffect = 'copy';\n },\n\n /**\n * Handle drag leave event\n * @param {DragEvent} e - Drag event\n * @private\n */\n _onFileDropDragLeave(e) {\n this._onFileDropPreventDefault(e);\n \n this._fileDropState.dragCounter--;\n \n if (this._fileDropState.dragCounter <= 0) {\n this._fileDropState.isDragActive = false;\n this._fileDropState.dragCounter = 0;\n this._applyFileDropVisualFeedback(false);\n }\n },\n\n /**\n * Handle drop event\n * @param {DragEvent} e - Drag event\n * @private\n */\n async _onFileDropDrop(e) {\n this._onFileDropPreventDefault(e);\n \n // Reset drag state\n this._fileDropState.isDragActive = false;\n this._fileDropState.dragCounter = 0;\n this._applyFileDropVisualFeedback(false);\n \n // Get files from drop\n const files = Array.from(e.dataTransfer.files);\n \n if (files.length === 0) return;\n \n // Handle single vs multiple files\n const filesToProcess = this._fileDropConfig.multiple ? files : [files[0]];\n \n // Validate files if enabled\n let validation = { valid: true, errors: [] };\n if (this._fileDropConfig.validateOnDrop) {\n validation = this._validateFileDropFiles(filesToProcess);\n \n if (!validation.valid) {\n if (typeof this.onFileDropError === 'function') {\n await this.onFileDropError(new Error(validation.errors.join(', ')), e, filesToProcess);\n }\n return;\n }\n }\n \n // Call the view's onFileDrop method\n if (typeof this.onFileDrop === 'function') {\n try {\n await this.onFileDrop(filesToProcess, e, validation);\n } catch (error) {\n if (typeof this.onFileDropError === 'function') {\n await this.onFileDropError(error, e, filesToProcess);\n } else {\n console.error('FileDropMixin: Error in onFileDrop callback:', error);\n }\n }\n } else {\n console.warn('FileDropMixin: No onFileDrop method found on view');\n }\n },\n\n /**\n * Apply visual feedback during drag operations\n * @param {boolean} isDragActive - Whether drag is active\n * @private\n */\n _applyFileDropVisualFeedback(isDragActive) {\n if (!this._fileDropConfig.visualFeedback || !this._fileDropZone) return;\n \n const { dragOverClass, dragActiveClass } = this._fileDropConfig;\n \n if (isDragActive) {\n this._fileDropZone.classList.add(dragOverClass, dragActiveClass);\n } else {\n this._fileDropZone.classList.remove(dragOverClass, dragActiveClass);\n }\n },\n\n /**\n * Validate dropped files\n * @param {File[]} files - Files to validate\n * @returns {Object} Validation result\n * @private\n */\n _validateFileDropFiles(files) {\n const errors = [];\n const config = this._fileDropConfig;\n \n for (const file of files) {\n // Check file type\n if (!this._isFileDropTypeAccepted(file.type)) {\n errors.push(`File type \"${file.type}\" is not accepted for file \"${file.name}\"`);\n continue;\n }\n \n // Check file size\n if (file.size > config.maxFileSize) {\n errors.push(`File \"${file.name}\" (${this._formatFileDropSize(file.size)}) exceeds maximum size (${this._formatFileDropSize(config.maxFileSize)})`);\n }\n }\n \n return {\n valid: errors.length === 0,\n errors\n };\n },\n\n /**\n * Check if file type is accepted\n * @param {string} fileType - MIME type of the file\n * @returns {boolean} Whether the file type is accepted\n * @private\n */\n _isFileDropTypeAccepted(fileType) {\n const { acceptedTypes } = this._fileDropConfig;\n \n // Allow all types if wildcard\n if (acceptedTypes.includes('*/*')) return true;\n \n return acceptedTypes.some(acceptedType => {\n // Exact match\n if (acceptedType === fileType) return true;\n \n // Wildcard match (e.g., \"image/*\")\n if (acceptedType.endsWith('/*')) {\n const category = acceptedType.split('/')[0];\n return fileType.startsWith(category + '/');\n }\n \n return false;\n });\n },\n\n /**\n * Format file size for display\n * @param {number} bytes - File size in bytes\n * @returns {string} Formatted file size\n * @private\n */\n _formatFileDropSize(bytes) {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n }\n};\n\n// Apply mixin to View prototype\nexport default function applyFileDropMixin(ViewClass) {\n Object.assign(ViewClass.prototype, FileDropMixin);\n}\n\n// Export the mixin for manual application\nexport { FileDropMixin };","/**\n * TagInputView - Advanced tag input component for MOJO framework\n * Allows users to add/remove tags with keyboard and mouse interaction\n * Integrates with FormView and uses EventDelegate patterns\n *\n * Features:\n * - Add tags via Enter, comma, or Tab key\n * - Remove tags with click or keyboard\n * - Duplicate prevention\n * - Bootstrap 5 styling\n * - Accessibility support\n * - Form integration with hidden input\n *\n * Example Usage:\n * ```javascript\n * const tagInput = new TagInputView({\n * name: 'tags',\n * value: 'javascript,react,node', // Initial tags\n * placeholder: 'Add tags...',\n * maxTags: 10,\n * allowDuplicates: false\n * });\n * ```\n */\n\nimport View from '@core/View.js';\n\nclass TagInputView extends View {\n constructor(options = {}) {\n const {\n name,\n value = '',\n placeholder = 'Add tags...',\n maxTags = 50,\n allowDuplicates = false,\n separator = ',',\n trimTags = true,\n minLength = 1,\n maxLength = 50,\n disabled = false,\n readonly = false,\n class: containerClass = '',\n tagClass = 'badge bg-primary',\n inputClass = 'form-control',\n ...viewOptions\n } = options;\n\n super({\n tagName: 'div',\n className: `tag-input-view ${containerClass}`,\n ...viewOptions\n });\n\n // Configuration\n this.name = name;\n this.placeholder = placeholder;\n this.maxTags = maxTags;\n this.allowDuplicates = allowDuplicates;\n this.separator = separator;\n this.trimTags = trimTags;\n this.minLength = minLength;\n this.maxLength = maxLength;\n this.disabled = disabled;\n this.readonly = readonly;\n this.tagClass = tagClass;\n this.inputClass = inputClass;\n\n // State\n this.tags = [];\n this.focusedTagIndex = -1;\n\n // Parse initial value\n if (value) {\n this.tags = this.parseTagString(value);\n }\n }\n\n /**\n * Render the tag input component\n */\n async renderTemplate() {\n const tagsHTML = this.renderTags();\n const hiddenInputHTML = this.renderHiddenInput();\n const inputHTML = this.renderInput();\n\n return `\n <div class=\"tag-input-container\">\n <div class=\"tag-input-wrapper border rounded p-2\"\n data-action=\"focus-input\"\n tabindex=\"0\"\n role=\"combobox\"\n aria-expanded=\"false\"\n aria-label=\"Tag input\">\n <div class=\"tags-container d-flex flex-wrap gap-1 mb-2\">\n ${tagsHTML}\n </div>\n ${inputHTML}\n </div>\n ${hiddenInputHTML}\n <div class=\"tag-input-feedback small text-muted mt-1\">\n <span class=\"tag-count\">${this.tags.length}</span>/${this.maxTags} tags\n </div>\n </div>\n `;\n }\n\n /**\n * Render individual tags\n */\n renderTags() {\n return this.tags.map((tag, index) => `\n <span class=\"${this.tagClass} tag-item\"\n data-tag-index=\"${index}\"\n tabindex=\"0\"\n role=\"button\"\n aria-label=\"Tag: ${this.escapeHtml(tag)}. Press Delete or Backspace to remove.\">\n <span class=\"tag-text\">${this.escapeHtml(tag)}</span>\n ${!this.readonly && !this.disabled ? `\n <i class=\"bi bi-x tag-remove ms-1\"\n data-action=\"remove-tag\"\n data-tag-index=\"${index}\"\n aria-label=\"Remove tag\"></i>\n ` : ''}\n </span>\n `).join('');\n }\n\n /**\n * Render the input field\n */\n renderInput() {\n if (this.readonly) {\n return '';\n }\n\n return `\n <input type=\"text\"\n class=\"${this.inputClass} tag-input-field border-0 p-0\"\n placeholder=\"${this.escapeHtml(this.placeholder)}\"\n ${this.disabled ? 'disabled' : ''}\n data-change-action=\"input-change\"\n style=\"outline: none; box-shadow: none; min-width: 120px;\"\n autocomplete=\"off\">\n `;\n }\n\n /**\n * Render hidden input for form submission\n */\n renderHiddenInput() {\n if (!this.name) return '';\n\n return `\n <input type=\"hidden\"\n name=\"${this.name}\"\n value=\"${this.escapeHtml(this.getTagString())}\"\n class=\"tag-input-hidden\">\n `;\n }\n\n /**\n * Handle component initialization after render\n */\n async onAfterRender() {\n await super.onAfterRender();\n this.updateTagCount();\n }\n\n // ========================================\n // EventDelegate Action Handlers\n // ========================================\n\n /**\n * Handle focus on container\n */\n async onActionFocusInput(_event, _element) {\n this.focus();\n }\n\n focus() {\n const input = this.element.querySelector('.tag-input-field');\n if (input && !this.disabled) {\n input.focus();\n }\n this.focusedTagIndex = -1;\n }\n\n /**\n * Handle tag removal\n */\n async onActionRemoveTag(event, element) {\n event.stopPropagation();\n\n const tagIndex = parseInt(element.getAttribute('data-tag-index'));\n if (tagIndex >= 0 && tagIndex < this.tags.length) {\n await this.removeTag(tagIndex);\n }\n }\n\n /**\n * Handle input changes (for adding tags)\n */\n async onChangeInputChange(event, element) {\n const value = element.value;\n const lastChar = value.slice(-1);\n\n // Check for separator keys\n if (lastChar === this.separator || lastChar === '\\n') {\n event.preventDefault();\n const tagText = value.slice(0, -1);\n if (tagText.trim()) {\n await this.addTag(tagText);\n element.value = '';\n }\n return;\n }\n\n // Handle other trigger keys via keydown\n\n }\n\n bindEvents() {\n if (!this.__bnd_keydown) this.__bnd_keydown = this.handleInputKeydown.bind(this);\n this.element.addEventListener('keydown', this.__bnd_keydown);\n this.events.bind(this.element);\n }\n\n unbindEvents() {\n if (this.__bnd_keydown)this.element.removeEventListener('keydown', this.__bnd_keydown);\n this.events.unbind();\n }\n\n /**\n * Handle keyboard interactions\n */\n handleInputKeydown(event) {\n const input = event.target;\n const value = input.value || '';\n switch (event.key) {\n case 'Enter':\n case 'Tab':\n case ',':\n if (value.trim()) {\n event.preventDefault();\n this.addTag(value);\n input.value = '';\n }\n break;\n\n case 'Backspace':\n if (value === '' && this.tags.length > 0) {\n event.preventDefault();\n if (this.focusedTagIndex >= 0) {\n this.removeTag(this.focusedTagIndex);\n if (this.focusedTagIndex == 0) {\n this.focus();\n } else {\n this.focusTag(this.focusedTagIndex - 1);\n }\n } else {\n this.removeTag(this.tags.length - 1);\n }\n }\n break;\n\n case 'ArrowLeft':\n if (value === '' && this.tags.length > 0) {\n event.preventDefault();\n if (this.focusedTagIndex >= 0) {\n const newIndex = this.focusedTagIndex - 1;\n if (newIndex >= 0) {\n this.focusTag(newIndex);\n } else {\n this.focus();\n }\n } else {\n this.focusTag(this.tags.length - 1);\n }\n }\n break;\n\n case 'ArrowRight':\n if (value === '' && this.tags.length > 0) {\n event.preventDefault();\n if (this.focusedTagIndex >= 0) {\n const newIndex = this.focusedTagIndex + 1;\n if (newIndex < this.tags.length) {\n this.focusTag(newIndex);\n } else {\n this.focus();\n }\n } else {\n this.focusTag(0);\n }\n }\n break;\n\n case 'Escape':\n input.value = '';\n input.blur();\n break;\n }\n }\n\n // ========================================\n // Tag Management Methods\n // ========================================\n\n /**\n * Add a new tag\n */\n async addTag(tagText) {\n if (this.readonly || this.disabled) return false;\n\n const cleanTag = this.trimTags ? tagText.trim() : tagText;\n\n // Validation\n if (!this.isValidTag(cleanTag)) {\n return false;\n }\n\n // Check for duplicates\n if (!this.allowDuplicates && this.tags.includes(cleanTag)) {\n this.showTagError(`Tag \"${cleanTag}\" already exists`);\n return false;\n }\n\n // Check max tags limit\n if (this.tags.length >= this.maxTags) {\n this.showTagError(`Maximum ${this.maxTags} tags allowed`);\n return false;\n }\n\n // Add the tag\n this.tags.push(cleanTag);\n await this.updateDisplay();\n\n // Emit events\n this.emit('tag:added', { tag: cleanTag, tags: this.tags });\n this.emit('change', { value: this.getTagString(), tags: this.tags });\n\n return true;\n }\n\n /**\n * Remove a tag by index\n */\n async removeTag(index) {\n if (this.readonly || this.disabled) return false;\n\n if (index >= 0 && index < this.tags.length) {\n const removedTag = this.tags[index];\n this.tags.splice(index, 1);\n await this.updateDisplay();\n\n // Emit events\n this.emit('tag:removed', { tag: removedTag, tags: this.tags });\n this.emit('change', { value: this.getTagString(), tags: this.tags });\n\n return true;\n }\n\n return false;\n }\n\n /**\n * Remove a tag by value\n */\n async removeTagByValue(tagValue) {\n const index = this.tags.indexOf(tagValue);\n if (index >= 0) {\n return await this.removeTag(index);\n }\n return false;\n }\n\n /**\n * Clear all tags\n */\n async clearTags() {\n if (this.readonly || this.disabled) return false;\n\n const oldTags = [...this.tags];\n this.tags = [];\n await this.updateDisplay();\n\n this.emit('tags:cleared', { oldTags });\n this.emit('change', { value: '', tags: [] });\n\n return true;\n }\n\n /**\n * Set tags from array or string\n */\n async setTags(tagsInput) {\n let newTags = [];\n\n if (Array.isArray(tagsInput)) {\n newTags = tagsInput;\n } else if (typeof tagsInput === 'string') {\n newTags = this.parseTagString(tagsInput);\n }\n\n // Validate and filter tags\n newTags = newTags\n .filter(tag => this.isValidTag(tag))\n .slice(0, this.maxTags);\n\n // Remove duplicates if not allowed\n if (!this.allowDuplicates) {\n newTags = [...new Set(newTags)];\n }\n\n this.tags = newTags;\n await this.updateDisplay();\n\n this.emit('tags:set', { tags: this.tags });\n this.emit('change', { value: this.getTagString(), tags: this.tags });\n }\n\n // ========================================\n // Utility Methods\n // ========================================\n\n /**\n * Validate a tag\n */\n isValidTag(tag) {\n if (typeof tag !== 'string') return false;\n if (tag.length < this.minLength) return false;\n if (tag.length > this.maxLength) return false;\n if (tag.trim() === '') return false;\n return true;\n }\n\n /**\n * Parse tag string into array\n */\n parseTagString(tagString) {\n if (!tagString) return [];\n\n return tagString\n .split(this.separator)\n .map(tag => this.trimTags ? tag.trim() : tag)\n .filter(tag => tag.length > 0);\n }\n\n /**\n * Get tags as a string\n */\n getTagString() {\n return this.tags.join(this.separator);\n }\n\n /**\n * Get tags as array\n */\n getTags() {\n return [...this.tags];\n }\n\n /**\n * Focus a specific tag\n */\n focusTag(index) {\n const tagElements = this.element.querySelectorAll('.tag-item');\n if (tagElements[index]) {\n this.focusedTagIndex = index;\n console.log(`Focused tag index: ${index}`);\n tagElements[index].focus();\n }\n }\n\n /**\n * Update the display after changes\n */\n async updateDisplay() {\n // Re-render tags container\n const tagsContainer = this.element.querySelector('.tags-container');\n if (tagsContainer) {\n tagsContainer.innerHTML = this.renderTags();\n }\n\n // Update hidden input\n const hiddenInput = this.element.querySelector('.tag-input-hidden');\n if (hiddenInput) {\n hiddenInput.value = this.getTagString();\n }\n\n // Update tag count\n this.updateTagCount();\n }\n\n /**\n * Update tag count display\n */\n updateTagCount() {\n const tagCountElement = this.element.querySelector('.tag-count');\n if (tagCountElement) {\n tagCountElement.textContent = this.tags.length;\n }\n }\n\n /**\n * Show tag error message\n */\n showTagError(message) {\n // Create or update error message\n let errorElement = this.element.querySelector('.tag-error');\n\n if (!errorElement) {\n errorElement = document.createElement('div');\n errorElement.className = 'tag-error small text-danger mt-1';\n\n const feedback = this.element.querySelector('.tag-input-feedback');\n if (feedback) {\n feedback.parentNode.insertBefore(errorElement, feedback.nextSibling);\n }\n }\n\n errorElement.textContent = message;\n\n // Auto-hide after 3 seconds\n setTimeout(() => {\n if (errorElement.parentNode) {\n errorElement.remove();\n }\n }, 3000);\n }\n\n /**\n * Enable/disable the component\n */\n setEnabled(enabled) {\n this.disabled = !enabled;\n\n const input = this.element.querySelector('.tag-input-field');\n if (input) {\n input.disabled = this.disabled;\n }\n\n const container = this.element.querySelector('.tag-input-wrapper');\n if (container) {\n container.classList.toggle('disabled', this.disabled);\n }\n }\n\n /**\n * Set readonly state\n */\n setReadonly(readonly) {\n this.readonly = readonly;\n\n const input = this.element.querySelector('.tag-input-field');\n if (input) {\n input.style.display = readonly ? 'none' : '';\n }\n\n // Hide remove buttons\n const removeButtons = this.element.querySelectorAll('.tag-remove');\n removeButtons.forEach(btn => {\n btn.style.display = readonly ? 'none' : '';\n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n if (str == null) return '';\n const div = document.createElement('div');\n div.textContent = String(str);\n return div.innerHTML;\n }\n\n /**\n * Get form value (for integration with forms)\n */\n getFormValue() {\n return this.getTagString();\n }\n\n /**\n * Set form value (for integration with forms)\n */\n async setFormValue(value) {\n await this.setTags(value);\n }\n\n /**\n * Static factory method\n */\n static create(options = {}) {\n return new TagInputView(options);\n }\n}\n\nexport default TagInputView;\n","/**\n * CollectionSelectView - A searchable dropdown component for selecting items from a Collection\n *\n * This component uses a parent-child architecture to prevent input focus loss:\n * - CollectionSelectView (parent): Manages input and coordination\n * - CollectionDropdownView (child): Handles dropdown rendering only\n */\n\nimport { View } from '@core/View.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\n\n/**\n * CollectionDropdownView - Child component for dropdown results only\n */\nclass CollectionDropdownView extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'collection-dropdown-view dropdown-menu show w-100 position-absolute',\n style: 'max-height: 250px; overflow-y: auto; z-index: 1000;',\n template: `\n {{#data.loading}}\n <div class=\"dropdown-item text-center\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n {{/data.loading}}\n\n {{^data.loading}}\n {{#data.items}}\n <button type=\"button\"\n class=\"dropdown-item {{#isSelected}}active{{/isSelected}} {{#isFocused}}bg-light{{/isFocused}}\"\n data-action=\"select-item\"\n data-value=\"{{valueField}}\"\n data-label=\"{{labelField}}\"\n data-index=\"{{index}}\">\n {{labelField}}\n </button>\n {{/data.items}}\n\n {{#data.showNoResults}}\n <div class=\"dropdown-item text-muted\">\n {{#data.hasSearched}}No results found{{/data.hasSearched}}\n {{^data.hasSearched}}Start typing to search...{{/data.hasSearched}}\n </div>\n {{/data.showNoResults}}\n {{/data.loading}}\n `,\n ...options\n });\n\n this.collection = options.collection;\n this.labelField = options.labelField || 'name';\n this.valueField = options.valueField || 'id';\n this.selectedValue = options.selectedValue || '';\n this.loading = options.loading || false;\n this.hasSearched = options.hasSearched || false;\n this.focusedIndex = options.focusedIndex || -1;\n }\n\n async getViewData() {\n const items = this.collection ? this.collection.toJSON().map((item, index) => {\n const labelValue = MOJOUtils.getNestedValue(item, this.labelField);\n const fieldValue = MOJOUtils.getNestedValue(item, this.valueField);\n\n return {\n ...item,\n labelField: labelValue,\n valueField: fieldValue,\n isSelected: fieldValue == this.selectedValue,\n isFocused: index === this.focusedIndex,\n index\n };\n }) : [];\n\n return {\n loading: this.loading,\n hasSearched: this.hasSearched,\n showNoResults: !this.loading && this.hasSearched && items.length === 0,\n items\n };\n }\n\n async handleActionSelectItem(event, element) {\n event.preventDefault();\n const value = element.getAttribute('data-value');\n const label = element.getAttribute('data-label');\n this.emit('item-selected', { value, label });\n }\n\n updateState(state) {\n Object.assign(this, state);\n }\n\n updateFocusedItem(newIndex) {\n this.focusedIndex = newIndex;\n const items = this.element?.querySelectorAll('.dropdown-item[data-action=\"select-item\"]');\n items?.forEach((item, index) => {\n item.classList.toggle('bg-light', index === this.focusedIndex);\n });\n }\n\n getItemCount() {\n return this.collection ? this.collection.length() : 0;\n }\n\n getFocusedItem() {\n if (this.focusedIndex >= 0 && this.collection) {\n const items = this.collection.toJSON();\n return items[this.focusedIndex] || null;\n }\n return null;\n }\n}\n\n/**\n * CollectionSelectView - Main component with stable input\n */\nclass CollectionSelectView extends View {\n constructor(options = {}) {\n super({\n className: 'collection-select-view',\n template: `\n <div class=\"position-relative\">\n <input type=\"text\"\n class=\"form-control {{#data.hasError}}is-invalid{{/data.hasError}} {{#data.showClear}}pe-5{{/data.showClear}}\"\n placeholder=\"{{data.placeholder}}\"\n value=\"{{data.displayValue}}\"\n autocomplete=\"off\" />\n\n <input type=\"hidden\"\n name=\"{{data.name}}\"\n value=\"{{data.selectedValue}}\" />\n\n {{#data.showClear}}\n <button type=\"button\"\n class=\"btn btn-link position-absolute top-50 end-0 translate-middle-y me-2 p-0 border-0\"\n style=\"z-index: 10; color: #6c757d;\"\n data-action=\"clear-selection\"\n title=\"Clear selection\">\n <i class=\"bi bi-x-circle\"></i>\n </button>\n {{/data.showClear}}\n\n <div class=\"dropdown-container\"></div>\n\n {{#data.hasError}}\n <div class=\"invalid-feedback\">{{data.errorMessage}}</div>\n {{/data.hasError}}\n </div>\n `,\n ...options\n });\n\n // Configuration\n this.collection = options.collection;\n this.labelField = options.labelField || 'name';\n this.valueField = options.valueField || 'id';\n this.maxItems = options.maxItems || 10;\n this.placeholder = options.placeholder || 'Search...';\n this.debounceMs = options.debounceMs || 400;\n this.name = options.name || 'collection_select';\n this.emptyFetch = options.emptyFetch !== false;\n this.requiresActiveGroup = options.requiresActiveGroup || false;\n\n // State\n this.selectedValue = options.value || '0';\n this.selectedLabel = '';\n this.searchValue = '';\n this.showDropdown = false;\n this.loading = false;\n this.hasSearched = false;\n this.focusedIndex = -1;\n this.hasError = false;\n this.errorMessage = '';\n\n if (this.selectedValue && typeof this.selectedValue === 'object') {\n // Support dot notation in labelField and valueField\n this.selectedLabel = MOJOUtils.getNestedValue(this.selectedValue, this.labelField) || '';\n this.selectedValue = MOJOUtils.getNestedValue(this.selectedValue, this.valueField) || '0';\n }\n\n // Internal\n this.searchTimer = null;\n this.dropdownView = null;\n this.defaultParams = {};\n this.defaultParamsOption = options.defaultParams || null; // Can be dict or callback\n\n // Bind methods\n this.handleDocumentClick = this.handleDocumentClick.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleInputEvents = this.handleInputEvents.bind(this);\n\n }\n\n onInit() {\n if (this.collection) {\n this.setupCollection();\n }\n }\n\n setupCollection() {\n this.defaultParams = { ...this.collection.params };\n this.collection.params.size = this.maxItems;\n this.defaultParams.size = this.maxItems;\n\n // Merge defaultParams from options (dict or callback)\n if (this.defaultParamsOption) {\n const extraParams = typeof this.defaultParamsOption === 'function' \n ? this.defaultParamsOption() \n : this.defaultParamsOption;\n \n if (extraParams && typeof extraParams === 'object') {\n Object.assign(this.defaultParams, extraParams);\n Object.assign(this.collection.params, extraParams);\n }\n }\n\n // Add active group filter if required\n if (this.requiresActiveGroup) {\n const app = this.getApp();\n if (app && app.activeGroup && app.activeGroup.id) {\n this.collection.params.group = app.activeGroup.id;\n this.defaultParams.group = app.activeGroup.id;\n }\n }\n\n this.collection.on('fetch:start', () => {\n this.loading = true;\n this.showDropdown = true;\n this.updateDropdown();\n });\n\n this.collection.on('fetch:end', () => {\n this.loading = false;\n this.showDropdown = true;\n this.updateDropdown();\n });\n\n if (this.selectedValue) {\n this.loadSelectedItem();\n }\n\n if (this.emptyFetch && this.collection.isEmpty()) {\n this.performInitialFetch();\n }\n }\n\n async performInitialFetch() {\n if (!this.collection) return;\n\n try {\n const fetchParams = { ...this.defaultParams };\n delete fetchParams.search;\n await this.collection.updateParams(fetchParams, true);\n } catch (error) {\n console.error('Initial fetch error:', error);\n }\n }\n\n async loadSelectedItem() {\n try {\n if (!this.selectedValue || this.selectedValue == '0') return;\n if (this.selectedLabel) return;\n const selectedModel = this.collection?.get(this.selectedValue);\n if (selectedModel) {\n // Support dot notation in labelField\n this.selectedLabel = this.getFieldValue(selectedModel, this.labelField);\n this.render(false);\n return;\n }\n\n let model = await this.collection.fetchOne(this.selectedValue);\n if (model) {\n // Support dot notation in labelField\n this.selectedLabel = this.getFieldValue(model, this.labelField) || `${model.constructor.name} #${model.id}`;\n this.render(false);\n }\n } catch (error) {\n console.error('Error loading selected item:', error);\n }\n }\n\n async getViewData() {\n let displayValue = '';\n if (this.showDropdown && this.hasSearched) {\n displayValue = this.searchValue;\n } else if (this.selectedValue && this.selectedLabel) {\n displayValue = this.selectedLabel;\n }\n\n return {\n name: this.name,\n placeholder: this.placeholder,\n displayValue: displayValue,\n selectedValue: this.selectedValue,\n showClear: !!(this.selectedValue && this.selectedValue !== '0' && this.selectedLabel),\n hasError: this.hasError,\n errorMessage: this.errorMessage\n };\n }\n\n async onAfterRender() {\n await super.onAfterRender();\n\n // Set up stable event listeners on input\n const input = this.getInput();\n if (input) {\n input.addEventListener('input', this.handleInputEvents);\n input.addEventListener('focus', this.handleInputEvents);\n input.addEventListener('keydown', this.handleKeyDown);\n }\n\n document.addEventListener('click', this.handleDocumentClick);\n\n // Create dropdown view\n this.createDropdownView();\n }\n\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n\n const input = this.getInput();\n if (input) {\n input.removeEventListener('input', this.handleInputEvents);\n input.removeEventListener('focus', this.handleInputEvents);\n input.removeEventListener('keydown', this.handleKeyDown);\n }\n\n document.removeEventListener('click', this.handleDocumentClick);\n\n if (this.searchTimer) {\n clearTimeout(this.searchTimer);\n }\n\n if (this.dropdownView) {\n this.dropdownView.destroy();\n }\n }\n\n createDropdownView() {\n if (this.dropdownView) {\n this.dropdownView.destroy();\n }\n\n this.dropdownView = new CollectionDropdownView({\n collection: this.collection,\n labelField: this.labelField,\n valueField: this.valueField,\n selectedValue: this.selectedValue,\n loading: this.loading,\n hasSearched: this.hasSearched,\n focusedIndex: this.focusedIndex\n });\n\n this.dropdownView.on('item-selected', (data) => {\n this.selectItem(data.value, data.label);\n });\n }\n\n async handleInputEvents(event) {\n const input = event.target;\n\n if (event.type === 'focus') {\n this.showDropdown = true;\n if (!this.hasSearched && this.emptyFetch && this.collection?.isEmpty()) {\n this.performInitialFetch();\n }\n this.updateDropdown();\n } else if (event.type === 'input') {\n this.searchValue = input.value;\n this.showDropdown = true;\n this.hasSearched = true;\n this.focusedIndex = -1;\n\n if (this.searchValue !== this.selectedLabel) {\n this.selectedValue = '0';\n this.selectedLabel = '';\n this.emit('change', { value: '0', label: '' });\n }\n\n if (this.searchTimer) {\n clearTimeout(this.searchTimer);\n }\n\n this.searchTimer = setTimeout(() => {\n this.performSearch();\n }, this.debounceMs);\n\n this.updateDropdown();\n }\n }\n\n async handleActionClearSelection(event, _element) {\n event.preventDefault();\n event.stopPropagation();\n\n this.clearSelection();\n }\n\n clearSelection() {\n this.selectedValue = '0';\n this.selectedLabel = '';\n this.searchValue = '';\n this.showDropdown = false;\n this.hasError = false;\n this.focusedIndex = -1;\n this.hasSearched = false;\n\n // Update input display\n const input = this.getInput();\n if (input) {\n input.value = '';\n input.focus(); // Focus back on input after clearing\n }\n\n // Update hidden input\n const hiddenInput = this.getHiddenInput();\n if (hiddenInput) {\n hiddenInput.value = '0';\n }\n\n this.updateDropdown();\n this.render(); // Re-render to hide clear button\n this.emit('change', { value: '0', label: '' });\n }\n\n async performSearch() {\n if (!this.collection) return;\n\n // this.loading = true;\n // this.updateDropdown();\n\n try {\n const searchParams = { ...this.defaultParams };\n if (this.searchValue && this.searchValue.trim()) {\n searchParams.search = this.searchValue.trim();\n }\n await this.collection.updateParams(searchParams, true);\n } catch (error) {\n console.error('Search error:', error);\n this.loading = false;\n this.updateDropdown();\n }\n }\n\n updateDropdown() {\n if (!this.dropdownView) return;\n\n this.dropdownView.updateState({\n selectedValue: this.selectedValue,\n loading: this.loading,\n hasSearched: this.hasSearched,\n focusedIndex: this.focusedIndex\n });\n\n if (this.showDropdown) {\n if (!this.dropdownView.isMounted()) {\n const container = this.element?.querySelector('.dropdown-container');\n if (container) {\n this.dropdownView.render(true, container);\n }\n } else {\n this.dropdownView.render();\n }\n } else if (this.dropdownView.isMounted()) {\n this.dropdownView.destroy();\n this.createDropdownView();\n }\n }\n\n selectItem(value, label) {\n this.selectedValue = value;\n this.selectedLabel = label;\n this.searchValue = '';\n this.showDropdown = false;\n this.hasError = false;\n this.focusedIndex = -1;\n this.hasSearched = false;\n\n // Update input display\n const input = this.getInput();\n if (input) {\n input.value = label;\n }\n\n // Update hidden input\n const hiddenInput = this.getHiddenInput();\n if (hiddenInput) {\n hiddenInput.value = value;\n }\n\n this.updateDropdown();\n this.emit('change', { value, label });\n }\n\n handleDocumentClick(event) {\n if (!this.element?.contains(event.target)) {\n this.showDropdown = false;\n this.focusedIndex = -1;\n this.updateDropdown();\n }\n }\n\n handleKeyDown(event) {\n if (!this.showDropdown || !this.collection) return;\n\n const itemCount = this.dropdownView?.getItemCount() || 0;\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.focusedIndex = Math.min(this.focusedIndex + 1, itemCount - 1);\n this.dropdownView?.updateFocusedItem(this.focusedIndex);\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n this.focusedIndex = Math.max(this.focusedIndex - 1, 0);\n this.dropdownView?.updateFocusedItem(this.focusedIndex);\n break;\n\n case 'Enter': {\n event.preventDefault();\n const focusedItem = this.dropdownView?.getFocusedItem();\n if (focusedItem) {\n this.selectItem(focusedItem[this.valueField], focusedItem[this.labelField]);\n }\n break;\n }\n\n case 'Escape':\n event.preventDefault();\n this.showDropdown = false;\n this.focusedIndex = -1;\n this.updateDropdown();\n break;\n }\n }\n\n // Helper methods\n getInput() {\n return this.element?.querySelector('input[type=\"text\"]');\n }\n\n getHiddenInput() {\n return this.element?.querySelector('input[type=\"hidden\"]');\n }\n\n // Public API\n setValue(value, label = '') {\n this.selectedValue = value;\n this.selectedLabel = label;\n this.searchValue = '';\n this.hasError = false;\n this.hasSearched = false;\n\n const input = this.getInput();\n if (input) {\n input.value = label;\n }\n\n const hiddenInput = this.getHiddenInput();\n if (hiddenInput) {\n hiddenInput.value = value;\n }\n }\n\n getValue() {\n // Return null if value is 0 or '0' (no selection)\n if (this.selectedValue === 0 || this.selectedValue === '0') {\n return null;\n }\n return this.selectedValue;\n }\n\n getLabel() {\n return this.selectedLabel;\n }\n\n setError(message) {\n this.hasError = true;\n this.errorMessage = message;\n this.render();\n }\n\n clearError() {\n this.hasError = false;\n this.errorMessage = '';\n this.render();\n }\n\n focus() {\n const input = this.getInput();\n if (input) {\n input.focus();\n }\n }\n\n // FormBuilder integration\n getFormValue() {\n // Return null if value is 0 or '0' (no selection)\n if (this.selectedValue === 0 || this.selectedValue === '0') {\n return null;\n }\n return this.selectedValue;\n }\n\n setFormValue(value) {\n let newValue = value;\n let newLabel = '';\n\n if (newValue && typeof newValue === 'object') {\n // Support dot notation in labelField and valueField\n newLabel = MOJOUtils.getNestedValue(newValue, this.labelField) || '';\n newValue = MOJOUtils.getNestedValue(newValue, this.valueField);\n }\n\n newValue = newValue || '0';\n\n if (newValue == this.selectedValue) {\n return;\n }\n\n this.selectedValue = newValue;\n this.selectedLabel = newLabel;\n this.searchValue = '';\n this.hasSearched = false;\n this.showDropdown = false;\n this.hasError = false;\n\n if (this.selectedValue && this.selectedValue !== '0') {\n if (!this.selectedLabel) {\n this.selectedLabel = `${this.collection.getModelName()} #${this.selectedValue}`;\n }\n this.loadSelectedItem();\n } else {\n this.render();\n }\n }\n\n /**\n * Helper method to get field value from model or plain object with dot notation support\n * @param {Object|Model} item - The item to get value from\n * @param {string} fieldPath - The field path (supports dot notation)\n * @returns {*} The field value\n */\n getFieldValue(item, fieldPath) {\n if (!item || !fieldPath) return undefined;\n\n // If item has a get() method (Model instance), try using it first\n if (typeof item.get === 'function') {\n // Try to get the value using Model's get() method\n const value = item.get(fieldPath);\n // If get() returns undefined and path has dots, fall back to MOJOUtils\n if (value === undefined && fieldPath.includes('.')) {\n return MOJOUtils.getNestedValue(item, fieldPath);\n }\n return value;\n }\n\n // For plain objects, use MOJOUtils for dot notation support\n return MOJOUtils.getNestedValue(item, fieldPath);\n }\n\n}\n\nexport default CollectionSelectView;\n","/**\n * CollectionMultiSelect - MVC multi-select component\n * \n * Architecture:\n * - CollectionMultiSelectView (parent) - Coordinates child views\n * - SearchView (child) - Search input with live search\n * - ListItemsView (child) - Checkbox list display\n */\n\nimport { View } from '@core/View.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\n\n/**\n * SearchView - Search input child view\n */\nclass SearchView extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'collection-multiselect-search',\n template: `\n <input type=\"text\" \n class=\"form-control form-control-sm mb-2\" \n placeholder=\"{{placeholder}}\"\n data-change-action=\"search\"\n data-filter=\"live-search\"\n data-filter-debounce=\"{{debounce}}\" />\n `,\n ...options\n });\n\n this.placeholder = options.placeholder || 'Search...';\n this.debounce = options.debounce || 400;\n }\n\n async onChangeSearch(event, element) {\n const searchValue = element.value.trim();\n this.emit('search', searchValue);\n }\n\n getValue() {\n return this.element?.querySelector('input')?.value || '';\n }\n\n clear() {\n const input = this.element?.querySelector('input');\n if (input) input.value = '';\n }\n}\n\n/**\n * ListItemsView - Checkbox list child view\n */\nclass ListItemsView extends View {\n constructor(options = {}) {\n // Build item template based on whether custom template is provided\n const hasCustomTemplate = !!options.customItemTemplate;\n const itemContentTemplate = hasCustomTemplate \n ? `{{{customContent}}}` \n : `<span {{#disabled}}class=\"text-muted\"{{/disabled}}>{{label}}</span>`;\n\n super({\n tagName: 'div',\n className: 'collection-multiselect-items',\n template: `\n {{#loading}}\n <div class=\"text-center py-3\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n {{/loading}}\n\n {{^loading}}\n {{#items.length}}\n {{#showSelectAll}}\n <div class=\"collection-multiselect-actions d-flex justify-content-between align-items-center mb-2 py-1\">\n <button type=\"button\" \n class=\"btn btn-link btn-sm text-decoration-none p-0 {{#allSelected}}text-muted{{/allSelected}}\" \n data-action=\"select-all\"\n {{#allSelected}}disabled{{/allSelected}}>\n <i class=\"bi bi-check-square me-1\"></i>\n SELECT {{#unselectedCount}}({{unselectedCount}}){{/unselectedCount}}\n </button>\n <button type=\"button\" \n class=\"btn btn-link btn-sm text-decoration-none p-0 {{#noneSelected}}text-muted{{/noneSelected}}\" \n data-action=\"deselect-all\"\n {{#noneSelected}}disabled{{/noneSelected}}>\n DESELECT {{#selectedCount}}({{selectedCount}}){{/selectedCount}}\n <i class=\"bi bi-square ms-1\"></i>\n </button>\n </div>\n {{/showSelectAll}}\n \n <div class=\"collection-multiselect-list border rounded\" \n style=\"max-height: {{maxHeight}}px; overflow-y: auto;\">\n {{#items}}\n <div class=\"collection-multiselect-item d-flex align-items-center py-2 px-3 {{^disabled}}clickable{{/disabled}}\" \n data-action=\"{{^disabled}}toggle{{/disabled}}\"\n data-value=\"{{value}}\"\n data-index=\"{{index}}\">\n <i class=\"bi {{#selected}}bi-check-square-fill text-primary{{/selected}}{{^selected}}bi-square{{/selected}} me-2\" \n style=\"font-size: 1.1rem;\"></i>\n ${itemContentTemplate}\n </div>\n {{/items}}\n </div>\n {{/items.length}}\n\n {{^items.length}}\n <div class=\"collection-multiselect-empty text-muted text-center py-4 border rounded\">\n <i class=\"bi bi-inbox fs-3 d-block mb-2 opacity-50\"></i>\n <div>No items available</div>\n </div>\n {{/^items.length}}\n {{/loading}}\n `,\n ...options\n });\n\n this.items = options.items || [];\n this.loading = options.loading || false;\n this.maxHeight = options.maxHeight || 336;\n this.showSelectAll = options.showSelectAll !== false;\n this.selectedCount = options.selectedCount || 0;\n this.totalCount = options.totalCount || 0;\n this.unselectedCount = options.unselectedCount || 0;\n this.allSelected = options.allSelected || false;\n this.noneSelected = options.noneSelected || true;\n this.customItemTemplate = options.customItemTemplate || null;\n this.lastClickedIndex = -1;\n }\n\n handleActionToggle(event, element) {\n const value = element.getAttribute('data-value');\n const index = parseInt(element.getAttribute('data-index'), 10);\n this.emit('toggle', { value, index, shiftKey: event.shiftKey, element });\n this.lastClickedIndex = index;\n }\n\n /**\n * Update just the checkbox icon for a specific item without re-rendering\n */\n updateItemCheckbox(element, selected) {\n const icon = element.querySelector('i.bi');\n if (icon) {\n if (selected) {\n icon.classList.remove('bi-square');\n icon.classList.add('bi-check-square-fill', 'text-primary');\n } else {\n icon.classList.remove('bi-check-square-fill', 'text-primary');\n icon.classList.add('bi-square');\n }\n }\n }\n\n /**\n * Update the select/deselect all buttons based on current counts\n */\n updateActionButtons() {\n const selectAllBtn = this.element?.querySelector('[data-action=\"select-all\"]');\n const deselectAllBtn = this.element?.querySelector('[data-action=\"deselect-all\"]');\n \n if (selectAllBtn) {\n const countSpan = selectAllBtn.querySelector('span') || selectAllBtn;\n if (this.allSelected) {\n selectAllBtn.classList.add('text-muted');\n selectAllBtn.disabled = true;\n } else {\n selectAllBtn.classList.remove('text-muted');\n selectAllBtn.disabled = false;\n }\n }\n \n if (deselectAllBtn) {\n if (this.noneSelected) {\n deselectAllBtn.classList.add('text-muted');\n deselectAllBtn.disabled = true;\n } else {\n deselectAllBtn.classList.remove('text-muted');\n deselectAllBtn.disabled = false;\n }\n }\n }\n\n async handleActionSelectAll(event) {\n event.preventDefault();\n this.emit('select-all');\n }\n\n async handleActionDeselectAll(event) {\n event.preventDefault();\n this.emit('deselect-all');\n }\n\n updateState(state) {\n Object.assign(this, state);\n }\n}\n\n/**\n * CollectionMultiSelectView - Parent coordinator view\n */\nclass CollectionMultiSelectView extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'collection-multiselect-view',\n template: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label class=\"form-label\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n \n <div class=\"collection-multiselect-search-container\"></div>\n <div class=\"collection-multiselect-list-container\"></div>\n\n {{#help}}\n <div class=\"form-text\">{{help}}</div>\n {{/help}}\n {{#error}}\n <div class=\"invalid-feedback d-block\">{{error}}</div>\n {{/error}}\n </div>\n `,\n ...options\n });\n\n // Basic config\n this.name = options.name || 'collection_multiselect';\n this.label = options.label || '';\n this.help = options.help || '';\n this.error = options.error || '';\n this.required = options.required || false;\n this.disabled = options.disabled || false;\n\n // Collection\n this.collection = options.collection;\n this.labelField = options.labelField || 'name';\n this.valueField = options.valueField || 'id';\n this.excludeIds = options.excludeIds || []; // Server-side filtering (deprecated)\n this.ignoreIds = options.ignoreIds || []; // Client-side filtering\n this.itemTemplate = options.itemTemplate || null; // Custom mustache template for items\n \n // Params\n this.collectionParams = options.collectionParams || {};\n this.defaultParamsOption = options.defaultParams || null;\n this.baseParams = {};\n this.requiresActiveGroup = options.requiresActiveGroup || false;\n\n // UI\n this.size = options.size || 8;\n this.maxHeight = options.maxHeight || (this.size * 42);\n this.showSelectAll = options.showSelectAll !== false;\n this.enableSearch = options.enableSearch || false;\n this.searchPlaceholder = options.searchPlaceholder || 'Search...';\n this.searchDebounce = options.searchDebounce || 400;\n\n // State\n this.selectedValues = Array.isArray(options.value) ? options.value : [];\n this.loading = false;\n this.items = [];\n\n // Child views\n this.searchView = null;\n this.listView = null;\n }\n\n onInit() {\n if (this.collection) {\n this.setupCollection();\n }\n }\n\n setupCollection() {\n // Store base params\n this.baseParams = { ...this.collection.params };\n\n // Apply collectionParams\n if (Object.keys(this.collectionParams).length > 0) {\n Object.assign(this.baseParams, this.collectionParams);\n Object.assign(this.collection.params, this.collectionParams);\n }\n\n // Apply defaultParams (dict or callback)\n if (this.defaultParamsOption) {\n const extraParams = typeof this.defaultParamsOption === 'function' \n ? this.defaultParamsOption() \n : this.defaultParamsOption;\n \n if (extraParams) {\n Object.assign(this.baseParams, extraParams);\n Object.assign(this.collection.params, extraParams);\n }\n }\n\n // Active group filter\n if (this.requiresActiveGroup) {\n const app = this.getApp();\n if (app?.activeGroup?.id) {\n this.baseParams.group = app.activeGroup.id;\n this.collection.params.group = app.activeGroup.id;\n }\n }\n\n // Collection events\n this.collection.on('fetch:start', () => {\n this.loading = true;\n this.updateListView();\n });\n\n this.collection.on('fetch:end', () => {\n this.loading = false;\n this.buildItems();\n this.updateListView();\n });\n\n // Use existing data if available\n if (!this.collection.isEmpty()) {\n this.buildItems();\n }\n }\n\n async onAfterRender() {\n await super.onAfterRender();\n\n // Create child views\n if (this.enableSearch) {\n this.createSearchView();\n }\n this.createListView();\n\n // Fetch if empty\n if (this.collection?.isEmpty()) {\n this.collection.fetch();\n }\n }\n\n createSearchView() {\n const container = this.element?.querySelector('.collection-multiselect-search-container');\n if (!container) return;\n\n this.searchView = new SearchView({\n placeholder: this.searchPlaceholder,\n debounce: this.searchDebounce\n });\n\n this.searchView.on('search', (searchValue) => {\n this.handleSearch(searchValue);\n });\n\n this.searchView.render(true, container);\n }\n\n createListView() {\n const container = this.element?.querySelector('.collection-multiselect-list-container');\n if (!container) return;\n\n const selectedCount = this.selectedValues.length;\n const totalCount = this.items.length;\n const unselectedCount = totalCount - selectedCount;\n\n this.listView = new ListItemsView({\n items: this.items,\n loading: this.loading,\n maxHeight: this.maxHeight,\n showSelectAll: this.showSelectAll,\n selectedCount,\n totalCount,\n unselectedCount,\n allSelected: selectedCount === totalCount && totalCount > 0,\n noneSelected: selectedCount === 0,\n customItemTemplate: this.itemTemplate\n });\n\n this.listView.on('toggle', (data) => {\n this.handleToggle(data);\n });\n\n this.listView.on('select-all', () => {\n this.selectAll();\n });\n\n this.listView.on('deselect-all', () => {\n this.deselectAll();\n });\n\n this.listView.render(true, container);\n }\n\n updateListView() {\n if (this.listView) {\n const selectedCount = this.selectedValues.length;\n const totalCount = this.items.length;\n const unselectedCount = totalCount - selectedCount;\n\n this.listView.updateState({\n items: this.items,\n loading: this.loading,\n selectedCount,\n totalCount,\n unselectedCount,\n allSelected: selectedCount === totalCount && totalCount > 0,\n noneSelected: selectedCount === 0\n });\n this.listView.render(false);\n }\n }\n\n // Build items array from collection\n buildItems() {\n const models = this.collection.models.filter(model => {\n const id = this.getFieldValue(model, this.valueField);\n if (id == null) return false;\n \n // Filter out excludeIds (legacy support)\n if (this.excludeIds.includes(id)) return false;\n \n // Filter out ignoreIds (client-side filtering)\n if (this.ignoreIds.some(ignoreId => ignoreId == id)) return false;\n \n return true;\n });\n\n this.items = models.map((model, index) => {\n const modelData = model.toJSON ? model.toJSON() : model;\n const value = this.getFieldValue(model, this.valueField);\n \n const item = {\n label: this.getFieldValue(model, this.labelField),\n value,\n index,\n selected: this.selectedValues.some(v => v == value),\n disabled: this.disabled,\n model: modelData // All model data nested under 'model' context\n };\n\n // Render custom template if provided\n if (this.itemTemplate) {\n item.customContent = this.renderItemTemplate(item);\n }\n\n return item;\n });\n }\n\n // Render custom item template\n renderItemTemplate(itemData) {\n if (!this.itemTemplate) return '';\n \n try {\n // Use renderTemplateString which includes DataFormatter pipe support\n return this.renderTemplateString(this.itemTemplate, itemData);\n } catch (error) {\n console.error('Error rendering item template:', error);\n return itemData.label;\n }\n }\n\n // Get field value (supports dot notation)\n getFieldValue(item, field) {\n if (!item || !field) return undefined;\n \n if (typeof item.get === 'function') {\n return item.get(field) ?? MOJOUtils.getNestedValue(item, field);\n }\n \n return MOJOUtils.getNestedValue(item, field);\n }\n\n // Handle search\n handleSearch(searchValue) {\n const params = { ...this.baseParams };\n if (searchValue) {\n params.search = searchValue;\n }\n this.collection.updateParams(params, true);\n }\n\n // Handle item toggle\n handleToggle({ value, index, shiftKey, element }) {\n // Shift-click range selection\n if (shiftKey && this.listView.lastClickedIndex >= 0) {\n const start = Math.min(this.listView.lastClickedIndex, index);\n const end = Math.max(this.listView.lastClickedIndex, index);\n const shouldSelect = !this.items[index].selected;\n \n // For shift-click, we need to update multiple items - do a full re-render\n for (let i = start; i <= end; i++) {\n const item = this.items[i];\n if (!item.disabled) {\n if (shouldSelect) {\n if (!this.selectedValues.includes(item.value)) {\n this.selectedValues.push(item.value);\n }\n } else {\n this.selectedValues = this.selectedValues.filter(v => v != item.value);\n }\n item.selected = shouldSelect;\n }\n }\n this.updateListView();\n } else {\n // Normal toggle - update just the clicked item's DOM\n const item = this.items[index];\n if (item.selected) {\n this.selectedValues = this.selectedValues.filter(v => v != value);\n item.selected = false;\n } else {\n this.selectedValues.push(value);\n item.selected = true;\n }\n \n // Update just the checkbox icon, not the entire list\n if (element && this.listView) {\n this.listView.updateItemCheckbox(element, item.selected);\n \n // Update counts and action buttons state\n this.listView.selectedCount = this.selectedValues.length;\n this.listView.unselectedCount = this.items.length - this.selectedValues.length;\n this.listView.allSelected = this.selectedValues.length === this.items.length && this.items.length > 0;\n this.listView.noneSelected = this.selectedValues.length === 0;\n this.listView.updateActionButtons();\n }\n }\n\n this.emit('change', { value: this.selectedValues, name: this.name });\n }\n\n // Select all\n selectAll() {\n this.selectedValues = this.items.filter(i => !i.disabled).map(i => i.value);\n this.items.forEach(i => { if (!i.disabled) i.selected = true; });\n this.updateListView();\n this.emit('change', { value: this.selectedValues, name: this.name });\n }\n\n // Deselect all\n deselectAll() {\n this.selectedValues = [];\n this.items.forEach(i => i.selected = false);\n this.updateListView();\n this.emit('change', { value: this.selectedValues, name: this.name });\n }\n\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n \n if (this.searchView) {\n this.searchView.destroy();\n }\n if (this.listView) {\n this.listView.destroy();\n }\n }\n\n // Public API\n getValue() { return this.selectedValues; }\n \n setValue(values) {\n this.selectedValues = Array.isArray(values) ? values : [];\n this.buildItems();\n this.updateListView();\n }\n\n setExcludeIds(ids) {\n this.excludeIds = Array.isArray(ids) ? ids : [];\n this.buildItems();\n this.updateListView();\n }\n\n setIgnoreIds(ids) {\n this.ignoreIds = Array.isArray(ids) ? ids : [];\n this.buildItems();\n this.updateListView();\n }\n\n async refresh() {\n await this.collection.fetch();\n }\n\n getFormValue() { return this.selectedValues; }\n setFormValue(value) { this.setValue(value); }\n}\n\nexport default CollectionMultiSelectView;\n","/**\n * MultiSelectDropdown - Standalone multi-select dropdown component\n * \n * Architecture:\n * - MultiSelectDropdown (parent) - Manages dropdown state and button\n * - MultiSelectItemsView (child) - Renders checkbox list\n * \n * Based on CollectionMultiSelect pattern but simplified:\n * - No search functionality (KISS principle)\n * - Simple static options list\n * - Checkbox selection with visual feedback\n * \n * @example\n * const dropdown = new MultiSelectDropdown({\n * name: 'status',\n * label: 'Status',\n * options: [\n * { value: 'new', label: 'New' },\n * { value: 'open', label: 'Open' }\n * ],\n * value: ['new']\n * });\n */\n\nimport { View } from '@core/View.js';\n\n/**\n * MultiSelectItemsView - Child view for rendering checkbox items\n */\nclass MultiSelectItemsView extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'multiselect-items',\n template: `\n {{#items.length}}\n <div class=\"multiselect-list\" style=\"max-height: {{maxHeight}}px; overflow-y: auto;\">\n {{#items}}\n <div class=\"multiselect-item form-check px-3 py-2\" \n data-action=\"toggle\"\n data-value=\"{{value}}\"\n data-index=\"{{index}}\">\n <input type=\"checkbox\" \n class=\"form-check-input\" \n id=\"{{id}}\"\n {{#selected}}checked{{/selected}}\n {{#disabled}}disabled{{/disabled}}>\n <label class=\"form-check-label w-100\" for=\"{{id}}\">\n {{label}}\n </label>\n </div>\n {{/items}}\n </div>\n <div class=\"multiselect-footer border-top p-2\">\n <button type=\"button\" class=\"btn btn-sm btn-primary w-100\" data-action=\"close-dropdown\">\n Done\n </button>\n </div>\n {{/items.length}}\n \n {{^items.length}}\n <div class=\"multiselect-empty text-muted text-center py-3\">\n <small>No options available</small>\n </div>\n {{/items.length}}\n `,\n ...options\n });\n\n this.items = options.items || [];\n this.maxHeight = options.maxHeight || 300;\n }\n\n /**\n * Handle item toggle\n */\n handleActionToggle(event, element) {\n const value = element.getAttribute('data-value');\n const index = parseInt(element.getAttribute('data-index'), 10);\n \n // Find the item and toggle its selected state\n const item = this.items[index];\n if (!item || item.disabled) return;\n\n item.selected = !item.selected;\n\n // Update just the checkbox without full re-render\n const checkbox = element.querySelector('input[type=\"checkbox\"]');\n if (checkbox) {\n checkbox.checked = item.selected;\n }\n\n // Emit toggle event to parent\n this.emit('toggle', { value, index, selected: item.selected });\n }\n\n /**\n * Handle close dropdown button\n */\n handleActionCloseDropdown(event, element) {\n this.emit('close-dropdown');\n }\n\n /**\n * Get currently selected values\n */\n getValue() {\n return this.items\n .filter(item => item.selected)\n .map(item => item.value);\n }\n\n /**\n * Set selected values\n */\n setValue(values) {\n const valueSet = new Set(Array.isArray(values) ? values : [values]);\n \n this.items.forEach(item => {\n item.selected = valueSet.has(item.value);\n });\n \n // Re-render to update checkboxes\n this.render(false);\n }\n\n /**\n * Update items and re-render\n */\n updateItems(items) {\n this.items = items;\n this.render(false);\n }\n}\n\n/**\n * MultiSelectDropdown - Parent view managing dropdown\n */\nclass MultiSelectDropdown extends View {\n constructor(options = {}) {\n super({\n tagName: 'div',\n className: 'multiselect-dropdown',\n template: `\n <div class=\"mojo-form-control\">\n {{#label}}\n <label class=\"form-label\">\n {{label}}{{#required}}<span class=\"text-danger\">*</span>{{/required}}\n </label>\n {{/label}}\n \n <div class=\"dropdown w-100\">\n <button class=\"btn btn-outline-secondary dropdown-toggle w-100 text-start d-flex justify-content-between align-items-center\" \n type=\"button\" \n data-bs-toggle=\"dropdown\" \n aria-expanded=\"false\"\n {{#disabled}}disabled{{/disabled}}>\n <span class=\"multiselect-button-text\">{{buttonText}}</span>\n <i class=\"bi bi-chevron-down\"></i>\n </button>\n <div class=\"dropdown-menu w-100\" data-bs-auto-close=\"outside\" data-container=\"items\"></div>\n </div>\n \n {{#help}}\n <div class=\"form-text\">{{help}}</div>\n {{/help}}\n {{#error}}\n <div class=\"invalid-feedback d-block\">{{error}}</div>\n {{/error}}\n </div>\n `,\n ...options\n });\n\n // Configuration\n this.name = options.name || 'multiselect';\n this.label = options.label || '';\n this.help = options.help || '';\n this.error = options.error || '';\n this.required = options.required || false;\n this.disabled = options.disabled || false;\n this.placeholder = options.placeholder || options.placeHolder || 'Select...'; // Support both casings\n this.maxHeight = options.maxHeight || 300;\n this.showSelectedLabels = options.showSelectedLabels !== false; // Show labels by default\n this.maxLabelsToShow = options.maxLabelsToShow || 3; // Max number of labels before \"X selected\"\n \n // Options and values\n this.options = options.options || [];\n this.selectedValues = Array.isArray(options.value) ? options.value : [];\n \n // Button text (computed)\n this.buttonText = this.computeButtonText();\n \n // Child view\n this.listView = null;\n }\n\n /**\n * Compute button text based on current selection\n */\n computeButtonText() {\n const count = this.selectedValues.length;\n \n if (count === 0) {\n return this.placeholder || 'Select...';\n } else if (this.showSelectedLabels && count <= this.maxLabelsToShow) {\n // Show comma-separated labels for small selections\n const labels = this.selectedValues.map(value => {\n const selectedOption = this.options.find(opt => {\n const optValue = typeof opt === 'string' ? opt : opt.value;\n return optValue === value;\n });\n return typeof selectedOption === 'string' ? selectedOption : (selectedOption?.label || selectedOption?.value || value);\n });\n return labels.join(', ');\n } else {\n // Show count for larger selections\n return `${count} selected`;\n }\n }\n\n /**\n * Initialize child view after render\n */\n async onAfterRender() {\n await super.onAfterRender();\n this.createListView();\n }\n\n /**\n * Create and mount the items list view\n */\n createListView() {\n const container = this.element?.querySelector('[data-container=\"items\"]');\n if (!container) return;\n\n // Build items array with selection state\n const items = this.options.map((option, index) => {\n const value = typeof option === 'string' ? option : option.value;\n const label = typeof option === 'string' ? option : (option.label || option.text || option.value);\n const disabled = typeof option === 'object' ? option.disabled : false;\n \n return {\n id: `${this.name}_${index}`,\n value,\n label,\n index,\n selected: this.selectedValues.includes(value),\n disabled\n };\n });\n\n // Create list view\n this.listView = new MultiSelectItemsView({\n items,\n maxHeight: this.maxHeight\n });\n\n // Listen for toggle events\n this.listView.on('toggle', (data) => {\n this.handleToggle(data);\n });\n\n // Listen for close dropdown event\n this.listView.on('close-dropdown', () => {\n this.closeDropdown();\n });\n\n // Render list view\n this.listView.render(true, container);\n }\n\n /**\n * Close the dropdown programmatically\n */\n closeDropdown() {\n const dropdownButton = this.element?.querySelector('.dropdown-toggle');\n if (dropdownButton && window.bootstrap?.Dropdown) {\n const dropdownInstance = window.bootstrap.Dropdown.getInstance(dropdownButton);\n if (dropdownInstance) {\n dropdownInstance.hide();\n }\n }\n }\n\n /**\n * Handle item toggle\n */\n handleToggle(data) {\n const { value, selected } = data;\n\n if (selected) {\n // Add to selected values\n if (!this.selectedValues.includes(value)) {\n this.selectedValues.push(value);\n }\n } else {\n // Remove from selected values\n this.selectedValues = this.selectedValues.filter(v => v !== value);\n }\n\n // Update button text\n this.updateButtonText();\n\n // Emit change event\n this.emit('change', {\n value: this.selectedValues,\n name: this.name\n });\n }\n\n /**\n * Update button text based on selection\n */\n updateButtonText() {\n const button = this.element?.querySelector('.multiselect-button-text');\n if (!button) return;\n\n const count = this.selectedValues.length;\n \n // Compute new button text\n this.buttonText = this.computeButtonText();\n button.textContent = this.buttonText;\n \n // Update muted class for placeholder\n if (count === 0) {\n button.classList.add('text-muted');\n } else {\n button.classList.remove('text-muted');\n }\n }\n\n /**\n * Get current selected values\n */\n getValue() {\n return this.selectedValues;\n }\n\n /**\n * Set selected values\n */\n setValue(values) {\n this.selectedValues = Array.isArray(values) ? values : (values ? [values] : []);\n \n if (this.listView) {\n this.listView.setValue(this.selectedValues);\n }\n \n this.updateButtonText();\n }\n\n /**\n * Update options list\n */\n setOptions(options) {\n this.options = options;\n \n if (this.listView) {\n // Rebuild items array\n const items = this.options.map((option, index) => {\n const value = typeof option === 'string' ? option : option.value;\n const label = typeof option === 'string' ? option : (option.label || option.text || option.value);\n const disabled = typeof option === 'object' ? option.disabled : false;\n \n return {\n id: `${this.name}_${index}`,\n value,\n label,\n index,\n selected: this.selectedValues.includes(value),\n disabled\n };\n });\n \n this.listView.updateItems(items);\n }\n }\n\n /**\n * Clear all selections\n */\n clear() {\n this.setValue([]);\n }\n\n /**\n * Get form value (for form integration)\n */\n getFormValue() {\n return this.getValue();\n }\n\n /**\n * Set form value (for form integration)\n */\n setFormValue(value) {\n this.setValue(value);\n }\n\n /**\n * Cleanup child view on destroy\n */\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n \n if (this.listView) {\n this.listView.destroy();\n }\n }\n}\n\nexport default MultiSelectDropdown;\n","/**\n * DatePicker - Enhanced date picker input with Easepick integration\n * Falls back to native HTML5 date input if Easepick is unavailable\n * \n * Features:\n * - Dynamic CDN loading of Easepick\n * - Configurable date formats and constraints\n * - Keyboard navigation and accessibility\n * - Form integration with FormBuilder\n * - Graceful fallback to native date input\n * \n * Example Usage:\n * ```javascript\n * const datePicker = new DatePicker({\n * name: 'birth_date',\n * value: '2023-01-15',\n * format: 'YYYY-MM-DD',\n * min: '1900-01-01',\n * max: '2030-12-31',\n * placeholder: 'Select date...'\n * });\n * ```\n */\n\nimport View from '@core/View.js';\n\nclass DatePicker extends View {\n constructor(options = {}) {\n const {\n name,\n value = '',\n format = 'YYYY-MM-DD',\n displayFormat = 'MMM DD, YYYY',\n min = null,\n max = null,\n placeholder = 'Select date...',\n disabled = false,\n readonly = false,\n required = false,\n class: containerClass = '',\n inputClass = 'form-control',\n autoApply = true,\n inline = false,\n ...viewOptions\n } = options;\n\n super({\n tagName: 'div',\n className: `date-picker-view ${containerClass}`,\n ...viewOptions\n });\n\n // Configuration\n this.name = name;\n this.format = format;\n this.displayFormat = displayFormat;\n this.min = min;\n this.max = max;\n this.placeholder = placeholder;\n this.disabled = disabled;\n this.readonly = readonly;\n this.required = required;\n this.inputClass = inputClass;\n this.autoApply = autoApply;\n this.inline = inline;\n\n // State\n this.currentValue = value;\n this.picker = null;\n this.useNative = false;\n this.easepickLoaded = false;\n\n // Check if Easepick is available\n this.checkEasepickAvailability();\n }\n\n /**\n * Check if Easepick is available and load if needed\n */\n async checkEasepickAvailability() {\n if (typeof window !== 'undefined' && window.easepick) {\n this.easepickLoaded = true;\n return true;\n }\n\n // Try to load Easepick from CDN\n try {\n await this.loadEasepick();\n this.easepickLoaded = true;\n return true;\n } catch (error) {\n console.warn('Easepick failed to load, falling back to native date input:', error);\n this.useNative = true;\n return false;\n }\n }\n\n /**\n * Dynamically load Easepick from CDN\n */\n async loadEasepick() {\n return new Promise((resolve, reject) => {\n // Check if already loaded\n if (window.easepick) {\n resolve();\n return;\n }\n\n // Load CSS first\n const css = document.createElement('link');\n css.rel = 'stylesheet';\n css.href = 'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css';\n document.head.appendChild(css);\n\n // Load JavaScript\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js';\n script.onload = () => {\n if (window.easepick) {\n resolve();\n } else {\n reject(new Error('Easepick not available after loading'));\n }\n };\n script.onerror = () => reject(new Error('Failed to load Easepick script'));\n document.head.appendChild(script);\n });\n }\n\n /**\n * Render the date picker component\n */\n async renderTemplate() {\n const inputId = this.getInputId();\n const inputType = this.useNative ? 'date' : 'text';\n const inputValue = this.formatValueForInput(this.currentValue);\n \n return `\n <div class=\"date-picker-container\">\n <input \n type=\"${inputType}\"\n id=\"${inputId}\"\n name=\"${this.name || ''}\"\n class=\"${this.inputClass}${this.hasError() ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(inputValue)}\"\n placeholder=\"${this.escapeHtml(this.placeholder)}\"\n ${this.min ? `min=\"${this.min}\"` : ''}\n ${this.max ? `max=\"${this.max}\"` : ''}\n ${this.disabled ? 'disabled' : ''}\n ${this.readonly ? 'readonly' : ''}\n ${this.required ? 'required' : ''}\n autocomplete=\"off\"\n data-change-action=\"date-changed\"\n />\n <div class=\"date-picker-feedback\"></div>\n </div>\n `;\n }\n\n /**\n * Initialize after render\n */\n async onAfterRender() {\n await super.onAfterRender();\n \n if (this.easepickLoaded && !this.useNative) {\n await this.initializeEasepick();\n } else {\n this.initializeNativeFallback();\n }\n }\n\n /**\n * Initialize Easepick date picker\n */\n async initializeEasepick() {\n const input = this.getInputElement();\n if (!input || !window.easepick) return;\n\n try {\n const config = {\n element: input,\n css: [\n 'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css',\n ],\n format: this.displayFormat,\n lang: 'en-US',\n autoApply: this.autoApply,\n inline: this.inline,\n readonly: this.readonly,\n zIndex: 9999,\n };\n\n // Add date constraints\n if (this.min) {\n config.minDate = new Date(this.min);\n }\n if (this.max) {\n config.maxDate = new Date(this.max);\n }\n\n // Add event handlers\n config.setup = (picker) => {\n picker.on('select', (e) => {\n const date = e.detail.date;\n this.handleDateChange(date ? this.formatDate(date, this.format) : '');\n });\n\n picker.on('clear', () => {\n this.handleDateChange('');\n });\n\n picker.on('show', () => {\n this.emit('picker:show');\n });\n\n picker.on('hide', () => {\n this.emit('picker:hide');\n });\n };\n\n this.picker = new window.easepick.create(config);\n\n // Set initial value if provided\n if (this.currentValue) {\n this.picker.setDate(this.currentValue);\n }\n\n } catch (error) {\n console.error('Failed to initialize Easepick:', error);\n this.useNative = true;\n this.initializeNativeFallback();\n }\n }\n\n /**\n * Initialize native HTML5 date input fallback\n */\n initializeNativeFallback() {\n const input = this.getInputElement();\n if (!input) return;\n\n // Convert to HTML5 date format if needed\n input.type = 'date';\n if (this.currentValue) {\n input.value = this.formatDate(this.currentValue, 'YYYY-MM-DD');\n }\n }\n\n // ========================================\n // Event Handlers\n // ========================================\n\n /**\n * Handle date change from input\n */\n async onChangeDateChanged(action, event, element) {\n const value = element.value;\n this.handleDateChange(value);\n }\n\n /**\n * Handle date change logic\n */\n handleDateChange(value) {\n const oldValue = this.currentValue;\n this.currentValue = value;\n\n // Update hidden input if exists\n this.updateHiddenInput();\n\n // Emit change events\n if (oldValue !== value) {\n this.emit('change', { \n value: value, \n formatted: this.formatValueForDisplay(value),\n oldValue: oldValue \n });\n this.emit('date:changed', { value, oldValue });\n }\n }\n\n // ========================================\n // Utility Methods\n // ========================================\n\n /**\n * Format date for different contexts\n */\n formatDate(date, format = this.format) {\n if (!date) return '';\n \n // Simple date formatting (can be enhanced)\n const d = new Date(date);\n if (isNaN(d.getTime())) return '';\n\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n \n switch (format) {\n case 'YYYY-MM-DD':\n return `${year}-${month}-${day}`;\n case 'MM/DD/YYYY':\n return `${month}/${day}/${year}`;\n case 'DD/MM/YYYY':\n return `${day}/${month}/${year}`;\n case 'MMM DD, YYYY':\n const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n return `${monthNames[d.getMonth()]} ${day}, ${year}`;\n default:\n return `${year}-${month}-${day}`;\n }\n }\n\n /**\n * Format value for input display\n */\n formatValueForInput(value) {\n if (!value) return '';\n return this.useNative ? this.formatDate(value, 'YYYY-MM-DD') : value;\n }\n\n /**\n * Format value for display\n */\n formatValueForDisplay(value) {\n if (!value) return '';\n return this.formatDate(value, this.displayFormat);\n }\n\n /**\n * Get unique input ID\n */\n getInputId() {\n return this.name ? `datepicker_${this.name}_${Date.now()}` : `datepicker_${Date.now()}`;\n }\n\n /**\n * Get input element\n */\n getInputElement() {\n return this.element?.querySelector('input');\n }\n\n /**\n * Update hidden input for form submission\n */\n updateHiddenInput() {\n // This method can be used if we need a separate hidden input\n // for form submission with a different format\n }\n\n /**\n * Check if field has error\n */\n hasError() {\n return false; // Can be enhanced with validation\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n if (str == null) return '';\n const div = document.createElement('div');\n div.textContent = String(str);\n return div.innerHTML;\n }\n\n // ========================================\n // Public API Methods\n // ========================================\n\n /**\n * Set the date value\n */\n setValue(value) {\n this.currentValue = value;\n \n if (this.picker && this.easepickLoaded) {\n this.picker.setDate(value || null);\n } else {\n const input = this.getInputElement();\n if (input) {\n input.value = this.formatValueForInput(value);\n }\n }\n \n this.emit('value:set', { value });\n }\n\n /**\n * Get the current date value\n */\n getValue() {\n return this.currentValue;\n }\n\n /**\n * Get formatted date value\n */\n getFormattedValue(format = this.displayFormat) {\n return this.formatDate(this.currentValue, format);\n }\n\n /**\n * Clear the date value\n */\n clear() {\n this.setValue('');\n }\n\n /**\n * Set min date constraint\n */\n setMin(minDate) {\n this.min = minDate;\n if (this.picker && this.easepickLoaded) {\n this.picker.options.minDate = new Date(minDate);\n } else {\n const input = this.getInputElement();\n if (input) {\n input.min = minDate;\n }\n }\n }\n\n /**\n * Set max date constraint\n */\n setMax(maxDate) {\n this.max = maxDate;\n if (this.picker && this.easepickLoaded) {\n this.picker.options.maxDate = new Date(maxDate);\n } else {\n const input = this.getInputElement();\n if (input) {\n input.max = maxDate;\n }\n }\n }\n\n /**\n * Enable/disable the picker\n */\n setEnabled(enabled) {\n this.disabled = !enabled;\n const input = this.getInputElement();\n if (input) {\n input.disabled = this.disabled;\n }\n if (this.picker && this.easepickLoaded) {\n // Easepick doesn't have direct disable method, so we handle it via input\n if (this.disabled) {\n this.picker.hide();\n }\n }\n }\n\n /**\n * Set readonly state\n */\n setReadonly(readonly) {\n this.readonly = readonly;\n const input = this.getInputElement();\n if (input) {\n input.readonly = readonly;\n }\n }\n\n /**\n * Focus the input\n */\n focus() {\n const input = this.getInputElement();\n if (input) {\n input.focus();\n }\n }\n\n /**\n * Show the picker (if using Easepick)\n */\n show() {\n if (this.picker && this.easepickLoaded) {\n this.picker.show();\n }\n }\n\n /**\n * Hide the picker (if using Easepick)\n */\n hide() {\n if (this.picker && this.easepickLoaded) {\n this.picker.hide();\n }\n }\n\n // ========================================\n // FormBuilder Integration\n // ========================================\n\n /**\n * Get form value (for FormBuilder integration)\n */\n getFormValue() {\n return this.getValue();\n }\n\n /**\n * Set form value (for FormBuilder integration)\n */\n async setFormValue(value) {\n this.setValue(value);\n }\n\n // ========================================\n // Lifecycle Methods\n // ========================================\n\n /**\n * Cleanup when component is destroyed\n */\n async onBeforeDestroy() {\n if (this.picker && this.easepickLoaded) {\n try {\n this.picker.destroy();\n } catch (error) {\n console.warn('Error destroying Easepick instance:', error);\n }\n }\n \n this.picker = null;\n await super.onBeforeDestroy();\n }\n\n /**\n * Static factory method\n */\n static create(options = {}) {\n return new DatePicker(options);\n }\n}\n\nexport default DatePicker;","/**\n * DateRangePicker - Enhanced date range picker with Easepick integration\n * Falls back to two native HTML5 date inputs if Easepick is unavailable\n * \n * Features:\n * - Dynamic CDN loading of Easepick\n * - Start and end date validation\n * - Configurable date formats and constraints\n * - Keyboard navigation and accessibility\n * - Form integration with FormBuilder\n * - Graceful fallback to native date inputs\n * \n * Example Usage:\n * ```javascript\n * const dateRangePicker = new DateRangePicker({\n * name: 'date_range',\n * startDate: '2023-01-01',\n * endDate: '2023-01-31',\n * format: 'YYYY-MM-DD',\n * min: '2020-01-01',\n * max: '2030-12-31',\n * placeholder: 'Select date range...'\n * });\n * ```\n */\n\nimport View from '@core/View.js';\n\nclass DateRangePicker extends View {\n constructor(options = {}) {\n const {\n name,\n startName,\n endName,\n fieldName,\n startDate = '',\n endDate = '',\n format = 'YYYY-MM-DD',\n displayFormat = 'MMM DD, YYYY',\n outputFormat = 'date', // 'date', 'epoch', 'iso'\n min = null,\n max = null,\n placeholder = 'Select date range...',\n startPlaceholder = 'Start date...',\n endPlaceholder = 'End date...',\n disabled = false,\n readonly = false,\n required = false,\n class: containerClass = '',\n inputClass = 'form-control',\n autoApply = true,\n inline = false,\n separator = ' - ',\n ...viewOptions\n } = options;\n\n super({\n tagName: 'div',\n className: `date-range-picker-view ${containerClass}`,\n ...viewOptions\n });\n\n // Configuration\n this.name = name;\n this.startName = startName;\n this.endName = endName;\n this.fieldName = fieldName;\n this.format = format;\n this.displayFormat = displayFormat;\n this.outputFormat = outputFormat;\n this.min = min;\n this.max = max;\n this.placeholder = placeholder;\n this.startPlaceholder = startPlaceholder;\n this.endPlaceholder = endPlaceholder;\n this.disabled = disabled;\n this.readonly = readonly;\n this.required = required;\n this.inputClass = inputClass;\n this.autoApply = autoApply;\n this.inline = inline;\n this.separator = separator;\n\n // State\n this.currentStartDate = startDate;\n this.currentEndDate = endDate;\n this.picker = null;\n this.useNative = false;\n this.easepickLoaded = false;\n\n // Check if Easepick is available\n this.checkEasepickAvailability();\n }\n\n /**\n * Check if Easepick is available and load if needed\n */\n async checkEasepickAvailability() {\n if (typeof window !== 'undefined' && window.easepick) {\n this.easepickLoaded = true;\n return true;\n }\n\n // Try to load Easepick from CDN\n try {\n await this.loadEasepick();\n this.easepickLoaded = true;\n return true;\n } catch (error) {\n console.warn('Easepick failed to load, falling back to native date inputs:', error);\n this.useNative = true;\n return false;\n }\n }\n\n /**\n * Dynamically load Easepick from CDN\n */\n async loadEasepick() {\n return new Promise((resolve, reject) => {\n // Check if already loaded\n if (window.easepick) {\n resolve();\n return;\n }\n\n // Load CSS first\n const css = document.createElement('link');\n css.rel = 'stylesheet';\n css.href = 'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css';\n document.head.appendChild(css);\n\n // Load JavaScript\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js';\n script.onload = () => {\n if (window.easepick) {\n resolve();\n } else {\n reject(new Error('Easepick not available after loading'));\n }\n };\n script.onerror = () => reject(new Error('Failed to load Easepick script'));\n document.head.appendChild(script);\n });\n }\n\n /**\n * Render the date range picker component\n */\n async renderTemplate() {\n const inputId = this.getInputId();\n const displayValue = this.getDisplayValue();\n \n if (this.useNative) {\n // Render two separate date inputs for native fallback\n return this.renderNativeTemplate(inputId);\n }\n\n // Determine field names\n const startFieldName = this.startName || (this.name ? `${this.name}_start` : '');\n const endFieldName = this.endName || (this.name ? `${this.name}_end` : '');\n \n // Get formatted values for output\n const startValue = this.currentStartDate ? this.formatForOutput(this.currentStartDate) : '';\n const endValue = this.currentEndDate ? this.formatForOutput(this.currentEndDate) : '';\n\n return `\n <div class=\"date-range-picker-container\">\n <input \n type=\"text\"\n id=\"${inputId}\"\n ${this.name ? `name=\"${this.name}\"` : ''}\n class=\"${this.inputClass} date-range-picker-input${this.hasError() ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(displayValue)}\"\n placeholder=\"${this.escapeHtml(this.placeholder)}\"\n ${this.disabled ? 'disabled' : ''}\n ${this.readonly ? 'readonly' : ''}\n ${this.required ? 'required' : ''}\n autocomplete=\"off\"\n data-change-action=\"range-changed\"\n style=\"background-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 16 16%22><path fill=%22%236c757d%22 fill-rule=%22evenodd%22 d=%22M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z%22/></svg>'); background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; padding-right: 2.25rem; cursor: pointer;\"\n />\n \n <!-- Hidden inputs for form submission -->\n ${startFieldName ? `<input type=\"hidden\" name=\"${startFieldName}\" value=\"${this.escapeHtml(startValue)}\" />` : ''}\n ${endFieldName ? `<input type=\"hidden\" name=\"${endFieldName}\" value=\"${this.escapeHtml(endValue)}\" />` : ''}\n ${this.fieldName ? `<input type=\"hidden\" name=\"${this.fieldName}\" value=\"${this.escapeHtml(this.name || '')}\" />` : ''}\n \n <div class=\"date-range-picker-feedback\"></div>\n </div>\n `;\n }\n\n /**\n * Render native fallback template with two date inputs\n */\n renderNativeTemplate(inputId) {\n return `\n <div class=\"date-range-picker-container date-range-native\">\n <div class=\"row g-2\">\n <div class=\"col\">\n <input \n type=\"date\"\n id=\"${inputId}_start\"\n name=\"${this.name}_start\"\n class=\"${this.inputClass}${this.hasError() ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(this.formatDate(this.currentStartDate, 'YYYY-MM-DD'))}\"\n placeholder=\"${this.escapeHtml(this.startPlaceholder)}\"\n ${this.min ? `min=\"${this.min}\"` : ''}\n ${this.max ? `max=\"${this.max}\"` : ''}\n ${this.disabled ? 'disabled' : ''}\n ${this.readonly ? 'readonly' : ''}\n ${this.required ? 'required' : ''}\n data-change-action=\"start-date-changed\"\n />\n </div>\n <div class=\"col-auto d-flex align-items-center\">\n <span class=\"text-muted\">${this.escapeHtml(this.separator.trim())}</span>\n </div>\n <div class=\"col\">\n <input \n type=\"date\"\n id=\"${inputId}_end\"\n name=\"${this.name}_end\"\n class=\"${this.inputClass}${this.hasError() ? ' is-invalid' : ''}\"\n value=\"${this.escapeHtml(this.formatDate(this.currentEndDate, 'YYYY-MM-DD'))}\"\n placeholder=\"${this.escapeHtml(this.endPlaceholder)}\"\n ${this.min ? `min=\"${this.min}\"` : ''}\n ${this.max ? `max=\"${this.max}\"` : ''}\n ${this.disabled ? 'disabled' : ''}\n ${this.readonly ? 'readonly' : ''}\n ${this.required ? 'required' : ''}\n data-change-action=\"end-date-changed\"\n />\n </div>\n </div>\n \n <!-- Hidden input for combined value -->\n <input type=\"hidden\" name=\"${this.name}\" value=\"${this.escapeHtml(this.getCombinedValue())}\" />\n ${this.fieldName ? `<input type=\"hidden\" name=\"${this.fieldName}\" value=\"${this.escapeHtml(this.name || '')}\" />` : ''}\n \n <div class=\"date-range-picker-feedback\"></div>\n </div>\n `;\n }\n\n /**\n * Initialize after render\n */\n async onAfterRender() {\n await super.onAfterRender();\n \n if (this.easepickLoaded && !this.useNative) {\n await this.initializeEasepick();\n } else {\n this.initializeNativeFallback();\n }\n }\n\n /**\n * Initialize Easepick date range picker\n */\n async initializeEasepick() {\n const input = this.getInputElement();\n if (!input || !window.easepick) return;\n\n try {\n const config = {\n element: input,\n css: [\n 'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css',\n ],\n format: this.displayFormat,\n lang: 'en-US',\n autoApply: this.autoApply,\n inline: this.inline,\n readonly: this.readonly,\n zIndex: 9999,\n plugins: ['RangePlugin'],\n RangePlugin: {\n tooltip: true,\n locale: {\n one: 'day',\n other: 'days'\n }\n }\n };\n\n // Add date constraints\n if (this.min) {\n config.minDate = new Date(this.min);\n }\n if (this.max) {\n config.maxDate = new Date(this.max);\n }\n\n // Add event handlers\n config.setup = (picker) => {\n picker.on('select', (e) => {\n const { start, end } = e.detail;\n // Easepick returns DateTime objects, convert to local date strings\n const startDate = start ? this.normalizeDateFromPicker(start) : '';\n const endDate = end ? this.normalizeDateFromPicker(end) : '';\n this.handleRangeChange(startDate, endDate);\n });\n\n picker.on('clear', () => {\n this.handleRangeChange('', '');\n });\n\n picker.on('show', () => {\n this.emit('picker:show');\n });\n\n picker.on('hide', () => {\n this.emit('picker:hide');\n });\n\n picker.on('ready', () => {\n this.applyInitialRange(picker);\n });\n };\n\n this.picker = new window.easepick.create(config);\n\n // Apply initial range immediately as well (in case ready already fired)\n this.applyInitialRange(this.picker);\n\n } catch (error) {\n console.error('Failed to initialize Easepick range picker:', error);\n this.useNative = true;\n await this.render(); // Re-render with native template\n this.initializeNativeFallback();\n }\n }\n\n /**\n * Initialize native HTML5 date inputs fallback\n */\n initializeNativeFallback() {\n // Native inputs are already set up in template\n // Just ensure proper constraint relationships\n this.updateConstraints();\n }\n\n // ========================================\n // Event Handlers\n // ========================================\n\n /**\n * Handle range change from Easepick\n */\n async onChangeRangeChanged(_action, _event, _element) {\n // This is handled by Easepick setup callback\n }\n\n /**\n * Handle start date change in native mode\n */\n async onChangeStartDateChanged(action, event, element) {\n const startDate = element.value;\n this.handleRangeChange(startDate, this.currentEndDate);\n this.updateConstraints();\n }\n\n /**\n * Handle end date change in native mode\n */\n async onChangeEndDateChanged(action, event, element) {\n const endDate = element.value;\n this.handleRangeChange(this.currentStartDate, endDate);\n this.updateConstraints();\n }\n\n /**\n * Handle date range change logic\n */\n handleRangeChange(startDate, endDate) {\n const oldStartDate = this.currentStartDate;\n const oldEndDate = this.currentEndDate;\n \n this.currentStartDate = startDate;\n this.currentEndDate = endDate;\n\n // Update hidden inputs\n this.updateHiddenInputs();\n\n // Emit change events\n if (oldStartDate !== startDate || oldEndDate !== endDate) {\n this.emit('change', {\n startDate,\n endDate,\n combined: this.getCombinedValue(),\n formatted: this.getDisplayValue(),\n oldStartDate,\n oldEndDate\n });\n \n this.emit('range:changed', {\n startDate,\n endDate,\n oldStartDate,\n oldEndDate\n });\n }\n }\n\n /**\n * Update constraints for native inputs\n */\n updateConstraints() {\n if (!this.useNative) return;\n\n const startInput = this.element?.querySelector(`[name=\"${this.name}_start\"]`);\n const endInput = this.element?.querySelector(`[name=\"${this.name}_end\"]`);\n\n if (startInput && endInput) {\n // End date should be >= start date\n if (this.currentStartDate) {\n endInput.min = this.currentStartDate;\n }\n \n // Start date should be <= end date\n if (this.currentEndDate) {\n startInput.max = this.currentEndDate;\n }\n }\n }\n\n // ========================================\n // Utility Methods\n // ========================================\n\n /**\n * Normalize date from Easepick DateTime object\n * Easepick DateTime objects may have timezone issues, so extract the date components directly\n */\n normalizeDateFromPicker(dateObj) {\n if (!dateObj) return '';\n \n // If it's an Easepick DateTime object, it has a toJSDate() method\n if (typeof dateObj.toJSDate === 'function') {\n const jsDate = dateObj.toJSDate();\n return this.formatDate(jsDate, this.format);\n }\n \n // If it has getFullYear/getMonth/getDate methods (like DateTime)\n if (typeof dateObj.getFullYear === 'function') {\n const year = dateObj.getFullYear();\n const month = String(dateObj.getMonth() + 1).padStart(2, '0');\n const day = String(dateObj.getDate()).padStart(2, '0');\n \n switch (this.format) {\n case 'YYYY-MM-DD':\n return `${year}-${month}-${day}`;\n case 'MM/DD/YYYY':\n return `${month}/${day}/${year}`;\n case 'DD/MM/YYYY':\n return `${day}/${month}/${year}`;\n default:\n return `${year}-${month}-${day}`;\n }\n }\n \n // Fallback to formatDate\n return this.formatDate(dateObj, this.format);\n }\n\n /**\n * Format date for different contexts\n */\n formatDate(date, format = this.format) {\n if (!date) return '';\n \n let year, month, day, d;\n \n // If date is a YYYY-MM-DD string, parse it manually to avoid timezone issues\n if (typeof date === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(date)) {\n const parts = date.split('-');\n year = parseInt(parts[0]);\n month = String(parseInt(parts[1])).padStart(2, '0');\n day = String(parseInt(parts[2])).padStart(2, '0');\n } else {\n // Handle Date objects or other string formats\n if (date instanceof Date) {\n d = date;\n } else {\n d = new Date(date);\n }\n \n if (isNaN(d.getTime())) return '';\n\n // Use getFullYear, getMonth, getDate (local time methods)\n year = d.getFullYear();\n month = String(d.getMonth() + 1).padStart(2, '0');\n day = String(d.getDate()).padStart(2, '0');\n }\n \n switch (format) {\n case 'YYYY-MM-DD':\n return `${year}-${month}-${day}`;\n case 'MM/DD/YYYY':\n return `${month}/${day}/${year}`;\n case 'DD/MM/YYYY':\n return `${day}/${month}/${year}`;\n case 'MMM DD, YYYY': {\n const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n const monthIndex = parseInt(month) - 1;\n return `${monthNames[monthIndex]} ${day}, ${year}`;\n }\n default:\n return `${year}-${month}-${day}`;\n }\n }\n\n /**\n * Format date for output based on outputFormat setting\n */\n formatForOutput(date) {\n if (!date) return '';\n \n // If date is already a string in YYYY-MM-DD format, don't re-parse it\n // to avoid timezone issues\n if (typeof date === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(date)) {\n switch (this.outputFormat) {\n case 'epoch':\n // For epoch, we need to parse but use local midnight\n const parts = date.split('-');\n const d = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]));\n return Math.floor(d.getTime() / 1000).toString();\n case 'iso':\n return new Date(date + 'T00:00:00').toISOString();\n case 'date':\n default:\n return date; // Already in the correct format\n }\n }\n \n // For Date objects or other formats, parse and convert\n const d = new Date(date);\n if (isNaN(d.getTime())) return '';\n\n switch (this.outputFormat) {\n case 'epoch':\n return Math.floor(d.getTime() / 1000).toString();\n case 'iso':\n return d.toISOString();\n case 'date':\n default:\n return this.formatDate(date, this.format);\n }\n }\n\n /**\n * Get display value for the input\n */\n getDisplayValue() {\n if (!this.currentStartDate && !this.currentEndDate) return '';\n \n const startFormatted = this.currentStartDate ? \n this.formatDate(this.currentStartDate, this.displayFormat) : '';\n const endFormatted = this.currentEndDate ? \n this.formatDate(this.currentEndDate, this.displayFormat) : '';\n\n if (startFormatted && endFormatted) {\n return `${startFormatted}${this.separator}${endFormatted}`;\n } else if (startFormatted) {\n return startFormatted;\n } else if (endFormatted) {\n return endFormatted;\n }\n \n return '';\n }\n\n /**\n * Get combined value for form submission\n */\n getCombinedValue() {\n if (!this.currentStartDate && !this.currentEndDate) return '';\n \n return JSON.stringify({\n start: this.currentStartDate,\n end: this.currentEndDate\n });\n }\n\n /**\n * Get unique input ID\n */\n getInputId() {\n return this.name ? `daterange_${this.name}_${Date.now()}` : `daterange_${Date.now()}`;\n }\n\n /**\n * Get main input element\n */\n getInputElement() {\n return this.element?.querySelector('input[type=\"text\"], input[name=\"' + this.name + '\"]');\n }\n\n /**\n * Update hidden inputs for form submission\n */\n updateHiddenInputs() {\n const startFieldName = this.startName || (this.name ? `${this.name}_start` : '');\n const endFieldName = this.endName || (this.name ? `${this.name}_end` : '');\n \n const startInput = startFieldName ? this.element?.querySelector(`[name=\"${startFieldName}\"]`) : null;\n const endInput = endFieldName ? this.element?.querySelector(`[name=\"${endFieldName}\"]`) : null;\n const combinedInput = this.name ? this.element?.querySelector(`[name=\"${this.name}\"]`) : null;\n const fieldNameInput = this.fieldName ? this.element?.querySelector(`[name=\"${this.fieldName}\"]`) : null;\n\n const startValue = this.currentStartDate ? this.formatForOutput(this.currentStartDate) : '';\n const endValue = this.currentEndDate ? this.formatForOutput(this.currentEndDate) : '';\n \n if (startInput) startInput.value = startValue;\n if (endInput) endInput.value = endValue;\n if (combinedInput) combinedInput.value = this.getDisplayValue();\n if (fieldNameInput) fieldNameInput.value = this.name || '';\n }\n\n /**\n * Check if field has error\n */\n hasError() {\n // Basic validation: end date should be after start date\n if (this.currentStartDate && this.currentEndDate) {\n return new Date(this.currentEndDate) < new Date(this.currentStartDate);\n }\n return false;\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n if (str == null) return '';\n const div = document.createElement('div');\n div.textContent = String(str);\n return div.innerHTML;\n }\n\n // ========================================\n // Public API Methods\n // ========================================\n\n /**\n * Set the date range values\n */\n setRange(startDate, endDate) {\n this.currentStartDate = startDate;\n this.currentEndDate = endDate;\n \n if (this.picker && this.easepickLoaded) {\n const start = this.normalizeDateValue(startDate);\n const end = this.normalizeDateValue(endDate);\n this.picker.setDateRange(start || null, end || null);\n } else if (this.useNative) {\n const startInput = this.element?.querySelector(`[name=\"${this.name}_start\"]`);\n const endInput = this.element?.querySelector(`[name=\"${this.name}_end\"]`);\n \n if (startInput) startInput.value = this.formatDate(startDate, 'YYYY-MM-DD');\n if (endInput) endInput.value = this.formatDate(endDate, 'YYYY-MM-DD');\n } else {\n const input = this.getInputElement();\n if (input) {\n input.value = this.getDisplayValue();\n }\n }\n \n this.updateHiddenInputs();\n this.emit('range:set', { startDate, endDate });\n }\n\n applyInitialRange(picker) {\n if (!picker || !(this.currentStartDate || this.currentEndDate)) {\n return;\n }\n const start = this.normalizeDateValue(this.currentStartDate);\n const end = this.normalizeDateValue(this.currentEndDate);\n if (start || end) {\n picker.setDateRange(start || null, end || null);\n }\n }\n\n normalizeDateValue(value) {\n if (!value && value !== 0) return null;\n if (value instanceof Date) {\n return isNaN(value) ? null : value;\n }\n const str = String(value).trim();\n if (!str) return null;\n\n // YYYY-MM-DD -> treat as local date (no timezone shift)\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(str)) {\n const [year, month, day] = str.split('-').map(Number);\n const date = new Date(year, month - 1, day);\n return isNaN(date) ? null : date;\n }\n\n // Numeric timestamp (seconds or milliseconds)\n if (/^-?\\d+$/.test(str)) {\n const num = Number(str);\n const ms = str.length <= 10 ? num * 1000 : num;\n const date = new Date(ms);\n return isNaN(date) ? null : date;\n }\n\n const date = new Date(str);\n return isNaN(date) ? null : date;\n }\n\n /**\n * Get the current date range\n */\n getRange() {\n return {\n start: this.currentStartDate,\n end: this.currentEndDate,\n combined: this.getCombinedValue()\n };\n }\n\n /**\n * Clear the date range\n */\n clear() {\n this.setRange('', '');\n }\n\n /**\n * Set start date only\n */\n setStartDate(startDate) {\n this.setRange(startDate, this.currentEndDate);\n }\n\n /**\n * Set end date only\n */\n setEndDate(endDate) {\n this.setRange(this.currentStartDate, endDate);\n }\n\n /**\n * Get start date\n */\n getStartDate() {\n return this.currentStartDate;\n }\n\n /**\n * Get end date\n */\n getEndDate() {\n return this.currentEndDate;\n }\n\n /**\n * Enable/disable the picker\n */\n setEnabled(enabled) {\n this.disabled = !enabled;\n const inputs = this.element?.querySelectorAll('input');\n inputs?.forEach(input => {\n input.disabled = this.disabled;\n });\n }\n\n /**\n * Set readonly state\n */\n setReadonly(readonly) {\n this.readonly = readonly;\n const inputs = this.element?.querySelectorAll('input:not([type=\"hidden\"])');\n inputs?.forEach(input => {\n input.readonly = readonly;\n });\n }\n\n /**\n * Focus the input\n */\n focus() {\n const input = this.getInputElement();\n if (input) {\n input.focus();\n }\n }\n\n /**\n * Show the picker (if using Easepick)\n */\n show() {\n if (this.picker && this.easepickLoaded) {\n this.picker.show();\n }\n }\n\n /**\n * Hide the picker (if using Easepick)\n */\n hide() {\n if (this.picker && this.easepickLoaded) {\n this.picker.hide();\n }\n }\n\n // ========================================\n // FormBuilder Integration\n // ========================================\n\n /**\n * Get form value (for FormBuilder integration)\n */\n getFormValue() {\n return this.getRange();\n }\n\n /**\n * Set form value (for FormBuilder integration)\n */\n async setFormValue(value) {\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value);\n this.setRange(parsed.start, parsed.end);\n } catch {\n // If not JSON, treat as start date only\n this.setRange(value, '');\n }\n } else if (value && typeof value === 'object') {\n this.setRange(value.start || '', value.end || '');\n }\n }\n\n // ========================================\n // Lifecycle Methods\n // ========================================\n\n /**\n * Cleanup when component is destroyed\n */\n async onBeforeDestroy() {\n if (this.picker && this.easepickLoaded) {\n try {\n this.picker.destroy();\n } catch (error) {\n console.warn('Error destroying Easepick range picker instance:', error);\n }\n }\n \n this.picker = null;\n await super.onBeforeDestroy();\n }\n\n /**\n * Static factory method\n */\n static create(options = {}) {\n return new DateRangePicker(options);\n }\n}\n\nexport default DateRangePicker;\n","/**\n * ComboInput - Editable select/autocomplete component for MOJO framework\n * Combines a text input with a dropdown of suggestions\n * Users can either select from the dropdown or type custom values\n *\n * Features:\n * - Autocomplete with filtering\n * - Keyboard navigation (Arrow keys, Enter, Escape)\n * - Custom value entry\n * - Bootstrap 5 styling\n * - Form integration\n * - Optional metadata support (display label but store value)\n *\n * Example Usage:\n * ```javascript\n * const comboInput = new ComboInput({\n * name: 'field_name',\n * value: 'level',\n * placeholder: 'Select or enter field name...',\n * options: [\n * { value: 'level', label: 'Level (error, warning, info)', meta: { type: 'str' } },\n * { value: 'source_ip', label: 'Source IP Address', meta: { type: 'str' } }\n * ],\n * allowCustom: true,\n * showDescription: true\n * });\n * ```\n */\n\nimport View from '@core/View.js';\n\nclass ComboInput extends View {\n constructor(options = {}) {\n const {\n name,\n value = '',\n placeholder = 'Select or type...',\n options: optionsList = [],\n allowCustom = true,\n showDescription = true,\n minChars = 0, // Minimum characters before showing suggestions\n maxSuggestions = 10,\n disabled = false,\n readonly = false,\n required = false,\n class: containerClass = '',\n inputClass = 'form-control',\n onSelect = null, // Callback when option is selected\n onChange = null, // Callback when value changes\n ...viewOptions\n } = options;\n\n super({\n tagName: 'div',\n className: `combo-input ${containerClass}`,\n ...viewOptions\n });\n\n // Configuration\n this.name = name;\n this.placeholder = placeholder;\n this.options = this.normalizeOptions(optionsList);\n this.allowCustom = allowCustom;\n this.showDescription = showDescription;\n this.minChars = minChars;\n this.maxSuggestions = maxSuggestions;\n this.disabled = disabled;\n this.readonly = readonly;\n this.required = required;\n this.inputClass = inputClass;\n this.onSelectCallback = onSelect;\n this.onChangeCallback = onChange;\n\n // State\n this.currentValue = value;\n this.inputValue = this.getDisplayValue(value);\n this.filteredOptions = [];\n this.highlightedIndex = -1;\n this.isOpen = false;\n this.selectedOption = this.findOptionByValue(value);\n }\n\n /**\n * Normalize options to consistent format\n */\n normalizeOptions(options) {\n if (!Array.isArray(options)) return [];\n\n return options.map(opt => {\n if (typeof opt === 'string') {\n return { value: opt, label: opt };\n } else if (typeof opt === 'object' && opt.value !== undefined) {\n return {\n value: opt.value,\n label: opt.label || String(opt.value),\n description: opt.description || opt.label || '',\n meta: opt.meta || {}\n };\n }\n return null;\n }).filter(opt => opt !== null);\n }\n\n /**\n * Find option by value\n */\n findOptionByValue(value) {\n return this.options.find(opt => opt.value === value) || null;\n }\n\n /**\n * Get display value for a given value\n */\n getDisplayValue(value) {\n const option = this.findOptionByValue(value);\n return option ? option.label : value;\n }\n\n /**\n * Render the combo input component\n */\n async renderTemplate() {\n return `\n <div class=\"combo-input-container position-relative\">\n <div class=\"input-wrapper position-relative\">\n ${this.renderInput()}\n ${this.renderDropdownToggle()}\n </div>\n ${this.renderHiddenInput()}\n ${this.renderDropdown()}\n </div>\n `;\n }\n\n /**\n * Render the input field\n */\n renderInput() {\n return `\n <input type=\"text\"\n class=\"${this.inputClass} combo-input-field\"\n placeholder=\"${this.escapeHtml(this.placeholder)}\"\n value=\"${this.escapeHtml(this.inputValue)}\"\n ${this.disabled ? 'disabled' : ''}\n ${this.readonly ? 'readonly' : ''}\n ${this.required ? 'required' : ''}\n data-change-action=\"input-change\"\n data-action=\"input-keydown\"\n autocomplete=\"off\"\n role=\"combobox\"\n aria-expanded=\"${this.isOpen}\"\n aria-autocomplete=\"list\"\n aria-controls=\"combo-dropdown-${this.cid}\">\n `;\n }\n\n /**\n * Render dropdown toggle button\n */\n renderDropdownToggle() {\n if (this.readonly || this.disabled) return '';\n\n return `\n <button type=\"button\"\n class=\"btn btn-sm combo-toggle position-absolute top-50 end-0 translate-middle-y border-0\"\n data-action=\"toggle-dropdown\"\n tabindex=\"-1\"\n aria-label=\"Toggle dropdown\"\n style=\"padding: 0.25rem 0.5rem;\">\n <i class=\"bi bi-chevron-down\"></i>\n </button>\n `;\n }\n\n /**\n * Render hidden input for form submission\n */\n renderHiddenInput() {\n if (!this.name) return '';\n\n return `\n <input type=\"hidden\"\n name=\"${this.name}\"\n value=\"${this.escapeHtml(this.currentValue)}\"\n class=\"combo-input-hidden\">\n `;\n }\n\n /**\n * Render dropdown menu\n */\n renderDropdown() {\n return `\n <div id=\"combo-dropdown-${this.cid}\"\n class=\"combo-dropdown dropdown-menu position-absolute w-100 ${this.isOpen ? 'show' : ''}\"\n role=\"listbox\"\n style=\"max-height: 300px; overflow-y: auto; z-index: 1050;\">\n ${this.renderDropdownContent()}\n </div>\n `;\n }\n\n /**\n * Render dropdown content based on filtered options\n */\n renderDropdownContent() {\n if (this.filteredOptions.length === 0) {\n return this.renderNoResults();\n }\n\n return this.filteredOptions\n .slice(0, this.maxSuggestions)\n .map((option, index) => this.renderOption(option, index))\n .join('');\n }\n\n /**\n * Render a single option\n */\n renderOption(option, index) {\n const isHighlighted = index === this.highlightedIndex;\n const isSelected = option.value === this.currentValue;\n\n return `\n <div class=\"dropdown-item combo-option ${isHighlighted ? 'active' : ''} ${isSelected ? 'selected' : ''}\"\n data-action=\"select-option\"\n data-option-index=\"${index}\"\n role=\"option\"\n aria-selected=\"${isSelected}\"\n style=\"cursor: pointer;\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"flex-grow-1\">\n <div class=\"combo-option-label fw-semibold\">${this.highlightMatch(option.label)}</div>\n ${this.showDescription && option.description ? `\n <div class=\"combo-option-description small text-muted\">${this.escapeHtml(option.description)}</div>\n ` : ''}\n </div>\n ${isSelected ? '<i class=\"bi bi-check text-primary ms-2\"></i>' : ''}\n </div>\n </div>\n `;\n }\n\n /**\n * Render no results message\n */\n renderNoResults() {\n if (this.allowCustom && this.inputValue.length >= this.minChars) {\n return `\n <div class=\"dropdown-item-text text-muted small\">\n <i class=\"bi bi-info-circle me-1\"></i>\n ${this.inputValue ? 'No matches found. Press Enter to use custom value.' : 'Start typing to see suggestions...'}\n </div>\n `;\n }\n\n return `\n <div class=\"dropdown-item-text text-muted small\">\n <i class=\"bi bi-search me-1\"></i>\n No matching options found.\n </div>\n `;\n }\n\n /**\n * Highlight matching text in option label\n */\n highlightMatch(label) {\n if (!this.inputValue) return this.escapeHtml(label);\n\n const escaped = this.escapeHtml(label);\n const pattern = new RegExp(`(${this.escapeRegex(this.inputValue)})`, 'gi');\n return escaped.replace(pattern, '<mark class=\"bg-warning bg-opacity-25\">$1</mark>');\n }\n\n /**\n * Handle component initialization after render\n */\n async onAfterRender() {\n await super.onAfterRender();\n this.updateFilteredOptions();\n\n // Close dropdown when clicking outside\n this.handleOutsideClick = (event) => {\n if (this.element && !this.element.contains(event.target)) {\n this.closeDropdown();\n }\n };\n\n document.addEventListener('click', this.handleOutsideClick);\n }\n\n // ========================================\n // EventDelegate Action Handlers\n // ========================================\n\n /**\n * Handle input changes (typing)\n */\n async onChangeInputChange(event, element) {\n this.inputValue = element.value;\n this.updateFilteredOptions();\n\n if (this.inputValue.length >= this.minChars) {\n this.openDropdown();\n } else {\n this.closeDropdown();\n }\n\n // Reset highlighted index when filtering\n this.highlightedIndex = -1;\n await this.updateDropdownDisplay();\n }\n\n /**\n * Handle input keydown for navigation\n */\n async onActionInputKeydown(event, _element) {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n if (!this.isOpen) {\n this.openDropdown();\n } else {\n this.highlightNext();\n }\n await this.updateDropdownDisplay();\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n if (this.isOpen) {\n this.highlightPrevious();\n await this.updateDropdownDisplay();\n }\n break;\n\n case 'Enter':\n event.preventDefault();\n if (this.isOpen && this.highlightedIndex >= 0) {\n await this.selectHighlightedOption();\n } else if (this.allowCustom && this.inputValue) {\n await this.selectCustomValue(this.inputValue);\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n this.closeDropdown();\n // Restore previous value\n const input = this.element.querySelector('.combo-input-field');\n if (input) {\n input.value = this.getDisplayValue(this.currentValue);\n this.inputValue = input.value;\n }\n break;\n\n case 'Tab':\n // Allow tab to close dropdown and move focus\n if (this.isOpen) {\n this.closeDropdown();\n }\n break;\n }\n }\n\n /**\n * Handle toggle dropdown button click\n */\n async onActionToggleDropdown(event, _element) {\n event.preventDefault();\n event.stopPropagation();\n\n if (this.isOpen) {\n this.closeDropdown();\n } else {\n // Show all options when toggle is clicked\n this.inputValue = '';\n const input = this.element.querySelector('.combo-input-field');\n if (input) {\n input.value = '';\n input.focus();\n }\n this.updateFilteredOptions();\n this.openDropdown();\n await this.updateDropdownDisplay();\n }\n }\n\n /**\n * Handle option selection\n */\n async onActionSelectOption(event, element) {\n event.preventDefault();\n event.stopPropagation();\n\n const index = parseInt(element.getAttribute('data-option-index'));\n if (index >= 0 && index < this.filteredOptions.length) {\n await this.selectOption(this.filteredOptions[index]);\n }\n }\n\n // ========================================\n // Dropdown Management\n // ========================================\n\n /**\n * Open dropdown\n */\n openDropdown() {\n this.isOpen = true;\n const dropdown = this.element?.querySelector('.combo-dropdown');\n if (dropdown) {\n dropdown.classList.add('show');\n }\n\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.setAttribute('aria-expanded', 'true');\n }\n }\n\n /**\n * Close dropdown\n */\n closeDropdown() {\n this.isOpen = false;\n this.highlightedIndex = -1;\n\n const dropdown = this.element?.querySelector('.combo-dropdown');\n if (dropdown) {\n dropdown.classList.remove('show');\n }\n\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.setAttribute('aria-expanded', 'false');\n }\n }\n\n /**\n * Update filtered options based on input\n */\n updateFilteredOptions() {\n const query = this.inputValue.toLowerCase().trim();\n\n if (!query) {\n this.filteredOptions = [...this.options];\n return;\n }\n\n this.filteredOptions = this.options.filter(option => {\n const labelMatch = option.label.toLowerCase().includes(query);\n const valueMatch = String(option.value).toLowerCase().includes(query);\n const descMatch = option.description?.toLowerCase().includes(query);\n return labelMatch || valueMatch || descMatch;\n });\n\n // Sort by relevance (exact matches first)\n this.filteredOptions.sort((a, b) => {\n const aLabelExact = a.label.toLowerCase() === query;\n const bLabelExact = b.label.toLowerCase() === query;\n if (aLabelExact && !bLabelExact) return -1;\n if (!aLabelExact && bLabelExact) return 1;\n\n const aLabelStarts = a.label.toLowerCase().startsWith(query);\n const bLabelStarts = b.label.toLowerCase().startsWith(query);\n if (aLabelStarts && !bLabelStarts) return -1;\n if (!aLabelStarts && bLabelStarts) return 1;\n\n return 0;\n });\n }\n\n /**\n * Update dropdown display\n */\n async updateDropdownDisplay() {\n const dropdown = this.element?.querySelector('.combo-dropdown');\n if (!dropdown) return;\n\n dropdown.innerHTML = this.renderDropdownContent();\n\n // Scroll highlighted option into view\n if (this.highlightedIndex >= 0) {\n const highlightedElement = dropdown.querySelector('.combo-option.active');\n if (highlightedElement) {\n highlightedElement.scrollIntoView({ block: 'nearest' });\n }\n }\n }\n\n /**\n * Highlight next option\n */\n highlightNext() {\n if (this.filteredOptions.length === 0) return;\n\n this.highlightedIndex = (this.highlightedIndex + 1) % Math.min(this.filteredOptions.length, this.maxSuggestions);\n }\n\n /**\n * Highlight previous option\n */\n highlightPrevious() {\n if (this.filteredOptions.length === 0) return;\n\n this.highlightedIndex = this.highlightedIndex <= 0\n ? Math.min(this.filteredOptions.length, this.maxSuggestions) - 1\n : this.highlightedIndex - 1;\n }\n\n /**\n * Select highlighted option\n */\n async selectHighlightedOption() {\n if (this.highlightedIndex >= 0 && this.highlightedIndex < this.filteredOptions.length) {\n await this.selectOption(this.filteredOptions[this.highlightedIndex]);\n }\n }\n\n /**\n * Select an option\n */\n async selectOption(option) {\n this.currentValue = option.value;\n this.inputValue = option.label;\n this.selectedOption = option;\n\n // Update input display\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.value = option.label;\n }\n\n // Update hidden input\n const hiddenInput = this.element?.querySelector('.combo-input-hidden');\n if (hiddenInput) {\n hiddenInput.value = option.value;\n }\n\n this.closeDropdown();\n\n // Emit events\n this.emit('select', { option, value: option.value, meta: option.meta });\n this.emit('change', { value: option.value, option, meta: option.meta });\n\n // Call callbacks\n if (typeof this.onSelectCallback === 'function') {\n this.onSelectCallback(option);\n }\n if (typeof this.onChangeCallback === 'function') {\n this.onChangeCallback(option.value);\n }\n }\n\n /**\n * Select custom value (not in options)\n */\n async selectCustomValue(value) {\n if (!this.allowCustom) return;\n\n this.currentValue = value;\n this.inputValue = value;\n this.selectedOption = null;\n\n // Update hidden input\n const hiddenInput = this.element?.querySelector('.combo-input-hidden');\n if (hiddenInput) {\n hiddenInput.value = value;\n }\n\n this.closeDropdown();\n\n // Emit events\n this.emit('custom', { value });\n this.emit('change', { value, custom: true });\n\n // Call callback\n if (typeof this.onChangeCallback === 'function') {\n this.onChangeCallback(value);\n }\n }\n\n // ========================================\n // Public API Methods\n // ========================================\n\n /**\n * Get current value\n */\n getValue() {\n return this.currentValue;\n }\n\n /**\n * Set value programmatically\n */\n async setValue(value) {\n this.currentValue = value;\n this.selectedOption = this.findOptionByValue(value);\n this.inputValue = this.getDisplayValue(value);\n\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.value = this.inputValue;\n }\n\n const hiddenInput = this.element?.querySelector('.combo-input-hidden');\n if (hiddenInput) {\n hiddenInput.value = value;\n }\n\n this.updateFilteredOptions();\n }\n\n /**\n * Get selected option with metadata\n */\n getSelectedOption() {\n return this.selectedOption;\n }\n\n /**\n * Update options\n */\n async setOptions(options) {\n this.options = this.normalizeOptions(options);\n this.updateFilteredOptions();\n\n if (this.isOpen) {\n await this.updateDropdownDisplay();\n }\n }\n\n /**\n * Enable/disable the component\n */\n setEnabled(enabled) {\n this.disabled = !enabled;\n\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.disabled = this.disabled;\n }\n\n const toggle = this.element?.querySelector('.combo-toggle');\n if (toggle) {\n toggle.disabled = this.disabled;\n }\n }\n\n /**\n * Set readonly state\n */\n setReadonly(readonly) {\n this.readonly = readonly;\n\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n if (readonly) {\n input.setAttribute('readonly', '');\n } else {\n input.removeAttribute('readonly');\n }\n }\n }\n\n /**\n * Focus the input\n */\n focus() {\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.focus();\n }\n }\n\n /**\n * Clear the input\n */\n async clear() {\n await this.setValue('');\n this.inputValue = '';\n\n const input = this.element?.querySelector('.combo-input-field');\n if (input) {\n input.value = '';\n }\n\n this.emit('clear');\n }\n\n // ========================================\n // Form Integration\n // ========================================\n\n /**\n * Get form value (for FormView integration)\n */\n getFormValue() {\n // If allowCustom is enabled and user has typed a value that hasn't been committed,\n // return the typed value instead of the last committed value\n if (this.allowCustom && this.inputValue && this.inputValue !== this.getDisplayValue(this.currentValue)) {\n return this.inputValue;\n }\n return this.currentValue;\n }\n\n /**\n * Set form value (for FormView integration)\n */\n async setFormValue(value) {\n await this.setValue(value);\n }\n\n // ========================================\n // Utility Methods\n // ========================================\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n if (str == null) return '';\n const div = document.createElement('div');\n div.textContent = String(str);\n return div.innerHTML;\n }\n\n /**\n * Escape regex special characters\n */\n escapeRegex(str) {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n /**\n * Cleanup on destroy\n */\n async onBeforeDestroy() {\n if (this.handleOutsideClick) {\n document.removeEventListener('click', this.handleOutsideClick);\n }\n await super.onBeforeDestroy();\n }\n\n /**\n * Static factory method\n */\n static create(options = {}) {\n return new ComboInput(options);\n }\n}\n\nexport default ComboInput;\n","/**\n * ComboBox - Autocomplete text input with dropdown suggestions\n *\n * A simple text input with dropdown suggestions that appears on focus/click.\n * Supports typing to filter suggestions and allows custom values (by default).\n *\n * Features:\n * - Click/focus shows all suggestions\n * - Type to filter suggestions (case-insensitive)\n * - Click suggestion to select\n * - Keyboard navigation (arrow keys, enter, escape)\n * - Optional: restrict to suggestions only (allowCustom: false)\n * - Bootstrap dropdown for consistency\n *\n * @example\n * const combo = new ComboBox({\n * name: 'country',\n * placeholder: 'Type or select...',\n * value: 'USA',\n * options: [\n * { value: 'USA', label: 'United States' },\n * { value: 'CAN', label: 'Canada' }\n * ],\n * allowCustom: true\n * });\n */\n\nimport { View } from '@core/View.js';\nimport Mustache from '@core/utils/mustache.js';\n\nclass ComboBox extends View {\n constructor(options = {}) {\n super(options);\n\n this.name = options.name || 'combo';\n this.placeholder = options.placeholder || options.placeHolder || 'Type or select...';\n this.value = options.value || '';\n this.options = (options.options || []).map(option => {\n if (typeof option === 'string') {\n return { label: option, value: option };\n }\n if (typeof option === 'object' && option !== null) {\n return option;\n } else {\n return { label: option, value: option };\n }\n });\n this.allowCustom = options.allowCustom !== false; // Default: true\n this.disabled = options.disabled || false;\n this.required = options.required || false;\n this.maxHeight = options.maxHeight || 300;\n\n this.filteredOptions = [...this.options];\n this.highlightedIndex = -1;\n this.isOpen = false;\n\n this.template = `\n <div class=\"combobox-container\">\n <div class=\"input-group\">\n <input type=\"text\"\n class=\"form-control combobox-input\"\n placeholder=\"{{placeholder}}\"\n value=\"{{value}}\"\n {{#disabled}}disabled{{/disabled}}\n {{#required}}required{{/required}}\n data-action=\"combobox-input\"\n autocomplete=\"off\">\n <button class=\"btn btn-outline-secondary combobox-toggle\"\n type=\"button\"\n data-action=\"combobox-toggle\"\n {{#disabled}}disabled{{/disabled}}>\n <i class=\"bi bi-chevron-down\"></i>\n </button>\n </div>\n <div class=\"dropdown-menu combobox-dropdown\"\n style=\"max-height: {{maxHeight}}px; overflow-y: auto; width: 100%;\">\n <div data-region=\"dropdown-items\"></div>\n {{^allowCustom}}\n <div class=\"combobox-no-match dropdown-item text-muted\" style=\"display: none;\">\n No matches found\n </div>\n {{/allowCustom}}\n </div>\n </div>\n `;\n\n this.itemTemplate = `\n {{#items}}\n <button type=\"button\"\n class=\"dropdown-item combobox-item {{#highlighted}}active{{/highlighted}}\"\n data-action=\"select-item\"\n data-value=\"{{value}}\"\n data-index=\"{{index}}\">\n {{label}}\n </button>\n {{/items}}\n `;\n }\n\n async onInit() {\n // onInit is for creating children, not DOM manipulation\n await super.onInit();\n }\n\n async onAfterRender() {\n await super.onAfterRender();\n\n // Now DOM is ready, we can query elements\n this.input = this.element.querySelector('.combobox-input');\n this.dropdown = this.element.querySelector('.combobox-dropdown');\n this.dropdownItems = this.element.querySelector('[data-region=\"dropdown-items\"]');\n this.noMatchDiv = this.element.querySelector('.combobox-no-match');\n\n // Set initial value if it was set before render\n if (this.value && this.input) {\n const option = this.options.find(opt => opt.value === this.value);\n if (option) {\n this.input.value = option.label || option.value;\n } else if (this.allowCustom) {\n this.input.value = this.value;\n }\n }\n\n // Render initial items\n this.renderItems();\n\n // Set up event listeners\n this.setupEventListeners();\n }\n\n setupEventListeners() {\n // Input events\n this.input.addEventListener('focus', () => this.openDropdown());\n this.input.addEventListener('input', (e) => this.handleInput(e));\n this.input.addEventListener('keydown', (e) => this.handleKeydown(e));\n\n // Click outside to close\n document.addEventListener('click', (e) => {\n if (!this.element.contains(e.target)) {\n this.closeDropdown();\n }\n });\n }\n\n handleInput(event) {\n const searchText = event.target.value.toLowerCase();\n\n // Filter options based on input\n this.filteredOptions = this.options.filter(opt => {\n const label = opt.label || opt.value;\n return label.toLowerCase().includes(searchText);\n });\n\n this.highlightedIndex = -1;\n this.renderItems();\n this.openDropdown();\n\n // Update no-match message\n if (!this.allowCustom && this.noMatchDiv) {\n this.noMatchDiv.style.display = this.filteredOptions.length === 0 ? 'block' : 'none';\n }\n\n // Emit change event\n this.value = event.target.value;\n this.emit('change', { value: this.value });\n }\n\n handleKeydown(event) {\n if (!this.isOpen && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {\n this.openDropdown();\n event.preventDefault();\n return;\n }\n\n if (!this.isOpen) return;\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.highlightedIndex = Math.min(this.highlightedIndex + 1, this.filteredOptions.length - 1);\n this.renderItems();\n this.scrollToHighlighted();\n break;\n\n case 'ArrowUp':\n event.preventDefault();\n this.highlightedIndex = Math.max(this.highlightedIndex - 1, -1);\n this.renderItems();\n this.scrollToHighlighted();\n break;\n\n case 'Enter':\n event.preventDefault();\n if (this.highlightedIndex >= 0) {\n this.selectItem(this.filteredOptions[this.highlightedIndex]);\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n this.closeDropdown();\n break;\n\n case 'Tab':\n this.closeDropdown();\n break;\n }\n }\n\n scrollToHighlighted() {\n if (this.highlightedIndex < 0) return;\n\n const items = this.dropdownItems.querySelectorAll('.combobox-item');\n const highlightedItem = items[this.highlightedIndex];\n\n if (highlightedItem) {\n highlightedItem.scrollIntoView({ block: 'nearest' });\n }\n }\n\n openDropdown() {\n if (this.disabled || this.isOpen) return;\n\n this.isOpen = true;\n this.dropdown.classList.add('show');\n\n // Reset filter to show all options if input is empty\n if (this.input.value === '') {\n this.filteredOptions = [...this.options];\n this.renderItems();\n }\n }\n\n closeDropdown() {\n if (!this.isOpen) return;\n\n this.isOpen = false;\n this.dropdown.classList.remove('show');\n this.highlightedIndex = -1;\n\n // Validate value if allowCustom is false\n if (!this.allowCustom) {\n const validOption = this.options.find(opt =>\n opt.value === this.input.value || opt.label === this.input.value\n );\n\n if (!validOption && this.input.value !== '') {\n // Reset to last valid value\n this.input.value = this.value;\n }\n }\n }\n\n selectItem(option) {\n const value = option.value;\n const label = option.label || option.value;\n\n this.input.value = label;\n this.value = value;\n\n this.closeDropdown();\n\n // Reset filter\n this.filteredOptions = [...this.options];\n this.highlightedIndex = -1;\n\n // Emit change event\n this.emit('change', { value: this.value, label: label });\n }\n\n renderItems() {\n const items = this.filteredOptions.map((opt, index) => ({\n value: opt.value,\n label: opt.label || opt.value,\n index,\n highlighted: index === this.highlightedIndex\n }));\n\n const html = Mustache.render(this.itemTemplate, { items });\n this.dropdownItems.innerHTML = html;\n }\n\n // Action handlers\n async onActionComboboxInput(event, element) {\n // Handled by event listeners\n }\n\n async onActionComboboxToggle(event, element) {\n if (this.isOpen) {\n this.closeDropdown();\n } else {\n this.input.focus();\n this.openDropdown();\n }\n }\n\n async onActionSelectItem(event, element) {\n const value = element.getAttribute('data-value');\n const option = this.options.find(opt => opt.value === value);\n\n if (option) {\n this.selectItem(option);\n }\n }\n\n // Form integration methods\n getValue() {\n return this.value;\n }\n\n setValue(value) {\n this.value = value;\n\n // If input doesn't exist yet (before onAfterRender), just store the value\n if (!this.input) {\n return;\n }\n\n // Find the option to get the label\n const option = this.options.find(opt => opt.value === value);\n if (option) {\n this.input.value = option.label || option.value;\n } else if (this.allowCustom) {\n this.input.value = value;\n }\n }\n\n setFormValue(value) {\n this.setValue(value);\n }\n\n getTemplateData() {\n return {\n placeholder: this.placeholder,\n value: this.input ? this.input.value : this.value,\n disabled: this.disabled,\n required: this.required,\n maxHeight: this.maxHeight,\n allowCustom: this.allowCustom\n };\n }\n}\n\nexport default ComboBox;\n","/**\n * FormView - Complete form component for MOJO framework\n * Uses FormBuilder for HTML generation and EventDelegate for event handling\n * Handles form lifecycle, validation, data management, and component integration\n */\n\nimport View from '@core/View.js';\nimport FormBuilder from './FormBuilder.js';\nimport applyFileDropMixin from '@core/mixins/FileDropMixin.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport { FormPlugins } from '@core/forms/FormPlugins.js';\n\nimport { TagInput, CollectionSelect, CollectionMultiSelect, DatePicker, DateRangePicker, ComboInput, ComboBox } from './inputs/index.js';\nimport MultiSelectDropdown from './inputs/MultiSelectDropdown.js';\n\nclass FormView extends View {\n constructor(options = {}) {\n const {\n formConfig = options.config,\n fields,\n model = null,\n data = {},\n defaults = null,\n errors = {},\n fileHandling = 'base64', // 'base64' | 'multipart'\n autosaveModelField = false, // Auto-save model on field changes\n ...viewOptions\n } = options;\n\n super({\n tagName: 'div',\n className: 'form-view',\n ...viewOptions\n });\n\n // Notify plugins this FormView is initializing\n FormPlugins.onFormViewInit?.(this);\n\n // Store data sources\n this.model = model;\n this.defaults = defaults || data;\n this._originalData = data;\n this.errors = errors;\n this.loading = false;\n this.fileHandling = fileHandling;\n this.autosaveModelField = autosaveModelField;\n this.customComponents = new Map();\n this.fieldStatusManagers = new Map(); // Track field status managers\n this.saveTimeouts = new Map(); // Debouncing timeouts for autosave\n this.pendingSaveFields = new Map(); // Track fields pending save (for batching)\n this.batchSaveTimeout = null; // Global timeout for batch saving\n this.isSaving = false; // Prevent save loops\n\n // Prepare combined data for FormBuilder\n this.data = this.prepareFormData();\n\n // Form configuration\n this.formConfig = formConfig || { fields: fields || [] };\n this.formBuilder = new FormBuilder({\n ...this.getFormConfig(),\n data: this.data, // Pass data so field.value defaults work\n errors\n });\n }\n\n /**\n * Prepare form data by combining defaults, model, and data in priority order\n * Priority: data > model > defaults\n * @returns {Object} Combined data object for FormBuilder\n */\n prepareFormData() {\n const formData = { ...this.defaults };\n\n // Add model data if available\n if (this.model) {\n if (this.model.attributes && typeof this.model.attributes === 'object') {\n Object.assign(formData, this.model.attributes);\n } else if (typeof this.model.toJSON === 'function') {\n const modelData = this.model.toJSON();\n Object.assign(formData, modelData);\n } else if (typeof this.model === 'object' && this.model.constructor === Object) {\n Object.assign(formData, this.model);\n }\n }\n\n // Original data takes highest priority\n if (this._originalData) {\n Object.assign(formData, this._originalData);\n }\n\n return formData;\n }\n\n getFormConfig() {\n const config = { ...this.formConfig };\n const app = this.getApp();\n\n // Handle case where fields might not be defined\n if (this.formConfig.fields && Array.isArray(this.formConfig.fields)) {\n config.fields = this.formConfig.fields.filter(field => {\n if (!field.permissions) return true;\n return app.activeUser?.hasPermission(field.permissions);\n });\n } else {\n config.fields = [];\n }\n\n return config;\n }\n\n /**\n * Use FormBuilder for template generation\n */\n async renderTemplate() {\n return this.formBuilder.buildFormHTML();\n }\n\n /**\n * Called after form is rendered to populate values and initialize\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n this.data = this.prepareFormData();\n // Populate form with current data\n this.populateFormValues();\n\n // Initialize form components\n this.initializeFormComponents();\n\n // Add generic change handlers for all form inputs\n this.initializeChangeHandlers();\n\n // Prevent form submission on Enter key\n const form = this.getFormElement();\n if (form) {\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n return false;\n });\n }\n\n // Allow plugins to run after the form renders and initializes\n FormPlugins.onFormViewAfterRender?.(this);\n }\n\n /**\n * Populate all form fields with current data values\n */\n populateFormValues() {\n if (!this.element || !this.formConfig?.fields) return;\n\n // Disable autosave during form population\n this._isPopulating = true;\n\n try {\n this.formConfig.fields.forEach(field => {\n this.populateFieldRecursive(field);\n });\n } finally {\n // Always re-enable autosave\n this._isPopulating = false;\n }\n }\n\n /**\n * Recursively populate a field and its nested fields\n * Handles groups, tabsets, and regular fields\n */\n populateFieldRecursive(field) {\n if (field.type === 'group' && field.fields) {\n // Handle group fields\n field.fields.forEach(groupField => {\n this.populateFieldRecursive(groupField);\n });\n } else if (field.type === 'tabset' && field.tabs) {\n // Handle tabset fields - iterate through tabs and their fields\n field.tabs.forEach(tab => {\n if (tab.fields && Array.isArray(tab.fields)) {\n tab.fields.forEach(tabField => {\n this.populateFieldRecursive(tabField);\n });\n }\n });\n } else {\n // Handle regular field\n this.populateFieldValue(field);\n }\n }\n\n /**\n * Populate a single field with its value from data\n */\n populateFieldValue(fieldConfig) {\n if (!fieldConfig.name || !this.element) return;\n\n const fieldElement = this.element.querySelector(`[name=\"${fieldConfig.name}\"]`);\n if (!fieldElement) return;\n\n // Use MOJOUtils to handle nested properties like 'permissions.manage_users'\n const value = MOJOUtils.getContextData(this.data, fieldConfig.name);\n\n // Only set value if we have actual data - don't overwrite field defaults with undefined\n if (value !== undefined && value !== null) {\n this.setFieldValue(fieldElement, fieldConfig, value);\n }\n }\n\n /**\n * Initialize form components after rendering\n */\n initializeFormComponents() {\n this.initializeImageFields();\n this.initializeCustomComponents();\n this.initializeTagInputs();\n this.initializeMultiSelectDropdowns();\n this.initializeComboBoxes();\n this.initializeCollectionSelects();\n this.initializeCollectionMultiSelects();\n this.initializeDatePickers();\n this.initializeDateRangePickers();\n this.initializePasswordFields();\n }\n\n\n\n /**\n * Initialize image fields with FileDropMixin\n */\n initializeImageFields() {\n const imageFields = this.element.querySelectorAll('.image-drop-zone.droppable');\n\n if (imageFields.length > 0) {\n // Enable file drop functionality (mixin already applied at module level)\n this.enableFileDrop({\n acceptedTypes: ['image/*'],\n maxFileSize: 10 * 1024 * 1024, // 10MB\n multiple: false,\n dropZoneSelector: '.image-drop-zone.droppable',\n visualFeedback: true,\n dragOverClass: 'drag-over',\n dragActiveClass: 'drag-active'\n });\n }\n }\n\n /**\n * Initialize custom components (tags, collection selects, enhanced date pickers, etc.)\n */\n initializeCustomComponents() {\n // Initialize enhanced input components\n this.initializeTagInputs();\n this.initializeCollectionSelects();\n this.initializeCollectionMultiSelects();\n this.initializeDatePickers();\n this.initializeDateRangePickers();\n this.initializeComboInputs();\n\n // Give plugins a chance to initialize fields (autocomplete, masks, etc.)\n try {\n const visitFields = (fields = []) => {\n fields.forEach(f => {\n if (f && f.type === 'group' && Array.isArray(f.fields)) {\n visitFields(f.fields);\n } else if (f && f.name) {\n const el = this.element.querySelector(`[name=\"${f.name}\"], #${f.id || f.name}`);\n if (el) {\n FormPlugins.onFieldInit?.(this, el, f);\n }\n }\n });\n };\n visitFields(this.formConfig?.fields || []);\n } catch (err) {\n console.warn('FormPlugins.onFieldInit error:', err);\n }\n\n // Find containers for other custom components\n const componentContainers = this.element.querySelectorAll('[data-component]');\n\n componentContainers.forEach(container => {\n const componentType = container.getAttribute('data-component');\n const fieldName = container.getAttribute('data-field');\n\n if (componentType && fieldName) {\n\n }\n });\n }\n\n /**\n * Initialize generic change handlers for all form inputs\n */\n initializeChangeHandlers() {\n if (!this.element) return;\n\n // Add change listeners to all form inputs that don't already have specific handlers\n const inputs = this.element.querySelectorAll('input:not([data-action]), select:not([data-action]), textarea:not([data-action])');\n\n console.log('FormView: initializeChangeHandlers - found', inputs.length, 'inputs');\n\n inputs.forEach(input => {\n console.log('FormView: Processing input:', input.type, input.name, input.getAttribute('data-change-action'));\n // Skip inputs that already have specific handlers or are handled by custom components\n if (input.hasAttribute('data-component') || input.classList.contains('form-check-input')) {\n return;\n }\n\n // Add change event listener\n input.addEventListener('change', (event) => {\n // Skip autosave during form population\n if (this._isPopulating) return;\n\n const fieldName = input.name;\n if (fieldName) {\n let value = input.value;\n\n // Handle different input types\n if (input.type === 'checkbox') {\n value = input.checked;\n } else if (input.type === 'radio') {\n // Only process if this radio is checked\n if (!input.checked) return;\n } else if (input.multiple && input.selectedOptions) {\n // Handle multi-select\n value = Array.from(input.selectedOptions).map(opt => opt.value);\n } else if (input.type === 'file') {\n // Handle file inputs (including images)\n const changeAction = input.getAttribute('data-change-action');\n if (changeAction === 'image-selected') {\n this.onChangeImageSelected(event, input);\n return; // Don't call handleFieldChange for images\n } else if (changeAction === 'file-selected') {\n this.onChangeFileSelected(event, input);\n return; // Don't call handleFieldChange for files\n }\n }\n\n this.handleFieldChange(fieldName, value);\n }\n });\n\n // Also add input event for text inputs for more responsive autosave\n if (input.type === 'text' || input.type === 'email' || input.type === 'url' || input.tagName === 'TEXTAREA') {\n input.addEventListener('input', (event) => {\n // Skip autosave during form population\n if (this._isPopulating) return;\n\n const fieldName = input.name;\n if (fieldName) {\n this.handleFieldChange(fieldName, input.value);\n }\n });\n }\n });\n }\n\n /**\n * Initialize TagInput components\n */\n initializeTagInputs() {\n const tagPlaceholders = this.element.querySelectorAll('[data-field-type=\"tag\"]');\n\n tagPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Create TagInput component\n const tagInput = new TagInput({\n ...config,\n containerId: null // We'll mount directly\n });\n\n // Replace placeholder with TagInput\n tagInput.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, tagInput);\n\n // Listen for changes\n tagInput.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n } catch (error) {\n // TagInput initialization failed\n }\n });\n }\n\n /**\n * Initialize MultiSelectDropdown components\n */\n initializeMultiSelectDropdowns() {\n const multiselectPlaceholders = this.element.querySelectorAll('[data-field-type=\"multiselect\"]');\n\n multiselectPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Get field configuration to retrieve options\n const fieldConfig = this.getFormFieldConfig(fieldName);\n if (!fieldConfig) {\n return;\n }\n\n // Create MultiSelectDropdown component\n const multiselect = new MultiSelectDropdown({\n ...config,\n options: fieldConfig.options || [],\n placeholder: fieldConfig.placeholder || config.placeholder || 'Select...',\n label: fieldConfig.label,\n containerId: null // We'll mount directly\n });\n\n // Set initial value - prioritize config.value (which may be an array), then this.data\n let value = config.value ?? MOJOUtils.getContextData(this.data, fieldName);\n \n if (value) {\n multiselect.setFormValue(value);\n }\n\n // Replace placeholder with MultiSelectDropdown\n multiselect.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, multiselect);\n\n // Listen for changes\n multiselect.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n } catch (error) {\n console.error('MultiSelectDropdown initialization failed:', error);\n }\n });\n }\n\n /**\n * Initialize ComboBox components (autocomplete dropdowns)\n */\n initializeComboBoxes() {\n const comboboxPlaceholders = this.element.querySelectorAll('[data-field-type=\"combobox\"]');\n\n comboboxPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Get field configuration to retrieve options\n const fieldConfig = this.getFormFieldConfig(fieldName);\n if (!fieldConfig) {\n return;\n }\n\n // Create ComboBox component\n const combobox = new ComboBox({\n ...config,\n options: fieldConfig.options || [],\n placeholder: fieldConfig.placeholder || config.placeholder || 'Type or select...',\n containerId: null // We'll mount directly\n });\n\n // Set initial value - prioritize config.value, then this.data\n let value = config.value ?? MOJOUtils.getContextData(this.data, fieldName);\n \n if (value) {\n combobox.setFormValue(value);\n }\n\n // Replace placeholder with ComboBox\n combobox.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, combobox);\n\n // Listen for changes\n combobox.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n } catch (error) {\n console.error('ComboBox initialization failed:', error);\n }\n });\n }\n\n /**\n * Initialize CollectionSelect components\n */\n initializeCollectionSelects() {\n const collectionPlaceholders = this.element.querySelectorAll('[data-field-type=\"collection\"]');\n\n collectionPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Get Collection class from field config\n const fieldConfig = this.getFormFieldConfig(fieldName); // this.formConfig.fields.find(f => f.name === fieldName);\n if (!fieldConfig || !fieldConfig.Collection) {\n\n return;\n }\n\n // Create collection instance\n const collection = new fieldConfig.Collection();\n if (fieldConfig.collectionParams) {\n collection.params = {...collection.params, ...fieldConfig.collectionParams};\n }\n\n // Create CollectionSelect component\n const collectionSelect = new CollectionSelect({\n ...config,\n collection,\n defaultParams: fieldConfig.defaultParams || null, // Can be dict or callback\n containerId: null // We'll mount directly\n });\n\n let value = MOJOUtils.getContextData(this.data, fieldName);\n if (value) {\n collectionSelect.setFormValue(value);\n }\n\n // Replace placeholder with CollectionSelect\n collectionSelect.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, collectionSelect);\n\n // Listen for changes\n collectionSelect.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n } catch (error) {\n // CollectionSelect initialization failed\n }\n });\n }\n\n /**\n * Initialize CollectionMultiSelect components\n */\n initializeCollectionMultiSelects() {\n const collectionMultiSelectPlaceholders = this.element.querySelectorAll('[data-field-type=\"collectionmultiselect\"]');\n\n collectionMultiSelectPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Get Collection class from field config\n const fieldConfig = this.getFormFieldConfig(fieldName);\n if (!fieldConfig || !fieldConfig.Collection) {\n return;\n }\n\n // Create collection instance\n const collection = new fieldConfig.Collection();\n if (fieldConfig.collectionParams) {\n collection.params = {...collection.params, ...fieldConfig.collectionParams};\n }\n\n // Create CollectionMultiSelect component\n const collectionMultiSelect = new CollectionMultiSelect({\n ...config,\n collection,\n defaultParams: fieldConfig.defaultParams || null, // Can be dict or callback\n itemTemplate: fieldConfig.itemTemplate || null, // Custom item template\n containerId: null // We'll mount directly\n });\n\n let value = MOJOUtils.getContextData(this.data, fieldName);\n if (value) {\n collectionMultiSelect.setFormValue(value);\n }\n\n // Replace placeholder with CollectionMultiSelect\n collectionMultiSelect.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, collectionMultiSelect);\n\n // Listen for changes\n collectionMultiSelect.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n } catch (error) {\n console.error('CollectionMultiSelect initialization failed:', error);\n }\n });\n }\n\n /**\n * Initialize DatePicker components\n */\n initializeDatePickers() {\n const datePickerPlaceholders = this.element.querySelectorAll('[data-field-type=\"datepicker\"]');\n\n datePickerPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Create DatePicker component\n const datePicker = new DatePicker({\n ...config,\n containerId: null // We'll mount directly\n });\n\n // Replace placeholder with DatePicker\n datePicker.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, datePicker);\n\n // Listen for changes\n datePicker.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n } catch (error) {\n // DatePicker initialization failed\n }\n });\n }\n\n /**\n * Initialize DateRangePicker components\n */\n initializeDateRangePickers() {\n const dateRangePlaceholders = this.element.querySelectorAll('[data-field-type=\"daterange\"]');\n\n dateRangePlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Create DateRangePicker component\n const dateRangePicker = new DateRangePicker({\n ...config,\n containerId: null // We'll mount directly\n });\n\n // Replace placeholder with DateRangePicker\n dateRangePicker.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, dateRangePicker);\n\n // Listen for changes\n dateRangePicker.on('change', (data) => {\n this.handleFieldChange(fieldName, data.combined);\n });\n\n } catch (error) {\n // DateRangePicker initialization failed\n }\n });\n }\n\n /**\n * Initialize ComboInput components\n */\n initializeComboInputs() {\n const comboPlaceholders = this.element.querySelectorAll('[data-field-type=\"combo\"]');\n\n comboPlaceholders.forEach(placeholder => {\n try {\n const fieldName = placeholder.getAttribute('data-field-name');\n const configData = placeholder.getAttribute('data-field-config');\n const config = JSON.parse(configData);\n\n // Create ComboInput component\n const comboInput = new ComboInput({\n ...config,\n containerId: null // We'll mount directly\n });\n\n let value = MOJOUtils.getContextData(this.data, fieldName);\n if (value) {\n comboInput.setValue(value);\n }\n\n // Replace placeholder with ComboInput\n comboInput.render(true, placeholder);\n\n // Store reference for cleanup\n this.customComponents.set(fieldName, comboInput);\n\n // Listen for changes\n comboInput.on('change', (data) => {\n this.handleFieldChange(fieldName, data.value);\n });\n\n // Listen for selection with metadata\n comboInput.on('select', (data) => {\n // Emit additional event with metadata for custom handling\n this.emit('field:select', {\n field: fieldName,\n value: data.value,\n option: data.option,\n meta: data.meta\n });\n });\n\n } catch (error) {\n console.error('ComboInput initialization failed:', error);\n }\n });\n }\n\n /**\n * Handle field changes from custom components\n */\n handleFieldChange(fieldName, value) {\n // Skip autosave during form population or field revert\n if (this._isPopulating) return;\n\n // Update internal data\n this.data[fieldName] = value;\n\n // Handle autosave or regular model update\n if (this.autosaveModelField && this.model) {\n // Auto-save individual field to model\n this.handleFieldSave(fieldName, value);\n } else if (this.model && this.options.allowModelChange) {\n // Regular model update without save\n this._isFormDrivenChange = true;\n this.model.set(fieldName, value);\n }\n\n // Emit change event\n this.emit('field:change', { field: fieldName, value });\n\n // Notify plugins of the field change\n FormPlugins.onFieldChange?.(this, fieldName, value);\n }\n\n /**\n * Handle saving field changes to the model with intelligent batching\n * When multiple fields change within a short time (e.g., autofill), they are batched together\n * @param {string} fieldName - Name of the field being saved\n * @param {*} value - New value to save\n */\n async handleFieldSave(fieldName, value) {\n if (!this.model) return;\n\n // Add field to pending save queue\n this.pendingSaveFields.set(fieldName, value);\n\n // Show saving status for this field\n const statusManager = this.getFieldStatusManager(fieldName);\n statusManager.showStatus('saving');\n\n // Clear existing batch timeout\n if (this.batchSaveTimeout) {\n clearTimeout(this.batchSaveTimeout);\n }\n\n // Set new batch timeout - if no more changes come in 300ms, save all pending fields\n this.batchSaveTimeout = setTimeout(async () => {\n await this.executeBatchSave();\n }, 300);\n }\n\n /**\n * Execute a batch save of all pending field changes\n * This sends all changed fields in a single request to avoid race conditions\n * @private\n */\n async executeBatchSave() {\n if (this.isSaving || this.pendingSaveFields.size === 0) return;\n\n // Collect all pending changes BEFORE clearing\n const changes = Object.fromEntries(this.pendingSaveFields);\n const fieldNames = Array.from(this.pendingSaveFields.keys());\n\n try {\n this.isSaving = true;\n\n // Clear pending fields before save\n this.pendingSaveFields.clear();\n this.batchSaveTimeout = null;\n\n // Mark as form-driven change to prevent sync back\n this._isFormDrivenChange = true;\n\n // Save all changes in a single request\n if (typeof this.model.save === 'function') {\n const resp = await this.model.save(changes);\n \n // Check if save was successful\n if (!resp || !resp.success || (resp.data && !resp.data.status)) {\n const errorMsg = resp?.data?.error || resp?.error || resp?.message || 'Save failed';\n \n // Show error toast\n this.getApp()?.toast?.error(errorMsg);\n \n // Revert fields to their original values\n this.revertFields(fieldNames);\n \n // Show error status for all fields\n fieldNames.forEach(fieldName => {\n const statusManager = this.getFieldStatusManager(fieldName);\n statusManager.showStatus('error', { message: errorMsg });\n });\n \n return; // Exit early, don't show success\n }\n } else {\n // Just update model attributes if no save method\n Object.entries(changes).forEach(([key, val]) => {\n this.model.set(key, val);\n });\n }\n\n // Show success status for all saved fields (only if we didn't return early)\n fieldNames.forEach(fieldName => {\n const statusManager = this.getFieldStatusManager(fieldName);\n statusManager.showStatus('saved');\n });\n\n } catch (error) {\n console.error('Batch save error:', error);\n\n // Show error toast\n this.getApp()?.toast?.error(error.message || 'An error occurred while saving');\n\n // Revert fields to their original values\n this.revertFields(fieldNames);\n\n // Show error status for all fields that were attempted\n fieldNames.forEach(fieldName => {\n const statusManager = this.getFieldStatusManager(fieldName);\n statusManager.showStatus('error', { message: error.message });\n });\n } finally {\n this.isSaving = false;\n }\n }\n\n /**\n * Revert form fields to their original model values\n * Called when save fails to keep UI in sync with server state\n * @param {Array<string>} fieldNames - Names of fields to revert\n * @private\n */\n revertFields(fieldNames) {\n if (!this.model) return;\n\n // Temporarily disable autosave during revert\n const wasPopulating = this._isPopulating;\n this._isPopulating = true;\n\n try {\n fieldNames.forEach(fieldName => {\n // Get original value from model\n const originalValue = this.model.get(fieldName);\n \n // Update internal data\n this.data[fieldName] = originalValue;\n \n // Find the field element and revert its value\n const fieldElement = this.element?.querySelector(`[name=\"${fieldName}\"]`);\n if (fieldElement) {\n const fieldConfig = this.getFormFieldConfig(fieldName);\n if (fieldConfig) {\n this.setFieldValue(fieldElement, fieldConfig, originalValue);\n } else {\n // Fallback if no config found\n if (fieldElement.type === 'checkbox') {\n fieldElement.checked = Boolean(originalValue);\n } else {\n fieldElement.value = originalValue ?? '';\n }\n }\n }\n });\n } finally {\n // Re-enable autosave\n this._isPopulating = wasPopulating;\n }\n }\n\n /**\n * Get or create a field status manager for a specific field\n * @param {string} fieldName - Name of the field\n * @returns {FieldStatusManager} Status manager instance\n */\n getFieldStatusManager(fieldName) {\n if (!this.fieldStatusManagers.has(fieldName)) {\n const fieldElement = this.element.querySelector(`[name=\"${fieldName}\"]`);\n if (fieldElement) {\n const statusManager = new FieldStatusManager(fieldElement);\n this.fieldStatusManagers.set(fieldName, statusManager);\n }\n }\n return this.fieldStatusManagers.get(fieldName);\n }\n\n /**\n * Refresh form data and repopulate values\n * Call this when model or data changes externally\n */\n refreshForm() {\n this.data = this.prepareFormData();\n\n // If mounted, repopulate form values\n if (this.element) {\n this.populateFormValues();\n }\n }\n\n /**\n * Get reason why a field is considered changed\n * @param {*} newValue - New value from form\n * @param {*} originalValue - Original value from model\n * @returns {string} Reason for change\n */\n getChangeReason(newValue, originalValue) {\n if (newValue instanceof File) {\n if (newValue.size === 0 || newValue.name === '' || newValue.name === 'blob') {\n return 'empty file, no change';\n }\n return `file upload: ${newValue.name}, ${newValue.size} bytes`;\n }\n\n if (typeof newValue === 'string' && newValue.startsWith('data:image/')) {\n return 'base64 image upload';\n }\n\n if (typeof newValue === 'boolean' || typeof originalValue === 'boolean') {\n const newBool = Boolean(newValue);\n const originalBool = originalValue === null || originalValue === undefined ? false : Boolean(originalValue);\n return `boolean: ${originalBool} → ${newBool}`;\n }\n\n const newStr = newValue === null || newValue === undefined ? '' : String(newValue).trim();\n const originalStr = originalValue === null || originalValue === undefined ? '' : String(originalValue).trim();\n\n if (originalValue === null || originalValue === undefined) {\n return 'was null/undefined, now has value';\n }\n\n if (newValue === null || newValue === undefined) {\n return 'was value, now null/undefined';\n }\n\n return 'text content changed';\n }\n\n /**\n * Set form data (updates the data property and rebuilds form)\n * @param {Object} newData - Data to set\n */\n setFormData(newData) {\n this._originalData = { ...this._originalData, ...newData };\n this.refreshForm();\n }\n\n\n\n // ========================================\n // EventDelegate Action Handlers\n // ========================================\n\n /**\n * Handle form submission\n */\n async onActionSubmitForm(event, element) {\n event.preventDefault();\n\n const result = await this.handleSubmit();\n\n if (result.success) {\n // Update internal data\n this.data = result.data;\n\n // Emit success event\n this.emit('submit', {\n data: result.data,\n result: result.result,\n form: this,\n event\n });\n\n // Call submit handler if provided (for non-model forms)\n if (!this.model && this.formConfig.onSubmit && typeof this.formConfig.onSubmit === 'function') {\n await this.formConfig.onSubmit(result.data, this);\n }\n } else {\n // Emit error event\n this.emit('error', {\n error: result.error,\n result: result,\n form: this\n });\n }\n }\n\n /**\n * Handle form reset\n */\n async onActionResetForm(event, _element) {\n const form = this.getFormElement();\n if (form) {\n form.reset();\n this.data = {};\n this.clearAllErrors();\n\n this.emit('reset', {\n form: this,\n event\n });\n }\n }\n\n /**\n * Handle image upload click\n */\n async onActionClickImageUpload(event, element) {\n console.log('FormView: onActionClickImageUpload called');\n console.log('FormView: element:', element);\n\n const fieldId = element.getAttribute('data-field-id');\n console.log('FormView: fieldId:', fieldId);\n\n if (!fieldId) {\n console.error('FormView: No fieldId attribute found');\n return;\n }\n\n const fileInput = this.element.querySelector(`#${fieldId}`);\n console.log('FormView: fileInput:', fileInput);\n\n if (fileInput && !fileInput.disabled) {\n fileInput.click();\n console.log('FormView: fileInput.click() called');\n } else if (!fileInput) {\n console.error('FormView: fileInput not found for fieldId:', fieldId);\n } else {\n console.log('FormView: fileInput is disabled');\n }\n }\n\n /**\n * Handle image removal\n */\n async onActionRemoveImage(event, element) {\n const fieldName = element.getAttribute('data-field');\n if (!fieldName) return;\n\n // Clear the field value\n const fileInput = this.element.querySelector(`input[name=\"${fieldName}\"]`);\n if (fileInput) {\n fileInput.value = '';\n fileInput.dispatchEvent(new Event('change', { bubbles: true }));\n }\n\n // Update data and re-render\n delete this.data[fieldName];\n this.emit('change', { field: fieldName, value: null, form: this });\n await this.updateField(fieldName);\n }\n\n /**\n * Handle clear color action\n */\n async onActionClearColor(event, element) {\n const fieldName = element.getAttribute('data-field');\n if (!fieldName) return;\n\n // Clear the color input value\n const colorInput = this.element.querySelector(`input[name=\"${fieldName}\"]`);\n if (colorInput) {\n colorInput.value = '';\n\n // Trigger field change handling (for autosave if enabled)\n this.handleFieldChange(fieldName, '');\n\n // Update the field to hide the clear button\n await this.updateField(fieldName);\n }\n }\n\n /**\n * Handle HTML preview button click\n */\n async onActionPreviewHtml(event, element) {\n event.preventDefault();\n \n const targetId = element.getAttribute('data-target');\n if (!targetId) return;\n\n const textarea = this.element.querySelector(`#${targetId}`);\n if (!textarea) return;\n\n const htmlContent = textarea.value || '';\n\n // Dynamically import Dialog to avoid circular dependencies\n const Dialog = (await import('@core/views/feedback/Dialog.js')).default;\n \n // Show HTML preview using Dialog.showHtmlPreview\n Dialog.showHtmlPreview({\n html: htmlContent,\n title: 'HTML Preview'\n });\n }\n\n /**\n * Handle button group selection\n */\n async onActionSelectButtonOption(event, element) {\n const fieldName = element.getAttribute('data-field');\n const value = element.getAttribute('data-value');\n\n if (!fieldName || !value) return;\n\n // Update form data\n this.data[fieldName] = value;\n\n // Update UI - remove active class from siblings and add to this button\n const buttonGroup = element.closest('.btn-group');\n if (buttonGroup) {\n const buttons = buttonGroup.querySelectorAll('button');\n buttons.forEach(btn => {\n btn.classList.remove('active');\n btn.classList.add('btn-outline-primary');\n btn.classList.remove('btn-primary');\n });\n\n element.classList.add('active');\n element.classList.remove('btn-outline-primary');\n element.classList.add('btn-primary');\n }\n\n // Emit field change event\n this.emit('field:changed', {\n field: fieldName,\n value: value,\n form: this\n });\n\n this.emit('change', {\n field: fieldName,\n value: value,\n form: this\n });\n\n // Emit general form change event\n this.emit('form:changed', await this.getFormData());\n }\n\n /**\n * Handle checklist dropdown filter apply\n */\n async onActionApplyFilter(event, element) {\n const dropdown = element.closest('.dropdown');\n const checkboxes = dropdown?.querySelectorAll('input[type=\"checkbox\"]');\n\n if (!checkboxes || checkboxes.length === 0) return;\n\n const fieldName = checkboxes[0].getAttribute('data-field');\n if (!fieldName) return;\n\n // Collect checked values\n const selectedValues = [];\n checkboxes.forEach(checkbox => {\n if (checkbox.checked) {\n selectedValues.push(checkbox.value);\n }\n });\n\n // Update form data\n this.data[fieldName] = selectedValues;\n\n // Close dropdown\n const dropdownBtn = dropdown.querySelector('[data-bs-toggle=\"dropdown\"]');\n if (dropdownBtn && window.bootstrap?.Dropdown) {\n const dropdownInstance = window.bootstrap.Dropdown.getInstance(dropdownBtn);\n dropdownInstance?.hide();\n }\n\n // Emit field change event\n this.emit('field:changed', {\n field: fieldName,\n value: selectedValues,\n form: this\n });\n\n this.emit('change', {\n field: fieldName,\n value: selectedValues,\n form: this\n });\n\n // Emit general form change event\n this.emit('form:changed', await this.getFormData());\n }\n\n // ========================================\n // EventDelegate Change Handlers\n // ========================================\n\n /**\n * Handle field validation on change\n */\n async onChangeValidateField(event, element) {\n const fieldName = element.name;\n if (fieldName) {\n const value = element.value;\n\n // Use handleFieldChange for consistent processing\n this.handleFieldChange(fieldName, value);\n\n // Validate the field\n this.validateField(fieldName);\n }\n }\n\n /**\n * Handle switch toggle\n */\n async onChangeToggleSwitch(event, element) {\n const fieldName = element.getAttribute('data-field');\n if (fieldName) {\n const value = element.checked;\n\n // Use handleFieldChange for consistent processing\n this.handleFieldChange(fieldName, value);\n\n // Emit specific switch events for backward compatibility\n this.emit('switch:toggle', {\n field: fieldName,\n checked: value,\n form: this\n });\n }\n }\n\n /**\n * Handle image selection\n */\n async onChangeImageSelected(event, element) {\n console.log('FormView: onChangeImageSelected called');\n console.log('FormView: element:', element);\n console.log('FormView: element.files:', element.files);\n\n const fieldName = element.getAttribute('data-field');\n const file = element.files[0];\n\n console.log('FormView: fieldName:', fieldName);\n console.log('FormView: file:', file);\n\n if (fieldName && file) {\n console.log('FormView: fieldName and file exist, processing...');\n\n // Find the field configuration to check for imageSize\n const fieldConfig = this.findFieldConfig(fieldName);\n console.log('FormView: fieldConfig:', fieldConfig);\n\n // Create temporary preview URL\n const previewUrl = URL.createObjectURL(file);\n console.log('FormView: previewUrl created:', previewUrl);\n\n // Check if image cropping is required\n if (fieldConfig && fieldConfig.imageSize) {\n console.log('FormView: Image cropping is required, imageSize:', fieldConfig.imageSize);\n try {\n // Check if lightbox extension is available for image cropping\n const ImageCropView = window.MOJO?.plugins?.ImageCropView;\n console.log('FormView: ImageCropView available?', !!ImageCropView);\n\n if (!ImageCropView) {\n // ImageCropView not available - fall back to normal handling without cropping\n console.log('FormView: ImageCropView not available, falling back to normal handling');\n this.data[fieldName] = file;\n await this.updateImagePreview(fieldName, previewUrl);\n this.emit('image:selected', { field: fieldName, file: file, form: this });\n return;\n }\n\n // Open crop dialog with crop and scale\n const result = await ImageCropView.showDialog(previewUrl, {\n title: `Crop ${fieldConfig.label || fieldName}`,\n cropAndScale: fieldConfig.imageSize,\n size: 'lg'\n });\n\n if (result.action === 'crop' && result.data) {\n // Convert data URL to blob\n const response = await fetch(result.data);\n const croppedBlob = await response.blob();\n\n // Create a new File object with the original name\n const croppedFile = new File([croppedBlob], file.name, {\n type: file.type || 'image/png'\n });\n\n this.data[fieldName] = croppedFile;\n\n // Update preview with cropped image\n await this.updateImagePreview(fieldName, result.data);\n\n this.emit('image:selected', {\n field: fieldName,\n file: croppedFile,\n originalFile: file,\n cropped: true,\n cropData: result.cropData,\n form: this\n });\n\n this.emit('change', {\n field: fieldName,\n value: croppedFile,\n form: this\n });\n } else {\n // User cancelled cropping, don't update the field\n element.value = ''; // Clear the input\n }\n } catch (error) {\n // Error during image cropping\n console.error('FormView: Error during image cropping:', error);\n // Fall back to normal image handling\n console.log('FormView: Falling back to normal image handling after error');\n this.data[fieldName] = file;\n await this.updateImagePreview(fieldName, previewUrl);\n\n this.emit('image:selected', {\n field: fieldName,\n file: file,\n form: this\n });\n\n this.emit('change', {\n field: fieldName,\n value: file,\n form: this\n });\n }\n } else {\n // Normal image handling without cropping\n console.log('FormView: Normal image handling (no cropping)');\n this.data[fieldName] = file;\n console.log('FormView: File stored in this.data[' + fieldName + ']');\n\n await this.updateImagePreview(fieldName, previewUrl);\n console.log('FormView: updateImagePreview completed');\n\n this.emit('image:selected', {\n field: fieldName,\n file: file,\n form: this\n });\n console.log('FormView: image:selected event emitted');\n }\n } else {\n console.log('FormView: Missing fieldName or file - not processing');\n }\n }\n\n /**\n * Handle file selection\n */\n async onChangeFileSelected(event, element) {\n const files = Array.from(element.files);\n\n // Store the file(s) in this.data so getFormData() can access them\n if (element.multiple) {\n this.data[element.name] = element.files; // Store as FileList for multiple\n } else {\n this.data[element.name] = files[0] || null; // Store single File object\n }\n\n this.emit('file:selected', {\n field: element.name,\n files: files,\n form: this\n });\n\n this.emit('change', {\n field: element.name,\n value: files,\n form: this\n });\n }\n\n /**\n * Handle range value changes\n */\n async onChangeRangeChanged(event, element) {\n const fieldName = element.name;\n const value = element.value;\n\n // Update display target if specified\n const targetId = element.getAttribute('data-target');\n if (targetId) {\n const valueDisplay = this.element.querySelector(`#${targetId}`);\n if (valueDisplay) {\n valueDisplay.textContent = value;\n }\n }\n\n // Use handleFieldChange for consistent processing\n if (fieldName) {\n this.handleFieldChange(fieldName, value);\n }\n\n // Emit specific range event for backward compatibility\n this.emit('range:changed', {\n field: fieldName,\n value: value,\n form: this\n });\n }\n\n /**\n * Handle search/filter operations\n */\n async onChangeFilterSearch(event, element) {\n const query = element.value;\n this.emit('search', {\n query: query,\n field: element.name,\n form: this\n });\n }\n\n /**\n * Handle select option filtering\n */\n async onChangeFilterSelectOptions(event, element) {\n const query = element.value.toLowerCase();\n const targetId = element.getAttribute('data-target');\n const select = targetId ? this.element.querySelector(`#${targetId}`) : null;\n\n if (select) {\n const options = select.querySelectorAll('option');\n options.forEach(option => {\n const text = option.textContent.toLowerCase();\n option.style.display = text.includes(query) ? '' : 'none';\n });\n }\n }\n\n // ========================================\n // FileDropMixin Integration\n // ========================================\n\n /**\n * Handle file drop on image fields\n */\n async onFileDrop(files, event, _validation) {\n const dropZone = event.target.closest('.image-drop-zone');\n if (!dropZone) return;\n\n const fieldName = dropZone.getAttribute('data-field');\n if (!fieldName) return;\n\n const file = files[0];\n\n // Update file input\n const fileInput = this.element.querySelector(`input[name=\"${fieldName}\"]`);\n if (fileInput) {\n const dataTransfer = new DataTransfer();\n dataTransfer.items.add(file);\n fileInput.files = dataTransfer.files;\n fileInput.dispatchEvent(new Event('change', { bubbles: true }));\n }\n\n // Update data and preview\n this.data[fieldName] = file;\n const previewUrl = URL.createObjectURL(file);\n await this.updateImagePreview(fieldName, previewUrl);\n\n this.emit('image:dropped', {\n field: fieldName,\n file: file,\n form: this\n });\n }\n\n /**\n * Handle file drop errors\n */\n async onFileDropError(error, event, files) {\n // File drop error\n this.showError(`File upload error: ${error.message}`);\n\n this.emit('file:error', {\n error: error,\n files: files,\n form: this\n });\n }\n\n // ========================================\n // Form Management Methods\n // ========================================\n\n /**\n * Get form element\n */\n getFormElement() {\n return this.element ? this.element.querySelector('form') : null;\n }\n\n getFormFieldConfig(name) {\n const searchInFields = (fields) => {\n for (const field of fields) {\n if (field.name === name) {\n return field;\n }\n // Search in nested fields recursively\n if (field.fields && Array.isArray(field.fields)) {\n const found = searchInFields(field.fields);\n if (found) return found;\n }\n }\n return null;\n };\n\n return searchInFields(this.formConfig.fields || []);\n }\n\n /**\n * Get all form data\n * Returns JSON object for base64 mode, FormData for multipart mode\n */\n async getFormData() {\n const form = this.getFormElement();\n if (!form) return this.fileHandling === 'multipart' ? new FormData() : {};\n\n if (this.fileHandling === 'multipart') {\n // Return FormData object with files as File objects\n const formData = new FormData(form);\n\n // Add files from this.data to FormData\n for (const [key, value] of Object.entries(this.data)) {\n if (value instanceof File) {\n formData.set(key, value);\n } else if (value instanceof FileList) {\n for (let i = 0; i < value.length; i++) {\n formData.append(`${key}[${i}]`, value[i]);\n }\n }\n }\n\n return formData;\n } else {\n // Return JSON object with files converted to base64\n const formData = new FormData(form);\n const data = {};\n\n // Handle regular form fields\n for (const [key, value] of formData.entries()) {\n if (data[key]) {\n // Handle multiple values\n if (!Array.isArray(data[key])) {\n data[key] = [data[key]];\n }\n data[key].push(value);\n } else {\n data[key] = value;\n }\n }\n\n // Handle checkboxes and switches to ensure correct boolean values\n const checkboxes = form.querySelectorAll('input[type=\"checkbox\"]');\n checkboxes.forEach(checkbox => {\n data[checkbox.name] = checkbox.checked;\n });\n\n // Convert number fields to actual numbers\n const numberInputs = form.querySelectorAll('input[type=\"number\"]');\n numberInputs.forEach(input => {\n if (input.name && data[input.name] !== undefined && data[input.name] !== '') {\n const num = Number(data[input.name]);\n if (!isNaN(num)) {\n data[input.name] = num;\n }\n }\n });\n\n // Convert select fields with numeric values based on field config\n this.formConfig.fields?.forEach(field => {\n if (field.type === 'select' && field.name && data[field.name] !== undefined) {\n const fieldConfig = this.getFormFieldConfig(field.name);\n // Check if all option values are numeric\n if (fieldConfig?.options && Array.isArray(fieldConfig.options)) {\n const allNumeric = fieldConfig.options.every(opt => {\n const val = typeof opt === 'object' ? opt.value : opt;\n return val === '' || !isNaN(Number(val));\n });\n\n if (allNumeric && data[field.name] !== '') {\n const num = Number(data[field.name]);\n if (!isNaN(num)) {\n data[field.name] = num;\n }\n }\n }\n }\n });\n\n // Handle JSON fields\n const jsonFields = form.querySelectorAll('[data-field-type=\"json\"]');\n jsonFields.forEach(textarea => {\n try {\n data[textarea.name] = JSON.parse(textarea.value);\n } catch (e) {\n // Invalid JSON in field\n data[textarea.name] = textarea.value; // Keep as string if invalid\n }\n });\n\n // Handle custom components (TagInput, CollectionSelect, DatePicker, etc.)\n this.customComponents.forEach((component, fieldName) => {\n if (component.getFormValue) {\n data[fieldName] = component.getFormValue();\n } else if (component.getValue) {\n data[fieldName] = component.getValue();\n }\n });\n\n // Convert files to base64 and add to data\n for (const [key, value] of Object.entries(this.data)) {\n if (value instanceof File) {\n try {\n data[key] = await this.fileToBase64(value);\n } catch (error) {\n // Failed to convert file to base64\n data[key] = null;\n }\n } else if (value instanceof FileList) {\n const base64Files = [];\n for (let i = 0; i < value.length; i++) {\n try {\n base64Files.push(await this.fileToBase64(value[i]));\n } catch (error) {\n // Failed to convert file to base64\n base64Files.push(null);\n }\n }\n data[key] = base64Files;\n }\n }\n\n return data;\n }\n }\n\n _onModelChange() {\n // Don't sync during save operations to prevent loops\n if (this.isSaving) return;\n\n // Always update internal data\n this.data = this.prepareFormData();\n\n if (this.isMounted()) {\n // Only sync if the change wasn't initiated by this form\n if (!this._isFormDrivenChange) {\n this.syncFormWithModel();\n }\n // Reset the flag\n this._isFormDrivenChange = false;\n }\n }\n\n /**\n * Sync form field values with current model data without full rebuild\n */\n syncFormWithModel() {\n if (!this.model || !this.element) return;\n\n // Check if form data actually differs from current displayed values\n if (this.formDataMatchesModelData(this.data)) {\n return; // No sync needed - data is already in sync\n }\n\n // Re-populate form values with updated data\n this.populateFormValues();\n }\n\n /**\n * Compare current form values with new model data\n * @param {Object} newModelData - New data from model\n * @returns {boolean} True if form and model data match\n */\n formDataMatchesModelData(newModelData) {\n if (!this.formConfig?.fields || !this.element) return true;\n\n for (const field of this.formConfig.fields) {\n if (field.type === 'group' && field.fields) {\n // Check group fields\n for (const groupField of field.fields) {\n if (!this.fieldValueMatchesModel(groupField, newModelData)) {\n return false;\n }\n }\n } else {\n if (!this.fieldValueMatchesModel(field, newModelData)) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * Check if a single field's current value matches the model data\n * @param {Object} fieldConfig - Field configuration\n * @param {Object} modelData - Model data to compare against\n * @returns {boolean} True if field value matches model\n */\n fieldValueMatchesModel(fieldConfig, modelData) {\n if (!fieldConfig.name) return true;\n\n const fieldElement = this.element.querySelector(`[name=\"${fieldConfig.name}\"]`);\n if (!fieldElement) return true;\n\n const currentValue = this.getFieldCurrentValue(fieldElement, fieldConfig);\n // Use MOJOUtils to handle nested properties like 'permissions.manage_users'\n const modelValue = MOJOUtils.getContextData(modelData, fieldConfig.name);\n\n return this.valuesAreDifferent(currentValue, modelValue) === false;\n }\n\n\n\n /**\n * Get current value from a form field element\n */\n getFieldCurrentValue(fieldElement, fieldConfig) {\n switch (fieldConfig.type) {\n case 'checkbox':\n case 'toggle':\n case 'switch':\n return fieldElement.checked;\n case 'radio':\n const checkedRadio = this.element.querySelector(`[name=\"${fieldConfig.name}\"]:checked`);\n return checkedRadio ? checkedRadio.value : '';\n case 'select':\n return fieldElement.multiple ?\n Array.from(fieldElement.selectedOptions).map(opt => opt.value) :\n fieldElement.value;\n case 'file':\n case 'image':\n return null; // Don't sync file fields\n case 'json':\n // Parse JSON string back to object\n try {\n return fieldElement.value ? JSON.parse(fieldElement.value) : null;\n } catch (e) {\n return fieldElement.value; // Return raw string if invalid JSON\n }\n default:\n return fieldElement.value;\n }\n }\n\n /**\n * Set value on a form field element\n */\n setFieldValue(fieldElement, fieldConfig, newValue) {\n switch (fieldConfig.type) {\n case 'checkbox':\n case 'toggle':\n case 'switch':\n fieldElement.checked = Boolean(newValue);\n break;\n case 'radio':\n const radioOption = this.element.querySelector(`[name=\"${fieldConfig.name}\"][value=\"${newValue}\"]`);\n if (radioOption) {\n radioOption.checked = true;\n }\n break;\n case 'select':\n if (fieldElement.multiple && Array.isArray(newValue)) {\n Array.from(fieldElement.options).forEach(option => {\n option.selected = newValue.includes(option.value);\n });\n } else {\n // Use nullish coalescing to preserve falsy values like 0\n fieldElement.value = newValue ?? '';\n }\n break;\n case 'file':\n case 'image':\n // Don't programmatically set file fields\n break;\n case 'json':\n // Convert objects to formatted JSON strings\n if (typeof newValue === 'object' && newValue !== null) {\n try {\n fieldElement.value = JSON.stringify(newValue, null, 2);\n } catch (e) {\n fieldElement.value = '{}';\n }\n } else if (typeof newValue === 'string') {\n fieldElement.value = newValue;\n } else {\n fieldElement.value = String(newValue || '');\n }\n break;\n default:\n fieldElement.value = newValue || '';\n break;\n }\n\n // Trigger change event for any field listeners\n fieldElement.dispatchEvent(new Event('change', { bubbles: true }));\n }\n\n /**\n * Set defaults (updates defaults and rebuilds form)\n * @param {Object} newDefaults - Default values to set\n */\n setDefaults(newDefaults) {\n this.defaults = { ...this.defaults, ...newDefaults };\n this.refreshForm();\n }\n\n /**\n * Handle form submission with validation, saving, and error handling\n * @returns {Promise<Object>} Submission result {success: boolean, data: Object, result?: Object, error?: string}\n */\n async handleSubmit() {\n try {\n // Get form data (async for base64 conversion)\n const formData = await this.getFormData();\n\n // Validate if needed\n if (this.formConfig.validateOnSubmit !== false) {\n const isValid = this.validate();\n if (!isValid) {\n this.focusFirstError();\n return {\n success: false,\n data: formData,\n error: 'Form validation failed'\n };\n }\n }\n\n // If there's a model, save via model\n if (this.model && typeof this.model.save === 'function') {\n const result = await this.saveModel(formData);\n\n // Check if save was successful\n if (result && result.success !== false) {\n return {\n success: true,\n data: formData,\n result: result\n };\n } else {\n const errorMsg = result?.message || result?.error || 'Save failed. Please try again.';\n return {\n success: false,\n data: formData,\n result: result,\n error: errorMsg\n };\n }\n } else {\n // No model - just return form data\n return formData;\n }\n\n } catch (error) {\n // Form submission error\n\n return {\n success: false,\n error: error.message || 'An error occurred while submitting the form'\n };\n }\n }\n\n /**\n * Save form data via model\n * Uses auto-detection for multipart vs JSON based on form data type\n * Only saves changed data for efficiency\n * @returns {Promise<Object>} Save result from model\n */\n async saveModel(formData = null) {\n if (!this.model || typeof this.model.save !== 'function') {\n throw new Error('No model available for saving');\n }\n\n // Get form data (auto-detects FormData vs Object)\n if (!formData) formData = await this.getFormData();\n\n // Check for changes before saving\n const changes = this.getChangedData(formData);\n\n if (!changes || Object.keys(changes).length === 0) {\n // No changes detected\n return {\n success: true,\n message: 'No changes to save',\n data: formData\n };\n }\n\n\n\n try {\n // Mark this as a form-driven change to prevent sync back\n this._isFormDrivenChange = true;\n\n // Model.save with only changed data\n const result = await this.model.save(changes);\n\n\n return result;\n } catch (error) {\n // Model save error\n throw error;\n }\n }\n\n /**\n * Compare current form data with original model data to detect changes\n * @param {Object|FormData} currentData - Current form data\n * @returns {Object|FormData|null} Changed data only, or null if no changes\n */\n getChangedData(currentData) {\n if (!this.model) return currentData;\n\n // Get original data from model\n const originalData = this.getOriginalModelData();\n\n\n let changes;\n if (currentData instanceof FormData) {\n // Handle FormData comparison\n\n changes = this.getChangedFormData(currentData, originalData);\n } else {\n // Handle Object comparison\n\n changes = this.getChangedObjectData(currentData, originalData);\n }\n\n\n return changes;\n }\n\n /**\n * Get original data from model for comparison\n * @returns {Object} Original model data\n */\n getOriginalModelData() {\n if (this.model.attributes) {\n return this.model.attributes;\n } else if (typeof this.model.toJSON === 'function') {\n return this.model.toJSON();\n } else {\n return {};\n }\n }\n\n /**\n * Compare FormData with original data\n * @param {FormData} currentData - Current form data\n * @param {Object} originalData - Original model data\n * @returns {FormData|null} FormData with only changed fields, or null if no changes\n */\n getChangedFormData(currentData, originalData) {\n const changedData = new FormData();\n let hasChanges = false;\n\n // Compare each form field\n for (const [key, value] of currentData.entries()) {\n if (value instanceof File) {\n // Check if file has actual content\n if (value.size === 0 || value.name === '' || value.name === 'blob') {\n\n } else {\n\n changedData.set(key, value);\n hasChanges = true;\n }\n } else {\n // Compare text values\n const originalValue = originalData[key];\n if (value !== originalValue && value !== String(originalValue)) {\n\n changedData.set(key, value);\n hasChanges = true;\n } else {\n\n }\n }\n }\n\n return hasChanges ? changedData : null;\n }\n\n /**\n * Compare Object data with original data\n * @param {Object} currentData - Current form data\n * @param {Object} originalData - Original model data\n * @returns {Object|null} Object with only changed fields, or null if no changes\n */\n getChangedObjectData(currentData, originalData) {\n const changedData = {};\n let hasChanges = false;\n const allKeys = new Set([...Object.keys(originalData), ...Object.keys(currentData)]);\n\n const resolveDotNotation = (obj, path) => path.split('.').reduce((o, i) => (o && typeof o === 'object' ? o[i] : undefined), obj);\n\n for (const key of allKeys) {\n const fieldConfig = this.findFieldConfig(key);\n // Only process fields that are actually defined in the form's schema.\n if (!fieldConfig) {\n continue;\n }\n\n const newValue = currentData[key];\n const originalValue = resolveDotNotation(originalData, key);\n\n const fieldType = fieldConfig.type || 'text';\n\n if (this.valuesAreDifferent(newValue, originalValue, fieldType, fieldConfig)) {\n changedData[key] = newValue;\n hasChanges = true;\n }\n }\n\n return hasChanges ? changedData : null;\n }\n\n /**\n * Compare two values to determine if they're different\n * @param {*} newValue - New value from form\n * @param {*} originalValue - Original value from model\n * @param {string} fieldType - The type of the field from the form config\n * @param {object} fieldConfig - The configuration of the field from the form config\n * @returns {boolean} True if values are different\n */\n valuesAreDifferent(newValue, originalValue, fieldType = 'text', fieldConfig = {}) {\n // Handle File objects (new uploads vs existing)\n if (newValue instanceof File) {\n return newValue.size > 0 && newValue.name !== '' && newValue.name !== 'blob';\n }\n\n // Handle base64 images (always considered changed if present)\n if (typeof newValue === 'string' && newValue.startsWith('data:image/')) {\n return true;\n }\n\n if (fieldType === \"collection\") {\n // this is the collection select, the field will typically be an ID or \"0\" for null;\n if (typeof originalValue === 'object' && originalValue !== null && originalValue !== undefined && typeof newValue === 'string') {\n // we really need to field config here if fieldKey is not id\n if (newValue === '0') {\n return originalValue !== null;\n }\n const valueField = fieldConfig.valueField || 'id';\n // we compare (str to int)\n if (originalValue[valueField] == newValue) {\n return false;\n }\n }\n }\n\n // For switches and checkboxes, perform a strict boolean comparison\n if (fieldType === 'switch' || fieldType === 'checkbox' || fieldType === 'toggle') {\n const newBool = !!newValue;\n const originalBool = !!originalValue;\n return newBool !== originalBool;\n }\n\n // For all other fields, compare their string representations\n const newStr = newValue === null || newValue === undefined ? '' : String(newValue).trim();\n const originalStr = originalValue === null || originalValue === undefined ? '' : String(originalValue).trim();\n\n return newStr !== originalStr;\n }\n\n /**\n * Validate entire form\n */\n validate() {\n const form = this.getFormElement();\n if (!form) return false;\n\n const isValid = form.checkValidity();\n\n if (!isValid) {\n form.classList.add('was-validated');\n }\n\n return isValid;\n }\n\n /**\n * Validate single field\n */\n validateField(fieldName) {\n const form = this.getFormElement();\n if (!form) return false;\n\n const field = form.elements[fieldName];\n if (!field) return false;\n\n const isValid = field.checkValidity();\n\n if (isValid) {\n field.classList.remove('is-invalid');\n field.classList.add('is-valid');\n delete this.errors[fieldName];\n } else {\n field.classList.remove('is-valid');\n field.classList.add('is-invalid');\n this.errors[fieldName] = field.validationMessage;\n }\n\n return isValid;\n }\n\n /**\n * Focus first error\n * If the invalid field is inside a hidden tab pane, activate that tab first.\n */\n focusFirstError() {\n const form = this.getFormElement();\n if (!form) return;\n\n const firstInvalid = form.querySelector(':invalid');\n if (!firstInvalid) return;\n\n // If inside a Bootstrap tab pane and not active, activate the corresponding tab\n const tabPane = firstInvalid.closest('.tab-pane');\n if (tabPane && !tabPane.classList.contains('active')) {\n const tabId = tabPane.id;\n\n // Find the tab trigger button (support aria-controls or data-bs-target)\n const trigger = form.querySelector(`[role=\"tab\"][aria-controls=\"${tabId}\"], [data-bs-target=\"#${tabId}\"]`);\n if (trigger) {\n // Prefer Bootstrap API if available\n const bsTab = window.bootstrap?.Tab?.getOrCreateInstance\n ? window.bootstrap.Tab.getOrCreateInstance(trigger)\n : null;\n\n if (bsTab && typeof bsTab.show === 'function') {\n bsTab.show();\n } else {\n // Fallback: manually toggle classes\n const navLinks = form.querySelectorAll('[role=\"tab\"].nav-link');\n navLinks.forEach(link => {\n const isActive = link === trigger;\n link.classList.toggle('active', isActive);\n link.setAttribute('aria-selected', isActive ? 'true' : 'false');\n });\n\n const panes = form.querySelectorAll('.tab-pane');\n panes.forEach(p => p.classList.remove('show', 'active'));\n tabPane.classList.add('show', 'active');\n }\n }\n }\n\n firstInvalid.focus();\n firstInvalid.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n /**\n * Clear all errors\n */\n clearAllErrors() {\n const form = this.getFormElement();\n if (!form) return;\n\n this.errors = {};\n form.classList.remove('was-validated');\n\n const invalidElements = form.querySelectorAll('.is-invalid');\n invalidElements.forEach(el => el.classList.remove('is-invalid'));\n\n const validElements = form.querySelectorAll('.is-valid');\n validElements.forEach(el => el.classList.remove('is-valid'));\n }\n\n /**\n * Set loading state\n */\n setLoading(loading) {\n this.loading = loading;\n\n const form = this.getFormElement();\n if (!form) return;\n\n const fields = form.querySelectorAll('input, select, textarea, button');\n const submitBtn = form.querySelector('button[type=\"submit\"]');\n\n if (loading) {\n // Disable all fields\n fields.forEach(field => field.disabled = true);\n\n // Update submit button\n if (submitBtn) {\n submitBtn.innerHTML = '<span class=\"spinner-border spinner-border-sm me-2\"></span>Loading...';\n }\n } else {\n // Enable all fields\n fields.forEach(field => field.disabled = false);\n\n // Restore submit button\n if (submitBtn) {\n const submitLabel = this.formConfig.options?.submitButton || 'Submit';\n submitBtn.innerHTML = typeof submitLabel === 'string' ? submitLabel : 'Submit';\n }\n }\n }\n\n /**\n * Show error message\n */\n showError(message) {\n // Form error\n\n this.emit('error', { message, form: this });\n\n // Show alert in form\n if (this.element) {\n // Remove existing alerts\n const existingAlerts = this.element.querySelectorAll('.alert');\n existingAlerts.forEach(alert => alert.remove());\n\n const alertDiv = document.createElement('div');\n alertDiv.className = 'alert alert-danger alert-dismissible fade show';\n alertDiv.innerHTML = `\n ${message}\n <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>\n `;\n\n this.element.insertBefore(alertDiv, this.element.firstChild);\n\n // Auto-remove after 5 seconds\n setTimeout(() => {\n if (alertDiv.parentNode) {\n alertDiv.remove();\n }\n }, 5000);\n }\n }\n\n /**\n * Update a specific field\n */\n async updateField(_fieldName) {\n // Re-create FormBuilder with updated data\n this.formBuilder = new FormBuilder({\n ...this.getFormConfig(),\n data: this.data,\n errors: this.errors\n });\n\n // Re-render the form\n await this.render();\n }\n\n /**\n * Update image preview\n */\n async updateImagePreview(fieldName, imageUrl) {\n const dropZone = this.element.querySelector(`[data-field=\"${fieldName}\"].image-drop-zone`);\n\n if (!dropZone) {\n // Drop zone not found for field\n return;\n }\n\n // Check if there's already a preview image\n let preview = dropZone.querySelector('img');\n const placeholder = dropZone.querySelector('.bi-image')?.parentElement;\n const fieldId = dropZone.getAttribute('data-field-id');\n\n if (imageUrl) {\n if (preview) {\n // Update existing preview\n preview.src = imageUrl;\n } else {\n // Create new preview image\n const previewId = `${fieldId}_preview`;\n dropZone.innerHTML = `\n <img id=\"${previewId}\"\n src=\"${imageUrl}\"\n alt=\"Preview\"\n class=\"img-thumbnail w-100 h-100\"\n style=\"object-fit: cover;\">\n <button type=\"button\"\n class=\"btn btn-sm btn-danger position-absolute top-0 end-0 m-1\"\n data-action=\"remove-image\"\n data-field-id=\"${fieldId}\"\n data-field=\"${fieldName}\"\n style=\"opacity: 0.8;\">\n <i class=\"bi bi-x\"></i>\n </button>\n `;\n }\n\n // Hide placeholder if it exists\n if (placeholder) {\n placeholder.style.display = 'none';\n }\n }\n }\n\n /**\n * Find field configuration by name\n * @param {string} fieldName - Field name to find\n * @returns {Object|null} Field configuration\n */\n findFieldConfig(fieldName) {\n const searchInFields = (fields) => {\n for (const field of fields) {\n if (field.name === fieldName) {\n return field;\n }\n\n // Search in nested field structures\n // Groups: { type: 'group', fields: [...] }\n if (field.fields && Array.isArray(field.fields)) {\n const found = searchInFields(field.fields);\n if (found) return found;\n }\n\n // Tabsets: { type: 'tabset', tabs: [{ fields: [...] }] }\n if (field.tabs && Array.isArray(field.tabs)) {\n for (const tab of field.tabs) {\n if (tab.fields && Array.isArray(tab.fields)) {\n const found = searchInFields(tab.fields);\n if (found) return found;\n }\n }\n }\n }\n return null;\n };\n\n return searchInFields(this.formConfig.fields || []);\n }\n\n /**\n * Convert File object to base64 string\n * @param {File} file - File to convert\n * @returns {Promise<string>} Base64 encoded string\n */\n async fileToBase64(file) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result);\n reader.onerror = reject;\n reader.readAsDataURL(file);\n });\n }\n\n /**\n * Check if form data contains any File objects\n * @param {Object|FormData} data - Form data to check\n * @returns {boolean} True if contains files\n */\n hasFiles(data) {\n if (data instanceof FormData) {\n for (const [key, value] of data.entries()) {\n if (value instanceof File) {\n return true;\n }\n }\n return false;\n } else {\n for (const value of Object.values(data)) {\n if (value instanceof File) {\n return true;\n }\n if (Array.isArray(value) && value.some(v => v instanceof File)) {\n return true;\n }\n }\n return false;\n }\n }\n\n /**\n * Reset form\n */\n reset() {\n const form = this.getFormElement();\n if (form) {\n form.reset();\n }\n\n this.data = {};\n this.errors = {};\n this.clearAllErrors();\n\n this.emit('reset', { form: this });\n }\n\n /**\n * Update form configuration\n */\n async updateConfig(newConfig) {\n this.formConfig = { ...this.formConfig, ...newConfig };\n this.formBuilder = new FormBuilder({\n ...this.getFormConfig(),\n data: this.data,\n errors: this.errors\n });\n\n await this.render();\n }\n\n /**\n * Clean up when destroying\n */\n async onBeforeDestroy() {\n // Clean up custom components\n const destroyPromises = [];\n for (const component of this.customComponents.values()) {\n if (component.destroy) {\n destroyPromises.push(component.destroy());\n }\n }\n await Promise.all(destroyPromises);\n this.customComponents.clear();\n\n // Clean up temporary URLs\n Object.values(this.data).forEach(value => {\n if (typeof value === 'string' && value.startsWith('blob:')) {\n URL.revokeObjectURL(value);\n }\n });\n\n await super.onBeforeDestroy();\n }\n /**\n * Initialize password fields: strength meter, caps lock warning, and hold-to-reveal\n */\n initializePasswordFields() {\n if (!this.element) return;\n\n // Find password inputs (either explicit data-field-type=\"password\" or native type=\"password\")\n const inputs = this.element.querySelectorAll('input[data-field-type=\"password\"], input[type=\"password\"]');\n\n inputs.forEach((input) => {\n // Initialize strength UI once\n this.updatePasswordStrengthUI(input);\n\n // Wire strength meter updates\n const onInput = (e) => {\n this.updatePasswordStrengthUI(e.target);\n };\n input.addEventListener('input', onInput);\n\n // Caps lock warning on key events\n const capsHandler = (e) => {\n if (typeof e.getModifierState === 'function') {\n const caps = e.getModifierState('CapsLock');\n this.updateCapsLockWarning(input, !!caps);\n }\n };\n input.addEventListener('keydown', capsHandler);\n input.addEventListener('keyup', capsHandler);\n\n // Initialize caps lock hidden state\n this.updateCapsLockWarning(input, false);\n });\n\n // Simple click toggle is handled by onActionTogglePassword; no extra listeners needed.\n }\n\n /**\n * Toggle password visibility on click (persistent toggle)\n * Ignored if currently in press-and-hold mode.\n */\n async onActionTogglePassword(event, element) {\n event.preventDefault();\n\n const targetId = element.getAttribute('data-target');\n let input = null;\n if (targetId) {\n input = this.element.querySelector('#' + targetId);\n }\n if (!input) {\n const group = element.closest('.input-group');\n if (group) {\n input = group.querySelector('input[type=\"password\"], input[data-field-type=\"password\"], input[type=\"text\"]');\n }\n }\n if (!input) return;\n\n const isHidden = input.type === 'password';\n input.type = isHidden ? 'text' : 'password';\n\n // Update button state and icon\n element.setAttribute('aria-pressed', isHidden ? 'true' : 'false');\n element.setAttribute('aria-label', isHidden ? 'Hide password' : 'Show password');\n const icon = element.querySelector('i');\n if (icon) {\n icon.classList.toggle('bi-eye', !isHidden);\n icon.classList.toggle('bi-eye-slash', isHidden);\n }\n\n // Keep focus on input and move caret to end\n input.focus();\n try {\n const len = input.value?.length ?? 0;\n input.setSelectionRange(len, len);\n } catch (_e) {\n // Not all input types support setSelectionRange; safe to ignore\n }\n }\n\n /**\n * Copy field value to clipboard\n * @param {Event} event - Click event\n * @param {HTMLElement} element - Button element with data-target\n */\n async onActionCopyToClipboard(event, element) {\n event.preventDefault();\n\n const targetId = element.getAttribute('data-target');\n if (!targetId) return;\n\n const input = this.element.querySelector('#' + targetId);\n if (!input) return;\n\n try {\n // Copy to clipboard using Clipboard API\n await navigator.clipboard.writeText(input.value);\n\n // Visual feedback - change icon temporarily\n const icon = element.querySelector('i');\n if (icon) {\n const originalClass = icon.className;\n icon.className = 'bi bi-check2';\n element.classList.add('btn-success');\n element.classList.remove('btn-outline-secondary');\n\n setTimeout(() => {\n icon.className = originalClass;\n element.classList.remove('btn-success');\n element.classList.add('btn-outline-secondary');\n }, 1500);\n }\n\n // Optional: Show toast notification if available\n if (this.app && this.app.toast) {\n this.app.toast.success('Copied to clipboard!');\n }\n } catch (err) {\n console.error('Failed to copy to clipboard:', err);\n if (this.app && this.app.toast) {\n this.app.toast.error('Failed to copy to clipboard');\n }\n }\n }\n\n /**\n * Compute a simple password strength score and associated UI metadata\n */\n computePasswordStrength(password = '') {\n const len = password.length;\n let score = 0;\n\n // Length-based scoring\n if (len >= 6) score++;\n if (len >= 8) score++;\n if (len >= 12) score++;\n\n // Character variety\n const hasLower = /[a-z]/.test(password);\n const hasUpper = /[A-Z]/.test(password);\n const hasDigit = /\\d/.test(password);\n const hasSymbol = /[^A-Za-z0-9]/.test(password);\n\n const variety = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;\n if (variety >= 2) score++;\n if (variety >= 3) score++;\n\n // Cap score between 0-4\n score = Math.max(0, Math.min(4, score));\n\n // Map to percent/label/class\n const map = [\n { percent: 0, label: 'Too short', barClass: 'bg-secondary' },\n { percent: 25, label: 'Weak', barClass: 'bg-danger' },\n { percent: 50, label: 'Fair', barClass: 'bg-warning' },\n { percent: 75, label: 'Good', barClass: 'bg-info' },\n { percent: 100, label: 'Strong', barClass: 'bg-success' }\n ];\n\n return map[score];\n }\n\n /**\n * Update strength meter UI if present for a given password input\n */\n updatePasswordStrengthUI(input) {\n if (!input || !input.id) return;\n\n const bar = this.element.querySelector(`#${input.id}_strength_bar`);\n const text = this.element.querySelector(`#${input.id}_strength_text`);\n if (!bar && !text) return;\n\n const { percent, label, barClass } = this.computePasswordStrength(input.value || '');\n\n if (bar) {\n // Reset to base class and apply strength color\n bar.className = `progress-bar ${barClass}`;\n bar.style.width = `${percent}%`;\n bar.setAttribute('aria-valuenow', String(percent));\n }\n if (text) {\n text.textContent = label;\n }\n }\n\n /**\n * Show/Hide a caps lock warning element if present for the given input\n */\n updateCapsLockWarning(input, capsOn) {\n if (!input || !input.id) return;\n const warn = this.element.querySelector(`#${input.id}_caps_warning`);\n if (!warn) return;\n\n if (capsOn) {\n warn.classList.remove('d-none');\n } else {\n warn.classList.add('d-none');\n }\n }\n}\n\n// Export for use in MOJO framework\n/**\n * FieldStatusManager - Manages save status indicators for form fields\n */\nclass FieldStatusManager {\n constructor(fieldElement) {\n this.fieldElement = fieldElement;\n this.statusContainer = this.findOrCreateStatusContainer();\n this.timeouts = new Map(); // Track active timeouts\n }\n\n /**\n * Find existing status container or create one based on field type\n */\n findOrCreateStatusContainer() {\n // Look for existing status container (check parent and labels)\n let container = this.fieldElement.parentElement.querySelector('.field-status-label-inline');\n\n // Also check within the field's label\n if (!container) {\n const label = this.findFieldLabel();\n if (label) {\n container = label.querySelector('.field-status-label-inline');\n }\n }\n\n if (!container) {\n container = this.createStatusContainer();\n }\n\n return container;\n }\n\n /**\n * Create appropriate status container based on field type\n */\n createStatusContainer() {\n const fieldType = this.getFieldType();\n const placement = this.getPlacementStrategy(fieldType);\n\n const container = document.createElement('div');\n\n // Since we only use label-inline now, simplify\n return this.createLabelInlineContainer(container);\n }\n\n /**\n * Determine field type from element\n */\n getFieldType() {\n const tagName = this.fieldElement.tagName.toLowerCase();\n const type = this.fieldElement.type?.toLowerCase();\n const classes = this.fieldElement.className;\n\n if (type === 'checkbox' || classes.includes('form-check-input') || classes.includes('form-switch')) {\n return 'toggle';\n } else if (tagName === 'select') {\n return 'select';\n } else if (tagName === 'textarea') {\n return 'textarea';\n } else if (tagName === 'input') {\n return 'input';\n }\n\n return 'input'; // default\n }\n\n /**\n * Determine placement strategy for field type\n */\n getPlacementStrategy(fieldType) {\n // Use label-inline for all field types - cleanest approach\n return 'label-inline';\n }\n\n /**\n * Create inline label status container (for all input types)\n */\n createLabelInlineContainer(container) {\n container.className = 'field-status-label-inline';\n container.innerHTML = this.getStatusHTML();\n\n // Find the associated label\n const label = this.findFieldLabel();\n if (label) {\n // Insert status container at the end of the label\n label.appendChild(container);\n } else {\n // Fallback to parent element if no label found\n this.fieldElement.parentElement.appendChild(container);\n }\n\n return container;\n }\n\n /**\n * Find the label associated with any field type\n */\n findFieldLabel() {\n // Try to find label by 'for' attribute matching field id\n if (this.fieldElement.id) {\n const label = document.querySelector(`label[for=\"${this.fieldElement.id}\"]`);\n if (label) return label;\n }\n\n // Look for label as sibling (common in Bootstrap form-check)\n const label = this.fieldElement.parentElement.querySelector('label');\n if (label) return label;\n\n // Look for label as parent (less common)\n const parentLabel = this.fieldElement.closest('label');\n if (parentLabel) return parentLabel;\n\n return null;\n }\n\n /**\n * Create input overlay container (for text inputs/selects)\n */\n createInputOverlayContainer(container) {\n container.className = 'field-status-overlay';\n container.innerHTML = this.getStatusHTML();\n\n // Make parent position relative if not already\n const parent = this.fieldElement.parentElement;\n if (getComputedStyle(parent).position === 'static') {\n parent.style.position = 'relative';\n }\n\n parent.appendChild(container);\n return container;\n }\n\n /**\n * Create full overlay container (for textareas/large inputs)\n */\n createFullOverlayContainer(container) {\n container.className = 'field-status-full-overlay d-none';\n container.innerHTML = `\n <div class=\"saving-indicator\">\n <div class=\"spinner-border spinner-border-sm text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Saving...</span>\n </div>\n <span class=\"ms-2\">Saving...</span>\n </div>\n <div class=\"success-indicator d-none\">\n <i class=\"bi bi-check-circle text-success\"></i>\n <span class=\"ms-2\">Saved</span>\n </div>\n <div class=\"error-indicator d-none\">\n <i class=\"bi bi-exclamation-circle text-danger\"></i>\n <span class=\"ms-2\">Error saving</span>\n </div>\n `;\n\n // Make parent position relative if not already\n const parent = this.fieldElement.parentElement;\n if (getComputedStyle(parent).position === 'static') {\n parent.style.position = 'relative';\n }\n\n parent.appendChild(container);\n return container;\n }\n\n /**\n * Get standard status HTML for simple containers\n */\n getStatusHTML() {\n return `\n <div class=\"spinner-border spinner-border-sm text-primary d-none\" data-status=\"saving\" role=\"status\">\n <span class=\"visually-hidden\">Saving...</span>\n </div>\n <i class=\"bi bi-check-circle text-success d-none\" data-status=\"saved\"></i>\n <i class=\"bi bi-exclamation-circle text-danger d-none\" data-status=\"error\"></i>\n `;\n }\n\n /**\n * Show a status indicator\n * @param {string} type - Status type: 'saving', 'saved', 'error'\n * @param {object} options - Additional options\n */\n showStatus(type, options = {}) {\n // Clear any existing timeouts for this field\n this.clearTimeout(type);\n\n // Since we only use label-inline now, always use standard status\n this.showStandardStatus(type, options);\n }\n\n /**\n * Show status for standard containers (right-side, input-overlay)\n */\n showStandardStatus(type, options = {}) {\n // Hide all status indicators\n this.hideAllStatuses();\n\n // Show the requested status\n const indicator = this.statusContainer.querySelector(`[data-status=\"${type}\"]`);\n if (indicator) {\n indicator.classList.remove('d-none');\n indicator.classList.add('d-inline-block', 'show');\n\n // Set auto-hide timeout for temporary states\n if (type === 'saved') {\n this.setTimeout(type, () => this.hideStatus(type), 2500);\n } else if (type === 'error') {\n // Show error message in title if provided\n if (options.message) {\n indicator.title = options.message;\n }\n this.setTimeout(type, () => this.hideStatus(type), 6000);\n }\n }\n }\n\n /**\n * Show status for full overlay containers (textareas)\n */\n showFullOverlayStatus(type, options = {}) {\n // Hide all indicators first\n const indicators = this.statusContainer.querySelectorAll('.saving-indicator, .success-indicator, .error-indicator');\n indicators.forEach(ind => ind.classList.add('d-none'));\n\n // Show the container\n this.statusContainer.classList.remove('d-none');\n\n // Show specific indicator\n let indicatorClass;\n switch (type) {\n case 'saving':\n indicatorClass = '.saving-indicator';\n break;\n case 'saved':\n indicatorClass = '.success-indicator';\n this.setTimeout(type, () => this.hideStatus(type), 2500);\n break;\n case 'error':\n indicatorClass = '.error-indicator';\n if (options.message) {\n const errorSpan = this.statusContainer.querySelector('.error-indicator span');\n if (errorSpan) errorSpan.textContent = options.message;\n }\n this.setTimeout(type, () => this.hideStatus(type), 6000);\n break;\n }\n\n if (indicatorClass) {\n const indicator = this.statusContainer.querySelector(indicatorClass);\n if (indicator) {\n indicator.classList.remove('d-none');\n }\n }\n }\n\n /**\n * Hide a specific status indicator\n * @param {string} type - Status type to hide\n */\n hideStatus(type) {\n const indicator = this.statusContainer.querySelector(`[data-status=\"${type}\"]`);\n if (indicator) {\n indicator.classList.remove('show');\n indicator.classList.add('hide');\n\n // Actually hide after animation\n setTimeout(() => {\n indicator.classList.add('d-none');\n indicator.classList.remove('d-inline-block', 'hide');\n indicator.title = ''; // Clear any error message\n }, 300);\n }\n }\n\n /**\n * Hide all status indicators\n */\n hideAllStatuses() {\n const indicators = this.statusContainer.querySelectorAll('[data-status]');\n indicators.forEach(indicator => {\n indicator.classList.add('d-none');\n indicator.classList.remove('d-inline-block', 'show', 'hide');\n indicator.title = '';\n });\n }\n\n /**\n * Set a timeout for auto-hiding status\n * @param {string} type - Status type\n * @param {function} callback - Callback to execute\n * @param {number} delay - Delay in milliseconds\n */\n setTimeout(type, callback, delay) {\n const timeoutId = setTimeout(callback, delay);\n this.timeouts.set(type, timeoutId);\n }\n\n /**\n * Clear a specific timeout\n * @param {string} type - Status type\n */\n clearTimeout(type) {\n if (this.timeouts.has(type)) {\n clearTimeout(this.timeouts.get(type));\n this.timeouts.delete(type);\n }\n }\n\n /**\n * Clean up all timeouts\n */\n destroy() {\n this.timeouts.forEach(timeoutId => clearTimeout(timeoutId));\n this.timeouts.clear();\n }\n}\n\n// Apply FileDropMixin to FormView class\napplyFileDropMixin(FormView);\n\nexport default FormView;\nexport { FormView };\n","import FormView from './FormView.js';\nimport Page from '@core/Page.js';\n\nexport default class FormPage extends Page {\n constructor(options = {}) {\n super({\n title: 'Form Page',\n description: 'A page for submitting forms',\n icon: 'form',\n fields: [],\n template: '<div data-container=\"form-view-container\"></div>',\n className: \"form-page container-sm\",\n ...options\n });\n }\n\n async onInit() {\n await super.onInit();\n this.formView = new FormView({\n containerId: 'form-view-container',\n fields: this.options.fields,\n autosaveModelField: true\n });\n this.addChild(this.formView);\n if (this.getApp().activeGroup) {\n this.formView.setModel(this.getApp().activeGroup);\n }\n }\n\n async onEnter() {\n await super.onEnter();\n if (this.formView) {\n // Recreate formView to ensure clean slate with new model\n await this.recreateFormView();\n }\n }\n\n async onGroupChange(group) {\n if (this.formView) {\n // Recreate formView to ensure clean slate with new model\n await this.recreateFormView();\n }\n }\n\n async recreateFormView() {\n // Destroy old formView\n if (this.formView) {\n await this.formView.destroy();\n this.removeChild(this.formView);\n }\n\n // Create new formView with current model\n this.formView = new FormView({\n containerId: 'form-view-container',\n fields: this.options.fields,\n autosaveModelField: true\n });\n this.addChild(this.formView);\n\n if (this.getApp().activeGroup) {\n this.formView.setModel(this.getApp().activeGroup);\n }\n\n // await this.formView.render();\n }\n}\n","/**\n * Dialog - Complete Bootstrap 5 Modal component for MOJO framework\n * Supports all Bootstrap 5 modal features including sizes, fullscreen, scrollable, etc.\n * Can accept View instances as body content\n */\n\nimport View from '@core/View.js';\n\n\nclass Dialog extends View {\n static _openDialogs = [];\n static _baseZIndex = {\n backdrop: 1050,\n modal: 1055\n };\n\n /**\n * Check if there's a fullscreen table and return appropriate z-index\n */\n static getFullscreenAwareZIndex() {\n const fullscreenTable = document.querySelector('.table-fullscreen');\n if (fullscreenTable) {\n // Fullscreen table uses z-index 9999, so modals should be much higher\n return {\n backdrop: 10040,\n modal: 10050\n };\n }\n return this._baseZIndex;\n }\n\n static _busyIndicator = null;\n static _busyCounter = 0;\n static _busyTimeout = null;\n\n /**\n * Fix all backdrop stacking - ensures proper layering of all open modals\n */\n static fixAllBackdropStacking() {\n const backdrops = document.querySelectorAll('.modal-backdrop');\n const openDialogs = Dialog._openDialogs;\n\n if (backdrops.length === 0 || openDialogs.length === 0) return;\n\n // Sort dialogs by z-index to get correct stacking order\n const sortedDialogs = [...openDialogs].sort((a, b) =>\n (a._dialogZIndex || 0) - (b._dialogZIndex || 0)\n );\n\n // Set backdrop z-indices to create proper stacking\n // Each backdrop should cover all previous modals but stay below its own modal\n backdrops.forEach((backdrop, index) => {\n if (index < sortedDialogs.length) {\n const dialog = sortedDialogs[index];\n const backdropZIndex = dialog._dialogZIndex - 5;\n backdrop.style.zIndex = backdropZIndex;\n\n // Move backdrop to correct container\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n\n if (backdrop.parentNode !== targetContainer) {\n targetContainer.appendChild(backdrop);\n }\n }\n });\n }\n\n /**\n * Update backdrop stacking for all open dialogs\n */\n static updateAllBackdropStacking() {\n Dialog.fixAllBackdropStacking();\n }\n\n /**\n * Shows a full-screen busy indicator.\n * Manages a counter for nested calls, only showing one indicator.\n * @param {object} options - Options { timeout, message }\n */\n static showBusy(options = {}) {\n const { timeout = 30000, message = 'Loading...' } = options;\n\n this._busyCounter++;\n\n if (this._busyCounter === 1) {\n if (this._busyTimeout) {\n clearTimeout(this._busyTimeout);\n }\n\n if (!this._busyIndicator) {\n const zIndexBase = this.getFullscreenAwareZIndex();\n const busyZIndex = zIndexBase.modal + 1000; // Higher than any modal\n\n this._busyIndicator = document.createElement('div');\n this._busyIndicator.className = 'mojo-busy-indicator';\n this._busyIndicator.innerHTML = `\n <div class=\"mojo-busy-spinner\">\n <div class=\"spinner-border text-light\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p class=\"mojo-busy-message mt-3 text-light\">${message}</p>\n </div>\n <style>\n .mojo-busy-indicator {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background-color: rgba(0, 0, 0, 0.5); z-index: ${busyZIndex};\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.15s linear;\n }\n .mojo-busy-indicator.show { opacity: 1; }\n .mojo-busy-spinner .spinner-border { width: 3rem; height: 3rem; }\n </style>\n `;\n document.body.appendChild(this._busyIndicator);\n }\n\n const msgElement = this._busyIndicator.querySelector('.mojo-busy-message');\n if (msgElement) msgElement.textContent = message;\n\n setTimeout(() => this._busyIndicator.classList.add('show'), 10);\n\n this._busyTimeout = setTimeout(() => {\n console.error('Busy indicator timed out.');\n this.hideBusy(true); // Force hide\n this.alert({\n title: 'Operation Timed Out',\n message: 'The operation took too long. Please check your connection and try again.',\n type: 'danger'\n });\n }, timeout);\n }\n }\n\n /**\n * Hides the full-screen busy indicator.\n * Decrements the counter and only hides when the counter reaches zero.\n * @param {boolean} force - If true, forces the indicator to hide immediately.\n */\n static hideBusy(force = false) {\n if (force) {\n this._busyCounter = 0;\n } else {\n this._busyCounter--;\n }\n\n if (this._busyCounter <= 0) {\n this._busyCounter = 0;\n if (this._busyTimeout) {\n clearTimeout(this._busyTimeout);\n this._busyTimeout = null;\n }\n\n if (this._busyIndicator) {\n this._busyIndicator.classList.remove('show');\n setTimeout(() => {\n if (this._busyIndicator && this._busyCounter === 0) {\n this._busyIndicator.remove();\n this._busyIndicator = null;\n }\n }, 150);\n }\n }\n }\n\n constructor(options = {}) {\n // Generate unique ID if not provided\n const modalId = options.id || `modal-${Date.now()}`;\n\n super({\n ...options,\n id: modalId, // Pass the ID to parent constructor\n tagName: 'div',\n className: `modal ${options.fade !== false ? 'fade' : ''} ${options.className || ''}`,\n attributes: {\n tabindex: '-1',\n 'aria-hidden': 'true',\n 'aria-labelledby': options.labelledBy || `${modalId}-label`,\n 'aria-describedby': options.describedBy || null,\n ...options.attributes\n }\n });\n\n // Store modal ID for internal use\n this.modalId = modalId;\n\n // Dialog configuration\n this.title = options.title || '';\n this.titleId = `${this.modalId}-label`;\n\n // Size options: sm, md (default), lg, xl, xxl, fullscreen, auto\n // Or responsive fullscreen: fullscreen-sm-down, fullscreen-md-down, etc.\n // 'auto' enables dynamic sizing based on content dimensions\n this.size = options.size || '';\n\n // Layout options\n this.centered = options.centered !== undefined ? options.centered : false;\n this.scrollable = options.scrollable !== undefined ? options.scrollable : false;\n // Auto-sizing: dynamically size modal based on content dimensions\n // Can be enabled with autoSize: true or size: 'auto'\n // Waits for modal animation to complete before measuring content\n this.autoSize = options.autoSize || options.size === 'auto'; // Auto-size modal based on content dimensions\n\n // Bootstrap modal options\n this.backdrop = options.backdrop !== undefined ? options.backdrop : true; // true, false, 'static'\n this.keyboard = options.keyboard !== undefined ? options.keyboard : true;\n this.focus = options.focus !== undefined ? options.focus : true;\n\n // Content\n this.header = options.header !== undefined ? options.header : true;\n this.headerContent = options.headerContent || null;\n this.headerView = null; // Will hold View instance if headerContent is a View\n this.closeButton = options.closeButton !== undefined ? options.closeButton : true;\n this.contextMenu = options.contextMenu || null;\n\n // Handle different header content types (support View instances)\n this._processHeaderContent(this.headerContent);\n\n // Enhanced body handling - support View, Promise<View>, or function returning View\n this.body = options.body || options.content || '';\n this.bodyView = null; // Will hold View instance if body is a View\n this.bodyClass = options.bodyClass || '';\n this.noBodyPadding = options.noBodyPadding || false; // Remove default modal-body padding\n\n // Auto-sizing constraints - only used when autoSize is enabled\n this.minWidth = options.minWidth || 300; // Minimum modal width (px)\n this.minHeight = options.minHeight || 200; // Minimum modal height (px)\n if (options.maxHeight) this.maxHeight = options.maxHeight;\n this.maxWidthPercent = options.maxWidthPercent || 0.9; // Max width as % of viewport\n this.maxHeightPercent = options.maxHeightPercent || 0.8; // Max height as % of viewport\n\n // Handle different body types\n this._processBodyContent(this.body);\n\n this.footer = options.footer || null;\n this.footerView = null; // Will hold View instance if footer is a View\n this.footerClass = options.footerClass || '';\n\n // Handle different footer types\n this._processFooterContent(this.footer);\n\n // Buttons configuration\n this.buttons = options.buttons || null;\n\n // Callbacks for Bootstrap events\n this.onShow = options.onShow || null; // show.bs.modal\n this.onShown = options.onShown || null; // shown.bs.modal\n this.onHide = options.onHide || null; // hide.bs.modal\n this.onHidden = options.onHidden || null; // hidden.bs.modal\n this.onHidePrevented = options.onHidePrevented || null; // hidePrevented.bs.modal\n\n // Auto show on creation\n this.autoShow = options.autoShow !== undefined ? options.autoShow : false;\n\n // Bootstrap modal instance\n this.modal = null;\n\n // Related target (button that triggered the modal)\n this.relatedTarget = options.relatedTarget || null;\n }\n\n /**\n * Process body content to detect and handle View instances\n */\n _processBodyContent(body) {\n if (body && body.render) {\n this.bodyView = body;\n this.body = ''; // Clear string body\n this.addChild(this.bodyView); // Add as child for proper lifecycle\n } else if (typeof body === 'function') {\n // Support lazy View creation\n try {\n const result = body();\n if (result instanceof View) {\n this.bodyView = result;\n this.body = '';\n this.addChild(this.bodyView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.bodyPromise = result;\n this.body = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.body = result;\n }\n } catch (error) {\n console.error('Error processing body function:', error);\n this.body = body;\n }\n } else {\n this.body = body;\n }\n }\n\n /**\n * Process header content to detect and handle View instances\n */\n _processHeaderContent(headerContent) {\n if (headerContent instanceof View) {\n this.headerView = headerContent;\n this.headerContent = null;\n this.addChild(this.headerView);\n } else if (typeof headerContent === 'function') {\n // Support lazy View creation\n try {\n const result = headerContent();\n if (result instanceof View) {\n this.headerView = result;\n this.headerContent = null;\n this.addChild(this.headerView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.headerPromise = result;\n this.headerContent = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.headerContent = result;\n }\n } catch (error) {\n console.error('Error processing headerContent function:', error);\n this.headerContent = headerContent;\n }\n } else {\n this.headerContent = headerContent;\n }\n }\n\n /**\n * Process footer content to detect and handle View instances\n */\n _processFooterContent(footer) {\n if (footer instanceof View) {\n this.footerView = footer;\n this.footer = null;\n this.addChild(this.footerView);\n } else if (typeof footer === 'function') {\n // Support lazy View creation\n try {\n const result = footer();\n if (result instanceof View) {\n this.footerView = result;\n this.footer = null;\n this.addChild(this.footerView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.footerPromise = result;\n this.footer = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.footer = result;\n }\n } catch (error) {\n console.error('Error processing footer function:', error);\n this.footer = footer;\n }\n } else {\n this.footer = footer;\n }\n }\n\n /**\n * Get dialog template with all Bootstrap 5 features\n */\n async getTemplate() {\n // Build dialog classes\n const dialogClasses = ['modal-dialog'];\n\n // Add size class (excluding 'auto' which uses default sizing)\n if (this.size && this.size !== 'auto') {\n if (this.size.startsWith('fullscreen')) {\n // Fullscreen or responsive fullscreen\n dialogClasses.push(`modal-${this.size}`);\n } else if (['sm', 'lg', 'xl', 'xxl'].includes(this.size)) {\n // Standard sizes\n dialogClasses.push(`modal-${this.size}`);\n // Auto fullscreen on small screens for large sizes\n if (['lg', 'xl', 'xxl'].includes(this.size)) {\n dialogClasses.push('modal-fullscreen-sm-down');\n }\n }\n }\n\n // Add centered class\n if (this.centered) {\n dialogClasses.push('modal-dialog-centered');\n }\n\n // Add scrollable class\n if (this.scrollable) {\n if (!this.maxHeight) {\n dialogClasses.push('modal-dialog-scrollable');\n } else {\n dialogClasses.push('overflow-hidden');\n }\n }\n\n return `\n <div class=\"${dialogClasses.join(' ')}\">\n <div class=\"modal-content\">\n ${await this.buildHeader()}\n ${await this.buildBody()}\n ${await this.buildFooter()}\n </div>\n </div>\n `;\n }\n\n /**\n * Build modal header\n */\n async buildHeader() {\n if (!this.header) {\n return '';\n }\n\n // If we have a View instance as header content\n if (this.headerView) {\n this.headerView.replaceById = true;\n return `<div class=\"modal-header\" data-view-container=\"header\">\n <!-- View will be mounted here -->\n <div id=\"${this.headerView.id}\"></div>\n </div>`;\n }\n\n if (this.headerContent) {\n return `<div class=\"modal-header\">${this.headerContent}</div>`;\n }\n\n // Build context menu or close button\n let headerActions = '';\n if (this.contextMenu && this.contextMenu.items && this.contextMenu.items.length > 0) {\n headerActions = await this.buildContextMenu();\n } else if (this.closeButton) {\n headerActions = '<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>';\n }\n\n return `\n <div class=\"modal-header\">\n ${this.title ? `<h5 class=\"modal-title\" id=\"${this.titleId}\">${this.title}</h5>` : ''}\n ${headerActions}\n </div>\n `;\n }\n\n async buildContextMenu() {\n const menuItems = await this.filterContextMenuItems();\n if (menuItems.length === 0) {\n // If no items pass permission checks, show regular close button\n return this.closeButton ? '<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>' : '';\n }\n\n const triggerIcon = this.contextMenu.icon || 'bi-three-dots-vertical';\n const buttonClass = this.contextMenu.buttonClass || 'btn btn-link p-1 mojo-modal-context-menu-btn';\n\n const menuItemsHtml = menuItems.map(item => {\n if (item.type === 'divider') {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n const icon = item.icon ? `<i class=\"${item.icon} me-2\"></i>` : '';\n const label = item.label || '';\n\n if (item.href) {\n return `<li><a class=\"dropdown-item\" href=\"${item.href}\"${item.target ? ` target=\"${item.target}\"` : ''}>${icon}${label}</a></li>`;\n } else if (item.action) {\n const dataAttrs = Object.keys(item)\n .filter(key => key.startsWith('data-'))\n .map(key => `${key}=\"${item[key]}\"`)\n .join(' ');\n return `<li><a class=\"dropdown-item\" data-action=\"${item.action}\" ${dataAttrs}>${icon}${label}</a></li>`;\n }\n\n return '';\n }).join('');\n\n return `\n <div class=\"dropdown\">\n <button class=\"${buttonClass}\" type=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"${triggerIcon}\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n ${menuItemsHtml}\n </ul>\n </div>\n `;\n }\n\n async filterContextMenuItems() {\n if (!this.contextMenu || !this.contextMenu.items) {\n return [];\n }\n\n const filteredItems = [];\n\n for (const item of this.contextMenu.items) {\n // Always include dividers\n if (item.type === 'divider') {\n filteredItems.push(item);\n continue;\n }\n\n // Check permissions if specified\n if (item.permissions) {\n try {\n const app = this.getApp?.();\n let user = null;\n\n if (app) {\n user = app.activeUser || app.getState?.('activeUser');\n }\n\n // Also check window.getApp as fallback for mock systems\n if (!user && typeof window !== 'undefined' && window.getApp) {\n try {\n const globalApp = window.getApp();\n user = globalApp?.activeUser;\n } catch (e) {\n // Ignore global app errors\n }\n }\n\n if (user && user.hasPermission) {\n if (!user.hasPermission(item.permissions)) {\n continue; // Skip this item\n }\n } else {\n // If no permission system available, skip items with permission requirements\n continue;\n }\n } catch (error) {\n console.warn('Error checking permissions for context menu item:', error);\n continue;\n }\n }\n\n filteredItems.push(item);\n }\n\n return filteredItems;\n }\n\n /**\n * Build modal body\n */\n async buildBody() {\n // If we have a View instance as body\n if (this.bodyView) {\n this.bodyView.replaceById = true;\n const bodyClass = this.noBodyPadding ? `modal-body p-0 ${this.bodyClass}` : `modal-body ${this.bodyClass}`;\n return `<div class=\"${bodyClass}\" data-view-container=\"body\">\n <!-- View will be mounted here -->\n <div id=\"${this.bodyView.id}\"></div>\n </div>`;\n }\n\n // Regular string/HTML body\n if (!this.body && this.body !== '') {\n return '';\n }\n\n const bodyClass = this.noBodyPadding ? `modal-body p-0 ${this.bodyClass}` : `modal-body ${this.bodyClass}`;\n return `\n <div class=\"${bodyClass}\">\n ${this.body}\n </div>\n `;\n }\n\n /**\n * Build modal footer\n */\n async buildFooter() {\n // If we have a View instance as footer\n if (this.footerView) {\n return `<div class=\"modal-footer ${this.footerClass}\" data-view-container=\"footer\"></div>`;\n }\n\n // Custom footer content\n if (this.footer !== null && typeof this.footer === 'string') {\n return `<div class=\"modal-footer ${this.footerClass}\">${this.footer}</div>`;\n }\n\n // Build footer from buttons\n if (this.buttons && this.buttons.length > 0) {\n const buttonsHtml = this.buttons.map(btn => {\n const dismissAttr = btn.dismiss ? 'data-bs-dismiss=\"modal\"' : '';\n const actionAttr = btn.action ? `data-action=\"${btn.action}\"` : '';\n const idAttr = btn.id ? `id=\"${btn.id}\"` : '';\n const disabledAttr = btn.disabled ? 'disabled' : '';\n\n return `\n <button type=\"${btn.type || 'button'}\"\n class=\"btn ${btn.class || 'btn-secondary'}\"\n ${idAttr}\n ${dismissAttr}\n ${actionAttr}\n ${disabledAttr}>\n ${btn.icon ? `<i class=\"bi ${btn.icon} me-1\"></i>` : ''}\n ${btn.text || 'Button'}\n </button>\n `;\n }).join('');\n\n return `<div class=\"modal-footer ${this.footerClass}\">${buttonsHtml}</div>`;\n }\n\n // No footer\n return '';\n }\n\n\n /**\n * Override mount to not require a container for dialogs\n * Dialogs are appended to body or fullscreen element\n */\n async mount(_container = null) {\n if (this.mounted || this.destroyed) {\n return;\n }\n\n // For dialogs, we only need the element, not a container\n if (!this.element) {\n throw new Error('Cannot mount dialog without element');\n }\n\n // Call lifecycle hooks\n await this.onBeforeMount();\n\n // Append to fullscreen element if it exists, otherwise to body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n targetContainer.appendChild(this.element);\n\n // Bind DOM events\n this.bindEvents();\n\n // Set mounted flag\n this.mounted = true;\n\n // Call after mount (this initializes Bootstrap modal)\n await this.onAfterMount();\n\n // Emit mounted event\n this.emit('mounted', { view: this });\n\n return this;\n }\n\n /**\n * After render - prepare for View instances and apply syntax highlighting\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n // Apply Prism syntax highlighting if available and there are code blocks\n if (window.Prism && this.element) {\n const codeBlocks = this.element.querySelectorAll('pre code');\n if (codeBlocks.length > 0) {\n // Use Prism's highlightAllUnder to highlight code within this dialog\n window.Prism.highlightAllUnder(this.element);\n }\n }\n\n // Child views will be mounted in onAfterMount when element is in DOM\n\n // Apply auto-sizing after rendering if enabled\n if (this.autoSize) {\n this.setupAutoSizing();\n } else if (this.maxHeight) {\n const modalBody = this.element.querySelector('.modal-body');\n if (modalBody) {\n modalBody.style.maxHeight = `${this.maxHeight}px`;\n // modalBody.style.overflowY = 'auto';\n }\n }\n }\n\n /**\n * After mount - initialize Bootstrap modal and mount child views\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n if (typeof window !== 'undefined' && window.bootstrap && window.bootstrap.Modal) {\n // Set data attributes if needed\n if (this.backdrop === 'static') {\n this.element.setAttribute('data-bs-backdrop', 'static');\n }\n if (!this.keyboard) {\n this.element.setAttribute('data-bs-keyboard', 'false');\n }\n\n // Initialize Bootstrap modal with options\n this.modal = new window.bootstrap.Modal(this.element, {\n backdrop: this.backdrop,\n keyboard: this.keyboard,\n focus: this.focus\n });\n\n // Bind Bootstrap events\n this.bindBootstrapEvents();\n\n // Auto show if requested\n if (this.autoShow) {\n this.show(this.relatedTarget);\n }\n }\n }\n\n /**\n * Setup auto-sizing - wait for modal animation to complete\n */\n setupAutoSizing() {\n if (!this.element) return;\n\n // Listen for modal shown event to apply sizing after animation\n this.element.addEventListener('shown.bs.modal', () => {\n this.applyAutoSizing();\n }, { once: true });\n\n // Fallback: apply immediately if modal is already shown or no animation\n setTimeout(() => {\n if (this.isShown()) {\n this.applyAutoSizing();\n }\n }, 100);\n }\n\n /**\n * Apply auto-sizing based on content dimensions\n */\n applyAutoSizing() {\n if (!this.element) return;\n\n try {\n const modalDialog = this.element.querySelector('.modal-dialog');\n const modalContent = this.element.querySelector('.modal-content');\n const modalBody = this.element.querySelector('.modal-body');\n\n if (!modalDialog || !modalContent || !modalBody) {\n console.warn('Dialog auto-sizing: Required elements not found');\n return;\n }\n\n // Wait for child views to fully render\n if (this.bodyView && !this.bodyView.element) {\n setTimeout(() => this.applyAutoSizing(), 50);\n return;\n }\n\n // Store original styles and classes for restoration\n const originalStyles = {\n dialogMaxWidth: modalDialog.style.maxWidth,\n dialogWidth: modalDialog.style.width,\n contentWidth: modalContent.style.width,\n contentMaxHeight: modalContent.style.maxHeight,\n hadScrollableClass: modalDialog.classList.contains('modal-dialog-scrollable')\n };\n\n // Temporarily remove size constraints to measure natural content size\n modalDialog.style.maxWidth = 'none';\n modalDialog.style.width = 'auto';\n modalContent.style.width = 'auto';\n modalContent.style.maxHeight = 'none';\n\n // Force layout recalculation\n modalContent.offsetHeight;\n\n // Measure content dimensions after forced layout\n const contentRect = modalContent.getBoundingClientRect();\n\n // Calculate viewport constraints with margins\n const viewportMargin = 40;\n const maxWidth = Math.min(\n window.innerWidth * this.maxWidthPercent,\n window.innerWidth - viewportMargin\n );\n let maxHeight = Math.min(\n window.innerHeight * this.maxHeightPercent,\n window.innerHeight - viewportMargin\n );\n\n // Calculate optimal dimensions with padding for content\n let optimalWidth = Math.max(this.minWidth, Math.ceil(contentRect.width + 20));\n let optimalHeight = Math.max(this.minHeight, Math.ceil(contentRect.height));\n if (this.maxHeight) {\n maxHeight = Math.min(this.maxHeight, maxHeight);\n modalDialog.style.maxHeight = `${maxHeight}px`;\n }\n // Apply viewport constraints\n optimalWidth = Math.min(optimalWidth, maxWidth);\n const heightExceedsMax = contentRect.height > maxHeight;\n\n // Apply the calculated size\n modalDialog.style.maxWidth = `${optimalWidth}px`;\n\n modalDialog.style.width = `${optimalWidth}px`;\n\n // Handle height overflow with scrolling - use Bootstrap's scrollable class\n if (heightExceedsMax) {\n // Add Bootstrap's modal-dialog-scrollable class for proper max-height behavior\n if (!modalDialog.classList.contains('modal-dialog-scrollable')) {\n modalDialog.classList.add('modal-dialog-scrollable');\n }\n modalContent.style.maxHeight = `${maxHeight}px`;\n optimalHeight = maxHeight;\n }\n\n // Store the applied dimensions\n this.autoSizedWidth = optimalWidth;\n this.autoSizedHeight = optimalHeight;\n this._originalStyles = originalStyles;\n\n } catch (error) {\n console.error('Error in dialog auto-sizing:', error);\n // Fallback: ensure modal is still usable\n this.element.querySelector('.modal-dialog').style.maxWidth = '';\n }\n }\n\n /**\n * Reset auto-sizing and restore original modal dimensions\n */\n resetAutoSizing() {\n if (!this.autoSize || !this._originalStyles || !this.element) return;\n\n try {\n const modalDialog = this.element.querySelector('.modal-dialog');\n const modalContent = this.element.querySelector('.modal-content');\n const modalBody = this.element.querySelector('.modal-body');\n\n if (modalDialog && modalContent && modalBody) {\n // Restore original styles\n modalDialog.style.maxWidth = this._originalStyles.dialogMaxWidth || '';\n modalDialog.style.width = this._originalStyles.dialogWidth || '';\n modalContent.style.width = this._originalStyles.contentWidth || '';\n modalContent.style.maxHeight = this._originalStyles.contentMaxHeight || '';\n\n // Restore scrollable class state\n if (!this._originalStyles.hadScrollableClass && modalDialog.classList.contains('modal-dialog-scrollable')) {\n modalDialog.classList.remove('modal-dialog-scrollable');\n }\n\n // Clear stored dimensions\n delete this.autoSizedWidth;\n delete this.autoSizedHeight;\n delete this._originalStyles;\n }\n } catch (error) {\n console.error('Error resetting dialog auto-sizing:', error);\n }\n }\n\n /**\n * Bind Bootstrap modal events\n */\n bindBootstrapEvents() {\n // show.bs.modal\n this.element.addEventListener('show.bs.modal', (e) => {\n // Manage stacking for multiple dialogs with fullscreen awareness\n const stackIndex = Dialog._openDialogs.length;\n const zIndexBase = Dialog.getFullscreenAwareZIndex();\n const newZIndex = zIndexBase.modal + (stackIndex * 20);\n this.element.style.zIndex = newZIndex;\n\n // Store z-index on this dialog for later reference\n this._dialogZIndex = newZIndex;\n this._backdropZIndex = newZIndex - 10; // Ensure backdrop covers previous modals\n\n Dialog._openDialogs.push(this);\n\n if (this.onShow) this.onShow(e);\n this.emit('show', {\n dialog: this,\n relatedTarget: e.relatedTarget\n });\n });\n\n // shown.bs.modal\n this.element.addEventListener('shown.bs.modal', (e) => {\n // Fix all backdrop stacking after Bootstrap has finished\n setTimeout(() => {\n Dialog.fixAllBackdropStacking();\n }, 50);\n\n if (this.onShown) this.onShown(e);\n this.emit('shown', {\n dialog: this,\n relatedTarget: e.relatedTarget\n });\n\n // Focus first input if exists\n if (this.focus) {\n const firstInput = this.element.querySelector('input:not([type=\"hidden\"]), textarea, select');\n if (firstInput) {\n firstInput.focus();\n }\n }\n });\n\n // hide.bs.modal\n this.element.addEventListener('hide.bs.modal', (e) => {\n // Blur any focused element inside the modal to prevent accessibility warning\n const focusedElement = this.element.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n if (this.onHide) {\n const result = this.onHide(e);\n if (result === false) {\n e.preventDefault();\n return;\n }\n }\n this.emit('hide', { dialog: this });\n });\n\n // hidden.bs.modal\n this.element.addEventListener('hidden.bs.modal', (e) => {\n // Manage stacking\n const index = Dialog._openDialogs.indexOf(this);\n if (index > -1) {\n Dialog._openDialogs.splice(index, 1);\n }\n\n // If there are still modals open, ensure body has modal-open class\n // and properly manage backdrop stacking\n if (Dialog._openDialogs.length > 0) {\n document.body.classList.add('modal-open');\n\n setTimeout(() => { // Let Bootstrap finish its hide animation\n Dialog.fixAllBackdropStacking();\n }, 50);\n }\n\n // Restore focus to the element that had it before modal opened\n if (this.previousFocus && document.body.contains(this.previousFocus)) {\n this.previousFocus.focus();\n }\n\n if (this.onHidden) this.onHidden(e);\n this.emit('hidden', { dialog: this });\n });\n\n // hidePrevented.bs.modal\n this.element.addEventListener('hidePrevented.bs.modal', (e) => {\n if (this.onHidePrevented) this.onHidePrevented(e);\n this.emit('hidePrevented', { dialog: this });\n });\n }\n\n\n\n /**\n * Show the dialog\n * @param {HTMLElement} relatedTarget - Optional element that triggered the modal\n */\n show(relatedTarget = null) {\n // Capture the currently focused element for later restoration\n this.previousFocus = document.activeElement;\n window.lastDialog = this;\n if (this.modal) {\n this.modal.show(relatedTarget);\n }\n }\n\n /**\n * Hide the dialog\n */\n hide() {\n // Blur any focused element inside the modal before hiding\n const focusedElement = this.element?.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n if (this.modal) {\n this.modal.hide();\n }\n }\n\n /**\n * Toggle the dialog\n * @param {HTMLElement} relatedTarget - Optional element that triggered the modal\n */\n toggle(relatedTarget = null) {\n if (this.modal) {\n this.modal.toggle(relatedTarget);\n }\n }\n\n /**\n * Destroy the dialog and clean up resources\n */\n async destroy() {\n // Hide modal if it's showing\n if (this.modal) {\n // Remove focus from any element inside the modal\n const focusedElement = this.element?.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n // Dispose of Bootstrap modal instance\n this.modal.dispose();\n this.modal = null;\n }\n\n // Restore previous focus if available\n if (this.previousFocus && document.body.contains(this.previousFocus)) {\n this.previousFocus.focus();\n this.previousFocus = null;\n }\n\n // Clean up auto-sizing\n if (this.autoSize) {\n this.resetAutoSizing();\n }\n\n // Call parent destroy\n await super.destroy();\n }\n\n /**\n * Handle dynamic height changes\n */\n handleUpdate() {\n if (this.modal) {\n this.modal.handleUpdate();\n }\n }\n\n /**\n * Update dialog content\n * @param {string|View} content - New content (string or View instance)\n */\n async setContent(content) {\n // Handle View instance\n if (content instanceof View) {\n // Clean up old view if exists\n if (this.bodyView) {\n await this.bodyView.destroy();\n this.removeChild(this.bodyView);\n }\n\n this.bodyView = content;\n this.body = '';\n this.addChild(this.bodyView);\n\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n bodyEl.innerHTML = '';\n // Pass container to render - it will handle mounting internally\n await this.bodyView.render(bodyEl);\n }\n } else {\n // String content\n this.body = content;\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n bodyEl.innerHTML = content;\n }\n }\n\n // Update modal position if needed\n this.handleUpdate();\n }\n\n /**\n * Update dialog title\n */\n setTitle(title) {\n this.title = title;\n const titleEl = this.element?.querySelector('.modal-title');\n if (titleEl) {\n titleEl.textContent = title;\n }\n }\n\n /**\n * Set loading state\n */\n setLoading(loading = true, message = 'Loading...') {\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n if (loading) {\n bodyEl.innerHTML = `\n <div class=\"text-center py-4\">\n <div class=\"spinner-border text-primary mb-3\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p>${message}</p>\n </div>\n `;\n } else if (this.bodyView) {\n bodyEl.replaceChildren(this.bodyView.element);\n }\n }\n }\n\n /**\n * Clean up\n */\n async onBeforeDestroy() {\n // Clean up child views\n if (this.headerView) {\n await this.headerView.destroy();\n }\n if (this.bodyView) {\n await this.bodyView.destroy();\n }\n if (this.footerView) {\n await this.footerView.destroy();\n }\n\n await super.onBeforeDestroy();\n\n // Dispose Bootstrap modal instance (defensive: destroy() also disposes, but\n // onBeforeDestroy can be called from parent lifecycle paths as well)\n if (this.modal) {\n this.modal.dispose();\n this.modal = null;\n }\n }\n\n /**\n * Static method to show code in a dialog\n */\n static async showCode(options = {}) {\n const dialog = new Dialog({\n title: options.title || 'Source Code',\n size: options.size || 'lg',\n scrollable: true,\n body: Dialog.formatCode(options.code, options.language),\n buttons: [\n {\n text: 'Copy to Clipboard',\n class: 'btn-primary',\n icon: 'bi-clipboard',\n action: 'copy'\n },\n {\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n }\n ]\n });\n\n // Handle copy action\n dialog.on('action:copy', async () => {\n if (navigator.clipboard) {\n try {\n await navigator.clipboard.writeText(options.code);\n dialog.showCopySuccess();\n } catch (err) {\n console.error('Failed to copy:', err);\n }\n }\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Apply syntax highlighting after mounting\n if (window.Prism && dialog.element) {\n window.Prism.highlightAllUnder(dialog.element);\n }\n\n // Show the dialog\n dialog.show();\n\n // Clean up when hidden\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n });\n\n return dialog;\n }\n\n /**\n * Format code for display with syntax highlighting support\n */\n static formatCode(code, language = 'javascript') {\n let highlightedCode;\n\n // Check if Prism.js is available and has the language\n if (window.Prism && window.Prism.languages[language]) {\n // Use Prism to highlight the code\n highlightedCode = window.Prism.highlight(code, window.Prism.languages[language], language);\n } else {\n // Fallback: just escape HTML\n highlightedCode = code\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n }\n\n // Add Prism classes for styling even if highlighting wasn't applied\n const prismClass = window.Prism ? `language-${language}` : '';\n\n // Modern VS Code-like dark theme styling\n const codeStyles = `\n max-height: 60vh;\n overflow-y: auto;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 1.25rem;\n border-radius: 0.5rem;\n margin: 0;\n font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', 'Monaco', monospace;\n font-size: 0.9rem;\n line-height: 1.6;\n border: 1px solid #2d2d30;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);\n `.replace(/\\s+/g, ' ').trim();\n\n return `\n <style>\n /* Custom Prism theme overrides for Dialog */\n .dialog-code-block .token.comment { color: #6a9955; }\n .dialog-code-block .token.string { color: #ce9178; }\n .dialog-code-block .token.keyword { color: #569cd6; }\n .dialog-code-block .token.function { color: #dcdcaa; }\n .dialog-code-block .token.number { color: #b5cea8; }\n .dialog-code-block .token.operator { color: #d4d4d4; }\n .dialog-code-block .token.class-name { color: #4ec9b0; }\n .dialog-code-block .token.punctuation { color: #d4d4d4; }\n .dialog-code-block .token.boolean { color: #569cd6; }\n .dialog-code-block .token.property { color: #9cdcfe; }\n .dialog-code-block .token.tag { color: #569cd6; }\n .dialog-code-block .token.attr-name { color: #9cdcfe; }\n .dialog-code-block .token.attr-value { color: #ce9178; }\n .dialog-code-block ::selection { background: #264f78; }\n </style>\n <pre class=\"${prismClass} dialog-code-block\" style=\"${codeStyles}\">\n <code class=\"${prismClass}\" style=\"color: inherit; background: transparent; text-shadow: none;\">${highlightedCode}</code>\n </pre>\n `;\n }\n\n /**\n * Trigger Prism highlighting on already rendered code blocks\n * Call this after inserting code into the DOM if not using formatCode\n */\n static highlightCodeBlocks(container = document) {\n if (window.Prism && window.Prism.highlightAllUnder) {\n window.Prism.highlightAllUnder(container);\n }\n }\n\n /**\n * Show copy success feedback\n */\n showCopySuccess() {\n const btn = this.element.querySelector('[data-action=\"copy\"]');\n if (btn) {\n const originalHtml = btn.innerHTML;\n btn.innerHTML = '<i class=\"bi bi-check me-1\"></i>Copied!';\n btn.classList.remove('btn-primary');\n btn.classList.add('btn-success');\n btn.disabled = true;\n\n setTimeout(() => {\n btn.innerHTML = originalHtml;\n btn.classList.remove('btn-success');\n btn.classList.add('btn-primary');\n btn.disabled = false;\n }, 2000);\n }\n }\n\n\n\n\n /**\n * Static method to show HTML preview in a sandboxed iframe\n */\n static async showHtmlPreview(options = {}) {\n const htmlContent = options.html || options.content || '';\n const title = options.title || 'HTML Preview';\n const size = options.size || 'lg';\n const height = options.height || 500;\n\n const previewHtml = `\n <div class=\"html-preview-container\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <small class=\"text-muted\">Preview (sandboxed)</small>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" data-action=\"refresh-preview\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <iframe \n id=\"html-preview-frame\"\n class=\"border rounded w-100\" \n style=\"height: ${height}px; background: white;\"\n sandbox=\"allow-same-origin\"\n frameborder=\"0\"\n ></iframe>\n </div>\n `;\n\n const dialog = new Dialog({\n title,\n size,\n scrollable: false,\n body: previewHtml,\n buttons: [\n {\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n }\n ]\n });\n\n // Handle refresh action\n dialog.on('action:refresh-preview', async (event) => {\n const iframe = dialog.element.querySelector('#html-preview-frame');\n if (!iframe) return;\n\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;\n iframeDoc.open();\n iframeDoc.write(htmlContent);\n iframeDoc.close();\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Render HTML in iframe after dialog is rendered\n const iframe = dialog.element.querySelector('#html-preview-frame');\n if (iframe) {\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;\n iframeDoc.open();\n iframeDoc.write(htmlContent);\n iframeDoc.close();\n }\n\n // Show the dialog\n dialog.show();\n\n // Clean up when hidden\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n });\n\n return dialog;\n }\n\n /**\n * Show a dialog with promise-based button handling\n * - If a button has a handler, it will be called. Return semantics:\n * - true or undefined: resolve and close (with button.value || button.action || index)\n * - null or false: keep dialog open (do not resolve)\n * - any other value: resolve with that value and close\n * - If no handler, resolve with action/value/index and close\n * @param {Object} options - Dialog options\n * @returns {Promise} Resolves with value/action/index or null on dismiss\n */\n static async showDialog(options = {}) {\n // Handle legacy signature (message, title, options)\n if (typeof options === 'string') {\n const message = arguments[0];\n const title = arguments[1] || 'Alert';\n const opts = arguments[2] || {};\n options = {\n ...opts,\n body: message,\n title: title\n };\n }\n\n const {\n title = 'Dialog',\n content,\n body = content || '',\n size = 'md',\n centered = true,\n buttons = [\n { text: 'OK', class: 'btn-primary', value: true }\n ],\n rejectOnDismiss = false, // Default to return null on dismissal\n ...dialogOptions\n } = options;\n\n // Create the dialog (preserve original button action/dismiss attributes)\n const dialog = new Dialog({\n title,\n body: body,\n size,\n centered,\n buttons,\n ...dialogOptions\n });\n\n // Render and mount\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Return promise that resolves based on button clicks\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n // Handle button clicks\n const buttonElements = dialog.element.querySelectorAll('.modal-footer button');\n buttonElements.forEach((btnElement, index) => {\n const buttonConfig = buttons[index];\n if (!buttonConfig) return;\n\n btnElement.addEventListener('click', async (e) => {\n if (resolved) return;\n\n const defaultResolveValue = (\n buttonConfig.value !== undefined\n ? buttonConfig.value\n : (buttonConfig.action ?? index)\n );\n\n // If a handler is provided, call it and respect its return semantics\n if (typeof buttonConfig.handler === 'function') {\n try {\n const result = await buttonConfig.handler({\n dialog,\n button: buttonConfig,\n index,\n event: e\n });\n\n // null/false -> keep dialog open\n if (result === null || result === false) {\n return;\n }\n\n // Determine resolve value and close\n const valueToResolve =\n (result === true || result === undefined)\n ? defaultResolveValue\n : result;\n\n resolved = true;\n // Close the dialog (Bootstrap will close if dismiss attribute is present)\n if (!buttonConfig.dismiss) {\n dialog.hide();\n }\n resolve(valueToResolve);\n } catch (err) {\n console.error('Dialog button handler error:', err);\n // Keep dialog open on handler error\n return;\n }\n } else {\n // No handler: resolve with action/value/index and close\n resolved = true;\n if (!buttonConfig.dismiss) {\n dialog.hide();\n }\n resolve(defaultResolveValue);\n }\n });\n });\n\n // Handle backdrop click or ESC key\n dialog.on('hidden', () => {\n // If not already resolved by a button handler, resolve as dismiss\n if (!resolved) {\n resolved = true;\n if (rejectOnDismiss) {\n reject(new Error('Dialog dismissed'));\n } else {\n resolve(null);\n }\n }\n // Always cleanup after hide\n setTimeout(() => {\n dialog.destroy();\n dialog.element.remove();\n }, 100);\n });\n\n // Show the dialog\n dialog.show();\n });\n }\n\n /**\n * Static alert dialog helper\n * @param {Object|string} options - Alert options or message string\n * @returns {Promise} Resolves when OK is clicked\n */\n static async alert(options = {}) {\n // Handle string argument\n if (typeof options === 'string') {\n options = {\n message: options,\n title: 'Alert'\n };\n }\n\n const {\n message = '',\n title = 'Alert',\n type = 'info', // info, success, warning, danger\n ...dialogOptions\n } = options;\n\n // Add icon based on type\n let icon = '';\n let titleClass = '';\n switch(type) {\n case 'success':\n icon = '<i class=\"bi bi-check-circle-fill text-success me-2\"></i>';\n titleClass = 'text-success';\n break;\n case 'warning':\n icon = '<i class=\"bi bi-exclamation-triangle-fill text-warning me-2\"></i>';\n titleClass = 'text-warning';\n break;\n case 'danger':\n case 'error':\n icon = '<i class=\"bi bi-x-circle-fill text-danger me-2\"></i>';\n titleClass = 'text-danger';\n break;\n default:\n icon = '<i class=\"bi bi-info-circle-fill text-info me-2\"></i>';\n titleClass = 'text-info';\n }\n\n return Dialog.showDialog({\n title: `<span class=\"${titleClass}\">${icon}${title}</span>`,\n body: `<p>${message}</p>`,\n size: 'sm',\n centered: true,\n buttons: [\n { text: 'OK', class: 'btn-primary', value: true }\n ],\n ...dialogOptions\n });\n }\n\n /**\n * Static confirm dialog\n */\n static async confirm(message, title = 'Confirm', options = {}) {\n if (typeof message === 'object') {\n options = message;\n message = options.message;\n title = options.title || title;\n }\n const dialog = new Dialog({\n title,\n body: `<p>${message}</p>`,\n size: options.size || 'sm',\n centered: true,\n backdrop: 'static',\n buttons: [\n { text: options.cancelText || 'Cancel', class: 'btn-secondary', dismiss: true, action: 'cancel' },\n { text: options.confirmText || 'Confirm', class: options.confirmClass || 'btn-primary', action: 'confirm' }\n ],\n ...options\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n dialog.show();\n\n return new Promise((resolve) => {\n let result = false;\n\n dialog.on('action:confirm', () => {\n result = true;\n dialog.hide();\n });\n\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n resolve(result);\n });\n });\n }\n\n /**\n * Static prompt dialog\n */\n static async prompt(message, title = 'Input', options = {}) {\n const inputId = `prompt-input-${Date.now()}`;\n const defaultValue = options.defaultValue || '';\n const inputType = options.inputType || 'text';\n const placeholder = options.placeholder || '';\n\n const dialog = new Dialog({\n title,\n body: `\n <p>${message}</p>\n <input type=\"${inputType}\"\n class=\"form-control\"\n id=\"${inputId}\"\n value=\"${defaultValue}\"\n placeholder=\"${placeholder}\">\n `,\n size: options.size || 'sm',\n centered: true,\n backdrop: 'static',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', dismiss: true },\n { text: 'OK', class: 'btn-primary', action: 'ok' }\n ],\n ...options\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n dialog.show();\n\n // Focus the input\n dialog.on('shown', () => {\n const input = dialog.element.querySelector(`#${inputId}`);\n if (input) {\n input.focus();\n input.select();\n }\n });\n\n return new Promise((resolve) => {\n let result = null;\n\n dialog.on('action:ok', () => {\n const input = dialog.element.querySelector(`#${inputId}`);\n result = input ? input.value : null;\n dialog.hide();\n });\n\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n resolve(result);\n });\n });\n }\n\n /**\n * Get Bootstrap modal instance\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Check if modal is shown\n */\n isShown() {\n return this.element?.classList.contains('show') || false;\n }\n\n /**\n * Show form in a dialog for simple data collection (no model saving)\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves with form data or null if cancelled\n */\n static async showForm(options = {}) {\n const {\n title = 'Form',\n formConfig = {},\n size = 'md',\n centered = true,\n submitText = 'Submit',\n cancelText = 'Cancel',\n ...dialogOptions\n } = options;\n\n // Create the FormView (no model for simple form) - lazy loaded\n const FormView = (await import('@core/forms/FormView.js')).default;\n const formView = new FormView({\n fileHandling: options.fileHandling || 'base64',\n data: options.data,\n defaults: options.defaults,\n model: options.model,\n formConfig: {\n fields: formConfig.fields || options.fields,\n ...formConfig,\n submitButton: false,\n resetButton: false\n }\n });\n\n // Create the dialog with the FormView as body\n const dialog = new Dialog({\n title,\n body: formView,\n size,\n centered,\n buttons: [\n {\n text: cancelText,\n class: 'btn-secondary',\n action: 'cancel'\n },\n {\n text: submitText,\n class: 'btn-primary',\n action: 'submit'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Handle dialog actions\n dialog.on('action:submit', async () => {\n if (resolved) return;\n\n // Validate form\n if (!formView.validate()) {\n formView.focusFirstError();\n return;\n }\n\n if (options.autoSave && options.model) {\n dialog.setLoading(true);\n const result = await formView.saveModel()\n if (!result.success) {\n dialog.setLoading(false);\n dialog.render();\n dialog.getApp().toast.error(result.message);\n return;\n }\n resolved = true;\n dialog.hide();\n resolve(result);\n }\n\n // Get form data and resolve\n try {\n const formData = await formView.getFormData();\n resolved = true;\n dialog.hide();\n resolve(formData);\n } catch (error) {\n console.error('Error collecting form data:', error);\n formView.showError('Error collecting form data');\n }\n });\n\n dialog.on('action:cancel', () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(null);\n });\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(null);\n }\n // Clean up\n setTimeout(() => {\n formView.destroy();\n dialog.destroy();\n }, 100);\n });\n });\n }\n\n /**\n * Show form in a dialog with automatic model saving\n * @param {object} options - Configuration options (requires model)\n * @returns {Promise} Promise that resolves with save result or null if cancelled\n */\n static async showModelForm(options = {}) {\n const {\n title = 'Edit',\n formConfig = {},\n size = 'md',\n centered = true,\n submitText = 'Save',\n cancelText = 'Cancel',\n model,\n fields,\n ...dialogOptions\n } = options;\n\n if (!model) {\n throw new Error('showModelForm requires a model');\n }\n\n // Create the FormView with model - lazy loaded\n const FormView = (await import('@core/forms/FormView.js')).default;\n const formView = new FormView({\n fileHandling: options.fileHandling || 'base64',\n model: model,\n data: options.data,\n defaults: options.defaults,\n formConfig: {\n // Support both formConfig.fields and direct fields parameter\n fields: fields || formConfig.fields || [],\n ...formConfig,\n submitButton: false,\n resetButton: false\n }\n });\n\n // Create the dialog with the FormView as body\n const dialog = new Dialog({\n title,\n body: formView,\n size,\n centered,\n buttons: [\n {\n text: cancelText,\n class: 'btn-secondary',\n action: 'cancel'\n },\n {\n text: submitText,\n class: 'btn-primary',\n action: 'submit'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Handle dialog actions\n dialog.on('action:submit', async () => {\n if (resolved) return;\n\n // Show loading state\n dialog.setLoading(true, 'Saving...');\n\n try {\n const result = await formView.handleSubmit();\n if (result.success) {\n resolved = true;\n dialog.hide();\n resolve(result);\n } else {\n // Restore form and show error\n dialog.setLoading(false);\n let errmsg = result.error;\n if (result.data && result.data.error) {\n errmsg = result.data.error;\n }\n dialog.getApp().toast.error(errmsg);\n // formView.showError(result.error || 'Save failed. Please try again.');\n }\n } catch (error) {\n console.error('Error saving form:', error);\n // Restore form and show error\n await dialog.setContent(formView);\n formView.showError(error.message || 'An error occurred while saving');\n }\n });\n\n dialog.on('action:cancel', () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(null);\n });\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(null);\n }\n // Clean up\n setTimeout(() => {\n formView.destroy();\n dialog.destroy();\n }, 100);\n });\n });\n }\n\n /**\n * Show data in a dialog using DataView component\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves when dialog is closed\n */\n static async showData(options = {}) {\n const {\n title = 'Data View',\n data = {},\n model = null,\n fields = [],\n columns = 2,\n responsive = true,\n showEmptyValues = false,\n emptyValueText = '—',\n size = 'lg',\n centered = true,\n closeText = 'Close',\n ...dialogOptions\n } = options;\n\n\n // Create the DataView - lazy loaded\n const DataView = (await import('@core/views/data/DataView.js')).default;\n const dataView = new DataView({\n data,\n model,\n fields,\n columns,\n responsive,\n showEmptyValues,\n emptyValueText\n });\n\n // Create the dialog with the DataView as body\n const dialog = new Dialog({\n title,\n body: dataView,\n size,\n centered,\n buttons: [\n {\n text: closeText,\n class: 'btn-secondary',\n value: 'close'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog and return promise\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Get close button\n const closeBtn = dialog.element.querySelector('.modal-footer button');\n\n // Handle close\n const handleClose = () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(true);\n };\n\n // Attach event listener\n closeBtn?.addEventListener('click', handleClose);\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(true);\n }\n // Clean up\n setTimeout(() => {\n dataView.destroy();\n dialog.destroy();\n dialog.element.remove();\n }, 100);\n });\n\n // Forward DataView events\n dataView.on('field:click', (data) => {\n dialog.emit('dataview:field:click', data);\n });\n\n dataView.on('error', (data) => {\n dialog.emit('dataview:error', data);\n });\n });\n }\n}\n\nDialog.showConfirm = Dialog.confirm;\nDialog.showError = Dialog.alert;\n\nexport default Dialog;\n","/**\n * ProgressView - File upload progress component\n * \n * Shows upload progress with progress bar, filename, and cancellation option\n * Integrates with FileUpload service for real-time progress updates\n * \n * Features:\n * - Bootstrap progress bar with percentage\n * - File information (name, size)\n * - Bytes uploaded/total display\n * - Cancel button with confirmation\n * - Responsive design\n * \n * Events:\n * - 'cancel' - Emitted when user cancels upload\n * \n * @example\n * const progressView = new ProgressView({\n * filename: 'document.pdf',\n * filesize: 1024000,\n * onCancel: () => fileUpload.cancel()\n * });\n * \n * // Update progress\n * progressView.updateProgress({ progress: 0.5, loaded: 512000, total: 1024000 });\n */\n\nimport View from '@core/View.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass ProgressView extends View {\n constructor(options = {}) {\n super({\n template: 'progress-view-template',\n ...options\n });\n\n // Initialize progress data\n this.filename = options.filename || 'Unknown file';\n this.filesize = options.filesize || 0;\n this.filesizeFormatted = dataFormatter.pipe(this.filesize, 'filesize');\n \n // Progress state\n this.progress = 0;\n this.percentage = 0;\n this.loaded = 0;\n this.total = this.filesize;\n this.loadedFormatted = '0 B';\n this.totalFormatted = this.filesizeFormatted;\n this.status = 'Starting upload...';\n \n // Options\n this.showCancel = options.showCancel !== false;\n this.onCancel = options.onCancel || null;\n \n // State\n this.cancelled = false;\n this.completed = false;\n }\n\n /**\n * Get template for the progress view\n */\n getTemplate() {\n return `\n <div class=\"progress-view\">\n <div class=\"d-flex justify-content-between align-items-start mb-2\">\n <div class=\"flex-grow-1 min-width-0\">\n <div class=\"fw-medium text-truncate\" title=\"{{filename}}\">\n <i class=\"bi bi-file-earmark me-1\"></i>\n {{filename}}\n </div>\n <small class=\"text-muted\">{{status}}</small>\n </div>\n {{#showCancel}}\n <button type=\"button\" \n class=\"btn btn-sm btn-outline-secondary ms-2\" \n data-action=\"cancel\"\n {{#cancelled}}disabled{{/cancelled}}>\n <i class=\"bi bi-x\"></i>\n </button>\n {{/showCancel}}\n </div>\n \n <div class=\"progress mb-2\" style=\"height: 8px;\">\n <div class=\"progress-bar\" \n role=\"progressbar\" \n style=\"width: {{percentage}}%\"\n aria-valuenow=\"{{percentage}}\" \n aria-valuemin=\"0\" \n aria-valuemax=\"100\">\n </div>\n </div>\n \n <div class=\"d-flex justify-content-between\">\n <small class=\"text-muted\">\n {{loadedFormatted}} / {{totalFormatted}}\n </small>\n <small class=\"text-muted\">\n {{percentage}}%\n </small>\n </div>\n </div>\n `;\n }\n\n /**\n * Update progress information\n * @param {Object} progressInfo - Progress data\n * @param {number} progressInfo.progress - Progress as decimal (0-1)\n * @param {number} progressInfo.loaded - Bytes loaded\n * @param {number} progressInfo.total - Total bytes\n * @param {number} progressInfo.percentage - Progress as percentage (0-100)\n */\n updateProgress(progressInfo) {\n if (this.cancelled || this.completed) {\n return;\n }\n\n this.progress = progressInfo.progress;\n this.percentage = progressInfo.percentage;\n this.loaded = progressInfo.loaded;\n this.total = progressInfo.total || this.filesize;\n \n // Format bytes for display\n this.loadedFormatted = dataFormatter.pipe(this.loaded, 'filesize');\n this.totalFormatted = dataFormatter.pipe(this.total, 'filesize');\n \n // Update status message\n if (this.percentage < 100) {\n this.status = `Uploading... ${this.percentage}%`;\n } else {\n this.status = 'Finalizing upload...';\n }\n\n // Re-render to show updated progress\n this.render();\n }\n\n /**\n * Mark upload as completed\n * @param {string} message - Success message\n */\n markCompleted(message = 'Upload completed!') {\n this.completed = true;\n this.progress = 1;\n this.percentage = 100;\n this.status = message;\n this.render();\n }\n\n /**\n * Mark upload as failed\n * @param {string} message - Error message\n */\n markFailed(message = 'Upload failed') {\n this.status = message;\n this.render();\n }\n\n /**\n * Mark upload as cancelled\n */\n markCancelled() {\n this.cancelled = true;\n this.status = 'Upload cancelled';\n this.render();\n }\n\n /**\n * Handle cancel button click\n * @param {string} action - Action name\n * @param {Event} event - Click event\n * @param {Element} element - Button element\n */\n async onActionCancel(action, event, element) {\n if (this.cancelled || this.completed) {\n return;\n }\n\n // Disable button immediately\n element.disabled = true;\n \n // Mark as cancelled\n this.markCancelled();\n \n // Emit cancel event\n this.emit('cancel');\n \n // Call cancel callback if provided\n if (typeof this.onCancel === 'function') {\n try {\n await this.onCancel();\n } catch (error) {\n console.error('Error in cancel callback:', error);\n }\n }\n }\n\n /**\n * Set filename\n * @param {string} filename - New filename\n */\n setFilename(filename) {\n this.filename = filename;\n this.render();\n }\n\n /**\n * Set file size\n * @param {number} size - File size in bytes\n */\n setFilesize(size) {\n this.filesize = size;\n this.filesizeFormatted = dataFormatter.pipe(size, 'filesize');\n this.total = size;\n this.totalFormatted = this.filesizeFormatted;\n this.render();\n }\n\n /**\n * Get current progress as percentage\n * @returns {number} Progress percentage (0-100)\n */\n getPercentage() {\n return this.percentage;\n }\n\n /**\n * Check if upload is completed\n * @returns {boolean} True if completed\n */\n isCompleted() {\n return this.completed;\n }\n\n /**\n * Check if upload is cancelled\n * @returns {boolean} True if cancelled\n */\n isCancelled() {\n return this.cancelled;\n }\n\n /**\n * Get upload statistics\n * @returns {Object} Upload stats\n */\n getStats() {\n return {\n filename: this.filename,\n filesize: this.filesize,\n progress: this.progress,\n percentage: this.percentage,\n loaded: this.loaded,\n total: this.total,\n cancelled: this.cancelled,\n completed: this.completed,\n status: this.status\n };\n }\n}\n\nexport default ProgressView;","/**\n * ListViewItem - Individual item view for ListView\n *\n * Each item is its own View with its own model, allowing for\n * independent re-rendering when the model changes.\n *\n * Events:\n * - 'item:click' - Emitted when item is clicked\n * - 'item:select' - Emitted when item is selected\n * - 'item:deselect' - Emitted when item is deselected\n *\n * @example\n * const item = new ListViewItem({\n * model: userModel,\n * template: '<div class=\"user-item\">{{name}} - {{email}}</div>'\n * });\n */\n\nimport View from '@core/View.js';\n\nclass ListViewItem extends View {\n constructor(options = {}) {\n super({\n className: 'list-view-item',\n ...options\n });\n\n // Item-specific properties\n this.selected = false;\n this.index = options.index ?? 0;\n this.listView = options.listView ?? null;\n\n // Default template if none provided\n if (!this.template) {\n this.template = `\n <div class=\"list-item-content\" data-action=\"select\">\n {{#model}}\n {{#id}}<span class=\"item-id\">{{id}}</span>{{/id}}\n {{#name}}<span class=\"item-name\">{{name}}</span>{{/name}}\n {{#title}}<span class=\"item-title\">{{title}}</span>{{/title}}\n {{#label}}<span class=\"item-label\">{{label}}</span>{{/label}}\n {{#description}}<p class=\"item-description\">{{description}}</p>{{/description}}\n {{/model}}\n {{^model}}\n <span class=\"item-empty\">No data</span>\n {{/model}}\n </div>\n `;\n }\n }\n\n /**\n * Handle item selection action\n */\n async onActionSelect(event, _element) {\n event.stopPropagation();\n\n if (this.selected) {\n this.deselect();\n } else {\n this.select();\n }\n }\n\n /**\n * Select this item\n */\n select() {\n if (this.selected) return;\n\n this.selected = true;\n this.addClass('selected');\n\n // Emit selection event with item data\n this.emit('item:select', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n\n // Notify parent ListView if available\n if (this.listView) {\n this.listView.emit('item:select', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n }\n }\n\n /**\n * Deselect this item\n */\n deselect() {\n if (!this.selected) return;\n\n this.selected = false;\n this.removeClass('selected');\n\n // Emit deselection event\n this.emit('item:deselect', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n\n // Notify parent ListView if available\n if (this.listView) {\n this.listView.emit('item:deselect', {\n item: this,\n model: this.model,\n index: this.index,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n }\n }\n\n /**\n * Handle click events on the item\n */\n async onActionDefault(action, _event, _element) {\n // Emit click event for any action not specifically handled\n this.emit('item:click', {\n item: this,\n model: this.model,\n index: this.index,\n action: action,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n\n // Notify parent ListView if available\n if (this.listView) {\n this.listView.emit('item:click', {\n item: this,\n model: this.model,\n index: this.index,\n action: action,\n data: this.model?.toJSON ? this.model.toJSON() : this.model\n });\n }\n }\n\n /**\n * Set the item's index in the list\n */\n setIndex(index) {\n this.index = index;\n this.element.setAttribute('data-index', index);\n return this;\n }\n\n /**\n * Update the item's selection state\n */\n setSelected(selected) {\n if (selected) {\n this.select();\n } else {\n this.deselect();\n }\n return this;\n }\n\n /**\n * Override destroy to clean up references\n */\n async destroy() {\n // Remove reference to parent ListView\n this.listView = null;\n\n // Call parent destroy\n await super.destroy();\n }\n}\n\nexport default ListViewItem;\n","/**\n * ListView - Visual list component for Collections\n *\n * Manages a collection of ListViewItem views, each with its own model.\n * When a model changes, only its corresponding ListViewItem re-renders.\n *\n * Events:\n * - 'item:click' - Emitted when any item is clicked\n * - 'item:select' - Emitted when an item is selected\n * - 'item:deselect' - Emitted when an item is deselected\n * - 'selection:change' - Emitted when selection changes\n * - 'list:empty' - Emitted when list becomes empty\n * - 'list:loaded' - Emitted when list is populated\n *\n * @example\n * // Basic usage with custom item template\n * const listView = new ListView({\n * collection: userCollection,\n * itemTemplate: '<div class=\"user-item\">{{name}} - {{email}}</div>',\n * selectionMode: 'single'\n * });\n *\n * // Custom template with model fields\n * const productList = new ListView({\n * collection: productCollection,\n * itemTemplate: `\n * <div class=\"product-card\" data-action=\"select\">\n * <h4>{{name}}</h4>\n * <p class=\"price\">{{price|currency}}</p>\n * <p>{{description|truncate(100)}}</p>\n * </div>\n * `,\n * selectionMode: 'multiple'\n * });\n *\n * // Using custom item class with template\n * const customList = new ListView({\n * collection: myCollection,\n * itemClass: CustomListItem, // Your custom ListViewItem subclass\n * itemTemplate: '<div>{{title}}</div>', // Passed as 'template' to itemClass constructor\n * selectionMode: 'none'\n * });\n *\n * // Dynamic template update\n * listView.setItemTemplate('<div class=\"compact\">{{name}}</div>', true);\n */\n\nimport View from '@core/View.js';\nimport Collection from '@core/Collection.js';\nimport ListViewItem from './ListViewItem.js';\n\nclass ListView extends View {\n constructor(options = {}) {\n super({\n className: 'list-view',\n template: `\n <div class=\"list-view-container\">\n {{#loading}}\n <div class=\"list-loading\">\n <div class=\"spinner-border spinner-border-sm\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n Loading...\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class=\"list-empty\">\n {{emptyMessage}}\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <div class=\"list-items\" data-container=\"items\"></div>\n {{/isEmpty}}\n {{/loading}}\n </div>\n `,\n ...options\n });\n\n // ListView specific properties\n this.collection = null;\n this.itemViews = new Map(); // Map of model.id -> ListViewItem\n this.selectedItems = new Set(); // Set of selected item IDs\n\n // Configuration\n this.itemTemplate = options.itemTemplate || null; // Template passed to each item's view\n this.itemClass = options.itemClass || ListViewItem; // Class for creating item views\n this.selectionMode = options.selectionMode || 'none'; // none, single, multiple\n this.emptyMessage = options.emptyMessage || 'No items to display';\n this.loading = false;\n this.isEmpty = true;\n\n }\n\n /**\n * Override onInit to set up initial state\n */\n async onInit() {\n // Initial render will happen automatically\n this._initCollection(this.options.collection || this.options.Collection);\n }\n\n\n /**\n * Initialize the collection\n */\n _initCollection(collectionOrClass) {\n if (!collectionOrClass) {\n console.log('Collection not provided');\n return;\n };\n\n // Check if it's already a Collection instance\n if (collectionOrClass instanceof Collection) {\n this.setCollection(collectionOrClass);\n }\n // Check if it's a Collection class\n else if (typeof collectionOrClass === 'function') {\n const collection = new collectionOrClass();\n this.setCollection(collection);\n }\n // Check if it's an array of data\n else if (Array.isArray(collectionOrClass)) {\n const collection = new Collection(null, {}, collectionOrClass);\n this.setCollection(collection);\n }\n }\n\n /**\n * Set the collection for this list view\n */\n setCollection(collection) {\n if (this.collection === collection) return this;\n\n // Clean up old collection listeners\n if (this.collection) {\n this.collection.off('add', this._onModelsAdded, this);\n this.collection.off('remove', this._onModelsRemoved, this);\n this.collection.off('reset', this._onCollectionReset, this);\n this.collection.off('fetch:start', this._onFetchStart, this);\n this.collection.off('fetch:end', this._onFetchEnd, this);\n }\n\n this.collection = collection;\n\n if (this.options.defaultQuery && !this.options.collectionParams) {\n this.collection.params = { ...this.collection.params, ...this.options.defaultQuery };\n }\n\n if (this.options.collectionParams) {\n this.collection.params = { ...this.collection.params, ...this.options.collectionParams };\n }\n\n // Set up new collection listeners\n if (this.collection) {\n this.collection.on('add', this._onModelsAdded, this);\n this.collection.on('remove', this._onModelsRemoved, this);\n this.collection.on('reset', this._onCollectionReset, this);\n this.collection.on('fetch:start', this._onFetchStart, this);\n this.collection.on('fetch:end', this._onFetchEnd, this);\n\n // Build items for existing models\n this._buildItems();\n }\n\n return this;\n }\n\n async _renderChildren() {\n await super._renderChildren();\n const itemsContainer = this.getChildElement(\"items\");\n if (!itemsContainer) {\n // console.warn('ListView: items container not found');\n return;\n }\n this.forEachItem((item, index) => {\n itemsContainer.appendChild(item.element);\n item.render(false);\n });\n }\n\n /**\n * Build item views for all models in collection\n */\n _buildItems() {\n // Clear existing items\n this._clearItems();\n\n if (!this.collection || this.collection.isEmpty()) {\n this.isEmpty = true;\n this.emit('list:empty');\n return;\n }\n\n this.isEmpty = false;\n\n // Create item views for each model\n this.collection.forEach((model, index) => {\n this._createItemView(model, index);\n });\n\n this.emit('list:loaded', { count: this.collection.length() });\n\n // Render if already mounted\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Create an item view for a model\n * The itemTemplate is passed as the template option to the itemClass constructor\n */\n _createItemView(model, index) {\n // Don't create duplicate views\n if (this.itemViews.has(model.id)) return;\n\n const itemView = new this.itemClass({\n model: model,\n index: index,\n listView: this,\n template: this.itemTemplate, // Pass the itemTemplate to the item view\n });\n\n // Store the item view\n this.itemViews.set(model.id, itemView);\n\n // Set up item event listeners\n itemView.on('item:select', this._onItemSelect.bind(this));\n itemView.on('item:deselect', this._onItemDeselect.bind(this));\n\n return itemView;\n }\n\n /**\n * Clear all item views\n */\n _clearItems() {\n this.forEachItem(itemView => {\n this.removeChild(itemView.id);\n });\n this.itemViews.clear();\n this.selectedItems.clear();\n }\n\n /**\n * Handle models added to collection\n */\n _onModelsAdded(event) {\n const { models } = event;\n\n models.forEach(model => {\n const index = this.collection.models.indexOf(model);\n this._createItemView(model, index);\n });\n\n this.isEmpty = this.collection.isEmpty();\n\n // Re-render to show new items\n if (!this.loading && this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Handle models removed from collection\n */\n _onModelsRemoved(event) {\n const { models } = event;\n\n models.forEach(model => {\n const itemView = this.itemViews.get(model.id);\n if (itemView) {\n this.removeChild(itemView.id);\n this.itemViews.delete(model.id);\n this.selectedItems.delete(model.id);\n }\n });\n\n this.isEmpty = this.collection.isEmpty();\n\n // Re-render to update display\n if (!this.loading && this.isMounted()) {\n this.render();\n }\n\n if (this.isEmpty) {\n this.emit('list:empty');\n }\n }\n\n /**\n * Handle collection reset\n */\n _onCollectionReset(_event) {\n this._buildItems();\n }\n\n /**\n * Handle fetch start\n */\n _onFetchStart() {\n this.loading = true;\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Handle fetch end\n */\n _onFetchEnd() {\n this.loading = false;\n if (this.isMounted()) {\n this.render();\n }\n }\n\n /**\n * Handle item selection\n */\n _onItemSelect(event) {\n const { model, item } = event;\n\n if (this.selectionMode === 'none') {\n item.deselect();\n return;\n }\n\n if (this.selectionMode === 'single') {\n // Deselect all other items\n this.itemViews.forEach((view, id) => {\n if (id !== model.id && view.selected) {\n view.deselect();\n }\n });\n this.selectedItems.clear();\n }\n\n this.selectedItems.add(model.id);\n\n this.emit('selection:change', {\n selected: Array.from(this.selectedItems),\n item: item,\n model: model\n });\n }\n\n /**\n * Handle item deselection\n */\n _onItemDeselect(event) {\n const { model } = event;\n\n this.selectedItems.delete(model.id);\n\n this.emit('selection:change', {\n selected: Array.from(this.selectedItems),\n item: event.item,\n model: model\n });\n }\n\n /**\n * Get selected items\n */\n getSelectedItems() {\n const selected = [];\n this.selectedItems.forEach(id => {\n const itemView = this.itemViews.get(id);\n if (itemView) {\n selected.push({\n view: itemView,\n model: itemView.model,\n data: itemView.model?.toJSON ? itemView.model.toJSON() : itemView.model\n });\n }\n });\n return selected;\n }\n\n /**\n * Iterate over each item view in the list\n * @param {function} callback - Function to execute for each item (itemView, model, index)\n * @param {object} thisArg - Optional value to use as this when executing callback\n * @returns {ListView} Returns the ListView for chaining\n */\n forEachItem(callback, thisArg) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n\n let index = 0;\n this.itemViews.forEach((itemView, modelId) => {\n callback.call(thisArg, itemView, itemView.model, index++);\n });\n\n return this;\n }\n\n /**\n * Clear selection\n */\n clearSelection() {\n this.forEachItem(itemView => {\n if (itemView.selected) {\n itemView.deselect();\n }\n });\n this.selectedItems.clear();\n\n this.emit('selection:change', {\n selected: []\n });\n }\n\n /**\n * Select item by model ID\n */\n selectItem(modelId) {\n const itemView = this.itemViews.get(modelId);\n if (itemView) {\n itemView.select();\n }\n return this;\n }\n\n /**\n * Deselect item by model ID\n */\n deselectItem(modelId) {\n const itemView = this.itemViews.get(modelId);\n if (itemView) {\n itemView.deselect();\n }\n return this;\n }\n\n /**\n * Set or update the item template\n * @param {string} template - New template string for items\n * @param {boolean} rerender - Whether to re-render existing items with new template\n * @returns {ListView} Returns the ListView for chaining\n */\n setItemTemplate(template, rerender = false) {\n this.itemTemplate = template;\n\n if (rerender && this.itemViews.size > 0) {\n // Update template for all existing item views\n this.forEachItem((itemView) => {\n itemView.setTemplate(template);\n if (itemView.isMounted()) {\n itemView.render();\n }\n });\n }\n\n return this;\n }\n\n async onAfterMount() {\n await super.onAfterMount();\n if (this.collection && (this.options.fetchOnMount || !this.collection.lastFetchTime)) {\n this.collection.fetch();\n }\n }\n\n /**\n * Refresh the list (re-fetch if collection supports it)\n */\n async refresh() {\n if (this.collection && this.collection.restEnabled) {\n return await this.collection.fetch();\n }\n this._buildItems();\n }\n\n /**\n * Override destroy to clean up\n */\n async destroy() {\n // Clean up collection listeners\n if (this.collection) {\n this.collection.off('add', this._onModelsAdded, this);\n this.collection.off('remove', this._onModelsRemoved, this);\n this.collection.off('reset', this._onCollectionReset, this);\n this.collection.off('fetch:start', this._onFetchStart, this);\n this.collection.off('fetch:end', this._onFetchEnd, this);\n }\n\n // Clear items\n this._clearItems();\n\n // Call parent destroy\n await super.destroy();\n }\n}\n\nexport default ListView;\n","/**\n * TableRow - Individual row view for TableView\n *\n * Extends ListViewItem to render table rows with proper cell formatting\n * and support for all table features like selection, actions, and context menus.\n *\n * @example\n * const row = new TableRow({\n * model: userModel,\n * columns: tableColumns,\n * actions: ['view', 'edit', 'delete']\n * });\n */\n\nimport ListViewItem from '../list/ListViewItem.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass TableRow extends ListViewItem {\n constructor(options = {}) {\n super({\n tagName: 'tr',\n className: 'table-row',\n enableTooltips: true,\n ...options\n });\n\n // Table-specific properties\n this.columns = options.columns || [];\n this.actions = options.actions || null;\n this.contextMenu = options.contextMenu || null;\n this.batchActions = options.batchActions || null;\n this.tableView = options.tableView || options.listView || null;\n\n // Inline editing state\n this.editingCells = new Set(); // Track which cells are being edited\n\n // Override template to generate table cells\n this.template = this.buildRowTemplate();\n }\n\n /**\n * Get responsive CSS classes for column visibility\n * @param {string|object} visibility - Bootstrap breakpoint or config object\n * - String: 'md' = show at md and up (hide below)\n * - Object: { hide: 'md' } = hide at md and up (show below)\n * - Object: { show: 'md', hide: 'lg' } = show from md to lg only\n * @returns {string} Bootstrap responsive display classes\n */\n getResponsiveClasses(visibility) {\n if (!visibility) return ''; // Always visible if no visibility specified\n\n const validBreakpoints = ['sm', 'md', 'lg', 'xl', 'xxl'];\n\n // Legacy string format: show at breakpoint and up\n if (typeof visibility === 'string') {\n if (!validBreakpoints.includes(visibility)) {\n console.warn(`Invalid visibility breakpoint: ${visibility}. Valid options are: ${validBreakpoints.join(', ')}`);\n return '';\n }\n return `d-none d-${visibility}-table-cell`;\n }\n\n // Object format for more control\n if (typeof visibility === 'object') {\n const classes = [];\n\n // Hide at breakpoint and up\n if (visibility.hide) {\n if (!validBreakpoints.includes(visibility.hide)) {\n console.warn(`Invalid hide breakpoint: ${visibility.hide}. Valid options are: ${validBreakpoints.join(', ')}`);\n return '';\n }\n classes.push(`d-table-cell d-${visibility.hide}-none`);\n }\n\n // Show at breakpoint and up (optionally combined with hide)\n if (visibility.show) {\n if (!validBreakpoints.includes(visibility.show)) {\n console.warn(`Invalid show breakpoint: ${visibility.show}. Valid options are: ${validBreakpoints.join(', ')}`);\n return '';\n }\n if (!visibility.hide) {\n classes.push(`d-none d-${visibility.show}-table-cell`);\n } else {\n classes.push(`d-${visibility.show}-table-cell`);\n }\n }\n\n return classes.join(' ');\n }\n\n return '';\n }\n\n /**\n * Build the row template with table cells\n */\n buildRowTemplate() {\n let template = '';\n\n // Selection checkbox cell\n if (this.tableView && this.tableView.isSelectable()) {\n template += `\n <td style=\"padding: 0;\">\n <div class=\"mojo-select-cell {{#selected}}selected{{/selected}}\"\n data-action=\"select\">\n <div class=\"mojo-checkbox\">\n <i class=\"bi bi-check\"></i>\n </div>\n </div>\n </td>\n `;\n }\n\n // Data cells for each column\n this.columns.forEach(column => {\n const cellClass = column.class || column.className || '';\n const responsiveClasses = this.getResponsiveClasses(column.visibility);\n const editableClass = column.editable ? 'editable-cell' : '';\n const combinedClasses = [cellClass, responsiveClasses, editableClass].filter(c => c).join(' ');\n const cellContent = this.buildCellTemplate(column);\n\n // Determine cell action\n let cellAction = column.action;\n if (!cellAction && column.editable) {\n cellAction = 'edit-cell';\n } else if (!cellAction && this.tableView.rowAction) {\n cellAction = this.tableView.rowAction;\n }\n\n if (cellAction) {\n template += `<td class=\"${combinedClasses}\" data-action=\"${cellAction}\" data-column=\"${column.key}\">${cellContent}</td>`;\n } else {\n template += `<td class=\"${combinedClasses}\" data-column=\"${column.key}\">${cellContent}</td>`;\n }\n });\n\n // Actions cell\n if (this.actions) {\n template += this.buildActionsTemplate();\n } else if (this.contextMenu) {\n template += this.buildContextMenuTemplate();\n }\n\n return template;\n }\n\n /**\n * Build template for a single cell\n */\n /**\n * Build template for a single cell\n */\n buildCellTemplate(column) {\n // Build path for Mustache to access the value\n const path = `model.${column.key}`;\n // Support both 'formatter' and 'format' for consistency with DataView\n const formatter = column.formatter || column.format;\n if (formatter) {\n // For string formatters that are pipe expressions\n if (typeof formatter === 'string') {\n return `{{{${path}|${formatter}}}}`;\n } else if (typeof formatter === 'function') {\n return `<span data-formatter=\"${column.key}\">{{${path}}}</span>`;\n }\n }\n\n if (column.template) {\n return column.template;\n }\n\n // For editable cells, wrap content in a span for easy replacement\n if (column.editable) {\n return `<span class=\"cell-content\" data-field=\"${column.key}\">{{{${path}}}}</span>`;\n }\n\n return `{{{${path}}}}`;\n }\n\n /**\n * Build actions cell template\n */\n buildActionsTemplate() {\n if (!this.actions || this.actions.length === 0) return '';\n\n const buttons = this.actions.map(action => {\n if (typeof action === 'string') {\n switch (action) {\n case 'view':\n return `\n <button class=\"btn btn-sm btn-outline-primary\"\n data-action=\"view\"\n title=\"View\">\n <i class=\"bi bi-eye\"></i>\n </button>\n `;\n\n case 'edit':\n return `\n <button class=\"btn btn-sm btn-outline-secondary\"\n data-action=\"edit\"\n title=\"Edit\">\n <i class=\"bi bi-pencil\"></i>\n </button>\n `;\n\n case 'delete':\n return `\n <button class=\"btn btn-sm btn-outline-danger\"\n data-action=\"delete\"\n title=\"Delete\">\n <i class=\"bi bi-trash\"></i>\n </button>\n `;\n\n default:\n return '';\n }\n } else if (typeof action === 'object') {\n return `\n <button class=\"btn btn-sm ${action.class || 'btn-outline-primary'}\"\n data-id=\"${this.model.id}\"\n data-action=\"${action.action}\"\n title=\"${action.label || ''}\">\n ${action.icon ? `<i class=\"${action.icon}\"></i>` : ''}\n ${action.label && !action.icon ? action.label : ''}\n </button>\n `;\n }\n return '';\n }).join('');\n\n return `<td><div class=\"btn-group btn-group-sm\">${buttons}</div></td>`;\n }\n\n /**\n * Build context menu cell template\n */\n buildContextMenuTemplate() {\n if (!this.contextMenu || this.contextMenu.length === 0) return '';\n\n return `\n <td class=\"text-end\" style=\"width: 1px;\">\n <div class=\"dropdown\">\n <button class=\"btn btn-sm btn-link border-0\"\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n aria-expanded=\"false\"\n style=\"color: #6c757d;\">\n <i class=\"bi bi-three-dots-vertical\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end shadow-sm\">\n ${this.buildContextMenuItems()}\n </ul>\n </div>\n </td>\n `;\n }\n\n /**\n * Build context menu items\n */\n buildContextMenuItems() {\n return this.contextMenu.map(menuItem => {\n if (menuItem.separator||menuItem.divider) {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n let itemClass = 'dropdown-item';\n if (menuItem.action === 'delete' || menuItem.danger) {\n itemClass += ' text-danger';\n }\n if (menuItem.disabled) {\n itemClass += ' disabled';\n }\n\n return `\n <li>\n <a class=\"${itemClass}\" href=\"#\"\n data-id=\"{{model.id}}\"\n data-action=\"${menuItem.action}\"\n ${menuItem.disabled ? 'aria-disabled=\"true\" tabindex=\"-1\"' : ''}>\n ${menuItem.icon ? `<i class=\"${menuItem.icon} me-2\"></i>` : ''}\n ${menuItem.label}\n </a>\n </li>\n `;\n }).join('');\n }\n\n /**\n * Override onAfterRender to apply function formatters and templates\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n // Apply function formatters\n this.columns.forEach(column => {\n if (column.formatter && typeof column.formatter === 'function') {\n const cell = this.element.querySelector(`[data-formatter=\"${column.key}\"]`);\n if (cell) {\n const value = this.model.get ? this.model.get(column.key) : this.model[column.key];\n const context = {\n value,\n row: this.model, // deprecate this\n model: this.model,\n column,\n table: this.tableView,\n index: this.index\n };\n try {\n cell.innerHTML = column.formatter(value, context);\n } catch (error) {\n console.error(`Error formatting cell for column ${column.key}:`, error);\n }\n }\n }\n\n // Apply function templates\n // if (column.template && typeof column.template === 'function') {\n // const cell = this.element.querySelector(`[data-template=\"${column.key}\"]`);\n // if (cell) {\n // const value = this.model.get ? this.model.get(column.key) : this.model[column.key];\n // cell.innerHTML = column.template(value, this.model);\n // }\n // }\n });\n\n // Update selection state\n if (this.selected) {\n this.element.classList.add('selected');\n }\n\n // Set data-id attribute for easy identification\n const id = this.model.get ? this.model.get('id') : this.model.id;\n if (id) {\n this.element.setAttribute('data-id', id);\n }\n }\n\n /**\n * Handle edit cell action\n */\n async onActionEditCell(event, element) {\n event.stopPropagation();\n\n const columnKey = element.getAttribute('data-column');\n const column = this.columns.find(col => col.key === columnKey);\n\n if (!column || !column.editable) return;\n\n // Don't enter edit mode if already editing this cell\n if (this.editingCells.has(columnKey)) return;\n\n await this.enterEditMode(columnKey, column, element);\n }\n\n /**\n * Handle row click action\n */\n async onActionRowClick(event, element) {\n // Don't trigger row click if clicking on action buttons or editing\n if (event.target.closest('.btn-group') || event.target.closest('.dropdown') || event.target.closest('.cell-editor')) {\n return;\n }\n\n // Emit row click event\n this.emit('row:click', {\n row: this,\n model: this.model,\n column: element.getAttribute('data-column'),\n event: event\n });\n\n // Notify parent TableView\n if (this.tableView) {\n this.tableView.emit('row:click', {\n row: this,\n model: this.model,\n column: element.getAttribute('data-column'),\n event: event\n });\n }\n }\n\n /**\n * Handle view action\n */\n async onActionView(event, element) {\n event.stopPropagation();\n\n this.emit('row:view', {\n row: this,\n model: this.model,\n event: event\n });\n\n if (this.tableView) {\n this.tableView.emit('row:view', {\n row: this,\n model: this.model,\n event: event\n });\n }\n }\n\n /**\n * Handle edit action\n */\n async onActionEdit(event, element) {\n event.stopPropagation();\n\n this.emit('row:edit', {\n row: this,\n model: this.model,\n event: event\n });\n\n if (this.tableView) {\n this.tableView.emit('row:edit', {\n row: this,\n model: this.model,\n event: event\n });\n }\n return true;\n }\n\n /**\n * Handle delete action\n */\n async onActionDelete(event, element) {\n event.stopPropagation();\n\n this.emit('row:delete', {\n row: this,\n model: this.model,\n event: event\n });\n\n if (this.tableView) {\n this.tableView.emit('row:delete', {\n row: this,\n model: this.model,\n event: event\n });\n }\n }\n\n /**\n * Enter edit mode for a cell\n */\n async enterEditMode(columnKey, column, cellElement) {\n const contentSpan = cellElement.querySelector('.cell-content');\n if (!contentSpan) return;\n\n this.editingCells.add(columnKey);\n const currentValue = this.model.get ? this.model.get(columnKey) : this.model[columnKey];\n\n // Create editor based on column configuration\n const editor = this.createCellEditor(column, currentValue);\n\n // Replace content with editor\n const originalContent = contentSpan.innerHTML;\n contentSpan.style.display = 'none';\n\n const editorContainer = document.createElement('div');\n editorContainer.className = 'cell-editor';\n editorContainer.innerHTML = editor;\n cellElement.appendChild(editorContainer);\n\n // Focus the input\n const input = editorContainer.querySelector('input, select, .form-check-input');\n if (input) {\n input.focus();\n if (input.type === 'text' || input.type === 'textarea') {\n input.select();\n }\n }\n\n // Store original content for cancel\n editorContainer.dataset.originalContent = originalContent;\n editorContainer.dataset.columnKey = columnKey;\n\n // Set up event listeners\n this.setupEditorEvents(editorContainer, columnKey, column);\n\n this.emit('cell:edit', {\n row: this,\n model: this.model,\n column: columnKey,\n originalValue: currentValue\n });\n }\n\n /**\n * Create cell editor HTML based on column configuration\n */\n createCellEditor(column, currentValue) {\n const options = column.editableOptions || {};\n\n switch (options.type) {\n case 'select':\n return this.createSelectEditor(options, currentValue);\n case 'switch':\n case 'checkbox':\n return this.createSwitchEditor(options, currentValue);\n case 'textarea':\n return this.createTextareaEditor(options, currentValue);\n default:\n return this.createTextEditor(options, currentValue);\n }\n }\n\n /**\n * Create text input editor\n */\n createTextEditor(options, currentValue) {\n const placeholder = options.placeholder || '';\n const inputType = options.inputType || 'text';\n\n return `\n <div class=\"d-flex gap-1 align-items-center\">\n <input type=\"${inputType}\"\n class=\"form-control form-control-sm cell-input\"\n value=\"${this.escapeHtml(currentValue || '')}\"\n placeholder=\"${placeholder}\">\n <button type=\"button\" class=\"btn btn-sm btn-success cell-save\" title=\"Save\">\n <i class=\"bi bi-check\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary cell-cancel\" title=\"Cancel\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n `;\n }\n\n /**\n * Create textarea editor\n */\n createTextareaEditor(options, currentValue) {\n const placeholder = options.placeholder || '';\n const rows = options.rows || 2;\n\n return `\n <div class=\"d-flex gap-1\">\n <textarea class=\"form-control form-control-sm cell-input\"\n rows=\"${rows}\"\n placeholder=\"${placeholder}\">${this.escapeHtml(currentValue || '')}</textarea>\n <div class=\"d-flex flex-column gap-1\">\n <button type=\"button\" class=\"btn btn-sm btn-success cell-save\" title=\"Save\">\n <i class=\"bi bi-check\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary cell-cancel\" title=\"Cancel\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n </div>\n `;\n }\n\n /**\n * Create select dropdown editor\n */\n createSelectEditor(options, currentValue) {\n const optionsArray = options.options || [];\n let optionsHtml = '';\n\n optionsArray.forEach(option => {\n if (typeof option === 'string') {\n const selected = option === currentValue ? 'selected' : '';\n optionsHtml += `<option value=\"${option}\" ${selected}>${option}</option>`;\n } else if (typeof option === 'object' && option.value !== undefined) {\n const selected = option.value === currentValue ? 'selected' : '';\n optionsHtml += `<option value=\"${option.value}\" ${selected}>${option.label || option.value}</option>`;\n }\n });\n\n return `\n <div class=\"d-flex gap-1 align-items-center\">\n <select class=\"form-select form-select-sm cell-input\">\n ${optionsHtml}\n </select>\n <button type=\"button\" class=\"btn btn-sm btn-success cell-save\" title=\"Save\">\n <i class=\"bi bi-check\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary cell-cancel\" title=\"Cancel\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n `;\n }\n\n /**\n * Create switch/checkbox editor\n */\n createSwitchEditor(options, currentValue) {\n const checked = currentValue ? 'checked' : '';\n const switchType = options.type === 'switch' ? 'form-switch' : '';\n\n return `\n <div class=\"d-flex gap-2 align-items-center\">\n <div class=\"form-check ${switchType}\">\n <input class=\"form-check-input cell-input\" type=\"checkbox\" ${checked}>\n </div>\n <div class=\"d-flex gap-1\">\n <button type=\"button\" class=\"btn btn-sm btn-success cell-save\" title=\"Save\">\n <i class=\"bi bi-check\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary cell-cancel\" title=\"Cancel\">\n <i class=\"bi bi-x\"></i>\n </button>\n </div>\n </div>\n `;\n }\n\n /**\n * Setup event listeners for cell editor\n */\n setupEditorEvents(editorContainer, columnKey, column) {\n const input = editorContainer.querySelector('.cell-input');\n const saveBtn = editorContainer.querySelector('.cell-save');\n const cancelBtn = editorContainer.querySelector('.cell-cancel');\n\n // Save on Enter (for text inputs)\n if (input && (input.type === 'text' || input.type === 'email' || input.type === 'number')) {\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.saveCellEdit(editorContainer, columnKey, column);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n this.cancelCellEdit(editorContainer, columnKey);\n }\n });\n }\n\n // Save on change for selects and checkboxes (if auto-save enabled)\n if (input && (input.type === 'checkbox' || input.tagName === 'SELECT') && column.autoSave !== false) {\n input.addEventListener('change', () => {\n this.saveCellEdit(editorContainer, columnKey, column);\n });\n }\n\n // Button events\n saveBtn?.addEventListener('click', () => {\n this.saveCellEdit(editorContainer, columnKey, column);\n });\n\n cancelBtn?.addEventListener('click', () => {\n this.cancelCellEdit(editorContainer, columnKey);\n });\n }\n\n /**\n * Save cell edit\n */\n async saveCellEdit(editorContainer, columnKey, column) {\n const input = editorContainer.querySelector('.cell-input');\n if (!input) return;\n\n let newValue;\n\n // Extract value based on input type\n if (input.type === 'checkbox') {\n newValue = input.checked;\n } else if (input.tagName === 'SELECT') {\n newValue = input.value;\n } else {\n newValue = input.value;\n }\n\n const oldValue = this.model.get ? this.model.get(columnKey) : this.model[columnKey];\n\n // Save to model and backend\n try {\n if (this.model.save) {\n await this.model.save({ [columnKey]: newValue });\n } else {\n // Fallback for models without save method\n this.model[columnKey] = newValue;\n }\n\n // Exit edit mode\n this.exitEditMode(editorContainer, columnKey, newValue);\n\n // Emit save event\n this.emit('cell:save', {\n row: this,\n model: this.model,\n column: columnKey,\n oldValue: oldValue,\n newValue: newValue\n });\n\n } catch (error) {\n // Show error and keep in edit mode\n console.error('Failed to save cell edit:', error);\n this.emit('cell:save:error', {\n row: this,\n model: this.model,\n column: columnKey,\n oldValue: oldValue,\n newValue: newValue,\n error: error\n });\n\n // Could show an error message in the UI\n editorContainer.classList.add('saving-error');\n setTimeout(() => editorContainer.classList.remove('saving-error'), 3000);\n }\n }\n\n /**\n * Cancel cell edit\n */\n cancelCellEdit(editorContainer, columnKey) {\n const originalContent = editorContainer.dataset.originalContent;\n this.exitEditMode(editorContainer, columnKey, null, originalContent);\n\n this.emit('cell:cancel', {\n row: this,\n model: this.model,\n column: columnKey\n });\n }\n\n /**\n * Exit edit mode and restore content\n */\n exitEditMode(editorContainer, columnKey, newValue = null, originalContent = null) {\n const cellElement = editorContainer.closest('td');\n const contentSpan = cellElement.querySelector('.cell-content');\n\n if (contentSpan) {\n if (newValue !== null) {\n // Update display with new value (with proper formatting if needed)\n const column = this.columns.find(col => col.key === columnKey);\n let displayValue = newValue;\n\n if (column && column.formatter && typeof column.formatter === 'string') {\n displayValue = dataFormatter.pipe(newValue, column.formatter);\n }\n\n contentSpan.innerHTML = this.escapeHtml(displayValue);\n } else if (originalContent) {\n // Restore original content on cancel\n contentSpan.innerHTML = originalContent;\n }\n\n contentSpan.style.display = '';\n }\n\n // Remove editor\n editorContainer.remove();\n this.editingCells.delete(columnKey);\n }\n\n /**\n * Escape HTML for safe display\n */\n escapeHtml(text) {\n if (text === null || text === undefined) return '';\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n /**\n * Override select to handle table-specific selection UI\n */\n select() {\n super.select();\n this.addClass('selected');\n\n // Update checkbox visual state\n const selectCell = this.element?.querySelector('.mojo-select-cell');\n if (selectCell) {\n selectCell.classList.add('selected');\n }\n }\n\n /**\n * Override deselect to handle table-specific selection UI\n */\n deselect() {\n super.deselect();\n this.removeClass('selected');\n\n // Update checkbox visual state\n const selectCell = this.element?.querySelector('.mojo-select-cell');\n if (selectCell) {\n selectCell.classList.remove('selected');\n }\n }\n}\n\nexport default TableRow;\n","/**\n * DjangoLookups - Utility for Django-style filter lookup parsing and formatting\n * \n * Provides utilities to parse filter keys like \"status__in\" or \"created__gte\"\n * and format them into human-readable display text for filter pills.\n * \n * @example\n * parseFilterKey('status__in') // { field: 'status', lookup: 'in' }\n * formatFilterDisplay('status__in', 'new,open', 'Status') // \"Status in 'new', 'open'\"\n */\n\n/**\n * Supported Django-style lookups with display configurations\n * Only includes commonly used lookups (KISS principle)\n */\nexport const LOOKUPS = {\n // Comparison\n 'exact': { \n display: 'is',\n description: 'Exact match'\n },\n 'in': { \n display: 'in',\n description: 'Match any of the values (comma-separated)'\n },\n 'not': { \n display: 'is not',\n description: 'Does not match'\n },\n 'not_in': { \n display: 'not in',\n description: 'Does not match any of the values'\n },\n 'gt': { \n display: '>',\n description: 'Greater than'\n },\n 'gte': { \n display: '>=',\n description: 'Greater than or equal to'\n },\n 'lt': { \n display: '<',\n description: 'Less than'\n },\n 'lte': { \n display: '<=',\n description: 'Less than or equal to'\n },\n \n // String operations\n 'contains': { \n display: 'contains',\n description: 'Contains substring (case-sensitive)'\n },\n 'icontains': { \n display: 'contains',\n description: 'Contains substring (case-insensitive)'\n },\n 'startswith': { \n display: 'starts with',\n description: 'Starts with substring (case-sensitive)'\n },\n 'istartswith': { \n display: 'starts with',\n description: 'Starts with substring (case-insensitive)'\n },\n 'endswith': { \n display: 'ends with',\n description: 'Ends with substring (case-sensitive)'\n },\n 'iendswith': { \n display: 'ends with',\n description: 'Ends with substring (case-insensitive)'\n },\n \n // Null checks\n 'isnull': { \n display: (val) => val === 'true' || val === true ? 'is null' : 'is not null',\n description: 'Check if value is null or not'\n },\n \n // Range operations\n 'range': { \n display: 'between',\n description: 'Between two values (comma-separated)'\n }\n};\n\n/**\n * Parse a filter key into field name and lookup operator\n * \n * @param {string} paramKey - Filter parameter key (e.g., \"status__in\", \"created__gte\")\n * @returns {Object} Object with field and lookup properties\n * \n * @example\n * parseFilterKey('status__in') // { field: 'status', lookup: 'in' }\n * parseFilterKey('status') // { field: 'status', lookup: null }\n * parseFilterKey('user__profile__name__icontains') // { field: 'user__profile__name', lookup: 'icontains' }\n */\nexport function parseFilterKey(paramKey) {\n if (!paramKey || typeof paramKey !== 'string') {\n return { field: paramKey, lookup: null };\n }\n\n const parts = paramKey.split('__');\n \n // Single part, no lookup\n if (parts.length === 1) {\n return { field: paramKey, lookup: null };\n }\n \n // Check if last part is a valid lookup\n const possibleLookup = parts[parts.length - 1];\n if (LOOKUPS[possibleLookup]) {\n return { \n field: parts.slice(0, -1).join('__'), \n lookup: possibleLookup \n };\n }\n \n // No valid lookup found, treat entire string as field name\n return { field: paramKey, lookup: null };\n}\n\n/**\n * Format a filter key and value into human-readable display text\n * \n * @param {string} paramKey - Filter parameter key (e.g., \"status__in\")\n * @param {string|Array} value - Filter value(s)\n * @param {string} label - Human-readable field label\n * @returns {string} Formatted display text\n * \n * @example\n * formatFilterDisplay('status__in', 'new,open', 'Status') \n * // \"Status in 'new', 'open'\"\n * \n * formatFilterDisplay('created__gte', '2025-01-01', 'Created') \n * // \"Created >= '2025-01-01'\"\n * \n * formatFilterDisplay('name__icontains', 'john', 'Name') \n * // \"Name contains 'john'\"\n */\nexport function formatFilterDisplay(paramKey, value, label) {\n if (!paramKey || value === null || value === undefined) {\n return '';\n }\n\n const { field, lookup } = parseFilterKey(paramKey);\n const lookupDef = LOOKUPS[lookup];\n \n // Handle object-based values (e.g., daterange payloads)\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const hasStart = value.start !== undefined && value.start !== null && value.start !== '';\n const hasEnd = value.end !== undefined && value.end !== null && value.end !== '';\n\n if (hasStart || hasEnd) {\n if (hasStart && hasEnd) {\n return `${label} between '${value.start}' and '${value.end}'`;\n }\n if (hasStart) {\n return `${label} from '${value.start}'`;\n }\n return `${label} until '${value.end}'`;\n }\n\n // Fallback to JSON if it's some other object shape\n return `${label} is '${JSON.stringify(value)}'`;\n }\n\n // Convert array to comma-separated string if needed\n const valueStr = Array.isArray(value) ? value.join(',') : String(value);\n \n // No lookup or exact lookup - simple \"is\" format\n if (!lookup || lookup === 'exact') {\n return `${label} is '${valueStr}'`;\n }\n \n // Multi-value lookups (in, not_in)\n if (lookup === 'in' || lookup === 'not_in') {\n const values = valueStr.split(',').map(v => v.trim()).filter(v => v);\n if (values.length === 0) {\n return `${label} ${lookupDef.display}`;\n }\n const formattedValues = values.map(v => `'${v}'`).join(', ');\n return `${label} ${lookupDef.display} ${formattedValues}`;\n }\n \n // Range lookup - special formatting\n if (lookup === 'range') {\n const values = valueStr.split(',').map(v => v.trim()).filter(v => v);\n if (values.length === 2) {\n return `${label} between '${values[0]}' and '${values[1]}'`;\n }\n return `${label} ${lookupDef.display} '${valueStr}'`;\n }\n \n // Null check - dynamic display based on value\n if (lookup === 'isnull') {\n const displayText = typeof lookupDef.display === 'function' \n ? lookupDef.display(valueStr) \n : lookupDef.display;\n return `${label} ${displayText}`;\n }\n \n // Standard lookup with operator\n if (lookupDef) {\n return `${label} ${lookupDef.display} '${valueStr}'`;\n }\n \n // Fallback for unknown lookups\n return `${label} is '${valueStr}'`;\n}\n\n/**\n * Get a user-friendly description of a lookup operator\n * \n * @param {string} lookup - Lookup operator (e.g., \"in\", \"gte\", \"icontains\")\n * @returns {string} Human-readable description\n * \n * @example\n * getLookupDescription('in') // \"Match any of the values (comma-separated)\"\n * getLookupDescription('gte') // \"Greater than or equal to\"\n */\nexport function getLookupDescription(lookup) {\n const lookupDef = LOOKUPS[lookup];\n return lookupDef ? lookupDef.description : 'Exact match';\n}\n\n/**\n * Check if a string is a valid lookup operator\n * \n * @param {string} lookup - Potential lookup operator\n * @returns {boolean} True if valid lookup\n * \n * @example\n * isValidLookup('in') // true\n * isValidLookup('foo') // false\n */\nexport function isValidLookup(lookup) {\n return lookup && LOOKUPS.hasOwnProperty(lookup);\n}\n\n/**\n * Get all available lookup operators\n * \n * @returns {Array<string>} Array of lookup operator names\n * \n * @example\n * getAvailableLookups() // ['exact', 'in', 'not', 'not_in', 'gt', ...]\n */\nexport function getAvailableLookups() {\n return Object.keys(LOOKUPS);\n}\n\n/**\n * Build a filter key from field name and lookup operator\n * \n * @param {string} field - Field name\n * @param {string} lookup - Lookup operator (optional)\n * @returns {string} Combined filter key\n * \n * @example\n * buildFilterKey('status', 'in') // \"status__in\"\n * buildFilterKey('status') // \"status\"\n */\nexport function buildFilterKey(field, lookup = null) {\n if (!field) return '';\n if (!lookup) return field;\n return `${field}__${lookup}`;\n}\n\nexport default {\n LOOKUPS,\n parseFilterKey,\n formatFilterDisplay,\n getLookupDescription,\n isValidLookup,\n getAvailableLookups,\n buildFilterKey\n};\n","/**\n * TableView - Advanced data table component extending ListView\n *\n * Leverages ListView's view management system for efficient row rendering.\n * Each row is a separate TableRow view that only re-renders when its model changes.\n *\n * @example\n * const table = new TableView({\n * collection: userCollection,\n * columns: [\n * { key: 'name', label: 'Name', sortable: true },\n * { key: 'email', label: 'Email', visibility: 'md' }, // Hidden on xs/sm, visible md+\n * { key: 'phone', label: 'Phone', visibility: 'lg' }, // Visible only on lg+\n * { key: 'created', label: 'Created', formatter: 'date', visibility: 'xl' } // Visible only on xl+\n * ],\n * actions: ['view', 'edit', 'delete'],\n * selectionMode: 'multiple'\n * });\n */\n\nimport ListView from '../list/ListView.js';\nimport TableRow from './TableRow.js';\nimport Mustache from '@core/utils/mustache.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport FormView from '@core/forms/FormView.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\nimport { parseFilterKey, formatFilterDisplay } from '@core/utils/DjangoLookups.js';\n\nclass TableView extends ListView {\n constructor(options = {}) {\n // Set up table-specific defaults before calling super\n const tableOptions = {\n className: 'table-view-component',\n itemClass: options.itemClass || TableRow,\n selectionMode: options.selectable ? 'multiple' : 'none',\n emptyMessage: options.emptyMessage || 'No data available',\n addButtonIcon: options.addButtonIcon || 'bi bi-plus-circle',\n ...options\n };\n\n super(tableOptions);\n\n // Fullscreen state\n this.isFullscreen = false;\n\n // Table-specific properties\n this.columns = options.columns || [];\n this.actions = options.actions || null;\n this.contextMenu = options.contextMenu || null;\n this.batchActions = options.batchActions || null;\n this.searchable = options.searchable !== false;\n this.sortable = options.sortable !== false;\n this.filterable = options.filterable !== false;\n this.paginated = options.paginated !== false;\n this.clickAction = options.clickAction || \"view\";\n\n // Model operation configurations\n this.itemView = options.itemView;\n this.addForm = options.addForm;\n this.editForm = options.editForm;\n this.deleteTemplate = options.deleteTemplate;\n this.formDialogConfig = options.formDialogConfig || {};\n this.viewDialogOptions = options.viewDialogOptions || {};\n\n // Export configuration\n this.exportOptions = options.exportOptions || null;\n if (this.options.showExport && !this.exportOptions) {\n this.exportOptions = [\n { format: 'csv', label: 'Export as CSV', icon: 'bi bi-file-earmark-spreadsheet' },\n { format: 'json', label: 'Export as JSON', icon: 'bi bi-file-earmark-code' }\n ];\n }\n this.exportSource = options.exportSource || 'remote';\n\n // Filter configuration\n this.filters = {};\n this.additionalFilters = options.filters || [];\n this.hideActivePills = options.hideActivePills || false;\n this.hideActivePillNames = options.hideActivePillNames || [];\n this.rowAction = options.rowAction || \"row-click\";\n this.batchBarLocation = options.batchBarLocation || \"bottom\"; // \"top\" or \"bottom\"\n\n this.options.addButtonLabel = options.addButtonLabel || 'Add';\n\n // Custom toolbar buttons\n this.toolbarButtons = options.toolbarButtons || [];\n\n // Table display options\n this.tableOptions = {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false,\n size: null, // null, 'sm', 'lg'\n ...options.tableOptions\n };\n\n // Search configuration\n this.searchPlacement = options.searchPlacement || 'toolbar'; // 'toolbar' or 'dropdown'\n this.searchPlaceholder = options.searchPlaceholder || 'Search...';\n\n // Initialize column configuration BEFORE building template\n this.initializeColumns();\n\n // Extract filters from columns BEFORE building template\n this.extractColumnFilters();\n\n // Detect columns that need footer totals\n this.footerTotalColumns = this.columns.filter(col => col.footer_total === true);\n this.hasFooterTotals = this.footerTotalColumns.length > 0;\n\n // Build template with Mustache variables\n this.template = this.buildTableTemplate();\n\n // Listen for collection changes to update totals\n this.setupCollectionListeners();\n }\n\n /**\n * Setup collection event listeners for totals updates\n */\n setupCollectionListeners() {\n if (this.hasFooterTotals && this.collection) {\n // Re-render totals when collection data changes\n this.collection.on('reset add remove change', () => {\n this.updateFooterTotals();\n });\n }\n }\n\n /**\n * Initialize column configuration\n */\n initializeColumns() {\n this.columns.forEach(column => {\n // Ensure each column has a key\n if (!column.key && column.name) {\n column.key = column.name;\n }\n\n // Set default label if not provided\n if (!column.label && !column.title) {\n column.label = column.key.charAt(0).toUpperCase() + column.key.slice(1);\n }\n });\n }\n\n /**\n * Get responsive CSS classes for column visibility\n * @param {string|object} visibility - Bootstrap breakpoint or config object\n * - String: 'md' = show at md and up (hide below)\n * - Object: { hide: 'md' } = hide at md and up (show below)\n * - Object: { show: 'md', hide: 'lg' } = show from md to lg only\n * @returns {string} Bootstrap responsive display classes\n */\n getResponsiveClasses(visibility) {\n if (!visibility) return ''; // Always visible if no visibility specified\n\n const validBreakpoints = ['sm', 'md', 'lg', 'xl', 'xxl'];\n\n // Legacy string format: show at breakpoint and up\n if (typeof visibility === 'string') {\n if (!validBreakpoints.includes(visibility)) {\n console.warn(`Invalid visibility breakpoint: ${visibility}. Valid options are: ${validBreakpoints.join(', ')}`);\n return '';\n }\n return `d-none d-${visibility}-table-cell`;\n }\n\n // Object format for more control\n if (typeof visibility === 'object') {\n const classes = [];\n\n // Hide at breakpoint and up\n if (visibility.hide) {\n if (!validBreakpoints.includes(visibility.hide)) {\n console.warn(`Invalid hide breakpoint: ${visibility.hide}. Valid options are: ${validBreakpoints.join(', ')}`);\n return '';\n }\n classes.push(`d-table-cell d-${visibility.hide}-none`);\n }\n\n // Show at breakpoint and up (optionally combined with hide)\n if (visibility.show) {\n if (!validBreakpoints.includes(visibility.show)) {\n console.warn(`Invalid show breakpoint: ${visibility.show}. Valid options are: ${validBreakpoints.join(', ')}`);\n return '';\n }\n if (!visibility.hide) {\n classes.push(`d-none d-${visibility.show}-table-cell`);\n } else {\n classes.push(`d-${visibility.show}-table-cell`);\n }\n }\n\n return classes.join(' ');\n }\n\n return '';\n }\n\n /**\n * Extract column key and formatter from combined key (e.g., \"sales_amount|currency\")\n */\n parseColumnKey(key) {\n const parts = key.split('|');\n return {\n fieldKey: parts[0],\n formatter: parts[1] || null\n };\n }\n\n /**\n * Update footer totals in the DOM without full re-render\n */\n updateFooterTotals() {\n if (!this.hasFooterTotals || !this.element) return;\n\n const totals = this.calculateFooterTotals();\n console.log('Updating footer totals in DOM:', totals);\n\n // Update each total cell in the footer\n let totalColumnIndex = 0;\n this.columns.forEach((column) => {\n if (column.footer_total) {\n const safeKey = `col_${totalColumnIndex}`;\n const cell = this.element.querySelector(`[data-total-column=\"${safeKey}\"]`);\n\n if (cell && totals[safeKey]) {\n const formatter = this.parseColumnKey(column.key).formatter || column.formatter;\n let displayValue;\n\n if (formatter && typeof formatter === 'string') {\n // Use DataFormatter if available\n displayValue = this.formatValue(totals[safeKey].value, formatter);\n } else {\n displayValue = totals[safeKey].value;\n }\n\n cell.textContent = displayValue;\n }\n totalColumnIndex++;\n }\n });\n }\n\n /**\n * Format a value using DataFormatter\n */\n formatValue(value, formatter) {\n try {\n return dataFormatter.pipe(value, formatter);\n } catch (e) {\n console.warn('Error formatting value:', e);\n return value;\n }\n }\n\n /**\n * Calculate totals for footer columns\n */\n calculateFooterTotals() {\n if (!this.hasFooterTotals || !this.collection || this.collection.length === 0) {\n return {};\n }\n\n const totals = {};\n\n this.footerTotalColumns.forEach((column, totalColumnIndex) => {\n const { fieldKey, formatter } = this.parseColumnKey(column.key);\n let sum = 0;\n\n // Sum values from all items in collection\n this.collection.forEach(model => {\n const value = model.get ? model.get(fieldKey) : model[fieldKey];\n const numValue = parseFloat(value) || 0;\n sum += numValue;\n });\n\n // Debug logging\n console.log(`Footer total for ${column.key}: ${sum} (from ${this.collection.length} items)`);\n\n // Use safe key for Mustache (avoid special characters)\n const safeKey = `col_${totalColumnIndex}`;\n\n // Store total with formatter info\n totals[safeKey] = {\n value: sum,\n formatter: formatter || column.formatter,\n fieldKey: fieldKey,\n originalKey: column.key\n };\n });\n\n return totals;\n }\n\n /**\n * Extract filters from column configuration\n */\n extractColumnFilters() {\n this.filters = {};\n this.columns.forEach(column => {\n if (column.filter) {\n const { fieldKey } = this.parseColumnKey(column.key);\n this.filters[fieldKey] = column.filter;\n }\n });\n }\n\n isSelectable() {\n return this.batchActions && this.batchActions.length > 0 && this.selectionMode == 'multiple';\n }\n\n /**\n * Build the complete table template\n */\n buildTableTemplate() {\n const batchPanelTop = this.batchBarLocation === 'top' ? this.buildBatchActionsPanel() : '';\n const batchPanelBottom = this.batchBarLocation === 'bottom' ? this.buildBatchActionsPanel() : '';\n\n return `\n <div class=\"mojo-table-wrapper\">\n ${this.buildToolbarTemplate()}\n ${batchPanelTop}\n <div class=\"table-container\"${(() => { const __fs = (this.tableOptions && this.tableOptions.fontSize != null) ? this.tableOptions.fontSize : (this.options && this.options.fontSize); const __val = __fs === 'sm' ? '0.9rem' : (__fs === 'xs' ? '0.8rem' : (__fs ? String(__fs) : null)); return __val ? ` style=\"font-size: ${__val};\"` : ''; })()}>\n {{#loading}}\n <div class=\"mojo-table-loading d-flex justify-content-center align-items-center py-5\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n {{/loading}}\n {{^loading}}\n {{#isEmpty}}\n <div class=\"table-empty text-center py-5\">\n <i class=\"bi bi-inbox fa-2x mb-2 text-muted\"></i>\n <p class=\"text-muted\">{{emptyMessage}}</p>\n </div>\n {{/isEmpty}}\n {{^isEmpty}}\n <table class=\"${this.buildTableClasses()}\">\n ${this.buildTableHeaderTemplate()}\n <tbody data-container=\"items\"></tbody>\n ${this.hasFooterTotals ? this.buildTableFooterTemplate() : ''}\n </table>\n {{/isEmpty}}\n {{/loading}}\n </div>\n ${batchPanelBottom}\n ${this.buildPaginationTemplate()}\n </div>\n `;\n }\n\n /**\n * Build table CSS classes\n */\n buildTableClasses() {\n let classes = ['table'];\n\n if (this.tableOptions.striped) classes.push('table-striped');\n if (this.tableOptions.bordered) classes.push('table-bordered');\n if (this.tableOptions.hover) classes.push('table-hover');\n if (this.tableOptions.responsive) classes.push('table-responsive');\n if (this.tableOptions.background) classes.push(`table-${this.tableOptions.background}`);\n if (this.tableOptions.size === 'sm') classes.push('table-sm');\n if (this.tableOptions.size === 'lg') classes.push('table-lg');\n\n return classes.join(' ');\n }\n\n /**\n * Build toolbar template\n */\n buildToolbarTemplate() {\n if (!this.searchable && !this.filterable) {\n return '';\n }\n\n return `\n <div class=\"table-action-buttons mb-3\">\n <div class=\"d-flex align-items-center gap-2\">\n ${this.buildActionButtonsTemplate()}\n ${this.filterable ? this.buildFilterDropdownTemplate() : ''}\n ${this.searchable && this.searchPlacement === 'toolbar' ? this.buildSearchTemplate() : ''}\n\n </div>\n <div data-container=\"filter-pills\"></div>\n </div>\n `;\n }\n\n /**\n * Build action buttons template\n */\n buildActionButtonsTemplate() {\n let buttons = [];\n\n // Refresh button\n buttons.push(`\n <button class=\"btn btn-sm btn-outline-secondary btn-refresh\"\n data-action=\"refresh\"\n title=\"Refresh\">\n <i class=\"bi bi-arrow-clockwise\"></i>\n </button>\n `);\n\n // Fullscreen button (only if browser supports it)\n if (this.isFullscreenSupported()) {\n buttons.push(`\n <button class=\"btn btn-sm btn-outline-secondary btn-fullscreen\"\n data-action=\"toggle-fullscreen\"\n title=\"Toggle Fullscreen\">\n <i class=\"bi bi-fullscreen\"></i>\n </button>\n `);\n }\n\n // Custom action buttons from options\n if (this.options.showAdd) {\n buttons.push(`\n <button class=\"btn btn-sm btn-success btn-add\"\n data-action=\"add\"\n title=\"${this.options.addButtonLabel}\">\n <i class=\"${this.options.addButtonIcon} me-1\"></i>\n <span class=\"d-none d-lg-inline\">${this.options.addButtonLabel}</span>\n </button>\n `);\n }\n\n if (this.options.showExport) {\n if (this.exportOptions && this.exportOptions.length > 1) {\n // Dropdown for multiple export options\n const dropdownItems = this.exportOptions.map(opt => `\n <li>\n <a class=\"dropdown-item\" href=\"#\" data-action=\"export\" data-format=\"${opt.format}\">\n <i class=\"${opt.icon || 'bi bi-file-earmark-arrow-down'} me-2\"></i>${opt.label}\n </a>\n </li>\n `).join('');\n\n buttons.push(`\n <div class=\"dropdown\">\n <button class=\"btn btn-sm btn-outline-secondary dropdown-toggle\" type=\"button\"\n data-bs-toggle=\"dropdown\" aria-expanded=\"false\" title=\"Export\">\n <i class=\"bi bi-download me-1\"></i>\n <span class=\"d-none d-lg-inline\">Export</span>\n </button>\n <ul class=\"dropdown-menu\">\n ${dropdownItems}\n </ul>\n </div>\n `);\n } else {\n // Single export button\n const format = this.exportOptions && this.exportOptions.length === 1 ? this.exportOptions[0].format : 'json';\n buttons.push(`\n <button class=\"btn btn-sm btn-outline-secondary btn-export\"\n data-action=\"export\"\n data-format=\"${format}\"\n title=\"Export\">\n <i class=\"bi bi-download me-1\"></i>\n <span class=\"d-none d-lg-inline\">Export</span>\n </button>\n `);\n }\n }\n\n // if (buttons.length > 0) {\n // buttons.push(`<div class=\"vr mx-2\"></div>`);\n // }\n\n // Render custom toolbar buttons\n if (this.toolbarButtons && this.toolbarButtons.length > 0) {\n this.toolbarButtons.forEach((button, index) => {\n const {\n label = 'Button',\n icon = '',\n action = '',\n handler = null,\n variant = 'outline-secondary',\n title = label,\n className = '',\n permissions = null\n } = button;\n\n // Check permissions if specified\n if (permissions && !this.checkPermissions(permissions)) {\n return;\n }\n\n const iconHtml = icon ? `<i class=\"${icon} me-1\"></i>` : '';\n const labelHtml = `<span class=\"d-none d-lg-inline\">${label}</span>`;\n\n // Use handler if provided, otherwise use action for data-action attribute\n let dataAttrs = '';\n if (handler) {\n dataAttrs = `data-action=\"custom-toolbar-button\" data-button-index=\"${index}\"`;\n } else if (action) {\n dataAttrs = `data-action=\"${action}\"`;\n }\n\n const btnClass = `btn btn-sm btn-${variant} ${className}`.trim();\n\n buttons.push(`\n <button class=\"${btnClass}\"\n ${dataAttrs}\n title=\"${title}\">\n ${iconHtml}${labelHtml}\n </button>\n `);\n });\n }\n\n return buttons.join('');\n }\n\n /**\n * Build search template\n */\n buildSearchTemplate() {\n return `\n <div class=\"flex-grow-1\" style=\"max-width: 400px;\">\n <div class=\"input-group input-group-sm\">\n <span class=\"input-group-text\">\n <i class=\"bi bi-search\"></i>\n </span>\n <input type=\"search\"\n class=\"form-control\"\n placeholder=\"{{searchPlaceholder}}\"\n data-filter=\"search\"\n data-change-action=\"apply-search\"\n value=\"{{collection.params.search}}\"\n aria-label=\"Search\">\n {{#searchValue}}\n <button class=\"btn btn-outline-secondary\" type=\"button\"\n data-action=\"clear-search\"\n title=\"Clear search\">\n <i class=\"bi bi-x\"></i>\n </button>\n {{/searchValue}}\n </div>\n </div>\n `;\n }\n\n /**\n * Build filter dropdown template\n */\n buildFilterDropdownTemplate() {\n const hasFilters = (this.filters && Object.keys(this.filters).length > 0) ||\n (this.additionalFilters && this.additionalFilters.length > 0);\n\n if (!hasFilters) {\n return '';\n }\n\n return `\n <div class=\"dropdown\">\n <button class=\"btn btn-sm btn-outline-secondary dropdown-toggle\" type=\"button\"\n data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"bi bi-filter me-1\"></i>\n <span class=\"d-none d-lg-inline\">Add Filter</span>\n </button>\n <div class=\"dropdown-menu\" style=\"min-width: 250px;\">\n ${this.buildFilterList()}\n </div>\n </div>\n `;\n }\n\n /**\n * Build simple filter selection list\n */\n buildFilterList() {\n const allFilters = this.getAllAvailableFilters();\n const activeFilters = this.getActiveFilters();\n\n if (allFilters.length === 0) {\n return '<div class=\"dropdown-item-text text-muted\">No filters available</div>';\n }\n\n const filterItems = allFilters.map(filter => {\n const isActive = activeFilters.hasOwnProperty(filter.key);\n const activeClass = isActive ? 'active' : '';\n const icon = this.getFilterIcon(filter.type || filter.config?.type);\n\n return `\n <button class=\"dropdown-item ${activeClass}\"\n data-action=\"add-filter\"\n data-filter-key=\"${filter.key}\">\n <i class=\"bi bi-${icon} me-2\"></i>\n ${filter.label}\n ${isActive ? '<i class=\"bi bi-check-circle ms-auto\"></i>' : ''}\n </button>\n `;\n }).join('');\n\n return `\n ${filterItems}\n ${Object.keys(activeFilters).length > 0 ? `\n <div class=\"dropdown-divider\"></div>\n <button class=\"dropdown-item text-danger\" data-action=\"clear-all-filters\">\n <i class=\"bi bi-x-circle me-2\"></i>Clear All Filters\n </button>\n ` : ''}\n `;\n }\n\n /**\n * Update filter pills in the DOM\n */\n updateFilterPills() {\n const container = this.element?.querySelector('[data-container=\"filter-pills\"]');\n\n if (!container) {\n return;\n }\n\n const activeFilters = this.getActiveFilters();\n\n const pillsHTML = this.buildActivePills();\n container.innerHTML = pillsHTML;\n }\n\n /**\n * Update search input value across all search inputs\n */\n updateSearchInputs(value) {\n const searchInputs = this.element?.querySelectorAll('[data-filter=\"search\"]');\n if (searchInputs) {\n searchInputs.forEach(input => {\n input.value = value || '';\n });\n }\n }\n\n /**\n * Build active filter pills display\n */\n buildActivePills() {\n if (this.hideActivePills) {\n return '';\n }\n\n const activeFilters = this.getActiveFilters();\n const hasSearch = activeFilters.search && activeFilters.search.toString().trim() !== '';\n let filterEntries = Object.entries(activeFilters).filter(([key, value]) =>\n value && value.toString().trim() !== '' && key !== 'search'\n );\n\n // Hide specific pills based on configuration\n if (this.hideActivePillNames && this.hideActivePillNames.length > 0) {\n filterEntries = filterEntries.filter(([key]) =>\n !this.hideActivePillNames.includes(key)\n );\n }\n\n if (filterEntries.length === 0 && !hasSearch) {\n return '';\n }\n\n const pills = filterEntries.map(([paramKey, value]) => {\n const { field } = parseFilterKey(paramKey);\n const label = this.getFilterLabel(field);\n const displayText = formatFilterDisplay(paramKey, value, label);\n const icon = 'filter'; // search won't appear as pill anymore\n\n return `\n <span class=\"badge bg-primary me-1 mb-1 py-1 px-2 position-relative\" style=\"font-size: 0.75rem;\">\n <i class=\"bi bi-${icon} me-1\" style=\"font-size: 0.65rem;\"></i>\n\n <button type=\"button\" class=\"btn btn-link text-white p-0 ms-1\"\n style=\"font-size: 0.65rem; line-height: 1;\"\n data-action=\"edit-filter\"\n data-filter=\"${paramKey}\"\n title=\"Edit filter\">\n ${displayText}\n </button>\n\n <button type=\"button\" class=\"btn-close btn-close-white ms-1\"\n style=\"font-size: 0.6rem; width: 0.5rem; height: 0.5rem;\"\n data-action=\"remove-filter\"\n data-filter=\"${paramKey}\"\n title=\"Remove filter\">\n </button>\n </span>\n `;\n }).join('');\n\n // Show Clear All if there are multiple filters, or any filter + search\n const showClearAll = filterEntries.length > 1 || (filterEntries.length > 0 && hasSearch) || (filterEntries.length === 0 && hasSearch);\n const clearAllButton = showClearAll ? `\n <button class=\"btn btn-sm btn-outline-secondary mb-1 py-0 px-2\" style=\"font-size: 0.75rem;\" data-action=\"clear-all-filters\">\n <i class=\"bi bi-x-circle me-1\" style=\"font-size: 0.7rem;\"></i>\n <small>Clear All</small>\n </button>\n ` : '';\n\n return `\n <div class=\"row mt-2\">\n <div class=\"col-12\">\n <div class=\"d-flex flex-wrap align-items-center\">\n ${pills}\n ${clearAllButton}\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Build table header template\n */\n buildTableHeaderTemplate() {\n let headerCells = '';\n\n // Selection checkbox header\n if (this.isSelectable()) {\n headerCells += `\n <th style=\"width: 40px; padding: 0;\">\n <div class=\"mojo-select-all-cell\" data-action=\"select-all\">\n <div class=\"mojo-checkbox\">\n <i class=\"bi bi-check\"></i>\n </div>\n </div>\n </th>\n `;\n }\n\n // Column headers\n this.columns.forEach(column => {\n // Parse column key to get field name without pipes/formatters\n const { fieldKey } = this.parseColumnKey(column.key);\n\n const sortable = this.sortable && column.sortable !== false;\n const currentSort = this.getSortBy() === fieldKey ? this.getSortDirection() : null;\n const sortIcon = this.getSortIcon(currentSort);\n const label = column.label || column.title || fieldKey;\n const responsiveClasses = this.getResponsiveClasses(column.visibility);\n\n const sortDropdown = sortable ? `\n <div class=\"dropdown d-inline-block ms-2\">\n <button class=\"btn btn-sm btn-link p-0 text-decoration-none\" type=\"button\"\n data-bs-toggle=\"dropdown\" aria-expanded=\"false\"\n data-column=\"${fieldKey}\">\n ${sortIcon}\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><a class=\"dropdown-item ${currentSort === 'asc' ? 'active' : ''}\"\n data-action=\"sort\" data-field=\"${fieldKey}\" data-direction=\"asc\">\n <i class=\"bi bi-sort-alpha-down me-2\"></i>Sort A-Z\n </a></li>\n <li><a class=\"dropdown-item ${currentSort === 'desc' ? 'active' : ''}\"\n data-action=\"sort\" data-field=\"${fieldKey}\" data-direction=\"desc\">\n <i class=\"bi bi-sort-alpha-down-alt me-2\"></i>Sort Z-A\n </a></li>\n <li><a class=\"dropdown-item ${currentSort === null ? 'active' : ''}\"\n data-action=\"sort\" data-field=\"${fieldKey}\" data-direction=\"none\">\n <i class=\"bi bi-x-circle me-2\"></i>No Sort\n </a></li>\n </ul>\n </div>\n ` : '';\n\n headerCells += `\n <th class=\"${sortable ? 'sortable' : ''} ${responsiveClasses}\">\n <div class=\"d-flex align-items-center\">\n <span>${label}</span>\n ${sortDropdown}\n </div>\n </th>\n `;\n });\n\n // Actions header\n if (this.actions) {\n headerCells += '<th>Actions</th>';\n } else if (this.contextMenu) {\n headerCells += '<th style=\"width: 1px;\"></th>';\n }\n\n return `\n <thead>\n <tr>\n ${headerCells}\n </tr>\n </thead>\n `;\n }\n\n /**\n * Build table footer template with totals\n */\n buildTableFooterTemplate() {\n let footerCells = '';\n\n // Selection checkbox footer (empty)\n if (this.isSelectable()) {\n footerCells += '<td></td>';\n }\n\n // Column footers\n let totalColumnIndex = 0;\n this.columns.forEach((column, index) => {\n const responsiveClasses = this.getResponsiveClasses(column.visibility);\n\n if (column.footer_total) {\n // Use safe key for Mustache template\n const safeKey = `col_${totalColumnIndex}`;\n const formatter = this.parseColumnKey(column.key).formatter || column.formatter;\n\n let cellContent;\n if (formatter && typeof formatter === 'string') {\n cellContent = `{{{footerTotals.${safeKey}.value|${formatter}}}}`;\n } else {\n cellContent = `{{footerTotals.${safeKey}.value}}`;\n }\n\n footerCells += `<td class=\"table-footer-total ${responsiveClasses}\" data-total-column=\"${safeKey}\">${cellContent}</td>`;\n totalColumnIndex++;\n } else if (index === 0) {\n // First column shows \"Totals\" label\n footerCells += `<td class=\"table-footer-label ${responsiveClasses}\"><strong>Totals</strong></td>`;\n } else {\n // Empty cell for non-total columns\n footerCells += `<td class=\"${responsiveClasses}\"></td>`;\n }\n });\n\n // Actions footer (empty)\n if (this.actions) {\n footerCells += '<td></td>';\n } else if (this.contextMenu) {\n footerCells += '<td></td>';\n }\n\n return `\n <tfoot>\n <tr class=\"table-totals-row\">\n ${footerCells}\n </tr>\n </tfoot>\n `;\n }\n\n /**\n * Build batch actions panel\n */\n buildBatchActionsPanel() {\n if (!this.batchActions || this.batchActions.length === 0) {\n return '';\n }\n\n if (this.batchBarLocation === 'top') {\n // Toolbar-style batch actions for top placement\n let actionsHTML = '';\n this.batchActions.forEach(action => {\n actionsHTML += `\n <button class=\"btn btn-sm btn-outline-secondary\" data-action=\"batch-${action.action}\" title=\"${action.label}\">\n <i class=\"${action.icon} me-1\"></i>\n <span class=\"d-none d-lg-inline\">${action.label}</span>\n </button>\n `;\n });\n\n return `\n <div class=\"batch-actions-panel-top alert alert-info d-none mb-3\" role=\"alert\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <div class=\"d-flex align-items-center\">\n <strong class=\"me-2\">\n <span class=\"batch-select-count\">0</span> ${this.options.batchPanelTitle || 'items'} selected\n </strong>\n </div>\n <div class=\"d-flex gap-2 align-items-center\">\n ${actionsHTML}\n <button class=\"btn btn-sm btn-outline-secondary\" data-action=\"clear-selection\" title=\"Clear Selection\">\n <i class=\"bi bi-x-circle me-1\"></i>\n <span class=\"d-none d-lg-inline\">Clear</span>\n </button>\n </div>\n </div>\n </div>\n `;\n } else {\n // Original bottom panel style\n let actionsHTML = '';\n this.batchActions.forEach(action => {\n actionsHTML += `\n <div class=\"batch-select-action text-center px-2\" data-action=\"batch-${action.action}\">\n <div class=\"batch-action-icon fs-3\">\n <i class=\"${action.icon}\"></i>\n </div>\n <div class=\"batch-action-title small\">${action.label}</div>\n </div>\n `;\n });\n\n return `\n <div class=\"batch-actions-panel rounded-start rounded-end\" style=\"display: none;\">\n <div class=\"batch-select-panel rounded-start rounded-end\">\n <div class=\"row g-0\">\n <div class=\"col-auto\">\n <div class=\"batch-select-count rounded-start\">0</div>\n </div>\n <div class=\"col\">\n <div class=\"ps-2 batch-select-title\">${this.options.batchPanelTitle || 'Rows'}</div>\n </div>\n <div class=\"col\">\n <div class=\"batch-select-actions d-flex justify-content-end\">\n ${actionsHTML}\n </div>\n </div>\n <div class=\"col-auto\">\n <div class=\"batch-select-end rounded-end\"></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n }\n\n /**\n * Build pagination template\n */\n buildPaginationTemplate() {\n if (!this.paginated) {\n return '';\n }\n\n return `\n <div class=\"table-status-bar mt-3\">\n <div class=\"d-flex flex-column flex-lg-row justify-content-center justify-content-lg-between align-items-center gap-3\">\n <div class=\"d-flex flex-column flex-sm-row align-items-center gap-2 gap-sm-3 text-center text-lg-start\">\n <span class=\"text-muted\">\n Showing <span data-value=\"start\">0</span> to <span data-value=\"end\">0</span>\n of <span data-value=\"total\">0</span> entries\n </span>\n <div class=\"d-flex align-items-center\">\n <label class=\"form-label me-2 mb-0\">Show:</label>\n <select class=\"form-select form-select-sm\" style=\"width: auto;\" data-change-action=\"page-size\">\n <option value=\"5\">5</option>\n <option value=\"10\">10</option>\n <option value=\"25\">25</option>\n <option value=\"50\">50</option>\n <option value=\"100\">100</option>\n </select>\n </div>\n </div>\n <nav aria-label=\"Table pagination\">\n <ul class=\"pagination pagination-sm mb-0 justify-content-center\" data-container=\"pagination\">\n <!-- Pagination will be rendered here -->\n </ul>\n </nav>\n </div>\n </div>\n `;\n }\n\n /**\n * Override _createItemView to pass table-specific options\n */\n _createItemView(model, index) {\n const itemView = new this.itemClass({\n model: model,\n index: index,\n listView: this,\n tableView: this, // Also pass as tableView for clarity\n template: this.itemTemplate,\n columns: this.columns,\n actions: this.actions,\n contextMenu: this.contextMenu,\n batchActions: this.batchActions,\n containerId: 'items'\n });\n\n // Store the item view\n this.itemViews.set(model.id, itemView);\n\n // Set up item event listeners\n itemView.on('item:select', (event) => {\n this._onItemSelect(event);\n this.updateBatchActionsPanel();\n });\n itemView.on('item:deselect', (event) => {\n this._onItemDeselect(event);\n this.updateBatchActionsPanel();\n });\n\n // Table-specific row events\n itemView.on('row:click', this._onRowClick.bind(this));\n itemView.on('row:view', this._onRowView.bind(this));\n itemView.on('row:edit', this._onRowEdit.bind(this));\n itemView.on('row:delete', this._onRowDelete.bind(this));\n itemView.on('cell:edit', this._onCellEdit.bind(this));\n itemView.on('cell:save', this._onCellSave.bind(this));\n itemView.on('cell:cancel', this._onCellCancel.bind(this));\n\n return itemView;\n }\n\n /**\n * Override onMounted to ensure filter pills are shown on initial load\n */\n async onMounted() {\n await super.onMounted();\n const activeFilters = this.getActiveFilters();\n\n // Ensure filter pills are displayed if there are active filters from URL\n if (this.collection && Object.keys(activeFilters).length > 0) {\n this.updateFilterPills();\n }\n\n // Add listener for native search clear button\n this.setupSearchClearListener();\n }\n\n /**\n * Setup listener for native search clear (X) button\n */\n setupSearchClearListener() {\n if (!this.element) return;\n\n const searchInputs = this.element.querySelectorAll('input[type=\"search\"][data-filter=\"search\"]');\n searchInputs.forEach(input => {\n // Listen for input event to detect native clear\n input.addEventListener('input', (event) => {\n // If value is empty and we had a search before, it was cleared\n if (event.target.value === '' && this.getActiveFilters().search) {\n this.onActionClearSearch(event, event.target);\n }\n });\n });\n }\n\n /**\n * Handle row click event\n */\n _onRowClick(event) {\n this.emit('row:click', event);\n\n // Default behavior - show item details if configured\n if (this.options.onRowClick) {\n return this.options.onRowClick(event.model, event.event);\n }\n\n if (this.clickAction === 'view') {\n this._onRowView(event);\n } else if (this.clickAction === 'edit') {\n this._onRowEdit(event);\n }\n }\n\n /**\n * Get the Model class from collection or instance\n */\n getModelClass(model) {\n // Try to get from collection first\n if (this.collection?.ModelClass) return this.collection.ModelClass;\n if (this.collection?.model) return this.collection.model;\n\n // Try to get from a model instance\n if (model?.constructor) return model.constructor;\n\n // Return null if we can't determine\n return null;\n }\n\n /**\n * Get model name for display\n */\n getModelName(model) {\n const ModelClass = this.getModelClass(model);\n if (!ModelClass) return 'Item';\n\n return ModelClass.MODEL_NAME ||\n ModelClass.name.replace(/Model$/, '') ||\n 'Item';\n }\n\n /**\n * Resolve item view class with fallbacks\n */\n getItemViewClass(model) {\n // Check instance options first\n if (this.itemView) return this.itemView;\n\n // Check Model class static property\n const ModelClass = this.getModelClass(model);\n if (ModelClass?.VIEW_CLASS) return ModelClass.VIEW_CLASS;\n\n return null; // Will use data view as fallback\n }\n\n /**\n * Resolve add form configuration with fallbacks\n */\n getAddFormConfig(ModelClass) {\n return this.addForm ||\n ModelClass?.ADD_FORM ||\n this.editForm ||\n ModelClass?.EDIT_FORM;\n }\n\n /**\n * Resolve edit form configuration with fallbacks\n */\n getEditFormConfig(ModelClass) {\n return this.editForm ||\n ModelClass?.EDIT_FORM ||\n this.addForm ||\n ModelClass?.ADD_FORM;\n }\n\n /**\n * Get form dialog configuration\n */\n getFormDialogConfig(ModelClass) {\n return {\n ...ModelClass?.FORM_DIALOG_CONFIG,\n ...this.formDialogConfig\n };\n }\n\n /**\n * Render a template string with model context\n */\n renderTemplateString(template, model) {\n if (!template) return '';\n\n // Use Mustache to render the template with the model as context\n return Mustache.render(template, model);\n }\n\n /**\n * Handle row view action\n */\n async _onRowView(event) {\n this.emit('row:view', event);\n\n // Check for custom handler first\n if (this.options.onItemView) {\n await this.options.onItemView(event.model, event.event);\n return;\n }\n\n const ViewClass = this.getItemViewClass(event.model);\n\n if (ViewClass) {\n // Use custom view class\n const viewInstance = new ViewClass({ model: event.model });\n await Dialog.showDialog({\n header: false,\n body: viewInstance,\n size: 'lg',\n centered: false,\n ...this.getFormDialogConfig(this.getModelClass(event.model)),\n ...this.viewDialogOptions\n });\n } else {\n // Fallback to data view\n await Dialog.showData({\n title: `View ${this.getModelName(event.model)} #${event.model.id}`,\n model: event.model\n });\n }\n }\n\n /**\n * Handle row edit action\n */\n async _onRowEdit(event) {\n this.emit('row:edit', event);\n\n // Check for custom handler first\n if (this.options.onItemEdit) {\n await this.options.onItemEdit(event.model, event.event);\n return;\n }\n\n const ModelClass = this.getModelClass(event.model);\n let formConfig = this.getEditFormConfig(ModelClass);\n\n if (formConfig) {\n if (!formConfig.fields) {\n formConfig = { title: `Edit ${this.getModelName(event.model)}`, fields: formConfig };\n }\n\n const result = await Dialog.showModelForm({\n model: event.model,\n ...formConfig,\n ...this.getFormDialogConfig(ModelClass)\n });\n\n if (!result) return; // Cancelled\n\n if (!result.success || !result?.result?.data.status) {\n Dialog.showError(result?.result?.data?.error || result?.result?.message || \"An error occurred\");\n return;\n }\n\n } else {\n // Fallback to basic form if no config provided\n // Using statically imported FormView\n const result = await Dialog.showDialog({\n title: `Edit ${this.getModelName(event.model)} #${event.model.id}`,\n body: new FormView({\n model: event.model,\n fields: this.options.formFields || []\n })\n });\n\n if (result) {\n const resp = await event.model.save(result);\n if (!resp.data?.status) {\n Dialog.showError(resp.data.error || 'An error occurred');\n return;\n }\n await this.refresh();\n }\n }\n }\n\n /**\n * Handle row delete action\n */\n async _onRowDelete(event) {\n this.emit('row:delete', event);\n\n // Check for custom handler first\n if (this.options.onItemDelete) {\n await this.options.onItemDelete(event.model, event.event);\n return;\n }\n\n const ModelClass = this.getModelClass(event.model);\n\n // Get delete template from options, Model class, or use default\n const template = this.deleteTemplate ||\n ModelClass?.DELETE_TEMPLATE ||\n 'Are you sure you want to delete this {{name||\"item\"}}?';\n\n // Render template with model context\n const message = this.renderTemplateString(template, event.model);\n\n const confirmed = await Dialog.confirm({\n message: message || 'Are you sure you want to delete this item?',\n title: 'Confirm Delete',\n confirmText: 'Delete',\n confirmClass: 'btn-danger'\n });\n\n if (confirmed) {\n await event.model.destroy();\n this.collection.fetch();\n }\n }\n\n /**\n * Handle cell edit event\n */\n _onCellEdit(event) {\n this.emit('cell:edit', event);\n }\n\n /**\n * Handle cell save event\n */\n async _onCellSave(event) {\n this.emit('cell:save', event);\n // Model save is now handled directly in TableRow.saveCellEdit()\n }\n\n /**\n * Handle cell cancel event\n */\n _onCellCancel(event) {\n this.emit('cell:cancel', event);\n }\n\n /**\n * Check if fullscreen is supported by the browser\n */\n isFullscreenSupported() {\n return !!(\n document.fullscreenEnabled ||\n document.mozFullScreenEnabled ||\n document.webkitFullscreenEnabled ||\n document.msFullscreenEnabled\n );\n }\n\n /**\n * Handle toggle fullscreen action\n */\n async onActionToggleFullscreen(event, element) {\n if (this.isFullscreen) {\n await this.exitFullscreen();\n } else {\n await this.enterFullscreen();\n }\n }\n\n /**\n * Enter fullscreen mode\n */\n async enterFullscreen() {\n try {\n // Use browser's native fullscreen API\n if (this.element.requestFullscreen) {\n await this.element.requestFullscreen();\n } else if (this.element.mozRequestFullScreen) {\n await this.element.mozRequestFullScreen();\n } else if (this.element.webkitRequestFullscreen) {\n await this.element.webkitRequestFullscreen();\n } else if (this.element.msRequestFullscreen) {\n await this.element.msRequestFullscreen();\n }\n\n this.isFullscreen = true;\n this.element.classList.add('table-fullscreen');\n this.updateFullscreenButton();\n\n // Listen for fullscreen change events\n this.setupFullscreenListeners();\n\n this.emit('table:fullscreen:enter');\n\n } catch (error) {\n console.warn('Could not enter fullscreen:', error);\n }\n }\n\n /**\n * Exit fullscreen mode\n */\n async exitFullscreen() {\n try {\n if (document.exitFullscreen) {\n await document.exitFullscreen();\n } else if (document.mozCancelFullScreen) {\n await document.mozCancelFullScreen();\n } else if (document.webkitExitFullscreen) {\n await document.webkitExitFullscreen();\n } else if (document.msExitFullscreen) {\n await document.msExitFullscreen();\n }\n\n this.isFullscreen = false;\n this.element.classList.remove('table-fullscreen');\n this.updateFullscreenButton();\n\n this.emit('table:fullscreen:exit');\n\n } catch (error) {\n console.warn('Could not exit fullscreen:', error);\n }\n }\n\n /**\n * Update fullscreen button icon and title\n */\n updateFullscreenButton() {\n const button = this.element?.querySelector('.btn-fullscreen');\n const icon = button?.querySelector('i');\n\n if (button && icon) {\n if (this.isFullscreen) {\n icon.className = 'bi bi-fullscreen-exit';\n button.title = 'Exit Fullscreen';\n } else {\n icon.className = 'bi bi-fullscreen';\n button.title = 'Enter Fullscreen';\n }\n }\n }\n\n /**\n * Setup fullscreen event listeners\n */\n setupFullscreenListeners() {\n // Don't add listeners multiple times\n if (this._fullscreenHandler) return;\n\n const handleFullscreenChange = () => {\n const isCurrentlyFullscreen = !!(\n document.fullscreenElement ||\n document.mozFullScreenElement ||\n document.webkitFullscreenElement ||\n document.msFullscreenElement\n );\n\n if (!isCurrentlyFullscreen && this.isFullscreen) {\n // User exited fullscreen via ESC or browser controls\n this.isFullscreen = false;\n this.element.classList.remove('table-fullscreen');\n this.updateFullscreenButton();\n this.emit('table:fullscreen:exit');\n }\n };\n\n // Add listeners for all browser prefixes\n document.addEventListener('fullscreenchange', handleFullscreenChange);\n document.addEventListener('mozfullscreenchange', handleFullscreenChange);\n document.addEventListener('webkitfullscreenchange', handleFullscreenChange);\n document.addEventListener('msfullscreenchange', handleFullscreenChange);\n\n // Store handler for cleanup\n this._fullscreenHandler = handleFullscreenChange;\n }\n\n /**\n * Cleanup fullscreen listeners\n */\n cleanupFullscreenListeners() {\n if (this._fullscreenHandler) {\n document.removeEventListener('fullscreenchange', this._fullscreenHandler);\n document.removeEventListener('mozfullscreenchange', this._fullscreenHandler);\n document.removeEventListener('webkitfullscreenchange', this._fullscreenHandler);\n document.removeEventListener('msfullscreenchange', this._fullscreenHandler);\n this._fullscreenHandler = null;\n }\n }\n\n /**\n * Override destroy to cleanup fullscreen listeners\n */\n destroy() {\n this.cleanupFullscreenListeners();\n super.destroy();\n }\n\n /**\n * Handle refresh action\n */\n async onActionRefresh(event, element) {\n await this.refresh();\n }\n\n /**\n * Handle add action\n */\n async onActionAdd(event, element) {\n // Check for custom handler first - if provided, just emit event and let handler deal with it\n if (this.options.onAdd) {\n this.emit('table:add', { event });\n await this.options.onAdd(event);\n return;\n }\n\n // Emit event for external listeners\n this.emit('table:add', { event });\n\n const ModelClass = this.getModelClass();\n if (!ModelClass) {\n console.warn('Cannot determine Model class for add operation');\n return;\n }\n\n let formConfig = this.getAddFormConfig(ModelClass);\n\n if (formConfig) {\n const model = new ModelClass();\n if (!formConfig.fields) {\n formConfig = { title: `Add ${this.getModelName()}`, fields: formConfig };\n }\n\n const result = await Dialog.showForm({\n model: model,\n ...formConfig,\n ...this.getFormDialogConfig(ModelClass)\n });\n\n if (result) {\n if (this.options.addRequiresActiveGroup) {\n result.group = this.getApp().activeGroup.id;\n }\n if (this.options.addRequiresActiveUser) {\n result.user = this.getApp().activeUser.id;\n }\n if (this.options.addFormDefaults) {\n Object.assign(result, this.options.addFormDefaults);\n }\n const resp = await model.save(result);\n if (!resp?.data.status) {\n Dialog.showError(resp?.data.error || 'An error occurred');\n return;\n }\n if (this.collection) {\n this.collection.add(model);\n }\n await this.refresh();\n }\n } else {\n // Fallback to basic form if no config provided\n // Using statically imported FormView\n const model = new ModelClass();\n\n const result = await Dialog.showDialog({\n title: `Add ${this.getModelName()}`,\n body: new FormView({\n model: model,\n fields: this.options.formFields || []\n })\n });\n\n if (result) {\n const resp = await model.save(result);\n if (!resp?.data.status) {\n Dialog.showError(resp.data.error || 'An error occurred');\n return;\n }\n if (this.collection) {\n this.collection.add(model);\n }\n await this.refresh();\n }\n }\n }\n\n /**\n * Handle export action\n */\n async onActionExport(event, element) {\n const format = element.getAttribute('data-format') || 'json';\n\n this.emit('table:export', {\n format: format,\n source: this.exportSource,\n event\n });\n\n if (this.exportSource === 'remote') {\n if (this.collection) {\n await this.collection.download(format);\n } else {\n console.warn('TableView: Cannot export from remote without a collection.');\n }\n } else {\n // Handle local export (future enhancement)\n if (this.options.onExport) {\n await this.options.onExport(this.collection?.toJSON() || [], format);\n } else {\n console.warn('TableView: onExport handler not implemented for local export.');\n }\n }\n }\n\n /**\n * Handle search action (Enter key triggers this via EventDelegate)\n */\n async onActionApplySearch(event, element) {\n const searchTerm = element.value.trim();\n\n if (this.collection) {\n this.setFilter('search', searchTerm);\n\n // Reset to first page when searching\n this.collection.params.start = 0;\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n } else {\n // Client-side filtering\n this.render();\n }\n }\n\n // Update filter pills when search changes\n this.updateFilterPills();\n\n this.emit('table:search', { searchTerm, event });\n this.emit('params-changed');\n }\n\n /**\n * Handle clear search button\n */\n async onActionClearSearch(event, element) {\n // Clear the search filter\n this.setFilter('search', null);\n\n // Reset to first page\n if (this.collection) {\n this.collection.params.start = 0;\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n }\n }\n\n // Render will rebuild the search input with empty value\n await this.render();\n this.updateFilterPills();\n\n this.emit('table:search', { searchTerm: '', event });\n this.emit('params-changed');\n }\n\n /**\n * Get current sort field\n */\n getSortBy() {\n const sort = this.collection?.params?.sort;\n if (!sort) return null;\n return sort.startsWith('-') ? sort.slice(1) : sort;\n }\n\n /**\n * Get current sort direction\n */\n getSortDirection() {\n const sort = this.collection?.params?.sort;\n if (!sort) return 'asc';\n return sort.startsWith('-') ? 'desc' : 'asc';\n }\n\n /**\n * Get sort icon based on current sort direction\n */\n getSortIcon(direction) {\n if (direction === 'asc') {\n return '<i class=\"bi bi-sort-alpha-down text-primary\"></i>';\n } else if (direction === 'desc') {\n return '<i class=\"bi bi-sort-alpha-down-alt text-primary\"></i>';\n } else {\n return '<i class=\"bi bi-three-dots-vertical text-muted\"></i>';\n }\n }\n\n /**\n * Handle sort action\n */\n async onActionSort(event, element) {\n event.preventDefault();\n const field = element.getAttribute('data-field');\n const direction = element.getAttribute('data-direction');\n\n if (this.collection) {\n let newSort;\n\n if (direction === 'none') {\n newSort = undefined; // Remove sort\n } else if (direction === 'desc') {\n newSort = `-${field}`; // Descending sort\n } else {\n newSort = field; // Ascending sort\n }\n\n this.collection.setParams({\n ...this.collection.params,\n sort: newSort,\n start: 0 // Reset to first page when sorting changes\n });\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n } else {\n // Client-side sorting\n if (newSort) {\n const desc = newSort.startsWith('-');\n const sortField = desc ? newSort.slice(1) : newSort;\n\n this.collection.sort((a, b) => {\n const aVal = a.get(sortField);\n const bVal = b.get(sortField);\n\n if (aVal < bVal) return desc ? 1 : -1;\n if (aVal > bVal) return desc ? -1 : 1;\n return 0;\n });\n }\n\n this.render();\n }\n }\n\n // Update sort icons in the DOM\n this.updateSortIcons();\n\n this.emit('table:sort', { field, event });\n this.emit('params-changed');\n }\n\n /**\n * Update sort icons in all column headers\n */\n updateSortIcons() {\n if (!this.element) return;\n\n const currentSortField = this.getSortBy();\n const currentSortDir = this.getSortDirection();\n\n // Update all sort dropdown buttons\n this.columns.forEach(column => {\n if (this.sortable && column.sortable !== false) {\n // Parse the column key to get just the field name (without pipes/formatters)\n const { fieldKey } = this.parseColumnKey(column.key);\n\n const dropdown = this.element.querySelector(`[data-bs-toggle=\"dropdown\"][data-column=\"${fieldKey}\"]`);\n if (dropdown) {\n const isSorted = currentSortField === fieldKey;\n const sortIcon = this.getSortIcon(isSorted ? currentSortDir : null);\n dropdown.innerHTML = sortIcon;\n\n // Update dropdown menu items\n const dropdownMenu = dropdown.nextElementSibling;\n if (dropdownMenu) {\n const ascItem = dropdownMenu.querySelector(`[data-field=\"${fieldKey}\"][data-direction=\"asc\"]`);\n const descItem = dropdownMenu.querySelector(`[data-field=\"${fieldKey}\"][data-direction=\"desc\"]`);\n const noneItem = dropdownMenu.querySelector(`[data-field=\"${fieldKey}\"][data-direction=\"none\"]`);\n\n if (ascItem) {\n ascItem.classList.toggle('active', isSorted && currentSortDir === 'asc');\n }\n if (descItem) {\n descItem.classList.toggle('active', isSorted && currentSortDir === 'desc');\n }\n if (noneItem) {\n noneItem.classList.toggle('active', !isSorted || currentSortField !== fieldKey);\n }\n }\n }\n }\n });\n }\n\n /**\n * Handle select all action\n */\n async onActionSelectAll(event, element) {\n event.stopPropagation();\n const isCurrentlyAllSelected = this.itemViews.size > 0 &&\n Array.from(this.itemViews.values()).every(item => item.selected);\n\n if (!isCurrentlyAllSelected) {\n // Select all visible items\n this.forEachItem(itemView => {\n if (!itemView.selected) {\n itemView.select();\n }\n });\n } else {\n // Deselect all\n this.clearSelection();\n }\n\n // Update select all checkbox visual state\n const selectAllCell = this.element?.querySelector('.mojo-select-all-cell');\n if (selectAllCell) {\n selectAllCell.classList.toggle('selected', !isCurrentlyAllSelected);\n }\n\n // Update batch actions panel\n this.updateBatchActionsPanel();\n }\n\n /**\n * Override onBeforeRender to set data properties before rendering\n */\n async onBeforeRender() {\n // Set properties that Mustache needs\n this.searchValue = this.getActiveFilters().search || '';\n this.footerTotals = this.calculateFooterTotals();\n }\n\n /**\n * Override onAfterRender to update pagination info\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n // Update footer totals in case collection loaded after initial render\n if (this.hasFooterTotals) {\n this.updateFooterTotals();\n }\n\n // Update pagination info\n if (this.paginated && this.collection) {\n const total = this.collection.meta?.count || this.collection.length();\n const start = this.collection.params?.start || 0;\n const size = this.collection.params?.size || 10;\n const end = Math.min(start + size, total);\n\n const startEl = this.element.querySelector('[data-value=\"start\"]');\n const endEl = this.element.querySelector('[data-value=\"end\"]');\n const totalEl = this.element.querySelector('[data-value=\"total\"]');\n\n if (startEl) startEl.textContent = start + 1;\n if (endEl) endEl.textContent = end;\n if (totalEl) totalEl.textContent = total;\n\n // Update page size selector\n const pageSizeSelect = this.element.querySelector('[data-change-action=\"page-size\"]');\n if (pageSizeSelect) {\n pageSizeSelect.value = size;\n }\n\n // Render pagination controls\n this.renderPagination();\n }\n\n // Update sort icons after render\n this.updateSortIcons();\n\n // Update filter pills after render - this is crucial for showing pills on page load\n this.updateFilterPills();\n\n // Re-setup search clear listener after render\n this.setupSearchClearListener();\n }\n\n /**\n * Render pagination controls\n * - Prev/Next wrap around (never disabled)\n * - Truncated page list with first/last and ellipses\n */\n renderPagination() {\n const paginationContainer = this.element.querySelector('[data-container=\"pagination\"]');\n if (!paginationContainer || !this.collection) return;\n\n const total = this.collection.meta?.count || this.collection.length();\n const size = this.collection.params?.size || 10;\n const start = this.collection.params?.start || 0;\n const currentPage = Math.floor(start / size) + 1;\n const totalPages = Math.ceil(total / size);\n\n if (totalPages <= 1) {\n paginationContainer.innerHTML = '';\n return;\n }\n\n const prevPage = currentPage > 1 ? currentPage - 1 : totalPages;\n const nextPage = currentPage < totalPages ? currentPage + 1 : 1;\n\n const pages = [];\n\n // Previous (wraps)\n pages.push(`\n <li class=\"page-item\">\n <a class=\"page-link\" href=\"#\" data-action=\"page\" data-page=\"${prevPage}\">\n <i class=\"bi bi-chevron-left\"></i>\n </a>\n </li>\n `);\n\n // Build truncated page list: always show 1 and totalPages, with neighbors around current\n const neighbors = 1; // how many pages to show on each side of current\n const visibleSet = new Set([1, totalPages]);\n for (let i = currentPage - neighbors; i <= currentPage + neighbors; i++) {\n if (i >= 1 && i <= totalPages) visibleSet.add(i);\n }\n const visible = Array.from(visibleSet).sort((a, b) => a - b);\n\n // Render pages with ellipses where there are gaps\n let last = 0;\n for (const p of visible) {\n if (last && p - last > 1) {\n // gap -> ellipsis\n pages.push(`\n <li class=\"page-item disabled\"><span class=\"page-link\">…</span></li>\n `);\n }\n pages.push(`\n <li class=\"page-item ${p === currentPage ? 'active' : ''}\">\n <a class=\"page-link\" href=\"#\" data-action=\"page\" data-page=\"${p}\">${p}</a>\n </li>\n `);\n last = p;\n }\n\n // Next (wraps)\n pages.push(`\n <li class=\"page-item\">\n <a class=\"page-link\" href=\"#\" data-action=\"page\" data-page=\"${nextPage}\">\n <i class=\"bi bi-chevron-right\"></i>\n </a>\n </li>\n `);\n\n paginationContainer.innerHTML = pages.join('');\n }\n\n /**\n * Handle page change\n * - Normalizes and wraps page number (1..totalPages)\n */\n async onActionPage(event, element) {\n event.preventDefault();\n\n const rawPage = parseInt(element.getAttribute('data-page'), 10);\n const size = this.collection.params?.size || 10;\n const total = this.collection.meta?.count || this.collection.length();\n const totalPages = Math.max(1, Math.ceil(total / size));\n\n let page = isNaN(rawPage) ? 1 : rawPage;\n if (page < 1) page = totalPages;\n if (page > totalPages) page = 1;\n\n this.collection.setParams({\n ...this.collection.params,\n start: (page - 1) * size\n });\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n } else {\n this.render();\n }\n\n this.emit('table:page', { page, event });\n this.emit('params-changed');\n }\n\n /**\n * Handle page size change\n */\n async onChangePageSize(event, element) {\n const newSize = parseInt(element.value);\n\n if (this.collection) {\n // Reset to first page when changing page size\n this.collection.setParams({\n ...this.collection.params,\n start: 0,\n size: newSize\n });\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n }\n this.render();\n }\n\n this.emit('table:pagesize', { size: newSize, event });\n this.emit('params-changed');\n }\n\n /**\n * Get active filters from collection params\n */\n getActiveFilters() {\n if (!this.collection?.params) {\n return {};\n }\n const { start, size, sort, ...allParams } = this.collection.params;\n const filters = {};\n\n // Reconstruct daterange filters from their component parts\n const processedKeys = new Set();\n\n // First pass: identify and process daterange filters\n const allFilterConfigs = this.getAllAvailableFilters();\n allFilterConfigs.forEach(filterDef => {\n if (filterDef.config.type === 'daterange') {\n const key = filterDef.key;\n const startName = filterDef.config.startName || 'dr_start';\n const endName = filterDef.config.endName || 'dr_end';\n const fieldName = filterDef.config.fieldName || 'dr_field';\n\n // Check if this daterange filter is active for this specific key\n if (allParams[fieldName] === key && (allParams[startName] || allParams[endName])) {\n filters[key] = {\n start: allParams[startName] || '',\n end: allParams[endName] || ''\n };\n\n processedKeys.add(startName);\n processedKeys.add(endName);\n processedKeys.add(fieldName);\n }\n }\n });\n\n // Second pass: add remaining filters\n Object.keys(allParams).forEach(paramKey => {\n if (!processedKeys.has(paramKey)) {\n filters[paramKey] = allParams[paramKey];\n }\n });\n\n // Normalize single-value vs multi-value filters where both exist (e.g., field and field__in)\n Object.keys(filters).forEach(key => {\n if (filters.hasOwnProperty(key)) {\n const inKey = `${key}__in`;\n if (filters.hasOwnProperty(inKey)) {\n // Prefer __in when explicitly provided; remove the base key to avoid duplicate pills\n delete filters[key];\n filters[inKey] = filters[inKey];\n }\n }\n });\n\n return filters;\n }\n\n /**\n * Set a filter value\n */\n setFilter(key, value) {\n if (!this.collection) return;\n\n const filterConfig = this.getFilterConfig(key);\n\n // Handle daterange filters specially\n if (filterConfig && filterConfig.type === 'daterange') {\n const startName = filterConfig.startName || 'dr_start';\n const endName = filterConfig.endName || 'dr_end';\n const fieldName = filterConfig.fieldName || 'dr_field';\n\n // Always remove old values first\n delete this.collection.params[startName];\n delete this.collection.params[endName];\n delete this.collection.params[fieldName];\n\n // Set new values if provided and not empty\n if (value && typeof value === 'object' && (value.start || value.end)) {\n if (value.start) this.collection.params[startName] = value.start;\n if (value.end) this.collection.params[endName] = value.end;\n this.collection.params[fieldName] = key;\n }\n } else {\n // Parse key to get field and lookup\n const { field, lookup } = parseFilterKey(key);\n \n // Clear old values - remove both base field and variants\n delete this.collection.params[key];\n delete this.collection.params[field];\n delete this.collection.params[`${field}__in`];\n \n if (!value || (Array.isArray(value) && value.length === 0)) {\n return; // Cleared\n }\n \n // Smart param generation for multiselect fields\n if (Array.isArray(value)) {\n if (value.length === 1) {\n // Single value from array - use simple key (no __in)\n this.collection.params[field] = value[0];\n } else {\n // Multiple values - use __in lookup\n this.collection.params[`${field}__in`] = value.join(',');\n }\n } else {\n // Single value - use key as-is (may include lookup like __gte)\n this.collection.params[key] = value;\n }\n }\n }\n\n /**\n * Get all available filters\n */\n getAllAvailableFilters() {\n const filters = [];\n\n // Add column-based filters\n this.columns.forEach(column => {\n if (column.filter) {\n const { fieldKey } = this.parseColumnKey(column.key);\n filters.push({\n key: fieldKey,\n label: column.filter.label || column.label || fieldKey,\n type: column.filter.type,\n config: column.filter\n });\n }\n });\n\n // Add additional filters\n if (this.additionalFilters && Array.isArray(this.additionalFilters)) {\n this.additionalFilters.forEach(filter => {\n filters.push({\n key: filter.name || filter.key,\n label: filter.label,\n type: filter.type,\n config: filter\n });\n });\n }\n\n return filters;\n }\n\n /**\n * Get filter configuration for a key\n */\n getFilterConfig(filterKey) {\n // Check column filters first\n const column = this.columns.find(col => {\n const { fieldKey } = this.parseColumnKey(col.key);\n return fieldKey === filterKey;\n });\n if (column && column.filter) {\n return column.filter;\n }\n\n // Check additional filters\n if (this.additionalFilters && Array.isArray(this.additionalFilters)) {\n const filter = this.additionalFilters.find(f => (f.name || f.key) === filterKey);\n if (filter) {\n return filter;\n }\n }\n\n return null;\n }\n\n /**\n * Get filter label\n */\n getFilterLabel(key) {\n if (key === 'search') return 'Search';\n\n const filter = this.filters[key];\n if (filter && filter.label) return filter.label;\n\n const additionalFilter = this.additionalFilters.find(f =>\n (f.name || f.key) === key\n );\n if (additionalFilter && additionalFilter.label) return additionalFilter.label;\n\n return key.charAt(0).toUpperCase() + key.slice(1);\n }\n\n /**\n * Get filter display value\n */\n getFilterDisplayValue(key, value) {\n if (key === 'search') return `\"${value}\"`;\n\n const filter = this.filters[key] ||\n this.additionalFilters.find(f => (f.name || f.key) === key);\n\n if (filter && filter.type === 'daterange' && typeof value === 'object') {\n const start = value.start || '';\n const end = value.end || '';\n return `${start} to ${end}`;\n }\n\n if (filter && filter.type === 'select' && filter.options) {\n if (typeof filter.options[0] === 'object') {\n const option = filter.options.find(opt => opt.value === value);\n return option ? option.label : value;\n }\n return value;\n }\n\n return value;\n }\n\n /**\n * Get icon for filter type\n */\n getFilterIcon(type) {\n const icons = {\n 'text': 'search',\n 'select': 'funnel',\n 'date': 'calendar',\n 'daterange': 'calendar-range',\n 'number': '123',\n 'boolean': 'toggle-on'\n };\n return icons[type] || 'filter';\n }\n\n /**\n * Handle add filter action\n */\n async onActionAddFilter(event, element) {\n const filterKey = element.getAttribute('data-filter-key');\n const filterConfig = this.getFilterConfig(filterKey);\n const currentValue = this.getActiveFilters()[filterKey];\n\n if (!filterConfig) {\n console.warn('No filter config found for key:', filterKey);\n return;\n }\n\n // Using statically imported Dialog\n\n // Show dialog for this specific filter\n const result = await Dialog.showForm({\n title: `${currentValue !== undefined && currentValue !== '' ? 'Edit' : 'Add'} ${this.getFilterLabel(filterKey)} Filter`,\n size: 'md',\n fields: [this.buildFilterDialogField(filterConfig, currentValue, filterKey)]\n });\n\n if (result) {\n // Extract the new filter value\n const newFilterValue = this.extractFilterValue(filterConfig, result);\n // Use the filter key for setFilter (it will handle the lookup internally)\n this.setFilter(filterKey, newFilterValue);\n await this.applyFilters();\n }\n }\n\n /**\n * Build filter dialog field configuration\n */\n buildFilterDialogField(filterConfig, currentValue, filterKey) {\n const field = {\n name: 'filter_value',\n label: filterConfig.label,\n value: currentValue,\n ...filterConfig,\n // Ensure placeholder is passed through (support both casings)\n placeholder: filterConfig.placeholder || filterConfig.placeHolder\n };\n\n // Set current value appropriately based on filter type\n if (filterConfig.type === 'daterange') {\n // Apply defaults for daterange\n field.startName = field.startName || 'dr_start';\n field.endName = field.endName || 'dr_end';\n field.fieldName = field.fieldName || 'dr_field';\n field.format = field.format || 'YYYY-MM-DD';\n field.displayFormat = field.displayFormat || 'MMM DD, YYYY';\n field.separator = field.separator || ' to ';\n field.label = field.label || 'Date Range';\n\n // Handle daterange current values\n if (currentValue && typeof currentValue === 'object') {\n const normalizeDateValue = (val) => {\n if (!val && val !== 0) return '';\n if (val instanceof Date && !isNaN(val)) {\n return val.toISOString().slice(0, 10);\n }\n\n const str = String(val).trim();\n if (!str) return '';\n\n // Numeric timestamps (seconds or milliseconds)\n if (/^-?\\d+$/.test(str)) {\n const num = Number(str);\n const ms = str.length <= 10 ? num * 1000 : num;\n const date = new Date(ms);\n if (!isNaN(date)) {\n return date.toISOString().slice(0, 10);\n }\n }\n\n // ISO or other parseable formats\n const date = new Date(str);\n if (!isNaN(date)) {\n return date.toISOString().slice(0, 10);\n }\n\n // Fallback: return original string\n return str;\n };\n\n field.startDate = normalizeDateValue(currentValue.start || currentValue.from || currentValue.begin || '');\n field.endDate = normalizeDateValue(currentValue.end || currentValue.to || currentValue.finish || '');\n }\n } else if (filterConfig.type === 'multiselect') {\n // Convert comma-separated string to array for multiselect\n let valueArray = [];\n if (currentValue) {\n if (Array.isArray(currentValue)) {\n valueArray = currentValue;\n } else if (typeof currentValue === 'string') {\n // Split by comma and trim whitespace\n valueArray = currentValue.split(',').map(v => v.trim()).filter(v => v);\n }\n }\n \n field.value = valueArray;\n \n // Ensure placeholder is set (support both casings)\n if (!field.placeholder && !field.placeHolder) {\n if (filterConfig.placeholder || filterConfig.placeHolder) {\n field.placeholder = filterConfig.placeholder || filterConfig.placeHolder;\n } else if (filterConfig.label) {\n field.placeholder = `Select ${filterConfig.label}...`;\n }\n }\n }\n\n return field;\n }\n\n /**\n * Extract filter value from form result\n */\n extractFilterValue(filterConfig, formResult) {\n if (filterConfig.type === 'daterange') {\n // Extract start/end values based on naming convention\n const startName = filterConfig.startName || 'dr_start';\n const endName = filterConfig.endName || 'dr_end';\n\n const result = {\n start: formResult[startName],\n end: formResult[endName]\n };\n\n return result;\n }\n\n if (filterConfig.type === 'multiselect') {\n // Return array as-is for multiselect\n return formResult.filter_value;\n }\n\n return formResult.filter_value;\n }\n\n /**\n * Apply filters to collection and refresh\n */\n async applyFilters() {\n // Reset to first page when filters change\n if (this.collection) {\n this.collection.params.start = 0;\n }\n\n // For REST collections, fetch data with new filters\n if (this.collection?.restEnabled) {\n try {\n await this.collection.fetch();\n this.render();\n } catch (error) {\n console.error('Failed to fetch filtered data:', error);\n this.render();\n }\n } else {\n this.render();\n }\n\n // Update filter pills display\n this.updateFilterPills();\n\n // Emit params changed event for URL synchronization\n this.emit('params-changed');\n }\n\n /**\n * Handle edit filter action from pill\n */\n async onActionEditFilter(event, element) {\n const filterKey = element.getAttribute('data-filter');\n \n // Parse the key to get the base field (handles lookup keys like status__in)\n const { field } = parseFilterKey(filterKey);\n \n // Try to get filter config using the parsed field name first, then original key\n let filterConfig = this.getFilterConfig(field) || this.getFilterConfig(filterKey);\n \n // Get current value - could be under filterKey or field\n const activeFilters = this.getActiveFilters();\n const currentValue = activeFilters[filterKey] || activeFilters[field];\n\n if (!filterConfig) {\n console.warn('No filter config found for key:', filterKey, 'or field:', field);\n return;\n }\n\n // Prepare initial form data\n const formData = { filter_value: currentValue };\n if (filterConfig.type === 'daterange' && currentValue && typeof currentValue === 'object') {\n const startName = filterConfig.startName || 'dr_start';\n const endName = filterConfig.endName || 'dr_end';\n formData[startName] = currentValue.start || '';\n formData[endName] = currentValue.end || '';\n }\n\n // Show mini dialog for this specific filter\n const result = await Dialog.showForm({\n title: `Edit ${this.getFilterLabel(field)} Filter`,\n size: 'md',\n data: formData,\n fields: [this.buildFilterDialogField(filterConfig, currentValue, field)]\n });\n\n if (result) {\n // Extract the new filter value\n const newFilterValue = this.extractFilterValue(filterConfig, result);\n this.setFilter(filterKey, newFilterValue);\n await this.applyFilters();\n }\n }\n\n /**\n * Handle remove filter action\n */\n async onActionRemoveFilter(event, element) {\n const filterKey = element.getAttribute('data-filter');\n \n // Parse to get the base field (handles lookup keys like status__in)\n const { field } = parseFilterKey(filterKey);\n \n // Clear the filter using the original key\n this.setFilter(filterKey, null);\n\n // If removing search filter, clear search inputs\n if (filterKey === 'search') {\n this.updateSearchInputs('');\n }\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n }\n this.render();\n\n // Update filter pills after removing\n this.updateFilterPills();\n\n this.emit('filter:remove', { key: filterKey, field });\n this.emit('params-changed');\n }\n\n /**\n * Handle clear all filters action\n */\n async onActionClearAllFilters(event, element) {\n if (!this.collection) return;\n\n // Clear all filters except pagination and sorting\n const { start, size, sort } = this.collection.params;\n this.collection.params = { start, size };\n if (sort) this.collection.params.sort = sort;\n\n // Clear all search inputs\n this.updateSearchInputs('');\n\n if (this.collection.restEnabled) {\n await this.collection.fetch();\n }\n this.render();\n\n // Update filter pills after clearing\n this.updateFilterPills();\n\n this.emit('filters:clear');\n this.emit('params-changed');\n }\n\n /**\n * Update batch actions panel visibility and count\n */\n updateBatchActionsPanel() {\n if (!this.batchActions || this.batchActions.length === 0) return;\n\n const selectedCount = this.getSelectedItems().length;\n\n if (this.batchBarLocation === 'top') {\n // Handle top panel style\n const panel = this.element?.querySelector('.batch-actions-panel-top');\n const countEl = this.element?.querySelector('.batch-select-count');\n\n if (panel && countEl) {\n countEl.textContent = selectedCount;\n\n // Use Bootstrap's d-none class for cleaner show/hide\n if (selectedCount > 0) {\n panel.classList.remove('d-none');\n } else {\n panel.classList.add('d-none');\n }\n }\n } else {\n // Handle bottom panel style (original)\n const panel = this.element?.querySelector('.batch-actions-panel');\n const countEl = this.element?.querySelector('.batch-select-count');\n\n if (panel && countEl) {\n countEl.textContent = selectedCount;\n panel.style.display = selectedCount > 0 ? 'block' : 'none';\n }\n }\n\n // Update select all checkbox state\n const selectAllCell = this.element?.querySelector('.mojo-select-all-cell');\n if (selectAllCell) {\n const allSelected = this.itemViews.size > 0 &&\n Array.from(this.itemViews.values()).every(item => item.selected);\n const someSelected = Array.from(this.itemViews.values()).some(item => item.selected);\n\n selectAllCell.classList.toggle('selected', allSelected);\n selectAllCell.classList.toggle('indeterminate', !allSelected && someSelected);\n\n // Update icon for indeterminate state\n const icon = selectAllCell.querySelector('i');\n if (icon) {\n icon.className = !allSelected && someSelected ? 'bi bi-dash' : 'bi bi-check';\n }\n }\n }\n\n /**\n * Handle batch action clicks\n */\n async onActionBatch(event, element) {\n const batchAction = element.getAttribute('data-action').replace('batch-', '');\n const selectedItems = this.getSelectedItems();\n\n this.emit('batch:action', {\n action: batchAction,\n items: selectedItems,\n event\n });\n }\n\n /**\n * Handle clear selection action (for top batch bar)\n */\n async onActionClearSelection(event, element) {\n this.clearSelection();\n this.updateBatchActionsPanel();\n }\n\n /**\n * Handle custom toolbar button with handler function\n */\n async onActionCustomToolbarButton(event, element) {\n const buttonIndex = parseInt(element.getAttribute('data-button-index'), 10);\n const button = this.toolbarButtons[buttonIndex];\n\n if (button && typeof button.handler === 'function') {\n await button.handler.call(this, event, element);\n }\n }\n}\n\nexport default TableView;\n","/**\n * WEB-MOJO Lite Entry (script-tag friendly)\n *\n * Goal:\n * - Provide a single browser bundle entry that attaches a \"lite\" set of core\n * components to `window.MOJO` for drop-in usage (no npm/build required for consumers).\n *\n * Notes:\n * - This file is intended to be bundled (IIFE/UMD) by the build pipeline.\n * - We \"merge\" into `window.MOJO` to reduce the chance of collisions if consumers\n * load other MOJO bundles (though typically they'd load one or the other).\n * - Bootstrap is expected to be provided by the consuming app (CSS + Icons + JS bundle).\n */\n\n// Core\nimport WebApp from '@core/WebApp.js';\nimport View from '@core/View.js';\nimport Page from '@core/Page.js';\nimport Router from '@core/Router.js';\nimport Model from '@core/Model.js';\nimport Collection from '@core/Collection.js';\nimport Rest from '@core/Rest.js';\n\n// Forms\nimport FormBuilder from '@core/forms/FormBuilder.js';\nimport FormView from '@core/forms/FormView.js';\nimport FormPage from '@core/forms/FormPage.js';\n// Basic UI Views\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport ProgressView from '@core/views/feedback/ProgressView.js';\nimport ListView from '@core/views/list/ListView.js';\nimport ListViewItem from '@core/views/list/ListViewItem.js';\nimport TableView from '@core/views/table/TableView.js';\nimport TableRow from '@core/views/table/TableRow.js';\n\n// Utilities that are commonly useful in \"lite\" contexts\nimport DataFormatter from '@core/utils/DataFormatter.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\n\n/**\n * Get (or create) the global MOJO namespace.\n * We merge to avoid clobbering an existing MOJO object.\n */\nfunction getGlobalMOJO() {\n if (typeof window === 'undefined') return null;\n if (!window.MOJO || typeof window.MOJO !== 'object') {\n window.MOJO = {};\n }\n return window.MOJO;\n}\n\n/**\n * Attach lite exports onto window.MOJO.\n * Keep this list intentionally small and \"basic\".\n */\nfunction attachLite(MOJO) {\n // If already attached, do nothing (idempotent).\n if (MOJO.__lite && MOJO.__lite.version) {\n return MOJO;\n }\n\n // Core\n MOJO.WebApp = WebApp;\n MOJO.View = View;\n MOJO.Page = Page;\n MOJO.Router = Router;\n MOJO.Model = Model;\n MOJO.Collection = Collection;\n MOJO.Rest = Rest;\n\n // Forms\n MOJO.FormBuilder = FormBuilder;\n MOJO.FormView = FormView;\n\n // Basic UI\n MOJO.Dialog = Dialog;\n MOJO.ProgressView = ProgressView;\n MOJO.ListView = ListView;\n MOJO.ListViewItem = ListViewItem;\n MOJO.TableView = TableView;\n MOJO.TableRow = TableRow;\n\n // Utils\n MOJO.DataFormatter = DataFormatter;\n MOJO.MOJOUtils = MOJOUtils;\n\n // Metadata (useful for debugging)\n MOJO.__lite = {\n version: 'dev', // build can optionally replace this\n build: 'web-mojo.lite'\n };\n\n /**\n * Convenience helper for \"embed-style\" usage where you just want to mount a view.\n * This is intentionally minimal and does not require WebApp or routing.\n *\n * @param {View} view - A MOJO View instance (or subclass instance)\n * @param {HTMLElement|string} container - DOM element or selector\n */\n MOJO.mount = async function mount(view, container) {\n if (!view) throw new Error('MOJO.mount(view, container) requires a view');\n const el =\n typeof container === 'string'\n ? document.querySelector(container)\n : container;\n\n if (!el) throw new Error('MOJO.mount(view, container) container not found');\n\n // Render/mount lifecycle is handled by the View implementation.\n // We call render() then mount().\n if (typeof view.render !== 'function' || typeof view.mount !== 'function') {\n throw new Error('MOJO.mount expects a View instance with render() and mount() methods');\n }\n\n await view.render();\n await view.mount(el);\n\n return view;\n };\n\n return MOJO;\n}\n\n// Attach immediately in the browser\nconst MOJO = getGlobalMOJO();\nif (MOJO) {\n attachLite(MOJO);\n}\n\n// Also export for ESM consumers (tree-shaking depends on bundler config)\nexport {\n // Core\n WebApp,\n View,\n Page,\n Router,\n Model,\n Collection,\n Rest,\n\n // Forms\n FormBuilder,\n FormView,\n FormPage,\n\n // Basic UI\n Dialog,\n ProgressView,\n ListView,\n ListViewItem,\n TableView,\n TableRow,\n\n // Utils\n DataFormatter,\n MOJOUtils\n};\n\nexport default MOJO;\n","/**\n * DataView - Key/Value data display component for MOJO framework\n * Extends View to display object data in a responsive grid layout with intelligent formatting support\n *\n * Features:\n * - Automatic field generation from data with intelligent type inference\n * - Smart DataFormatter pipe chains (e.g., \"date('MMM D, YYYY')|capitalize\")\n * - Contextual formatting based on field names and values\n * - Support for complex pipe chains like \"truncate(100)|capitalize|badge\"\n * - Nested DataView support with type=\"dataview\" for complex objects\n * - Custom format overrides with fluent API\n * - No format collision: custom field.format completely overrides DataView formatting\n *\n * Format Behavior:\n * - No field.format: DataView applies type-based HTML formatting (badges, links, etc.)\n * - With field.format: DataFormatter handles ALL formatting, DataView only escapes HTML\n *\n * Example Usage:\n * ```javascript\n * const dataView = new DataView({\n * data: {\n * name: 'john doe',\n * email: 'john@example.com',\n * createdAt: '2024-01-15T10:30:00Z',\n * price: 99.99,\n * description: 'A very long description that should be truncated...',\n * isActive: true\n * }\n * });\n *\n * // Auto-inferred formats (DataView adds HTML styling):\n * // name: \"truncate(50)|capitalize\"\n * // email: no format → DataView creates mailto: link\n * // createdAt: \"date('MMM D, YYYY')\"\n * // price: \"currency\"\n * // description: \"truncate(200)\"\n * // isActive: no format → DataView creates badge styling\n *\n * // Custom format overrides (DataFormatter handles ALL formatting):\n * dataView\n * .setFieldFormat('name', 'uppercase|truncate(20)') // No DataView HTML styling\n * .setFieldFormat('email', 'email|uppercase') // No mailto: link\n * .setFieldFormat('isActive', 'boolean|uppercase') // No badge styling\n * .setFieldFormats({\n * description: 'truncate(50)|capitalize',\n * createdAt: 'relative'\n * });\n *\n * // Nested DataView for complex objects:\n * dataView.setFieldFormat('permissions', null); // Clear auto-format\n * // Then configure field: { name: 'permissions', type: 'dataview', label: 'User Permissions' }\n * ```\n */\n\nimport View from '@core/View.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass DataView extends View {\n constructor(options = {}) {\n // Extract DataView-specific options\n const {\n data,\n model,\n fields,\n columns,\n responsive,\n showEmptyValues,\n emptyValueText,\n ...viewOptions\n } = options;\n\n // Set default view options\n super({\n tagName: 'div',\n className: 'data-view',\n ...viewOptions\n });\n\n // Core properties\n this.data = data || {};\n this.fields = fields || [];\n this.model = model || null;\n\n // If a model is provided, use it as data source\n if (this.model) {\n this.data = this.model;\n }\n\n // DataView-specific configuration\n this.dataViewOptions = {\n columns: columns || 2,\n responsive: responsive !== false, // Default to true\n showEmptyValues: showEmptyValues || false,\n emptyValueText: emptyValueText || '—',\n rowClass: 'row g-3',\n itemClass: 'data-view-item',\n labelClass: 'data-view-label fw-semibold text-muted small text-uppercase',\n valueClass: 'data-view-value'\n };\n }\n\n /**\n * Lifecycle hook - prepare data and fields before rendering\n */\n async onBeforeRender() {\n\n // Auto-generate fields from data if none provided\n if (this.fields.length === 0 && this.getData()) {\n this.generateFieldsFromData();\n }\n }\n\n /**\n * Override renderTemplate to generate HTML directly\n * @returns {string} Complete HTML string\n */\n async renderTemplate() {\n const items = this.buildItemsHTML();\n\n return `\n <div class=\"${this.dataViewOptions.rowClass}\">\n ${items}\n </div>\n `;\n }\n\n /**\n * Auto-generate field definitions from data object with intelligent type inference\n */\n generateFieldsFromData() {\n const dataObj = this.getData();\n\n if (dataObj && typeof dataObj === 'object') {\n this.fields = Object.keys(dataObj).map(key => {\n const value = dataObj[key];\n const fieldType = this.inferFieldType(value, key);\n const formatter = this.inferFormatter(value, key, fieldType);\n\n return {\n name: key,\n label: this.formatLabel(key),\n type: fieldType,\n format: formatter,\n formatter: formatter // Alias for consistency with TableView\n };\n });\n }\n }\n\n /**\n * Format field name into a readable label\n * @param {string} name - Field name\n * @returns {string} Formatted label\n */\n formatLabel(name) {\n return name\n .replace(/([A-Z])/g, ' $1') // Add space before capital letters\n .replace(/[_-]/g, ' ') // Replace underscores and hyphens with spaces\n .replace(/\\b\\w/g, l => l.toUpperCase()) // Title case\n .trim();\n }\n\n /**\n * Infer field type from value and key with improved intelligence\n * @param {*} value - Field value\n * @param {string} key - Field key\n * @returns {string} Field type\n */\n inferFieldType(value, key = '') {\n if (value === null || value === undefined) return 'text';\n\n const keyLower = key.toLowerCase();\n const type = typeof value;\n\n // Date/time patterns\n if (keyLower.includes('date') || keyLower.includes('time') ||\n keyLower.includes('created') || keyLower.includes('updated') ||\n keyLower.includes('modified') || keyLower.includes('last_login') ||\n keyLower.includes('expires') || keyLower.includes('last_activity')) {\n return 'datetime';\n }\n\n // Email patterns\n if (keyLower.includes('email') || keyLower.includes('mail')) {\n return 'email';\n }\n\n // URL patterns\n if (keyLower.includes('url') || keyLower.includes('link') ||\n keyLower.includes('website') || keyLower.includes('homepage')) {\n return 'url';\n }\n\n // Phone patterns\n if (keyLower.includes('phone') || keyLower.includes('tel') ||\n keyLower.includes('mobile') || keyLower.includes('cell')) {\n return 'phone';\n }\n\n // Currency/price patterns\n if (keyLower.includes('price') || keyLower.includes('cost') ||\n keyLower.includes('amount') || keyLower.includes('fee') ||\n keyLower.includes('salary') || keyLower.includes('revenue')) {\n return 'currency';\n }\n\n // File size patterns\n if (keyLower.includes('size') || keyLower.includes('bytes')) {\n return 'filesize';\n }\n\n // Percentage patterns\n if (keyLower.includes('percent') || keyLower.includes('rate') ||\n keyLower.includes('ratio') && type === 'number') {\n return 'percent';\n }\n\n // Type-based inference\n if (type === 'boolean') return 'boolean';\n if (type === 'number') return 'number';\n\n if (type === 'object') {\n if (Array.isArray(value)) return 'array';\n if (value && value.renditions) return 'file';\n\n // Check if object should be displayed as nested DataView\n if (this.shouldUseDataView(value, keyLower)) {\n return 'dataview';\n }\n\n return 'object';\n }\n\n if (type === 'string') {\n // Pattern matching for strings\n if (value.includes('@') && value.includes('.')) return 'email';\n if (value.match(/^\\d{4}-\\d{2}-\\d{2}/)) return 'date';\n if (value.match(/^https?:\\/\\//)) return 'url';\n if (value.match(/^\\+?[\\d\\s\\-\\(\\)]+$/)) return 'phone';\n }\n\n return 'text';\n }\n\n /**\n * Infer appropriate formatter based on type and context\n * @param {*} value - Field value\n * @param {string} key - Field key\n * @param {string} fieldType - Inferred field type\n * @returns {string|null} Formatter pipe string\n */\n inferFormatter(value, key, fieldType) {\n const keyLower = key.toLowerCase();\n const formatters = [];\n\n switch (fieldType) {\n case 'datetime':\n if (keyLower.includes('time') && !keyLower.includes('date')) {\n formatters.push('time');\n } else if (keyLower.includes('relative') || keyLower.includes('ago') || keyLower.includes('last_')) {\n formatters.push('relative');\n } else if (keyLower.includes('created') || keyLower.includes('updated') || keyLower.includes('modified')) {\n formatters.push('date(\"MMM D, YYYY\")');\n } else {\n formatters.push('date(\"MMMM D, YYYY\")');\n }\n break;\n\n case 'date':\n if (keyLower.includes('birth') || keyLower.includes('dob')) {\n formatters.push('date(\"MMMM D, YYYY\")');\n } else {\n formatters.push('date(\"MMM D, YYYY\")');\n }\n break;\n\n case 'email':\n // Don't apply email formatter - DataView handles mailto: links automatically\n break;\n\n case 'url':\n // Don't apply url formatter - DataView handles clickable links automatically\n break;\n\n case 'phone':\n formatters.push('phone');\n break;\n\n case 'currency':\n formatters.push('currency');\n // Add currency symbol detection if needed\n if (keyLower.includes('eur') || keyLower.includes('euro')) {\n formatters[formatters.length - 1] = 'currency(\"EUR\")';\n } else if (keyLower.includes('gbp') || keyLower.includes('pound')) {\n formatters[formatters.length - 1] = 'currency(\"GBP\")';\n }\n break;\n\n case 'filesize':\n formatters.push('filesize');\n break;\n\n case 'percent':\n formatters.push('percent');\n break;\n\n case 'number':\n // Smart number formatting with pipe chains\n if (typeof value === 'number') {\n if (keyLower.includes('count') || keyLower.includes('total') || keyLower.includes('followers') || keyLower.includes('views')) {\n if (value >= 1000) {\n formatters.push('compact');\n } else {\n formatters.push('number');\n }\n } else if (keyLower.includes('score') || keyLower.includes('rating')) {\n formatters.push('number');\n // Add decimal places for scores\n if (value % 1 !== 0) {\n formatters[formatters.length - 1] = 'number(1)';\n }\n } else if (keyLower.includes('version') || keyLower.includes('id')) {\n // Don't format IDs and versions\n return null;\n } else {\n formatters.push('number');\n }\n }\n break;\n\n case 'boolean':\n // Don't apply boolean formatter - DataView handles badge styling automatically\n break;\n\n case 'text':\n // Smart text formatting with contextual pipe chains\n if (typeof value === 'string') {\n // Handle different text contexts\n if (keyLower.includes('description') || keyLower.includes('content') || keyLower.includes('body')) {\n if (value.length > 200) {\n formatters.push('truncate(200)');\n } else if (value.length > 100) {\n formatters.push('truncate(100)');\n }\n } else if (keyLower.includes('summary') || keyLower.includes('excerpt')) {\n if (value.length > 150) {\n formatters.push('truncate(150)');\n }\n } else if (keyLower.includes('name') || keyLower.includes('title') || keyLower.includes('label')) {\n formatters.push('capitalize');\n if (value.length > 50) {\n formatters.unshift('truncate(50)'); // Truncate first, then capitalize\n }\n } else if (keyLower.includes('slug') || keyLower.includes('handle') || keyLower.includes('username')) {\n formatters.push('slug');\n } else if (keyLower.includes('code') || keyLower.includes('token') || keyLower.includes('key')) {\n // Show codes/tokens with masking if long\n if (value.length > 20) {\n formatters.push('mask');\n }\n } else {\n // Generic text handling\n if (value.length > 100) {\n formatters.push('truncate(100)');\n }\n }\n }\n break;\n\n case 'array':\n case 'object':\n // Don't apply json formatter - DataView handles JSON display automatically\n break;\n\n case 'dataview':\n // Don't apply any formatter - nested DataView handles its own formatting\n break;\n\n default:\n // Handle any missed cases with basic text formatting\n if (typeof value === 'string' && value.length > 100) {\n formatters.push('truncate(100)');\n }\n break;\n }\n\n return formatters.length > 0 ? formatters.join('|') : null;\n }\n\n /**\n * Determine if an object should be displayed as nested DataView vs JSON\n * @param {object} value - Object value to check\n * @param {string} keyLower - Lowercase field key\n * @returns {boolean} True if should use DataView\n */\n shouldUseDataView(value, keyLower) {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false;\n }\n\n // Check for common patterns that benefit from DataView display\n const dataViewPatterns = [\n 'permissions', 'perms', 'access', 'rights',\n 'settings', 'config', 'configuration', 'options',\n 'profile', 'info', 'details', 'data',\n 'metadata', 'meta', 'attributes', 'props',\n 'preferences', 'prefs', 'user_data',\n 'contact', 'address', 'location',\n 'stats', 'statistics', 'metrics', 'counts'\n ];\n\n if (window.utils && window.utils.isObject(value) && value.id) {\n return true;\n }\n\n // Check if key matches common patterns\n const matchesPattern = dataViewPatterns.some(pattern => keyLower.includes(pattern));\n\n if (matchesPattern) {\n // Additional checks to ensure it's suitable for DataView\n const keys = Object.keys(value);\n\n // Good candidates: objects with multiple simple key-value pairs\n if (keys.length >= 2 && keys.length <= 20) {\n const hasComplexNesting = keys.some(k =>\n typeof value[k] === 'object' &&\n value[k] !== null &&\n !Array.isArray(value[k]) &&\n Object.keys(value[k]).length > 3\n );\n\n // Use DataView if not too deeply nested\n if (!hasComplexNesting) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get data object (handles both raw objects and Models)\n * @returns {object} Data object\n */\n getData() {\n if (this.model && this.model.attributes) {\n return { ...this.model.attributes };\n }\n return this.data || {};\n }\n\n /**\n * Get field value with formatting support\n * @param {object} field - Field definition\n * @returns {*} Field value (formatted if specified)\n */\n getFieldValue(field) {\n let value;\n let key = field.name || field.key;\n let formatString = field.format || field.formatter;\n if (!key) return null;\n\n // Check if the name field contains a pipe (e.g., 'is_tor|status_text')\n // This allows inline formatter syntax in the name field\n if (key && key.includes('|')) {\n const parts = key.split('|');\n key = parts[0].trim();\n // If no explicit formatter was set, use the one from the name\n if (!formatString) {\n formatString = parts.slice(1).join('|').trim();\n }\n }\n\n // Get raw value from data source\n if (this.model && typeof this.model.get === 'function') {\n // For models, get raw value first, then apply formatting separately\n // This ensures we don't break pipe chains by concatenating strings\n value = this.model.get(key);\n } else {\n // Plain object access\n value = this.getData()[key];\n }\n\n // Apply formatting using DataFormatter pipe system if specified\n // Support both 'format' and 'formatter' for consistency with TableView\n if (formatString) {\n value = dataFormatter.pipe(value, formatString);\n }\n\n // Handle empty values\n if (value === null || value === undefined || value === '') {\n return this.dataViewOptions.showEmptyValues ? this.dataViewOptions.emptyValueText : null;\n }\n\n // Apply template if provided\n if (field.template) {\n const modelData = this.model ? this.model : this.data;\n return this.renderTemplateString(field.template, modelData);\n }\n\n return value;\n }\n\n /**\n * Render a template string with data from a model or object.\n * Replaces {{key}} and {{nested.key}} placeholders.\n * @param {string} templateString - The template string.\n * @param {object} data - The data object or model.\n * @returns {string} The rendered string.\n */\n renderTemplateString(templateString, data) {\n if (!templateString || !data) {\n return '';\n }\n\n // Regex to find all {{...}} placeholders\n return templateString.replace(/\\{\\{([^}]+)\\}\\}/g, (match, key) => {\n const trimmedKey = key.trim();\n let value;\n\n // Handle potential formatters in the template key\n const parts = trimmedKey.split('|');\n const dataKey = parts[0];\n const formatters = parts.slice(1).join('|');\n\n // Get value from model or plain object\n if (this.model && typeof this.model.get === 'function') {\n value = this.model.get(dataKey);\n } else {\n // Handle nested keys for plain objects\n value = dataKey.split('.').reduce((o, i) => (o ? o[i] : undefined), data);\n }\n\n // Apply formatters if any\n if (formatters) {\n value = dataFormatter.pipe(value, formatters);\n }\n\n return value !== undefined && value !== null ? value : '';\n });\n }\n\n /**\n * Generate column classes based on configuration\n * @param {object} field - Field definition\n * @returns {string} CSS classes\n */\n getColumnClasses(field) {\n let classes = this.getColumnSizeClasses(field);\n if (field.justify == \"right\") {\n classes += ` d-flex justify-content-end`;\n } else if (field.justify == \"center\") {\n classes += ` d-flex justify-content-center`;\n }\n return classes;\n }\n\n /**\n * Generate column size classes based on configuration\n * @param {object} field - Field definition\n * @returns {string} CSS classes\n */\n getColumnSizeClasses(field) {\n // JSON objects and nested DataViews always use full width for better display\n if (field.type === 'array' || field.type === 'object' || field.type === 'dataview') {\n return 'col-12';\n }\n\n const colSize = field.columns || field.colSize || field.cols || Math.floor(12 / this.dataViewOptions.columns);\n\n if (this.dataViewOptions.responsive) {\n // Responsive breakpoints: 1 column on small, configured on larger screens\n return `col-12 col-md-${colSize}`;\n }\n\n return `col-${colSize}`;\n }\n\n /**\n * Build HTML for all data items\n * @returns {string} Items HTML\n */\n buildItemsHTML() {\n return this.fields\n .map(field => this.buildItemHTML(field))\n .filter(Boolean) // Remove empty items\n .join('');\n }\n\n /**\n * Build HTML for a single data item\n * @param {object} field - Field definition\n * @returns {string} Item HTML\n */\n buildItemHTML(field) {\n const value = this.getFieldValue(field);\n\n // Skip fields with no value if showEmptyValues is false\n if (value === null && !this.dataViewOptions.showEmptyValues) {\n return '';\n }\n\n const label = field.label || this.formatLabel(field.name);\n const colClasses = this.getColumnClasses(field);\n\n return `\n <div class=\"${colClasses}\">\n <div class=\"${this.dataViewOptions.itemClass} ${field.className}\" data-field=\"${field.name}\">\n ${this.buildLabelHTML(label, field)}\n ${this.buildValueHTML(value, field)}\n </div>\n </div>\n `;\n }\n\n /**\n * Build label HTML\n * @param {string} label - Label text\n * @param {object} field - Field definition\n * @returns {string} Label HTML\n */\n buildLabelHTML(label, field) {\n const labelClass = field.labelClass || this.dataViewOptions.labelClass;\n return `<div class=\"${labelClass}\">${this.escapeHtml(label)}:</div>`;\n }\n\n /**\n * Build value HTML with type-specific formatting\n * @param {*} value - Field value\n * @param {object} field - Field definition\n * @returns {string} Value HTML\n */\n buildValueHTML(value, field) {\n const valueClass = field.valueClass || this.dataViewOptions.valueClass;\n const displayValue = this.formatDisplayValue(value, field);\n return `<div class=\"${valueClass}\">${displayValue}</div>`;\n }\n\n /**\n * Format value for display with enhanced type handling\n * @param {*} value - Formatted value from DataFormatter (or raw if no format)\n * @param {object} field - Field definition\n * @returns {string} Formatted display value with HTML markup\n */\n formatDisplayValue(value, field) {\n if (value === null || value === undefined) {\n return this.dataViewOptions.emptyValueText;\n }\n\n // If a template was used, the value is already the final HTML. Return it directly.\n if (field.template) {\n return String(value);\n }\n\n // If a custom format is specified, trust the DataFormatter output.\n // Support both 'format' and 'formatter' for consistency with TableView\n const formatString = field.format || field.formatter;\n if (formatString) {\n // If a formatter is explicitly specified, render its output as HTML\n // The DataFormatter is responsible for proper escaping when needed\n return String(value);\n }\n\n // No custom format - apply DataView's default type-specific HTML formatting\n // Get original raw value for special cases\n const rawValue = this.getData()[field.name];\n\n // Handle types that need special HTML presentation (only when no custom format)\n switch (field.type) {\n case 'boolean':\n // Use standard boolean badges (no custom format was applied)\n return rawValue ?\n '<span class=\"badge bg-success\">Yes</span>' :\n '<span class=\"badge bg-secondary\">No</span>';\n\n case 'email':\n // Create clickable email links (no custom format was applied)\n const emailStr = String(value);\n return `<a href=\"mailto:${this.escapeHtml(emailStr)}\" class=\"text-decoration-none\">${this.escapeHtml(emailStr)}</a>`;\n\n case 'url':\n // Create clickable URL links (no custom format was applied)\n const urlStr = String(value);\n return `<a href=\"${this.escapeHtml(urlStr)}\" target=\"_blank\" rel=\"noopener\" class=\"text-decoration-none\">${this.escapeHtml(urlStr)} <i class=\"bi bi-box-arrow-up-right\"></i></a>`;\n\n case 'array':\n case 'object':\n // Display as JSON with special HTML formatting (no custom format was applied)\n return this.formatAsJson(rawValue);\n\n case 'dataview':\n // Create nested DataView for complex objects\n return this.formatAsDataView(rawValue, field);\n\n case 'phone':\n // Create tel: links for phone numbers (no custom format was applied)\n const phoneStr = String(value);\n const telHref = phoneStr.replace(/[^\\d\\+]/g, ''); // Clean for tel: link\n return `<a href=\"tel:${telHref}\" class=\"text-decoration-none\">${this.escapeHtml(phoneStr)}</a>`;\n\n default:\n // For all other types with no custom format, just escape and return\n return this.escapeHtml(String(value));\n }\n }\n\n /**\n * Format object/array values as styled JSON\n * @param {*} value - Object or array value\n * @returns {string} Formatted JSON HTML\n */\n formatAsJson(value) {\n try {\n const jsonString = JSON.stringify(value, null, 2);\n const escapedJson = this.escapeHtml(jsonString);\n const lines = jsonString.split('\\n').length;\n const isLarge = lines > 10 || jsonString.length > 500;\n const uniqueId = `json-${Math.random().toString(36).substr(2, 9)}`;\n\n // Create collapsible JSON display for large objects\n if (isLarge) {\n const preview = JSON.stringify(value).substring(0, 100) + (JSON.stringify(value).length > 100 ? '...' : '');\n const escapedPreview = this.escapeHtml(preview);\n\n return `\n <div class=\"json-container\">\n <div class=\"d-flex align-items-center justify-content-between mb-1\">\n <small class=\"text-muted\">${Array.isArray(value) ? 'Array' : 'Object'} (${lines} lines)</small>\n <div class=\"btn-group btn-group-sm\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-toggle\" data-bs-toggle=\"collapse\" data-bs-target=\"#${uniqueId}\" aria-expanded=\"false\">\n <i class=\"bi bi-eye\"></i> Show\n </button>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-copy\" data-json='${this.escapeHtml(jsonString)}' title=\"Copy JSON\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n </div>\n <div class=\"json-preview bg-light p-2 rounded small border\" style=\"font-family: 'Courier New', monospace;\">\n <code class=\"text-muted\">${escapedPreview}</code>\n </div>\n <div class=\"collapse mt-2\" id=\"${uniqueId}\">\n <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(escapedJson)}</code></pre>\n </div>\n </div>\n `;\n } else {\n // Small objects - show directly with copy button\n return `\n <div class=\"json-container\">\n <div class=\"d-flex align-items-center justify-content-between mb-1\">\n <small class=\"text-muted\">${Array.isArray(value) ? 'Array' : 'Object'}</small>\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm json-copy\" data-json='${this.escapeHtml(jsonString)}' title=\"Copy JSON\">\n <i class=\"bi bi-clipboard\"></i>\n </button>\n </div>\n <pre class=\"json-display bg-light p-2 rounded small mb-0 border\" style=\"white-space: pre-wrap; font-family: 'Courier New', monospace;\"><code>${this.syntaxHighlightJson(escapedJson)}</code></pre>\n </div>\n `;\n }\n } catch (error) {\n // Fallback for objects that can't be stringified\n return `<span class=\"text-muted fst-italic\">[Object: ${typeof value}] - Cannot display as JSON</span>`;\n }\n }\n\n /**\n * Apply basic syntax highlighting to JSON\n * @param {string} json - Escaped JSON string\n * @returns {string} JSON with basic syntax highlighting\n */\n syntaxHighlightJson(json) {\n return json\n .replace(/(\"([^\"\\\\]|\\\\.)*\")\\s*:/g, '<span style=\"color: #0969da;\">$1</span>:') // Keys (blue)\n .replace(/:\\s*(\"([^\"\\\\]|\\\\.)*\")/g, ': <span style=\"color: #0a3069;\">$1</span>') // String values (dark blue)\n .replace(/:\\s*(true|false)/g, ': <span style=\"color: #8250df;\">$1</span>') // Booleans (purple)\n .replace(/:\\s*(null)/g, ': <span style=\"color: #656d76;\">$1</span>') // Null (gray)\n .replace(/:\\s*(-?\\d+\\.?\\d*)/g, ': <span style=\"color: #0550ae;\">$1</span>'); // Numbers (blue)\n }\n\n /**\n * Bind events including JSON interaction handlers\n */\n bindEvents() {\n super.bindEvents();\n\n if (!this.element) return;\n\n // Add click handler for field interactions\n this.element.addEventListener('click', (e) => {\n const fieldElement = e.target.closest('[data-field]');\n if (fieldElement) {\n const fieldName = fieldElement.dataset.field;\n const field = this.fields.find(f => f.name === fieldName);\n this.emit('field:click', { field, fieldName, element: fieldElement, event: e });\n }\n\n // Handle JSON copy button clicks\n if (e.target.closest('.json-copy')) {\n e.preventDefault();\n e.stopPropagation();\n this.handleJsonCopy(e.target.closest('.json-copy'));\n }\n\n // Handle JSON toggle button clicks\n if (e.target.closest('.json-toggle')) {\n this.handleJsonToggle(e.target.closest('.json-toggle'));\n }\n });\n }\n\n /**\n * Handle copying JSON to clipboard\n * @param {HTMLElement} button - Copy button element\n */\n handleJsonCopy(button) {\n const jsonData = button.getAttribute('data-json');\n if (!jsonData) return;\n\n try {\n // Use modern clipboard API if available\n if (navigator.clipboard && window.isSecureContext) {\n navigator.clipboard.writeText(jsonData).then(() => {\n this.showCopyFeedback(button);\n });\n } else {\n // Fallback for older browsers\n const textarea = document.createElement('textarea');\n textarea.value = jsonData;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n this.showCopyFeedback(button);\n }\n } catch (error) {\n console.warn('Failed to copy JSON:', error);\n }\n }\n\n /**\n * Handle JSON toggle button state\n * @param {HTMLElement} button - Toggle button element\n */\n handleJsonToggle(button) {\n const icon = button.querySelector('i');\n const isExpanded = button.getAttribute('aria-expanded') === 'true';\n\n // Update button text and icon\n setTimeout(() => {\n if (isExpanded) {\n icon.className = 'bi bi-eye-slash';\n button.innerHTML = '<i class=\"bi bi-eye-slash\"></i> Hide';\n } else {\n icon.className = 'bi bi-eye';\n button.innerHTML = '<i class=\"bi bi-eye\"></i> Show';\n }\n }, 10);\n }\n\n /**\n * Show visual feedback for successful copy\n * @param {HTMLElement} button - Copy button element\n */\n showCopyFeedback(button) {\n const originalIcon = button.querySelector('i').className;\n const icon = button.querySelector('i');\n\n // Show success state\n icon.className = 'bi bi-check text-success';\n button.classList.add('btn-success');\n button.classList.remove('btn-outline-secondary');\n\n // Reset after 1 second\n setTimeout(() => {\n icon.className = originalIcon;\n button.classList.remove('btn-success');\n button.classList.add('btn-outline-secondary');\n }, 1000);\n }\n\n /**\n * Format complex objects as nested DataView\n * @param {object} value - Object value to display as DataView\n * @param {object} field - Field definition\n * @returns {string} Formatted DataView HTML\n */\n formatAsDataView(value, field) {\n if (!value || typeof value !== 'object') {\n return `<span class=\"text-muted fst-italic\">No data available</span>`;\n }\n\n try {\n // Create nested DataView instance\n const nestedView = new (this.constructor)({\n data: value,\n columns: field.dataViewColumns || 2,\n showEmptyValues: field.showEmptyValues ?? true,\n emptyValueText: field.emptyValueText || 'Not set',\n // Pass any other dataView-specific options from field config\n ...(field.dataViewOptions || {})\n });\n\n nestedView.onInit();\n nestedView.generateFieldsFromData();\n // Generate the nested DataView HTML\n const nestedHtml = nestedView.buildItemsHTML();\n\n // Wrap in a styled container with optional label\n // const labelHtml = field.label ?\n // `<h6 class=\"fw-semibold text-muted mb-3 border-bottom pb-2\">${this.escapeHtml(field.label)}</h6>` :\n // '';\n\n return `\n <div class=\"nested-dataview border rounded p-3 bg-light\">\n <div class=\"${nestedView.dataViewOptions.rowClass}\">\n ${nestedHtml}\n </div>\n </div>\n `;\n } catch (error) {\n console.error('Error creating nested DataView:', error);\n return `<span class=\"text-danger\">Error displaying nested data</span>`;\n }\n }\n\n /**\n * Update the data and re-render\n * @param {object} newData - New data object\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateData(newData) {\n this.data = newData;\n\n // If model is provided, update it instead\n if (this.model && typeof this.model.set === 'function') {\n this.model.set(newData);\n }\n\n // Clear fields to trigger regeneration on next render\n if (this.fields.length > 0 && !this.options.fields) {\n this.fields = [];\n }\n\n await this.render();\n this.emit('data:updated', { data: newData });\n return this;\n }\n\n /**\n * Update field configuration and re-render\n * @param {array} newFields - New field configuration\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateFields(newFields) {\n this.fields = newFields;\n await this.render();\n this.emit('fields:updated', { fields: newFields });\n return this;\n }\n\n /**\n * Update configuration and re-render\n * @param {object} newOptions - New configuration options\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async updateConfig(newOptions) {\n this.dataViewOptions = { ...this.dataViewOptions, ...newOptions };\n await this.render();\n this.emit('config:updated', { options: this.dataViewOptions });\n return this;\n }\n\n /**\n * Refresh data from model if available\n * @returns {Promise<DataView>} Promise resolving to this instance\n */\n async refresh() {\n if (this.model && typeof this.model.fetch === 'function') {\n try {\n await this.model.fetch();\n this.emit('data:refreshed', { model: this.model });\n } catch (error) {\n this.emit('error', { error, message: 'Failed to refresh data' });\n throw error;\n }\n }\n return this;\n }\n\n /**\n * Get current data\n * @returns {object} Current data object\n */\n getCurrentData() {\n return this.getData();\n }\n\n /**\n * Get field definition by name\n * @param {string} name - Field name\n * @returns {object|null} Field definition\n */\n getField(name) {\n return this.fields.find(field => field.name === name) || null;\n }\n\n /**\n * Set custom format for a specific field\n * @param {string} fieldName - Name of the field\n * @param {string} format - Pipe format string (e.g., \"currency|uppercase\")\n * @returns {DataView} This instance for chaining\n */\n setFieldFormat(fieldName, format) {\n const field = this.getField(fieldName);\n if (field) {\n field.format = format;\n field.formatter = format; // Keep both in sync\n } else {\n // Create new field if it doesn't exist\n this.fields.push({\n name: fieldName,\n label: this.formatLabel(fieldName),\n type: this.inferFieldType(this.getData()[fieldName], fieldName),\n format: format,\n formatter: format // Keep both in sync\n });\n }\n return this;\n }\n\n /**\n * Add additional formatter to existing field format pipe chain\n * @param {string} fieldName - Name of the field\n * @param {string} formatter - Formatter to add (e.g., \"uppercase\", \"truncate(50)\")\n * @returns {DataView} This instance for chaining\n */\n addFormatPipe(fieldName, formatter) {\n const field = this.getField(fieldName);\n if (field) {\n if (field.format) {\n field.format += `|${formatter}`;\n field.formatter = field.format; // Keep both in sync\n } else {\n field.format = formatter;\n field.formatter = formatter; // Keep both in sync\n }\n }\n return this;\n }\n\n /**\n * Clear custom format for a field (revert to auto-inferred format)\n * @param {string} fieldName - Name of the field\n * @returns {DataView} This instance for chaining\n */\n clearFieldFormat(fieldName) {\n const field = this.getField(fieldName);\n if (field) {\n const data = this.getData();\n const inferred = this.inferFormatter(data[fieldName], fieldName, field.type);\n field.format = inferred;\n field.formatter = inferred; // Keep both in sync\n }\n return this;\n }\n\n /**\n * Get formatted value for a specific field without rendering\n * @param {string} fieldName - Name of the field\n * @param {*} value - Optional value to format (uses current data if not provided)\n * @returns {*} Formatted value\n */\n getFormattedValue(fieldName, value = null) {\n const field = this.getField(fieldName);\n if (!field) return null;\n\n const targetValue = value !== null ? value : this.getData()[fieldName];\n\n // Support both 'format' and 'formatter' for consistency with TableView\n const formatString = field.format || field.formatter;\n if (formatString && targetValue != null) {\n return dataFormatter.pipe(targetValue, formatString);\n }\n\n return targetValue;\n }\n\n /**\n * Set multiple field formats at once\n * @param {object} formats - Object mapping field names to format strings\n * @returns {DataView} This instance for chaining\n */\n setFieldFormats(formats) {\n Object.entries(formats).forEach(([fieldName, format]) => {\n this.setFieldFormat(fieldName, format);\n });\n return this;\n }\n\n /**\n * Get all current field formats as an object\n * @returns {object} Object mapping field names to their current formats\n */\n getFieldFormats() {\n const formats = {};\n this.fields.forEach(field => {\n const formatString = field.format || field.formatter;\n if (formatString) {\n formats[field.name] = formatString;\n }\n });\n return formats;\n }\n\n /**\n * Set up model event listeners if model is provided\n */\n onInit() {\n super.onInit();\n\n // Listen for model changes\n if (this.model && typeof this.model.on === 'function') {\n this.model.on('change', () => {\n this.render();\n });\n }\n }\n\n /**\n * Static factory method\n * @param {object} options - DataView options\n * @returns {DataView} New DataView instance\n */\n static create(options = {}) {\n return new DataView(options);\n }\n}\n\nexport default DataView;\n"],"names":["Router","options","pattern","pageName","path","replace","state","trigger","queryParams","_event","routePath","route","publicUrl","match","params","name","index","input","pathPart","queryPart","urlParams","key","value","error","currentUrl","url","regexPattern","page","query","route1","route2","pageName1","pageName2","EventBus","event","callback","eventName","data","listeners","promises","resolve","result","regularCount","onceCount","regularEvents","onceEvents","max","namespace","prefixEvent","middleware","originalEmit","finalData","originalEvent","timeout","reject","timeoutId","cleanup","listener","enable","eventNames","stats","count","durationMs","durationMinutes","limit","a","b","Rest","storageKey","storedDuid","e","c","r","config","oldTrackDevice","type","interceptor","baseURL","endpoint","status","searchParams","v","queryString","request","processedRequest","response","responseData","contentType","jsonData","errorInfo","method","fetchOptions","signals","errorResponse","mockResponse","contentDisposition","filename","filenameMatch","reader","stream","controller","pump","done","blob","downloadUrl","file","xhr","files","additionalData","formData","token","rest","WebApp","routeInfo","info","container","PageClass","pageInfo","constructorOptions","pageOptions","fromRouter","force","pageInstance","oldPage","notFoundPage","deniedPage","fullPath","message","Dialog$2","opts","title","handleVisibilityChange","wasFocused","handleFocus","handleBlur","unsafe","updates","oldState","ComponentClass","ModelClass","pages","systemPages","component","GENERIC_AVATAR_SVG","DataFormatter","str","map","m","num","fn","text","escaped","defaults","urlRegex","prefix","href","emailRegex","email","mode","escapedText","showText","buttonHtml","lang","language","content","formatter","args","pipeString","context","currentValue","pipe","pipes","tokens","s","parsed","parenMatch","argsString","colonMatch","current","inQuotes","quoteChar","depth","i","char","contextValue","MOJOUtils","format","date","year","month","day","tokenPattern","hours","replacements","sortedKeys","dateFormat","timeFormat","dateStr","timeStr","locale","timeZone","getTzAbbr","abbr","tzPart","p","tz2","initials","w","parts","get","pt","y4","M2","D2","H2","m2","s2","M","D","H","hNum","A","monthLong","monthShort","weekdayLong","weekdayShort","dateTokens","timeTokens","replaceTokens","fmt","startValue","endValue","endVal","startStr","endStr","short","diffMs","absDiffMs","diffSecs","diffMins","diffHours","diffDays","isFuture","years","suffix","months","weeks","dateOnly","decimals","symbol","centsStr","sign","dollars","cents","formatted","adjustedCents","multiply","percent","binary","bytes","units","divisor","size","unitIndex","decimalPlaces","suffixOnly","j","k","abs","addend","num1","num2","subtrahend","multiplier","all","search","replacement","flags","searchStr","regexLike","rxFlags","length","halfSize","front","back","separator","word","showLast","masked","visible","subject","body","className","link","phone","newWindow","item","badgeType","lowered","icons","colors","noIcons","noText","defaultIcons","defaultColors","iconClass","color","icon","trueText","falseText","colored","mapping","yesIcon","noIcon","rendition","classes","alt","sizeClasses","sizeStyle","allClasses","placement","html","displayValue","tooltipText","preferredRendition","availableRenditions","defaultValue","compareValue","trueResult","falseResult","singular","plural","includeCount","array","conjunction","moreText","items","hasMore","remaining","unit","precision","ms","absMs","u","unitName","searchTerm","escapedTerm","regex","uppercase","withPrefix","hexStr","toHexFromBytes","hex","n","byte","indent","dataFormatter","field","parenDepth","pipeIndex","keys","remainingPath","obj","clonedObj","target","sources","source","func","wait","later","inThrottle","timestamp","randomStr","entityMap","password","feedback","details","score","commonPatterns","strength","lowercase","numbers","specialChars","charPool","requiredChars","rootContext","DataWrapper","objectToString","isArray","isFunction","isObject","getDataFormatter","escapeHtml","string","Scanner","re","Context","view","parentContext","cacheKey","actualName","shouldIterate","val","cache","intermediateValue","names","lookupHit","hasProperty","primitiveHasOwnProperty","propName","primitive","Writer","template","tags","openingTag","closingTag","scanner","start","chr","openingTagRe","escapeRegExp","closingTagRe","closingCurlyRe","openSection","squashedTokens","lastToken","nestedTokens","collector","sections","sectionToken","section","partials","renderCache","originalTemplate","buffer","childTokens","itemContext","itemResult","pushedContext","defaultWriter","Mustache","EventDelegate","rootEl","onClick","actionEl","action","navEl","onChange","el","handled","onInput","debounceMs","timerId","timer","onKeyDown","changeKeys","onSubmit","form","element","dropdown","dropdownBtn","tooltip","cap","specific","generic","passThru","changeHandler","root","child","EventEmitter","onceWrapper","View","model","isDiff","id","tpl","childView","idOrView","err","newData","rerender","now","allowMount","cleanId","ch","templateContent","templatePath","paramsAttr","app","router","base","apps","evt","div","tooltipTriggerEl","theme","customClass","trimmedClass","textarea","originalClass","msg","Page","user","radio","meta","descMeta","metaEl","errorDiv","successDiv","definition","DefinedPage","Model","previousAttributes","hasChanged","attrKey","attrValue","attr","finalValue","oldValue","topLevelKey","attrTarget","instanceTarget","currentKey","finalKey","requestKey","abortController","isNew","changed","rules","rulesArray","rule","Collection","tmp","additionalParams","fetchParams","newParams","autoFetch","existingModel","downloadParams","baseName","rangeSuffix","acceptHeader","hasStart","hasEnd","sanitize","modelsData","addedModels","modelData","existingIndex","models","modelsToRemove","removedModels","removedModel","previousModels","criteria","results","thisArg","comparator","aVal","bVal","collection","FormPlugins","plugin","renderer","hook","builder","formView","fieldEl","fieldConfig","fieldName","FormBuilder","groupField","fieldsHTML","buttonsHTML","groupsInRow","totalColumns","nextGroup","nextColumns","shouldWrap","cols","groupsHTML","group","fieldsInRow","nextField","f","columns","fields","groupClass","titleClass","responsive","colClasses","fallback","colClass","fieldClass","fieldHTML","customRenderer","custom","passwordUsage","inferredAutocomplete","attributes","min","step","baseField","attrs","searchField","hexType","allowPrefix","originalMinLength","originalMaxLength","minLength","maxLength","placeholder","help","hexField","label","required","disabled","readonly","inputClass","fieldValue","fieldId","enhancedContext","rows","_cols","rawValue","formattedValue","multiple","searchable","end","generatedOptions","stepValue","autoOptions","optionsHTML","option","selected","searchInput","maxHeight","opt","optValue","optLabel","checked","sizeClass","inline","radioId","radioValue","radioLabel","accept","allowDrop","dropZoneId","previewId","sizeMap","sizeConfig","imageUrl","preferredSizes","renditionName","buttonAction","level","headerLevel","headerId","headerClass","button","submitLabel","resetLabel","maxTags","allowDuplicates","_Collection","labelField","valueField","maxItems","emptyFetch","requiresActiveGroup","collectionParams","excludeIds","ignoreIds","showSelectAll","enableSearch","searchPlaceholder","searchDebounce","displayFormat","startName","endName","startDate","endDate","outputFormat","startFieldName","endFieldName","selectedValues","templateData","selectedValue","isActive","variant","allowCustom","tabs","navClass","contentClass","safe","nav","t","panes","increment","FileDropMixin","originalOnAfterRender","originalOnBeforeDestroy","dropZone","filesToProcess","validation","isDragActive","dragOverClass","dragActiveClass","errors","fileType","acceptedTypes","acceptedType","category","sizes","applyFileDropMixin","ViewClass","TagInputView","trimTags","containerClass","tagClass","viewOptions","tagsHTML","hiddenInputHTML","inputHTML","tag","_element","tagIndex","lastChar","tagText","newIndex","cleanTag","removedTag","tagValue","oldTags","tagsInput","newTags","tagString","tagElements","tagsContainer","hiddenInput","tagCountElement","errorElement","enabled","btn","CollectionDropdownView","labelValue","CollectionSelectView","extraParams","selectedModel","itemCount","focusedItem","newValue","newLabel","fieldPath","SearchView","searchValue","ListItemsView","itemContentTemplate","selectAllBtn","deselectAllBtn","CollectionMultiSelectView","selectedCount","totalCount","unselectedCount","ignoreId","itemData","shiftKey","shouldSelect","values","ids","MultiSelectItemsView","checkbox","valueSet","MultiSelectDropdown","selectedOption","dropdownButton","dropdownInstance","DatePicker","autoApply","css","script","inputId","inputType","inputValue","picker","d","minDate","maxDate","DateRangePicker","startPlaceholder","endPlaceholder","_action","oldStartDate","oldEndDate","startInput","endInput","dateObj","jsDate","monthNames","monthIndex","startFormatted","endFormatted","combinedInput","fieldNameInput","ComboInput","optionsList","showDescription","minChars","maxSuggestions","onSelect","isHighlighted","isSelected","labelMatch","valueMatch","descMatch","aLabelExact","bLabelExact","aLabelStarts","bLabelStarts","highlightedElement","toggle","ComboBox","searchText","highlightedItem","FormView","formConfig","fileHandling","autosaveModelField","tab","tabField","fieldElement","visitFields","inputs","changeAction","configData","tagInput","TagInput","multiselect","combobox","collectionSelect","CollectionSelect","collectionMultiSelect","CollectionMultiSelect","datePicker","dateRangePicker","comboInput","changes","fieldNames","resp","errorMsg","wasPopulating","originalValue","statusManager","FieldStatusManager","newBool","fileInput","colorInput","targetId","htmlContent","buttonGroup","checkboxes","previewUrl","ImageCropView","croppedBlob","croppedFile","valueDisplay","select","_validation","dataTransfer","searchInFields","found","base64Files","newModelData","modelValue","checkedRadio","radioOption","newDefaults","currentData","originalData","changedData","hasChanges","allKeys","resolveDotNotation","fieldType","newStr","originalStr","isValid","firstInvalid","tabPane","tabId","bsTab","loading","submitBtn","alert","alertDiv","_fieldName","preview","newConfig","destroyPromises","capsHandler","caps","isHidden","len","hasLower","hasUpper","hasDigit","hasSymbol","variety","bar","barClass","capsOn","warn","tagName","parentLabel","parent","indicator","ind","indicatorClass","errorSpan","delay","FormPage","Dialog","backdrops","openDialogs","sortedDialogs","backdrop","backdropZIndex","targetContainer","busyZIndex","msgElement","modalId","headerContent","footer","dialogClasses","headerActions","menuItems","triggerIcon","buttonClass","menuItemsHtml","dataAttrs","filteredItems","buttonsHtml","dismissAttr","actionAttr","idAttr","disabledAttr","_container","modalBody","modalDialog","modalContent","originalStyles","contentRect","viewportMargin","maxWidth","optimalWidth","optimalHeight","heightExceedsMax","stackIndex","newZIndex","firstInput","focusedElement","relatedTarget","bodyEl","titleEl","dialog","code","highlightedCode","prismClass","codeStyles","originalHtml","previewHtml","iframe","iframeDoc","centered","buttons","rejectOnDismiss","dialogOptions","resolved","btnElement","buttonConfig","defaultResolveValue","valueToResolve","submitText","cancelText","FormView$1","errmsg","showEmptyValues","emptyValueText","closeText","DataView","DataView$1","dataView","closeBtn","handleClose","ProgressView","progressInfo","ListViewItem","ListView","collectionOrClass","itemsContainer","itemView","modelId","TableRow","visibility","validBreakpoints","column","cellClass","responsiveClasses","editableClass","combinedClasses","cellContent","cellAction","menuItem","itemClass","cell","columnKey","col","cellElement","contentSpan","editor","originalContent","editorContainer","optionsArray","optionsHtml","saveBtn","cancelBtn","selectCell","LOOKUPS","parseFilterKey","paramKey","possibleLookup","formatFilterDisplay","lookup","lookupDef","valueStr","formattedValues","displayText","TableView","tableOptions","totals","totalColumnIndex","safeKey","fieldKey","sum","numValue","batchPanelTop","batchPanelBottom","__fs","__val","dropdownItems","handler","permissions","iconHtml","labelHtml","btnClass","allFilters","activeFilters","filter","activeClass","pillsHTML","searchInputs","hasSearch","filterEntries","pills","clearAllButton","headerCells","sortable","currentSort","sortIcon","sortDropdown","footerCells","actionsHTML","viewInstance","handleFullscreenChange","sort","direction","newSort","desc","sortField","currentSortField","currentSortDir","isSorted","dropdownMenu","ascItem","descItem","noneItem","isCurrentlyAllSelected","selectAllCell","total","startEl","endEl","totalEl","pageSizeSelect","paginationContainer","currentPage","totalPages","prevPage","nextPage","neighbors","visibleSet","last","rawPage","newSize","allParams","filters","processedKeys","filterDef","inKey","filterConfig","filterKey","additionalFilter","newFilterValue","normalizeDateValue","valueArray","formResult","panel","countEl","allSelected","someSelected","batchAction","selectedItems","buttonIndex","getGlobalMOJO","attachLite","MOJO","dataObj","l","keyLower","formatters","dataViewPatterns","formatString","templateString","trimmedKey","dataKey","o","colSize","valueClass","emailStr","urlStr","phoneStr","jsonString","escapedJson","lines","isLarge","uniqueId","escapedPreview","json","isExpanded","originalIcon","nestedView","nestedHtml","newFields","newOptions","inferred","targetValue","formats"],"mappings":"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"}