web-mojo 2.2.16 → 2.2.18

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 (44) 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 +71 -7
  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 +3 -3
  9. package/dist/chunks/{ChatView-BqViL7e-.js → ChatView-BJbSvaqi.js} +2 -2
  10. package/dist/chunks/{ChatView-BqViL7e-.js.map → ChatView-BJbSvaqi.js.map} +1 -1
  11. package/dist/chunks/{ChatView-DMC-gH-t.js → ChatView-BdRd1iSt.js} +18 -3
  12. package/dist/chunks/{ChatView-DMC-gH-t.js.map → ChatView-BdRd1iSt.js.map} +1 -1
  13. package/dist/chunks/{Dialog-DgYEebtR.js → Dialog-BGsJJdvU.js} +3 -3
  14. package/dist/chunks/{Dialog-DgYEebtR.js.map → Dialog-BGsJJdvU.js.map} +1 -1
  15. package/dist/chunks/{Dialog-VyLukswR.js → Dialog-Bib7iGr7.js} +2 -2
  16. package/dist/chunks/{Dialog-VyLukswR.js.map → Dialog-Bib7iGr7.js.map} +1 -1
  17. package/dist/chunks/{FormView-DCI29KlM.js → FormView-72VleKRt.js} +2 -2
  18. package/dist/chunks/{FormView-DCI29KlM.js.map → FormView-72VleKRt.js.map} +1 -1
  19. package/dist/chunks/{FormView-GO8yahxE.js → FormView-D_VTD2EY.js} +16 -7
  20. package/dist/chunks/FormView-D_VTD2EY.js.map +1 -0
  21. package/dist/chunks/{MetricsMiniChartWidget-JLmDm3sm.js → MetricsMiniChartWidget-CMplnXWP.js} +2 -2
  22. package/dist/chunks/{MetricsMiniChartWidget-JLmDm3sm.js.map → MetricsMiniChartWidget-CMplnXWP.js.map} +1 -1
  23. package/dist/chunks/{MetricsMiniChartWidget-XTU45k_e.js → MetricsMiniChartWidget-DfW7iH5B.js} +2 -2
  24. package/dist/chunks/{MetricsMiniChartWidget-XTU45k_e.js.map → MetricsMiniChartWidget-DfW7iH5B.js.map} +1 -1
  25. package/dist/chunks/{PDFViewer-DbPamhVj.js → PDFViewer-BYKfJGbN.js} +2 -2
  26. package/dist/chunks/{PDFViewer-DbPamhVj.js.map → PDFViewer-BYKfJGbN.js.map} +1 -1
  27. package/dist/chunks/{PDFViewer-DMpN-d6N.js → PDFViewer-C9hCxlyT.js} +2 -2
  28. package/dist/chunks/{PDFViewer-DMpN-d6N.js.map → PDFViewer-C9hCxlyT.js.map} +1 -1
  29. package/dist/chunks/{TokenManager-A0zSdQ6i.js → TokenManager-BPJHPKPk.js} +2 -2
  30. package/dist/chunks/{TokenManager-A0zSdQ6i.js.map → TokenManager-BPJHPKPk.js.map} +1 -1
  31. package/dist/chunks/{TokenManager-DH4-kElJ.js → TokenManager-DhnHDZlt.js} +2 -2
  32. package/dist/chunks/{TokenManager-DH4-kElJ.js.map → TokenManager-DhnHDZlt.js.map} +1 -1
  33. package/dist/chunks/{version-BpfLBsbA.js → version-DBGAt8po.js} +2 -2
  34. package/dist/chunks/{version-BpfLBsbA.js.map → version-DBGAt8po.js.map} +1 -1
  35. package/dist/chunks/{version-iQsUQBiz.js → version-WosnMiWn.js} +4 -4
  36. package/dist/chunks/{version-iQsUQBiz.js.map → version-WosnMiWn.js.map} +1 -1
  37. package/dist/docit.cjs.js +1 -1
  38. package/dist/docit.es.js +3 -3
  39. package/dist/index.cjs.js +1 -1
  40. package/dist/index.es.js +7 -7
  41. package/dist/lightbox.cjs.js +1 -1
  42. package/dist/lightbox.es.js +4 -4
  43. package/package.json +1 -1
  44. package/dist/chunks/FormView-GO8yahxE.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog-VyLukswR.js","sources":["../../src/core/Router.js","../../src/core/utils/EventBus.js","../../src/core/WebApp.js","../../src/core/views/feedback/Dialog.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 * 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 this.events.emit('page:showing', {\n page: pageInstance,\n pageName: pageInstance.pageName,\n params,\n query,\n fromRouter\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 // 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 * 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.closeButton = options.closeButton !== undefined ? options.closeButton : true;\n this.contextMenu = options.contextMenu || null;\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 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 (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.bodyView) {\n await this.bodyView.destroy();\n }\n if (this.footerView) {\n await this.footerView.destroy();\n }\n\n await super.onBeforeDestroy();\n\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 * 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"],"names":["Router","constructor","options","this","defaultRoute","routes","currentRoute","eventEmitter","boundHandlePopState","handlePopState","bind","start","window","addEventListener","handleCurrentLocation","stop","removeEventListener","addRoute","pattern","pageName","push","normalizePattern","regex","patternToRegex","paramNames","extractParamNames","navigate","path","replace","state","trigger","queryParams","parseInput","handleRouteChange","back","history","forward","getCurrentRoute","getCurrentPath","parseCurrentUrl","buildPublicUrl","_event","allowPopState","console","warn","routePath","route","matchRoute","publicUrl","emit","params","query","match","forEach","name","index","input","includes","pathPart","queryPart","split","urlParams","URLSearchParams","has","get","key","value","startsWith","substring","error","location","search","set","Object","entries","String","toString","updateBrowserUrl","currentUrl","URL","origin","pathname","searchParams","url","replaceState","pushState","regexPattern","RegExp","map","updateUrl","buildUrl","page","doRoutesMatch","route1","route2","pageName1","pageName2","EventBus","listeners","onceListeners","maxListeners","debugMode","eventStats","on","event","callback","Error","Array","isArray","eventName","length","once","off","indexOf","splice","data","updateEventStats","stopPropagation","preventDefault","emitError","emitAsync","promises","Promise","resolve","all","removeAllListeners","listenerCount","eventNames","regularEvents","keys","onceEvents","Set","setMaxListeners","max","namespace","prefixEvent","use","middleware","originalEmit","result","finalData","call","originalEvent","setTimeout","waitFor","timeout","reject","timeoutId","listener","clearTimeout","debug","enable","getStats","stats","totalEvents","totalListeners","events","emissions","count","firstEmission","Date","now","lastEmission","getEventStats","avgEmissionsPerMinute","calculateEmissionRate","durationMs","durationMinutes","Math","round","resetStats","getTopEvents","limit","rate","sort","a","b","slice","debugInfo","WebApp","config","initPluginRegistry","version","container","layoutType","layout","layoutConfig","sidebar","sidebarConfig","topbar","topbarConfig","containerId","pageContainer","basePath","routerMode","router","mode","base","session","navigation","currentPage","previousPage","loading","rest","api","configure","async","routeInfo","showPage","fromRouter","MOJO","setupFocusTracking","pageCache","Map","pageClasses","componentClasses","modelClasses","isStarted","matchUUID","app","__app__","setupPageContainer","validateDefaultRoute","setupRouter","showError","info","_show404","document","querySelector","innerHTML","registerPage","PageClass","constructorOptions","getPage","getPagePermissions","permissions","pageInfo","getOrCreatePage","force","pageInstance","oldPage","canEnter","_showDeniedPage","_exitOldPage","onParams","onEnter","syncUrl","render","message","originalPage","notFoundPage","setInfo","deniedPage","setDeniedPage","onExit","unmount","fullPath","queryString","navigateToDefault","getCurrentPage","getPageContainer","Dialog","then","Dialog$1","default","alert","size","class","e","type","showSuccess","showInfo","showWarning","showNotification","showLoading","opts","showBusy","hideLoading","hideBusy","showModelForm","showForm","showDialog","confirm","title","isFocused","hidden","handleVisibilityChange","wasFocused","handleFocus","handleBlur","_focusHandlers","visibilitychange","focus","blur","setupErrorHandling","reason","escapeHtml","unsafe","getState","setState","updates","oldState","assign","newState","registerComponent","ComponentClass","getComponent","registerModel","ModelClass","getModel","setupRest","destroy","pages","from","values","allSettled","clear","buildPagePath","toLowerCase","findFallbackPage","systemPages","create","plugins","registerPlugin","component","View","static","backdrop","modal","getFullscreenAwareZIndex","_baseZIndex","fixAllBackdropStacking","backdrops","querySelectorAll","openDialogs","_openDialogs","sortedDialogs","_dialogZIndex","backdropZIndex","style","zIndex","targetContainer","body","parentNode","appendChild","updateAllBackdropStacking","_busyCounter","_busyTimeout","_busyIndicator","busyZIndex","createElement","className","msgElement","textContent","classList","add","remove","modalId","id","super","tagName","fade","attributes","tabindex","labelledBy","describedBy","titleId","centered","scrollable","autoSize","keyboard","header","headerContent","closeButton","contextMenu","content","bodyView","bodyClass","noBodyPadding","minWidth","minHeight","maxHeight","maxWidthPercent","maxHeightPercent","_processBodyContent","footer","footerView","footerClass","_processFooterContent","buttons","onShow","onShown","onHide","onHidden","onHidePrevented","autoShow","relatedTarget","addChild","bodyPromise","footerPromise","getTemplate","dialogClasses","join","buildHeader","buildBody","buildFooter","headerActions","items","buildContextMenu","menuItems","filterContextMenuItems","triggerIcon","icon","buttonClass","item","label","href","target","action","dataAttrs","filter","filteredItems","getApp","user","activeUser","globalApp","hasPermission","replaceById","buttonsHtml","btn","dismissAttr","dismiss","actionAttr","idAttr","disabledAttr","disabled","text","mount","_container","mounted","destroyed","element","onBeforeMount","bindEvents","onAfterMount","view","onAfterRender","Prism","highlightAllUnder","setupAutoSizing","modalBody","bootstrap","Modal","setAttribute","bindBootstrapEvents","show","applyAutoSizing","isShown","modalDialog","modalContent","originalStyles","dialogMaxWidth","maxWidth","dialogWidth","width","contentWidth","contentMaxHeight","hadScrollableClass","contains","offsetHeight","contentRect","getBoundingClientRect","viewportMargin","min","innerWidth","innerHeight","optimalWidth","ceil","optimalHeight","height","heightExceedsMax","autoSizedWidth","autoSizedHeight","_originalStyles","resetAutoSizing","stackIndex","newZIndex","_backdropZIndex","dialog","firstInput","focusedElement","previousFocus","activeElement","lastDialog","hide","toggle","dispose","handleUpdate","setContent","removeChild","bodyEl","setTitle","titleEl","setLoading","replaceChildren","onBeforeDestroy","showCode","formatCode","code","language","navigator","clipboard","writeText","showCopySuccess","err","highlightedCode","languages","highlight","prismClass","trim","highlightCodeBlocks","originalHtml","arguments","rejectOnDismiss","dialogOptions","resolved","btnElement","buttonConfig","defaultResolveValue","handler","button","valueToResolve","titleClass","cancelText","confirmText","confirmClass","prompt","inputId","defaultValue","inputType","placeholder","select","getModal","formConfig","submitText","formView","FormView","require","n","FormView$1","fileHandling","defaults","model","fields","submitButton","resetButton","validate","autoSave","saveModel","success","toast","formData","getFormData","focusFirstError","handleSubmit","errmsg","showData","columns","responsive","showEmptyValues","emptyValueText","closeText","dataView","DataView","closeBtn","showConfirm"],"mappings":"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"}
1
+ {"version":3,"file":"Dialog-Bib7iGr7.js","sources":["../../src/core/Router.js","../../src/core/utils/EventBus.js","../../src/core/WebApp.js","../../src/core/views/feedback/Dialog.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 * 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 this.events.emit('page:showing', {\n page: pageInstance,\n pageName: pageInstance.pageName,\n params,\n query,\n fromRouter\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 // 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 * 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.closeButton = options.closeButton !== undefined ? options.closeButton : true;\n this.contextMenu = options.contextMenu || null;\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 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 (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.bodyView) {\n await this.bodyView.destroy();\n }\n if (this.footerView) {\n await this.footerView.destroy();\n }\n\n await super.onBeforeDestroy();\n\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 * 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"],"names":["Router","constructor","options","this","defaultRoute","routes","currentRoute","eventEmitter","boundHandlePopState","handlePopState","bind","start","window","addEventListener","handleCurrentLocation","stop","removeEventListener","addRoute","pattern","pageName","push","normalizePattern","regex","patternToRegex","paramNames","extractParamNames","navigate","path","replace","state","trigger","queryParams","parseInput","handleRouteChange","back","history","forward","getCurrentRoute","getCurrentPath","parseCurrentUrl","buildPublicUrl","_event","allowPopState","console","warn","routePath","route","matchRoute","publicUrl","emit","params","query","match","forEach","name","index","input","includes","pathPart","queryPart","split","urlParams","URLSearchParams","has","get","key","value","startsWith","substring","error","location","search","set","Object","entries","String","toString","updateBrowserUrl","currentUrl","URL","origin","pathname","searchParams","url","replaceState","pushState","regexPattern","RegExp","map","updateUrl","buildUrl","page","doRoutesMatch","route1","route2","pageName1","pageName2","EventBus","listeners","onceListeners","maxListeners","debugMode","eventStats","on","event","callback","Error","Array","isArray","eventName","length","once","off","indexOf","splice","data","updateEventStats","stopPropagation","preventDefault","emitError","emitAsync","promises","Promise","resolve","all","removeAllListeners","listenerCount","eventNames","regularEvents","keys","onceEvents","Set","setMaxListeners","max","namespace","prefixEvent","use","middleware","originalEmit","result","finalData","call","originalEvent","setTimeout","waitFor","timeout","reject","timeoutId","listener","clearTimeout","debug","enable","getStats","stats","totalEvents","totalListeners","events","emissions","count","firstEmission","Date","now","lastEmission","getEventStats","avgEmissionsPerMinute","calculateEmissionRate","durationMs","durationMinutes","Math","round","resetStats","getTopEvents","limit","rate","sort","a","b","slice","debugInfo","WebApp","config","initPluginRegistry","version","container","layoutType","layout","layoutConfig","sidebar","sidebarConfig","topbar","topbarConfig","containerId","pageContainer","basePath","routerMode","router","mode","base","session","navigation","currentPage","previousPage","loading","rest","api","configure","async","routeInfo","showPage","fromRouter","MOJO","setupFocusTracking","pageCache","Map","pageClasses","componentClasses","modelClasses","isStarted","matchUUID","app","__app__","setupPageContainer","validateDefaultRoute","setupRouter","showError","info","_show404","document","querySelector","innerHTML","registerPage","PageClass","constructorOptions","getPage","getPagePermissions","permissions","pageInfo","getOrCreatePage","force","pageInstance","oldPage","canEnter","_showDeniedPage","_exitOldPage","onParams","onEnter","syncUrl","render","message","originalPage","notFoundPage","setInfo","deniedPage","setDeniedPage","onExit","unmount","fullPath","queryString","navigateToDefault","getCurrentPage","getPageContainer","Dialog","then","Dialog$1","default","alert","size","class","e","type","showSuccess","showInfo","showWarning","showNotification","showLoading","opts","showBusy","hideLoading","hideBusy","showModelForm","showForm","showDialog","confirm","title","isFocused","hidden","handleVisibilityChange","wasFocused","handleFocus","handleBlur","_focusHandlers","visibilitychange","focus","blur","setupErrorHandling","reason","escapeHtml","unsafe","getState","setState","updates","oldState","assign","newState","registerComponent","ComponentClass","getComponent","registerModel","ModelClass","getModel","setupRest","destroy","pages","from","values","allSettled","clear","buildPagePath","toLowerCase","findFallbackPage","systemPages","create","plugins","registerPlugin","component","View","static","backdrop","modal","getFullscreenAwareZIndex","_baseZIndex","fixAllBackdropStacking","backdrops","querySelectorAll","openDialogs","_openDialogs","sortedDialogs","_dialogZIndex","backdropZIndex","style","zIndex","targetContainer","body","parentNode","appendChild","updateAllBackdropStacking","_busyCounter","_busyTimeout","_busyIndicator","busyZIndex","createElement","className","msgElement","textContent","classList","add","remove","modalId","id","super","tagName","fade","attributes","tabindex","labelledBy","describedBy","titleId","centered","scrollable","autoSize","keyboard","header","headerContent","closeButton","contextMenu","content","bodyView","bodyClass","noBodyPadding","minWidth","minHeight","maxHeight","maxWidthPercent","maxHeightPercent","_processBodyContent","footer","footerView","footerClass","_processFooterContent","buttons","onShow","onShown","onHide","onHidden","onHidePrevented","autoShow","relatedTarget","addChild","bodyPromise","footerPromise","getTemplate","dialogClasses","join","buildHeader","buildBody","buildFooter","headerActions","items","buildContextMenu","menuItems","filterContextMenuItems","triggerIcon","icon","buttonClass","item","label","href","target","action","dataAttrs","filter","filteredItems","getApp","user","activeUser","globalApp","hasPermission","replaceById","buttonsHtml","btn","dismissAttr","dismiss","actionAttr","idAttr","disabledAttr","disabled","text","mount","_container","mounted","destroyed","element","onBeforeMount","bindEvents","onAfterMount","view","onAfterRender","Prism","highlightAllUnder","setupAutoSizing","modalBody","bootstrap","Modal","setAttribute","bindBootstrapEvents","show","applyAutoSizing","isShown","modalDialog","modalContent","originalStyles","dialogMaxWidth","maxWidth","dialogWidth","width","contentWidth","contentMaxHeight","hadScrollableClass","contains","offsetHeight","contentRect","getBoundingClientRect","viewportMargin","min","innerWidth","innerHeight","optimalWidth","ceil","optimalHeight","height","heightExceedsMax","autoSizedWidth","autoSizedHeight","_originalStyles","resetAutoSizing","stackIndex","newZIndex","_backdropZIndex","dialog","firstInput","focusedElement","previousFocus","activeElement","lastDialog","hide","toggle","dispose","handleUpdate","setContent","removeChild","bodyEl","setTitle","titleEl","setLoading","replaceChildren","onBeforeDestroy","showCode","formatCode","code","language","navigator","clipboard","writeText","showCopySuccess","err","highlightedCode","languages","highlight","prismClass","trim","highlightCodeBlocks","originalHtml","arguments","rejectOnDismiss","dialogOptions","resolved","btnElement","buttonConfig","defaultResolveValue","handler","button","valueToResolve","titleClass","cancelText","confirmText","confirmClass","prompt","inputId","defaultValue","inputType","placeholder","select","getModal","formConfig","submitText","formView","FormView","require","n","FormView$1","fileHandling","defaults","model","fields","submitButton","resetButton","validate","autoSave","saveModel","success","toast","formData","getFormData","focusFirstError","handleSubmit","errmsg","showData","columns","responsive","showEmptyValues","emptyValueText","closeText","dataView","DataView","closeBtn","showConfirm"],"mappings":"mDAAA,MAAMA,OACJ,WAAAC,CAAYC,EAAU,IACpBC,KAAKC,aAAeF,EAAQE,cAAgB,OAC5CD,KAAKE,OAAS,GACdF,KAAKG,aAAe,KACpBH,KAAKI,aAAeL,EAAQK,cAAgB,KAE5CJ,KAAKK,oBAAsBL,KAAKM,eAAeC,KAAKP,KACtD,CAEA,KAAAQ,GAEEC,OAAOC,iBAAiB,WAAYV,KAAKK,qBAGzCL,KAAKW,uBACP,CAEA,IAAAC,GACEH,OAAOI,oBAAoB,WAAYb,KAAKK,oBAC9C,CAEA,QAAAS,CAASC,EAASC,GAChBhB,KAAKE,OAAOe,KAAK,CACfF,QAASf,KAAKkB,iBAAiBH,GAC/BI,MAAOnB,KAAKoB,eAAeL,GAC3BC,WACAK,WAAYrB,KAAKsB,kBAAkBP,IAEvC,CAEA,cAAMQ,CAASC,EAAMzB,EAAU,IAC7B,MAAM0B,QAAEA,GAAU,EAAAC,MAAOA,EAAQ,KAAAC,QAAMA,GAAU,GAAS5B,GAGpDiB,SAAEA,EAAAY,YAAUA,GAAgB5B,KAAK6B,WAAWL,GAM9CG,SACI3B,KAAK8B,kBAAkBd,EAAUY,EAE3C,CAGA,IAAAG,GACEtB,OAAOuB,QAAQD,MACjB,CAEA,OAAAE,GACExB,OAAOuB,QAAQC,SACjB,CAGA,eAAAC,GACE,OAAOlC,KAAKG,YACd,CAEA,cAAAgC,GACE,MAAMnB,SAAEA,EAAAY,YAAUA,GAAgB5B,KAAKoC,kBACvC,OAAOpC,KAAKqC,eAAerB,EAAUY,EACvC,CAGA,cAAAtB,CAAegC,GACTtC,KAAKuC,cACLvC,KAAKW,wBAEL6B,QAAQC,KAAK,+BAEnB,CAEA,2BAAM9B,GACJ,MAAMK,SAAEA,EAAAY,YAAUA,GAAgB5B,KAAKoC,wBACjCpC,KAAK8B,kBAAkBd,EAAUY,EACzC,CAEA,uBAAME,CAAkBd,EAAUY,GAEhC,MAAMc,EAAY,IAAM1B,EAClB2B,EAAQ3C,KAAK4C,WAAWF,GAGxBG,EAAY7C,KAAKqC,eAAerB,EAAUY,GAGhD,OAAKe,GASL3C,KAAKG,aAAewC,EAGhB3C,KAAKI,cACPJ,KAAKI,aAAa0C,KAAK,gBAAiB,CACtCtB,KAAMqB,EACN7B,SAAU2B,EAAM3B,SAChB+B,OAAQJ,EAAMI,OACdC,MAAOpB,EACPe,UAKGA,IArBD3C,KAAKI,cACPJ,KAAKI,aAAa0C,KAAK,iBAAkB,CAAEtB,KAAMqB,IAE5C,KAmBX,CAEA,UAAAD,CAAWpB,GACT,IAAA,MAAWmB,KAAS3C,KAAKE,OAAQ,CAC/B,MAAM+C,EAAQzB,EAAKyB,MAAMN,EAAMxB,OAC/B,GAAI8B,EAAO,CACT,MAAMF,EAAS,CAAA,EAKf,OAJAJ,EAAMtB,WAAW6B,QAAQ,CAACC,EAAMC,KAC9BL,EAAOI,GAAQF,EAAMG,EAAQ,KAGxB,IACFT,EACHI,SACAvB,OAEJ,CACF,CACA,OAAO,IACT,CAGA,UAAAK,CAAWwB,GACT,IAAIrC,EAAWhB,KAAKC,aAChB2B,EAAc,CAAA,EAElB,IAAKyB,EACH,MAAO,CAAErC,WAAUY,eAGrB,IAEE,GAAIyB,EAAMC,SAAS,KAAM,CACvB,MAAOC,EAAUC,GAAaH,EAAMI,MAAM,IAAK,GACzCC,EAAY,IAAIC,gBAAgBH,GAGtC,GAAIE,EAAUE,IAAI,QAAS,CACzB5C,EAAW0C,EAAUG,IAAI,SAAW7D,KAAKC,aAGzC,IAAA,MAAY6D,EAAKC,KAAUL,EACb,SAARI,IACFlC,EAAYkC,GAAOC,EAGzB,KAAO,CAGH/C,EADEuC,EAASS,WAAW,KACXT,EAASU,UAAU,IAAMjE,KAAKC,aAE9BsD,GAAYvD,KAAKC,aAI9B,IAAA,MAAY6D,EAAKC,KAAUL,EACzB9B,EAAYkC,GAAOC,CAEvB,CACF,MAEE/C,EAFSqC,EAAMW,WAAW,KAEfX,EAAMY,UAAU,IAAMjE,KAAKC,aAG3BoD,CAEf,OAASa,GACP1B,QAAQC,KAAK,yBAA0BY,EAAOa,GAC9ClD,EAAWhB,KAAKC,aAChB2B,EAAc,CAAA,CAChB,CAEA,MAAO,CAAEZ,WAAUY,cACrB,CAGA,eAAAQ,GACE,MAAMsB,EAAY,IAAIC,gBAAgBlD,OAAO0D,SAASC,QAChDpD,EAAW0C,EAAUG,IAAI,SAAW7D,KAAKC,aAEzC2B,EAAc,CAAA,EACpB,IAAA,MAAYkC,EAAKC,KAAUL,EACb,SAARI,IACFlC,EAAYkC,GAAOC,GAIvB,MAAO,CAAE/C,WAAUY,cACrB,CAGA,cAAAS,CAAerB,EAAUY,EAAc,IACrC,MAAM8B,EAAY,IAAIC,gBAStB,OARAD,EAAUW,IAAI,OAAQrD,GAEtBsD,OAAOC,QAAQ3C,GAAasB,QAAQ,EAAEY,EAAKC,MACrCA,SAAmD,KAAVA,GAC3CL,EAAUW,IAAIP,EAAKU,OAAOT,MAIvB,IAAML,EAAUe,UACzB,CAGA,gBAAAC,CAAiB1D,EAAUY,EAAaH,EAASC,GAC/C,MAAMiD,EAAa,IAAIC,IAAInE,OAAO0D,SAASU,OAASpE,OAAO0D,SAASW,UACpEH,EAAWI,aAAaV,IAAI,OAAQrD,GAGpCsD,OAAOC,QAAQ3C,GAAasB,QAAQ,EAAEY,EAAKC,MACrCA,SAAmD,KAAVA,GAC3CY,EAAWI,aAAaV,IAAIP,EAAKU,OAAOT,MAI5C,MAAMiB,EAAML,EAAWF,WAEnBhD,EACFhB,OAAOuB,QAAQiD,aAAavD,EAAO,GAAIsD,GAEvCvE,OAAOuB,QAAQkD,UAAUxD,EAAO,GAAIsD,EAExC,CAGA,cAAA5D,CAAeL,GAGb,IAAIoE,EAAepE,EAChBU,QAAQ,2BAA4B,QACpCA,QAAQ,iBAAkB,iBAC1BA,QAAQ,YAAa,WAExB,OAAO,IAAI2D,OAAO,IAAID,KACxB,CAEA,iBAAA7D,CAAkBP,GAEhB,OADgBA,EAAQkC,MAAM,kBAAoB,IACnCoC,IAAIpC,GAASA,EAAMxB,QAAQ,QAAS,IACrD,CAEA,gBAAAP,CAAiBH,GACf,OAAOA,EAAQiD,WAAW,KAAOjD,EAAU,IAAIA,GACjD,CAOA,SAAAuE,CAAUvC,EAAS,GAAIhD,EAAU,CAAA,GAC/B,MAAM0B,QAAEA,GAAU,GAAU1B,GAEtBiB,SAAEA,GAAahB,KAAKoC,kBAC1BpC,KAAK0E,iBAAiB1D,EAAU+B,EAAQtB,EAC1C,CAKA,QAAA8D,CAASC,EAAMxC,EAAQ,IACrB,OAAOhD,KAAKqC,eAAemD,EAAMxC,EACnC,CAQA,aAAAyC,CAAcC,EAAQC,GACpB,IAAKD,IAAWC,EAAQ,OAAO,EAG/B,MAAQ3E,SAAU4E,GAAc5F,KAAK6B,WAAW6D,IACxC1E,SAAU6E,GAAc7F,KAAK6B,WAAW8D,GAGhD,OAAOC,IAAcC,CACvB,EC/RF,MAAMC,SACJ,WAAAhG,GACEE,KAAK+F,UAAY,CAAA,EACjB/F,KAAKgG,cAAgB,CAAA,EACrBhG,KAAKiG,aAAe,IACpBjG,KAAKkG,WAAY,EACjBlG,KAAKmG,WAAa,EACpB,CAQA,EAAAC,CAAGC,EAAOC,GACR,GAAwB,mBAAbA,EACT,MAAM,IAAIC,MAAM,+BAIlB,OAAIC,MAAMC,QAAQJ,IAChBA,EAAMnD,QAAQwD,GAAa1G,KAAKoG,GAAGM,EAAWJ,IACvCtG,OAGJA,KAAK+F,UAAUM,KAClBrG,KAAK+F,UAAUM,GAAS,IAItBrG,KAAK+F,UAAUM,GAAOM,QAAU3G,KAAKiG,cACvCzD,QAAQC,KAAK,kBAAkBzC,KAAKiG,qCAAqCI,KAG3ErG,KAAK+F,UAAUM,GAAOpF,KAAKqF,GACpBtG,KACT,CAQA,IAAA4G,CAAKP,EAAOC,GACV,GAAwB,mBAAbA,EACT,MAAM,IAAIC,MAAM,+BAIlB,OAAIC,MAAMC,QAAQJ,IAChBA,EAAMnD,QAAQwD,GAAa1G,KAAK4G,KAAKF,EAAWJ,IACzCtG,OAGJA,KAAKgG,cAAcK,KACtBrG,KAAKgG,cAAcK,GAAS,IAG9BrG,KAAKgG,cAAcK,GAAOpF,KAAKqF,GACxBtG,KACT,CAQA,GAAA6G,CAAIR,EAAOC,GAET,GAAIE,MAAMC,QAAQJ,GAEhB,OADAA,EAAMnD,QAAQwD,GAAa1G,KAAK6G,IAAIH,EAAWJ,IACxCtG,KAGT,IAAKsG,EAIH,cAFOtG,KAAK+F,UAAUM,UACfrG,KAAKgG,cAAcK,GACnBrG,KAIT,GAAIA,KAAK+F,UAAUM,GAAQ,CACzB,MAAMjD,EAAQpD,KAAK+F,UAAUM,GAAOS,QAAQR,IAC9B,IAAVlD,IACFpD,KAAK+F,UAAUM,GAAOU,OAAO3D,EAAO,GAGC,IAAjCpD,KAAK+F,UAAUM,GAAOM,eACjB3G,KAAK+F,UAAUM,GAG5B,CAGA,GAAIrG,KAAKgG,cAAcK,GAAQ,CAC7B,MAAMjD,EAAQpD,KAAKgG,cAAcK,GAAOS,QAAQR,IAClC,IAAVlD,IACFpD,KAAKgG,cAAcK,GAAOU,OAAO3D,EAAO,GAGC,IAArCpD,KAAKgG,cAAcK,GAAOM,eACrB3G,KAAKgG,cAAcK,GAGhC,CAEA,OAAOrG,IACT,CAQA,IAAA8C,CAAKuD,EAAOW,GAEVhH,KAAKiH,iBAAiBZ,GAGlBrG,KAAKkG,UAIT,MAAMH,EAAY,GAgDlB,OA7CI/F,KAAK+F,UAAUM,IACjBN,EAAU9E,QAAQjB,KAAK+F,UAAUM,IAI/BrG,KAAK+F,UAAU,MACjBA,EAAU9E,QAAQjB,KAAK+F,UAAU,MAI/B/F,KAAKgG,cAAcK,KACrBN,EAAU9E,QAAQjB,KAAKgG,cAAcK,WAC9BrG,KAAKgG,cAAcK,IAIxBrG,KAAKgG,cAAc,OACrBD,EAAU9E,QAAQjB,KAAKgG,cAAc,aAC9BhG,KAAKgG,cAAc,MAIxBhG,KAAKkG,WAAaH,EAAUY,OAAS,GACbZ,EAAUY,OAItCZ,EAAU7C,QAAQoD,IAChB,IACMA,EAASU,EAAMX,KACXA,EAAMa,iBACNb,EAAMa,kBAENb,EAAMc,gBACNd,EAAMc,iBAGhB,OAASjD,GACP1B,QAAQ0B,MAAM,gCAAgCmC,MAAWnC,GAGzDlE,KAAKoH,UAAUlD,EAAOmC,EAAOC,EAC/B,IAGKtG,IACT,CAQA,eAAMqH,CAAUhB,EAAOW,GACrB,MAAMjB,EAAY,GAGd/F,KAAK+F,UAAUM,IACjBN,EAAU9E,QAAQjB,KAAK+F,UAAUM,IAI/BrG,KAAK+F,UAAU,MACjBA,EAAU9E,QAAQjB,KAAK+F,UAAU,MAI/B/F,KAAKgG,cAAcK,KACrBN,EAAU9E,QAAQjB,KAAKgG,cAAcK,WAC9BrG,KAAKgG,cAAcK,IAIxBrG,KAAKgG,cAAc,OACrBD,EAAU9E,QAAQjB,KAAKgG,cAAc,aAC9BhG,KAAKgG,cAAc,MAI5B,MAAMsB,EAAWvB,EAAUV,IAAIiB,GACtB,IAAIiB,QAAQC,IACjB,IAEEA,EADelB,EAASU,EAAMX,GAEhC,OAASnC,GACP1B,QAAQ0B,MAAM,sCAAsCmC,MAAWnC,GAC/DlE,KAAKoH,UAAUlD,EAAOmC,EAAOC,GAC7BkB,GACF,KAKJ,aADMD,QAAQE,IAAIH,GACXtH,IACT,CAMA,kBAAA0H,GAGE,OAFA1H,KAAK+F,UAAY,CAAA,EACjB/F,KAAKgG,cAAgB,CAAA,EACdhG,IACT,CAOA,aAAA2H,CAActB,GAGZ,OAFqBrG,KAAK+F,UAAUM,GAASrG,KAAK+F,UAAUM,GAAOM,OAAS,IAC1D3G,KAAKgG,cAAcK,GAASrG,KAAKgG,cAAcK,GAAOM,OAAS,EAEnF,CAMA,UAAAiB,GACE,MAAMC,EAAgBvD,OAAOwD,KAAK9H,KAAK+F,WACjCgC,EAAazD,OAAOwD,KAAK9H,KAAKgG,eACpC,MAAO,mBAAI,IAAIgC,IAAI,IAAIH,KAAkBE,IAC3C,CAOA,eAAAE,CAAgBC,GACd,GAAmB,iBAARA,GAAoBA,EAAM,EACnC,MAAM,IAAI3B,MAAM,+CAGlB,OADAvG,KAAKiG,aAAeiC,EACblI,IACT,CAOA,SAAAmI,CAAUA,GACR,MAAMC,EAAe/B,GAAU,GAAG8B,KAAa9B,IAE/C,MAAO,CACLD,GAAI,CAACC,EAAOC,IAAatG,KAAKoG,GAAGgC,EAAY/B,GAAQC,GACrDM,KAAM,CAACP,EAAOC,IAAatG,KAAK4G,KAAKwB,EAAY/B,GAAQC,GACzDO,IAAK,CAACR,EAAOC,IAAatG,KAAK6G,IAAIuB,EAAY/B,GAAQC,GACvDxD,KAAM,CAACuD,EAAOW,IAAShH,KAAK8C,KAAKsF,EAAY/B,GAAQW,GACrDK,UAAW,CAAChB,EAAOW,IAAShH,KAAKqH,UAAUe,EAAY/B,GAAQW,GAEnE,CAOA,GAAAqB,CAAIC,GACF,GAA0B,mBAAfA,EACT,MAAM,IAAI/B,MAAM,iCAGlB,MAAMgC,EAAevI,KAAK8C,KAqB1B,OAnBA9C,KAAK8C,KAAO,CAACuD,EAAOW,KAClB,IACE,MAAMwB,EAASF,EAAWjC,EAAOW,GAGjC,IAAe,IAAXwB,EACF,OAAOxI,KAIT,MAAMyI,OAAuB,IAAXD,EAAuBA,EAASxB,EAClD,OAAOuB,EAAaG,KAAK1I,KAAMqG,EAAOoC,EAExC,OAASvE,GAEP,OADA1B,QAAQ0B,MAAM,6BAA8BA,GACrCqE,EAAaG,KAAK1I,KAAMqG,EAAOW,EACxC,GAGKhH,IACT,CASA,SAAAoH,CAAUlD,EAAOyE,EAAerC,GAER,UAAlBqC,GAIJC,WAAW,KACT5I,KAAK8C,KAAK,QAAS,CACjBoB,QACAyE,gBACArC,SAAUA,EAAS7B,cAEpB,EACL,CAQA,OAAAoE,CAAQxC,EAAOyC,EAAU,MACvB,OAAO,IAAIvB,QAAQ,CAACC,EAASuB,KAC3B,IAAIC,EAAY,KAEhB,MAMMC,EAAYjC,IALZgC,GACFE,aAAaF,GAMfxB,EAAQR,IAGVhH,KAAK4G,KAAKP,EAAO4C,GAEbH,IACFE,EAAYJ,WAAW,KACrB5I,KAAK6G,IAAIR,EAAO4C,GAChBF,EAAO,IAAIxC,MAAM,8BAA8BF,OAC9CyC,KAGT,CAOA,KAAAK,CAAMC,GAAS,GAOb,OANApJ,KAAKkG,UAAYkD,EAMVpJ,IACT,CAMA,QAAAqJ,GACE,MAAMzB,EAAa5H,KAAK4H,aAClB0B,EAAQ,CACZC,YAAa3B,EAAWjB,OACxB6C,eAAgB,EAChBC,OAAQ,CAAA,EACRC,UAAW,IAAK1J,KAAKmG,aASvB,OANAyB,EAAW1E,QAAQmD,IACjB,MAAMsD,EAAQ3J,KAAK2H,cAActB,GACjCiD,EAAMG,OAAOpD,GAASsD,EACtBL,EAAME,gBAAkBG,IAGnBL,CACT,CAOA,gBAAArC,CAAiBZ,GACVrG,KAAKmG,WAAWE,KACnBrG,KAAKmG,WAAWE,GAAS,CACvBsD,MAAO,EACPC,cAAeC,KAAKC,MACpBC,aAAc,OAIlB/J,KAAKmG,WAAWE,GAAOsD,QACvB3J,KAAKmG,WAAWE,GAAO0D,aAAeF,KAAKC,KAC7C,CAOA,aAAAE,CAAc3D,GACZ,MAAMiD,EAAQtJ,KAAKmG,WAAWE,GAC9B,OAAKiD,EAIE,IACFA,EACH3B,cAAe3H,KAAK2H,cAActB,GAClC4D,sBAAuBjK,KAAKkK,sBAAsBZ,IAN3C,IAQX,CAQA,qBAAAY,CAAsBZ,GACpB,IAAKA,EAAMM,gBAAkBN,EAAMS,aACjC,OAAO,EAGT,MAAMI,EAAab,EAAMS,aAAeT,EAAMM,cAC9C,GAAmB,IAAfO,EACF,OAAO,EAGT,MAAMC,EAAkBD,MACxB,OAAOE,KAAKC,MAAOhB,EAAMK,MAAQS,EAAmB,KAAO,GAC7D,CAMA,UAAAG,GAEE,OADAvK,KAAKmG,WAAa,CAAA,EACXnG,IACT,CAOA,YAAAwK,CAAaC,EAAQ,IACnB,OAAOnG,OAAOC,QAAQvE,KAAKmG,YACxBd,IAAI,EAAEgB,EAAOiD,MAAK,CACjBjD,QACAsD,MAAOL,EAAMK,MACbe,KAAM1K,KAAKkK,sBAAsBZ,GACjCvD,UAAW/F,KAAK2H,cAActB,MAE/BsE,KAAK,CAACC,EAAGC,IAAMA,EAAElB,MAAQiB,EAAEjB,OAC3BmB,MAAM,EAAGL,EACd,CAMA,SAAAM,GAE6B/K,KAAKkG,UACFlG,KAAKiG,aAEnC,MAAMqD,EAAQtJ,KAAKqJ,WASnB,OAR6BC,EAAMC,YACHD,EAAME,eAElClF,OAAOwD,KAAK9H,KAAKmG,YAAYQ,OAAS,GACb3G,KAAKwK,aAAa,GAIxCxK,IACT,EC9eF,MAAMgL,OACF,WAAAlL,CAAYmL,EAAS,IAEjBjL,KAAKiL,OAASA,EACdjL,KAAKkL,qBAGLlL,KAAKmD,KAAO8H,EAAO9H,MAAQ,WAC3BnD,KAAKmL,QAAUF,EAAOE,SAAW,QACjCnL,KAAKmJ,MAAQ8B,EAAO9B,QAAS,EAC7BnJ,KAAKoL,UAAYH,EAAOG,WAAa,OAGrCpL,KAAKqL,WAAaJ,EAAOK,QAAU,SACnCtL,KAAKuL,aAAeN,EAAOM,cAAgB,CAAA,EACvCN,EAAOO,UACPxL,KAAKuL,aAAaE,cAAgBR,EAAOO,SAEzCP,EAAOS,SACP1L,KAAKuL,aAAaI,aAAeV,EAAOS,QAE5C1L,KAAKsL,OAAS,KAEdtL,KAAKuL,aAAaK,YAAc5L,KAAKoL,WAAapL,KAAK4L,aAAe,OACtE5L,KAAK6L,cAAgBZ,EAAOY,eAAiB,kBAC7C7L,KAAK8L,SAAWb,EAAOa,UAAY,GAGnC9L,KAAK+L,WAAad,EAAOc,YAAcd,EAAOe,QAAQC,MAAQ,QAC9DjM,KAAK8L,SAAWb,EAAOa,UAAYb,EAAOe,QAAQE,MAAQ,GAC1DlM,KAAKC,aAAegL,EAAOhL,cAAgB,OAG3CD,KAAKmM,QAAUlB,EAAOkB,SAAW,CAAA,EAGjCnM,KAAKgM,OAAS,KAGdhM,KAAKoM,WAAanB,EAAOmB,YAAc,CAAA,EAGvCpM,KAAK0B,MAAQ,CACT2K,YAAa,KACbC,aAAc,KACdC,SAAS,GAIbvM,KAAKyJ,OAAS,IAAI3D,SAClB9F,KAAKwM,KAAOA,EAAAA,KACRvB,EAAOwB,KACPzM,KAAKwM,KAAKE,UAAUzB,EAAOwB,KAI/BzM,KAAKgM,OAAS,IAAInM,OAAO,CACrBoM,KAA0B,UAApBjM,KAAK+L,WAAyB,SAAW/L,KAAK+L,WACpDD,SAAU9L,KAAK8L,SACf7L,aAAcD,KAAKC,aACnBG,aAAcJ,KAAKyJ,SAIvBzJ,KAAKyJ,OAAOrD,GAAG,gBAAiBuG,MAAOC,IACnC,MAAM5L,SAAEA,EAAA+B,OAAUA,EAAAC,MAAQA,GAAU4J,QAC9B5M,KAAK6M,SAAS7L,EAAUgC,EAAOD,EAAQ,CAAE+J,YAAY,MAKzC,oBAAXrM,SACPA,OAAOsM,KAAOtM,OAAOsM,MAAQ,CAAA,EAC7BtM,OAAOsM,KAAKf,OAAShM,KAAKgM,QAI9BhM,KAAKgN,qBAGLhN,KAAKiN,6BAAgBC,IACrBlN,KAAKmN,+BAAkBD,IACvBlN,KAAKoN,oCAAuBF,IAC5BlN,KAAKqN,gCAAmBH,IAGxBlN,KAAKqM,YAAc,KAGnBrM,KAAKsN,WAAY,EAEb7M,OAAO8M,UACP9M,OAAOA,OAAO8M,WAAavN,KACpBS,OAAOsM,KACdtM,OAAOsM,KAAKS,IAAMxN,KAElBS,OAAOgN,QAAUzN,IAEzB,CAKA,WAAMQ,GACF,GAAIR,KAAKsN,UACL9K,QAAQC,KAAK,+BAIjB,IAIIzC,KAAK0N,qBAGL1N,KAAK2N,6BAGC3N,KAAK4N,cAGX5N,KAAKsN,WAAY,EAEjBtN,KAAKgM,OAAOzJ,eAAgB,EAG5BvC,KAAKyJ,OAAO3G,KAAK,YAAa,CAAE0K,IAAKxN,MACzC,OAASkE,GAGL,MAFA1B,QAAQ0B,MAAM,mBAAmBlE,KAAKmD,QAASe,GAC/ClE,KAAK6N,UAAU,+BACT3J,CACV,CACJ,CAKA,iBAAM0J,GACG5N,KAAKgM,QAKVhM,KAAKyJ,OAAOrD,GAAG,iBAAkBuG,MAAOmB,IACpCtL,QAAQC,KAAK,oBAAoBqL,EAAKtM,QAEtCxB,KAAK+N,SAASD,EAAKtM,QAIvBxB,KAAKgM,OAAOxL,QACqBR,KAAK+L,YAZlCvJ,QAAQ0B,MAAM,yBAatB,CAKA,kBAAAwJ,GAEI,MAAMtC,EAAsC,iBAAnBpL,KAAKoL,UACxB4C,SAASC,cAAcjO,KAAKoL,WAC5BpL,KAAKoL,UAEPA,IAAcA,EAAU6C,cAAc,qBACtC7C,EAAU8C,UAAY,mCAG1BlO,KAAK6L,cAAgB,iBACzB,CAMA,YAAAsC,CAAanN,EAAUoN,EAAWrO,EAAU,CAAA,GAExC,GAAwB,iBAAbiB,IAA0BA,EAEjC,OADAwB,QAAQ0B,MAAM,qDACPlE,KAGX,GAAyB,mBAAdoO,EAEP,OADA5L,QAAQ0B,MAAM,0DACPlE,KAWX,GARKD,EAAQ6L,cAAa7L,EAAQ6L,YAAc5L,KAAK6L,eAErD7L,KAAKmN,YAAY9I,IAAIrD,EAAU,CAC3BoN,YACAC,mBAAoBtO,IAIpBC,KAAKgM,OAAQ,CAEb,IAAIrJ,EAAQ5C,EAAQ4C,OAAS,IAAI3B,IAG5B2B,EAAMqB,WAAW,OAClBrB,EAAQ,IAAIA,KAIhB5C,EAAQ4C,MAAQA,EAGhB3C,KAAKgM,OAAOlL,SAAS6B,EAAO3B,EAGhC,CAEA,OAAOhB,IACX,CAKA,OAAAsO,CAAQtN,GACJ,OAAOhB,KAAKiN,UAAUpJ,IAAI7C,EAC9B,CAEA,kBAAAuN,CAAmBvN,GACf,GAAIhB,KAAKiN,UAAUrJ,IAAI5C,GACnB,OAAOhB,KAAKiN,UAAUpJ,IAAI7C,GAAUwN,YAExC,MAAMC,EAAWzO,KAAKmN,YAAYtJ,IAAI7C,GACtC,IAAKyN,EAAU,OAAO,KACtB,MAAML,UAAEA,EAAAC,mBAAWA,GAAuBI,EAC1C,OAAKJ,EACEA,EAAmBG,YADM,IAEpC,CAKA,eAAAE,CAAgB1N,GAEZ,GAAIhB,KAAKiN,UAAUrJ,IAAI5C,GACnB,OAAOhB,KAAKiN,UAAUpJ,IAAI7C,GAI9B,MAAMyN,EAAWzO,KAAKmN,YAAYtJ,IAAI7C,GACtC,IAAKyN,EAED,OADAjM,QAAQ0B,MAAM,wBAAwBlD,KAC/B,KAGX,MAAMoN,UAAEA,EAAAC,mBAAWA,GAAuBI,EAE1C,IAEI,MAMMjJ,EAAO,IAAI4I,EANG,CAChBpN,cACGqN,EACHb,IAAKxN,OAcT,OARIqO,EAAmB1L,QACnB6C,EAAK7C,MAAQ0L,EAAmB1L,OAIpC3C,KAAKiN,UAAU5I,IAAIrD,EAAUwE,GAEwBA,EAAK7C,MACnD6C,CAEX,OAAStB,GAEL,OADA1B,QAAQ0B,MAAM,yBAAyBlD,KAAakD,GAC7C,IACX,CACJ,CAYA,cAAM2I,CAASrH,EAAMxC,EAAQ,CAAA,EAAID,EAAS,CAAA,EAAIhD,EAAU,IACpD,MAAM+M,WAAEA,GAAa,EAAArL,QAAOA,GAAU,EAAAkN,MAAOA,GAAQ,GAAU5O,EAE/D,IAEI,IAAI6O,EAAc5N,EACE,iBAATwE,GACPxE,EAAWwE,EACXoJ,EAAe5O,KAAK0O,gBAAgBlJ,IAC7BA,GAAwB,iBAATA,IACtBoJ,EAAepJ,EACfxE,EAAWwE,EAAKxE,UAGpBhB,KAAKyJ,OAAO3G,KAAK,eAAgB,CAC7B0C,KAAMoJ,EACN5N,SAAU4N,EAAa5N,SACvB+B,SACAC,QACA8J,eAGJ,MAAM+B,EAAU7O,KAAKqM,YACrB,IAAKuC,EAED,YADA5O,KAAK+N,SAAS/M,EAAU+B,EAAQC,EAAO8J,GAK3C,IAAK8B,EAAaE,WAEd,YADA9O,KAAK+O,gBAAgBH,EAAc7L,EAAQC,EAAO8J,GAKlD+B,GAAWA,IAAYD,SACjB5O,KAAKgP,aAAaH,SAKtBD,EAAaK,SAASlM,EAAQC,GAGhC6L,IAAYD,SACNA,EAAaM,UAGvBN,EAAaO,UAGbnP,KAAKyJ,OAAO3G,KAAK,YAAa,CAC1B0C,KAAMoJ,EACN5N,SAAU4N,EAAa5N,SACvB+B,SACAC,QACA8J,qBAIE8B,EAAaQ,SACnBpP,KAAKqM,YAAcuC,EAEYA,EAAa5N,QAEhD,OAASkD,GACL1B,QAAQ0B,MAAM,qBAAsBA,GACpClE,KAAK6N,UAAU,wBAAwB3J,EAAMmL,WAGhC,UAAT7J,SACMxF,KAAK6M,SAAS,QAAS,GAAI,CAAE3I,QAAOoL,aAAc9J,GAAQ,CAAEsH,cAE1E,CACJ,CAEA,cAAMiB,CAAS/M,EAAU+B,EAAQC,EAAO8J,GACpC,MAAMyC,EAAevP,KAAK0O,gBAAgB,OACrCa,IACDA,EAAaC,SACbD,EAAaC,QAAQxO,SAEnBhB,KAAKgP,aAAahP,KAAKqM,mBACvBkD,EAAaH,SACnBpP,KAAKqM,YAAckD,EACnBvP,KAAKyJ,OAAO3G,KAAK,WAAY,CACzB0C,KAAM,KACNxE,WACA+B,SACAC,QACA8J,eAER,CAEA,qBAAMiC,CAAgBH,EAAc7L,EAAQC,EAAO8J,GAC/C,MAAM2C,EAAazP,KAAK0O,gBAAgB,UACpCe,EAAWC,eACXD,EAAWC,cAAcd,SAEvB5O,KAAKgP,aAAahP,KAAKqM,mBACvBoD,EAAWL,SACjBpP,KAAKqM,YAAcoD,EACnBzP,KAAKyJ,OAAO3G,KAAK,cAAe,CAC5B0C,KAAMoJ,EACN5N,SAAU4N,EAAa5N,SACvB+B,SACAC,QACA8J,cAER,CAEA,kBAAMkC,CAAaH,GACf,GAAKA,EACL,UACUA,EAAQc,eACRd,EAAQe,UACd5P,KAAKyJ,OAAO3G,KAAK,YAAa,CAAE0C,KAAMqJ,GAC1C,OAAS3K,GACL1B,QAAQ0B,MAAM,sBAAsB2K,EAAQ7N,YAAakD,EAC7D,CACJ,CAQA,cAAM3C,CAASoB,EAAOK,EAAQ,CAAA,EAAIjD,EAAU,CAAA,GACxC,IAAKC,KAAKgM,OAEN,YADAxJ,QAAQ0B,MAAM,0BAOlB,IAAI2L,EAAWlN,EACf,GAAI2B,OAAOwD,KAAK9E,GAAO2D,OAAS,EAAG,CAC/B,MAAMmJ,EAAc,IAAInM,gBAAgBX,GAAOyB,WAC/CoL,IAAalN,EAAMW,SAAS,KAAO,IAAM,KAAOwM,CACpD,CAGA,aAAa9P,KAAKgM,OAAOzK,SAASsO,EAAU9P,EAChD,CAKA,uBAAMgQ,CAAkBhQ,EAAU,IAC9B,aAAaC,KAAK6M,SAAS7M,KAAKC,aAAc,CAAA,EAAI,CAAA,EAAIF,EAC1D,CASA,IAAAgC,GACQ/B,KAAKgM,OACLhM,KAAKgM,OAAOjK,OAEZS,QAAQC,KAAK,yBAErB,CAKA,OAAAR,GACQjC,KAAKgM,OACLhM,KAAKgM,OAAO/J,UAEZO,QAAQC,KAAK,yBAErB,CAKA,cAAAuN,GACI,OAAOhQ,KAAKqM,WAChB,CAKA,gBAAA4D,GAEI,OAAIjQ,KAAKsL,QAAUtL,KAAKsL,OAAO2E,iBACpBjQ,KAAKsL,OAAO2E,mBAIyB,iBAAvBjQ,KAAK6L,cACxBmC,SAASC,cAAcjO,KAAK6L,eAC5B7L,KAAK6L,aAGf,CAKA,eAAMgC,CAAUwB,GACZ,IACI,MAAMa,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,cACtDH,EAAOI,MAAMjB,EAAS,QAAS,CAAEkB,KAAM,KAAMC,MAAO,eAC9D,OAASC,GACLzQ,KAAKyJ,OAAO3G,KAAK,eAAgB,CAAEuM,UAASqB,KAAM,UAC5B,oBAAXjQ,QAA0BA,QAAQ+B,SACzCA,QAAQ0B,MAAM,+BAAgCuM,GAE5B,oBAAXhQ,QACP6P,MAAM,UAAUjB,IAExB,CACJ,CAKA,iBAAMsB,CAAYtB,GACd,IACI,MAAMa,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,cACtDH,EAAOI,MAAMjB,EAAS,UAAW,CAAEkB,KAAM,KAAMC,MAAO,gBAChE,OAASC,GACLzQ,KAAKyJ,OAAO3G,KAAK,eAAgB,CAAEuM,UAASqB,KAAM,YAC5B,oBAAXjQ,QAA0BA,QAAQ+B,SACzCA,QAAQC,KAAK,iCAAkCgO,GAE7B,oBAAXhQ,QACP6P,MAAM,YAAYjB,IAE1B,CACJ,CAKA,cAAMuB,CAASvB,GACX,IACI,MAAMa,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,cACtDH,EAAOI,MAAMjB,EAAS,cAAe,CAAEkB,KAAM,KAAMC,MAAO,aACpE,OAASC,GACLzQ,KAAKyJ,OAAO3G,KAAK,eAAgB,CAAEuM,UAASqB,KAAM,SAC5B,oBAAXjQ,QAA0BA,OAGf,oBAAXA,QACP6P,MAAM,SAASjB,IAEvB,CACJ,CAKA,iBAAMwB,CAAYxB,GACd,IACI,MAAMa,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,cACtDH,EAAOI,MAAMjB,EAAS,UAAW,CAAEkB,KAAM,KAAMC,MAAO,gBAChE,OAASC,GACLzQ,KAAKyJ,OAAO3G,KAAK,eAAgB,CAAEuM,UAASqB,KAAM,YAC5B,oBAAXjQ,QAA0BA,QAAQ+B,SACzCA,QAAQC,KAAK,iCAAkCgO,GAE7B,oBAAXhQ,QACP6P,MAAM,YAAYjB,IAE1B,CACJ,CAKA,gBAAAyB,CAAiBzB,EAASqB,EAAO,QAC7B1Q,KAAKyJ,OAAO3G,KAAK,eAAgB,CAAEuM,UAASqB,QAChD,CAKA,iBAAMK,CAAYC,EAAO,IACD,iBAATA,IACPA,EAAO,CAAE3B,QAAS2B,IAEtB,WAC0BzJ,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,QACrDY,SAASD,EACpB,OAASP,GACiB,oBAAXhQ,QAA0BA,QAAQ+B,SACzCA,QAAQC,KAAK,iCAAkCgO,EAAGO,GAGtDhR,KAAKyJ,OAAO3G,KAAK,eAAgB,CAAEuM,QAAS2B,EAAK3B,SAAW,aAAcqB,KAAM,QACpF,CACJ,CAKA,iBAAMQ,GACF,WAC0B3J,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,QACrDc,UACX,OAASV,GACiB,oBAAXhQ,QAA0BA,QAAQ+B,SACzCA,QAAQC,KAAK,iCAAkCgO,EAGvD,CACJ,CAEA,mBAAMW,CAAcrR,EAAU,IAC1B,IACI,MAAMmQ,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,QAC5D,aAAaH,EAAOkB,cAAcrR,EACtC,OAAS0Q,GAIL,KAHsB,oBAAXhQ,QAA0BA,QAAQ+B,SACzCA,QAAQ0B,MAAM,iCAAkCuM,GAE9CA,CACV,CACJ,CAEA,cAAMY,CAAStR,EAAU,IACrB,IACI,MAAMmQ,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,QAC5D,aAAaH,EAAOmB,SAAStR,EACjC,OAAS0Q,GAIL,KAHsB,oBAAXhQ,QAA0BA,QAAQ+B,SACzCA,QAAQ0B,MAAM,4BAA6BuM,GAEzCA,CACV,CACJ,CAEA,gBAAMa,CAAWvR,EAAU,IACvB,IACI,MAAMmQ,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,QAC5D,aAAaH,EAAOoB,WAAWvR,EACnC,OAAS0Q,GAIL,KAHsB,oBAAXhQ,QAA0BA,QAAQ+B,SACzCA,QAAQ0B,MAAM,8BAA+BuM,GAE3CA,CACV,CACJ,CAEA,aAAMc,CAAQlC,EAASmC,EAAQ,UAAWzR,EAAU,CAAA,GAChD,MAAMmQ,SAAgB3I,QAAAC,UAAA2I,KAAA,IAAAC,IAAsCC,QAC5D,aAAaH,EAAOqB,QAAQlC,EAASmC,EAAOzR,EAChD,CAKA,kBAAAiN,GACI,GAAsB,oBAAXvM,OAAwB,OAEnCT,KAAKyR,WAAazD,SAAS0D,OAG3B,MAAMC,EAAyB,KAC3B,MAAMC,EAAa5R,KAAKyR,UACxBzR,KAAKyR,WAAazD,SAAS0D,OAEvBE,IAAe5R,KAAKyR,YAChBzR,KAAKyR,UAELzR,KAAKyJ,OAAO3G,KAAK,iBAGjB9C,KAAKyJ,OAAO3G,KAAK,kBAMvB+O,EAAc,KACX7R,KAAKyR,YACNzR,KAAKyR,WAAY,EAEjBzR,KAAKyJ,OAAO3G,KAAK,mBAInBgP,EAAa,KACX9R,KAAKyR,YACLzR,KAAKyR,WAAY,EAEjBzR,KAAKyJ,OAAO3G,KAAK,kBAKzBkL,SAAStN,iBAAiB,mBAAoBiR,GAG9ClR,OAAOC,iBAAiB,QAASmR,GACjCpR,OAAOC,iBAAiB,OAAQoR,GAGhC9R,KAAK+R,eAAiB,CAClBC,iBAAkBL,EAClBM,MAAOJ,EACPK,KAAMJ,EAEd,CAKA,kBAAAK,GACI1R,OAAOC,iBAAiB,QAAU2F,IAC9B7D,QAAQ0B,MAAM,gBAAiBmC,EAAMnC,OACjClE,KAAKmJ,OACLnJ,KAAK6N,UAAU,UAAUxH,EAAMnC,OAAOmL,SAAW,qBAIzD5O,OAAOC,iBAAiB,qBAAuB2F,IAC3C7D,QAAQ0B,MAAM,+BAAgCmC,EAAM+L,QAChDpS,KAAKmJ,OACLnJ,KAAK6N,UAAU,qBAAqBxH,EAAM+L,QAAQ/C,SAAW,oBAGzE,CAKA,UAAAgD,CAAWC,GACP,OAAOA,EACF7Q,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,SACvB,CAKA,QAAA8Q,CAASzO,GACL,OAAOA,EAAM9D,KAAK0B,MAAMoC,GAAO9D,KAAK0B,KACxC,CAKA,QAAA8Q,CAASC,GACL,MAAMC,EAAW,IAAK1S,KAAK0B,OAC3B4C,OAAOqO,OAAO3S,KAAK0B,MAAO+Q,GAG1BzS,KAAKyJ,OAAO3G,KAAK,gBAAiB,CAC9B4P,WACAE,SAAU5S,KAAK0B,MACf+Q,WAER,CAKA,iBAAAI,CAAkB1P,EAAM2P,GACpB9S,KAAKoN,iBAAiB/I,IAAIlB,EAAM2P,EACpC,CAKA,YAAAC,CAAa5P,GACT,OAAOnD,KAAKoN,iBAAiBvJ,IAAIV,EACrC,CAKA,aAAA6P,CAAc7P,EAAM8P,GAChBjT,KAAKqN,aAAahJ,IAAIlB,EAAM8P,EAChC,CAKA,QAAAC,CAAS/P,GACL,OAAOnD,KAAKqN,aAAaxJ,IAAIV,EACjC,CAKA,SAAAgQ,GACInT,KAAKwM,KAAOA,EAAAA,KACZA,OAAKE,UAAU1M,KAAKyM,IACxB,CAKA,aAAM2G,GAIEpT,KAAKgM,QACLhM,KAAKgM,OAAOpL,OAIZZ,KAAK+R,gBAAoC,oBAAXtR,SAC9BuN,SAASnN,oBAAoB,mBAAoBb,KAAK+R,eAAeC,kBACrEvR,OAAOI,oBAAoB,QAASb,KAAK+R,eAAeE,OACxDxR,OAAOI,oBAAoB,OAAQb,KAAK+R,eAAeG,OAI3D,MAAMmB,EAAQ7M,MAAM8M,KAAKtT,KAAKiN,UAAUsG,UAcxC,SAbMhM,QAAQiM,WACVH,EAAMhO,IAAIsH,MAAOnH,IACb,IACQA,EAAK4N,eACC5N,EAAK4N,SAEnB,OAASlP,GACL1B,QAAQ0B,MAAM,yBAA0BA,EAC5C,KAKJlE,KAAKsL,QAAUtL,KAAKsL,OAAO8H,QAC3B,UACUpT,KAAKsL,OAAO8H,SACtB,OAASlP,GACL1B,QAAQ0B,MAAM,2BAA4BA,EAC9C,CAIJlE,KAAKiN,UAAUwG,QACfzT,KAAKmN,YAAYsG,QACjBzT,KAAKoN,iBAAiBqG,QACtBzT,KAAKqN,aAAaoG,QAGI,oBAAXhT,QAA0BA,OAAOsM,aACjCtM,OAAOsM,KAAKf,OAGvBhM,KAAKsN,WAAY,EACAtN,KAAKmD,IAC1B,CAKA,aAAAuQ,CAAclO,EAAMzC,EAAQC,GACxB,IAAIxB,EAAOgE,EAAK7C,OAAS,IAAI6C,EAAKxE,SAAS2S,gBAU3C,GAPArP,OAAOwD,KAAK/E,GAAQG,QAAQY,IACG,iBAAhBf,EAAOe,IAA4C,iBAAhBf,EAAOe,KACjDtC,EAAOA,EAAKC,QAAQ,IAAIqC,IAAOf,EAAOe,OAK1Cd,GAASsB,OAAOwD,KAAK9E,GAAO2D,OAAS,EAAG,CACxC,MAAMmJ,EAAc,IAAInM,gBAAgBX,GAAOyB,WAC/CjD,IAASA,EAAK8B,SAAS,KAAO,IAAM,KAAOwM,CAC/C,CAEA,OAAOtO,CACX,CAQA,oBAAAmM,GACS3N,KAAKmN,YAAYvJ,IAAI5D,KAAKC,cAKKD,KAAKC,cAJrCuC,QAAQC,KAAK,sBAAsBzC,KAAKC,oCACxCuC,QAAQC,KAAK,gDAAgDzC,KAAKC,kCAClEuC,QAAQC,KAAK,0EAIrB,CAKA,gBAAAmR,GAEI,MAAMC,EAAc,CAAC,MAAO,QAAS,UAErC,IAAA,MAAY7S,KAAahB,KAAKmN,YAAY5I,UACtC,IAAKsP,EAAYvQ,SAAStC,GACtB,OAAOA,EAIf,OAAO,IACX,CAEA,aAAO8S,CAAO7I,EAAS,IACnB,OAAO,IAAID,OAAOC,EACtB,CAMA,kBAAAC,GAC0B,oBAAXzK,SAEFA,OAAOsM,OACRtM,OAAOsM,KAAO,CAAA,GAIbtM,OAAOsM,KAAKgH,UACbtT,OAAOsM,KAAKgH,QAAU,CAAA,GAI1BtT,OAAOsM,KAAKS,IAAMxN,KAE1B,CAOA,qBAAOgU,CAAe7Q,EAAM8Q,GACF,oBAAXxT,SACFA,OAAOsM,OACRtM,OAAOsM,KAAO,CAAA,GAEbtM,OAAOsM,KAAKgH,UACbtT,OAAOsM,KAAKgH,QAAU,CAAA,GAG1BtT,OAAOsM,KAAKgH,QAAQ5Q,GAAQ8Q,EAGpC,ECj8BJ,MAAM/D,eAAegE,EAAAA,KACnBC,oBAAsB,GACtBA,mBAAqB,CACnBC,SAAU,KACVC,MAAO,MAMT,+BAAOC,GAEL,OADwBtG,SAASC,cAAc,qBAGtC,CACLmG,SAAU,MACVC,MAAO,OAGJrU,KAAKuU,WACd,CAEAJ,sBAAwB,KACxBA,oBAAsB,EACtBA,oBAAsB,KAKtB,6BAAOK,GACL,MAAMC,EAAYzG,SAAS0G,iBAAiB,mBACtCC,EAAczE,OAAO0E,aAE3B,GAAyB,IAArBH,EAAU9N,QAAuC,IAAvBgO,EAAYhO,OAAc,OAGxD,MAAMkO,EAAgB,IAAIF,GAAahK,KAAK,CAACC,EAAGC,KAC7CD,EAAEkK,eAAiB,IAAMjK,EAAEiK,eAAiB,IAK/CL,EAAUvR,QAAQ,CAACkR,EAAUhR,KAC3B,GAAIA,EAAQyR,EAAclO,OAAQ,CAChC,MACMoO,EADSF,EAAczR,GACC0R,cAAgB,EAC9CV,EAASY,MAAMC,OAASF,EAGxB,MACMG,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAElDf,EAASgB,aAAeF,GAC1BA,EAAgBG,YAAYjB,EAEhC,GAEJ,CAKA,gCAAOkB,GACLpF,OAAOsE,wBACT,CAOA,eAAOvD,CAASlR,EAAU,IACtB,MAAM+I,QAAEA,EAAU,IAAAuG,QAAOA,EAAU,cAAiBtP,EAIpD,GAFAC,KAAKuV,eAEqB,IAAtBvV,KAAKuV,aAAoB,CAKzB,GAJIvV,KAAKwV,cACLtM,aAAalJ,KAAKwV,eAGjBxV,KAAKyV,eAAgB,CACtB,MACMC,EADa1V,KAAKsU,2BACMD,MAAQ,IAEtCrU,KAAKyV,eAAiBzH,SAAS2H,cAAc,OAC7C3V,KAAKyV,eAAeG,UAAY,sBAChC5V,KAAKyV,eAAevH,UAAY,iTAKuBmB,gRAKMqG,qYAQ7D1H,SAASmH,KAAKE,YAAYrV,KAAKyV,eACnC,CAEA,MAAMI,EAAa7V,KAAKyV,eAAexH,cAAc,sBACjD4H,MAAuBC,YAAczG,GAEzCzG,WAAW,IAAM5I,KAAKyV,eAAeM,UAAUC,IAAI,QAAS,IAE5DhW,KAAKwV,aAAe5M,WAAW,KAC3BpG,QAAQ0B,MAAM,6BACdlE,KAAKmR,UAAS,GACdnR,KAAKsQ,MAAM,CACPkB,MAAO,sBACPnC,QAAS,2EACTqB,KAAM,YAEX5H,EACP,CACJ,CAOA,eAAOqI,CAASxC,GAAQ,GAChBA,EACA3O,KAAKuV,aAAe,EAEpBvV,KAAKuV,eAGLvV,KAAKuV,cAAgB,IACrBvV,KAAKuV,aAAe,EAChBvV,KAAKwV,eACLtM,aAAalJ,KAAKwV,cAClBxV,KAAKwV,aAAe,MAGpBxV,KAAKyV,iBACLzV,KAAKyV,eAAeM,UAAUE,OAAO,QACrCrN,WAAW,KACH5I,KAAKyV,gBAAwC,IAAtBzV,KAAKuV,eAC3BvV,KAAKyV,eAAeQ,SACpBjW,KAAKyV,eAAiB,OAE5B,MAGf,CAEA,WAAA3V,CAAYC,EAAU,IAEpB,MAAMmW,EAAUnW,EAAQoW,IAAM,SAAStM,KAAKC,QAE5CsM,MAAM,IACDrW,EACHoW,GAAID,EACJG,QAAS,MACTT,UAAW,UAA0B,IAAjB7V,EAAQuW,KAAiB,OAAS,MAAMvW,EAAQ6V,WAAa,KACjFW,WAAY,CACVC,SAAU,KACV,cAAe,OACf,kBAAmBzW,EAAQ0W,YAAc,GAAGP,UAC5C,mBAAoBnW,EAAQ2W,aAAe,QACxC3W,EAAQwW,cAKfvW,KAAKkW,QAAUA,EAGflW,KAAKwR,MAAQzR,EAAQyR,OAAS,GAC9BxR,KAAK2W,QAAU,GAAG3W,KAAKkW,gBAKvBlW,KAAKuQ,KAAOxQ,EAAQwQ,MAAQ,GAG5BvQ,KAAK4W,cAAgC,IAArB7W,EAAQ6W,UAAyB7W,EAAQ6W,SACzD5W,KAAK6W,gBAAoC,IAAvB9W,EAAQ8W,YAA2B9W,EAAQ8W,WAI7D7W,KAAK8W,SAAW/W,EAAQ+W,UAA6B,SAAjB/W,EAAQwQ,KAG5CvQ,KAAKoU,cAAgC,IAArBrU,EAAQqU,UAAyBrU,EAAQqU,SACzDpU,KAAK+W,cAAgC,IAArBhX,EAAQgX,UAAyBhX,EAAQgX,SACzD/W,KAAKiS,WAA0B,IAAlBlS,EAAQkS,OAAsBlS,EAAQkS,MAGnDjS,KAAKgX,YAA4B,IAAnBjX,EAAQiX,QAAuBjX,EAAQiX,OACrDhX,KAAKiX,cAAgBlX,EAAQkX,eAAiB,KAC9CjX,KAAKkX,iBAAsC,IAAxBnX,EAAQmX,aAA4BnX,EAAQmX,YAC/DlX,KAAKmX,YAAcpX,EAAQoX,aAAe,KAG1CnX,KAAKmV,KAAOpV,EAAQoV,MAAQpV,EAAQqX,SAAW,GAC/CpX,KAAKqX,SAAW,KAChBrX,KAAKsX,UAAYvX,EAAQuX,WAAa,GACtCtX,KAAKuX,cAAgBxX,EAAQwX,gBAAiB,EAG9CvX,KAAKwX,SAAWzX,EAAQyX,UAAY,IACpCxX,KAAKyX,UAAY1X,EAAQ0X,WAAa,IAClC1X,EAAQ2X,YAAW1X,KAAK0X,UAAY3X,EAAQ2X,WAChD1X,KAAK2X,gBAAkB5X,EAAQ4X,iBAAmB,GAClD3X,KAAK4X,iBAAmB7X,EAAQ6X,kBAAoB,GAGpD5X,KAAK6X,oBAAoB7X,KAAKmV,MAE9BnV,KAAK8X,OAAS/X,EAAQ+X,QAAU,KAChC9X,KAAK+X,WAAa,KAClB/X,KAAKgY,YAAcjY,EAAQiY,aAAe,GAG1ChY,KAAKiY,sBAAsBjY,KAAK8X,QAGhC9X,KAAKkY,QAAUnY,EAAQmY,SAAW,KAGlClY,KAAKmY,OAASpY,EAAQoY,QAAU,KAChCnY,KAAKoY,QAAUrY,EAAQqY,SAAW,KAClCpY,KAAKqY,OAAStY,EAAQsY,QAAU,KAChCrY,KAAKsY,SAAWvY,EAAQuY,UAAY,KACpCtY,KAAKuY,gBAAkBxY,EAAQwY,iBAAmB,KAGlDvY,KAAKwY,cAAgC,IAArBzY,EAAQyY,UAAyBzY,EAAQyY,SAGzDxY,KAAKqU,MAAQ,KAGbrU,KAAKyY,cAAgB1Y,EAAQ0Y,eAAiB,IAChD,CAKA,mBAAAZ,CAAoB1C,GAClB,GAAIA,GAAQA,EAAK/F,OACfpP,KAAKqX,SAAWlC,EAChBnV,KAAKmV,KAAO,GACZnV,KAAK0Y,SAAS1Y,KAAKqX,eACrB,GAA2B,mBAATlC,EAEhB,IACE,MAAM3M,EAAS2M,IACX3M,aAAkB0L,EAAAA,MACpBlU,KAAKqX,SAAW7O,EAChBxI,KAAKmV,KAAO,GACZnV,KAAK0Y,SAAS1Y,KAAKqX,WACV7O,aAAkBjB,SAE3BvH,KAAK2Y,YAAcnQ,EACnBxI,KAAKmV,KAAO,uFAEZnV,KAAKmV,KAAO3M,CAEhB,OAAStE,GACP1B,QAAQ0B,MAAM,kCAAmCA,GACjDlE,KAAKmV,KAAOA,CACd,MAEAnV,KAAKmV,KAAOA,CAEhB,CAKA,qBAAA8C,CAAsBH,GACpB,GAAIA,aAAkB5D,EAAAA,KACpBlU,KAAK+X,WAAaD,EAClB9X,KAAK8X,OAAS,KACd9X,KAAK0Y,SAAS1Y,KAAK+X,iBACrB,GAA6B,mBAAXD,EAEhB,IACE,MAAMtP,EAASsP,IACXtP,aAAkB0L,EAAAA,MACpBlU,KAAK+X,WAAavP,EAClBxI,KAAK8X,OAAS,KACd9X,KAAK0Y,SAAS1Y,KAAK+X,aACVvP,aAAkBjB,SAE3BvH,KAAK4Y,cAAgBpQ,EACrBxI,KAAK8X,OAAS,uFAEd9X,KAAK8X,OAAStP,CAElB,OAAStE,GACP1B,QAAQ0B,MAAM,oCAAqCA,GACnDlE,KAAK8X,OAASA,CAChB,MAEA9X,KAAK8X,OAASA,CAElB,CAKA,iBAAMe,GAEJ,MAAMC,EAAgB,CAAC,gBA+BvB,OA5BI9Y,KAAKuQ,MAAsB,SAAdvQ,KAAKuQ,OAChBvQ,KAAKuQ,KAAKvM,WAAW,cAEvB8U,EAAc7X,KAAK,SAASjB,KAAKuQ,QACxB,CAAC,KAAM,KAAM,KAAM,OAAOjN,SAAStD,KAAKuQ,QAEjDuI,EAAc7X,KAAK,SAASjB,KAAKuQ,QAE7B,CAAC,KAAM,KAAM,OAAOjN,SAAStD,KAAKuQ,OACpCuI,EAAc7X,KAAK,8BAMrBjB,KAAK4W,UACPkC,EAAc7X,KAAK,yBAIjBjB,KAAK6W,aACF7W,KAAK0X,UAGNoB,EAAc7X,KAAK,mBAFnB6X,EAAc7X,KAAK,4BAMlB,uBACS6X,EAAcC,KAAK,gEAErB/Y,KAAKgZ,kCACLhZ,KAAKiZ,gCACLjZ,KAAKkZ,mDAIrB,CAKA,iBAAMF,GACJ,IAAKhZ,KAAKgX,OACR,MAAO,GAGT,GAAIhX,KAAKiX,cACP,MAAO,6BAA6BjX,KAAKiX,sBAI3C,IAAIkC,EAAgB,GAOpB,OANInZ,KAAKmX,aAAenX,KAAKmX,YAAYiC,OAASpZ,KAAKmX,YAAYiC,MAAMzS,OAAS,EAChFwS,QAAsBnZ,KAAKqZ,mBAClBrZ,KAAKkX,cACdiC,EAAgB,gGAGX,+CAEDnZ,KAAKwR,MAAQ,+BAA+BxR,KAAK2W,YAAY3W,KAAKwR,aAAe,eACjF2H,uBAGR,CAEA,sBAAME,GACJ,MAAMC,QAAkBtZ,KAAKuZ,yBAC7B,GAAyB,IAArBD,EAAU3S,OAEZ,OAAO3G,KAAKkX,YAAc,+FAAiG,GAG7H,MAAMsC,EAAcxZ,KAAKmX,YAAYsC,MAAQ,yBAwB7C,MAAO,0DAvBazZ,KAAKmX,YAAYuC,aAAe,uIA0BlCF,+FAxBIF,EAAUjU,IAAIsU,IAClC,GAAkB,YAAdA,EAAKjJ,KACP,MAAO,yCAGT,MAAM+I,EAAOE,EAAKF,KAAO,aAAaE,EAAKF,kBAAoB,GACzDG,EAAQD,EAAKC,OAAS,GAE5B,GAAID,EAAKE,KACP,MAAO,sCAAsCF,EAAKE,QAAQF,EAAKG,OAAS,YAAYH,EAAKG,UAAY,MAAML,IAAOG,aACpH,GAAWD,EAAKI,OAAQ,CACtB,MAAMC,EAAY1V,OAAOwD,KAAK6R,GAC3BM,OAAOnW,GAAOA,EAAIE,WAAW,UAC7BqB,IAAIvB,GAAO,GAAGA,MAAQ6V,EAAK7V,OAC3BiV,KAAK,KACR,MAAO,6CAA6CY,EAAKI,WAAWC,KAAaP,IAAOG,YAC1F,CAEA,MAAO,KACNb,KAAK,wCAYV,CAEA,4BAAMQ,GACJ,IAAKvZ,KAAKmX,cAAgBnX,KAAKmX,YAAYiC,MACzC,MAAO,GAGT,MAAMc,EAAgB,GAEtB,IAAA,MAAWP,KAAQ3Z,KAAKmX,YAAYiC,MAElC,GAAkB,YAAdO,EAAKjJ,KAAT,CAMA,GAAIiJ,EAAKnL,YACP,IACE,MAAMhB,EAAMxN,KAAKma,WACjB,IAAIC,EAAO,KAOX,GALI5M,IACF4M,EAAO5M,EAAI6M,YAAc7M,EAAI+E,WAAW,gBAIrC6H,GAA0B,oBAAX3Z,QAA0BA,OAAO0Z,OACnD,IACE,MAAMG,EAAY7Z,OAAO0Z,SACzBC,EAAOE,GAAWD,UACpB,OAAS5J,GAET,CAGF,IAAI2J,IAAQA,EAAKG,cAMf,SALA,IAAKH,EAAKG,cAAcZ,EAAKnL,aAC3B,QAMN,OAAStK,GACP1B,QAAQC,KAAK,oDAAqDyB,GAClE,QACF,CAGFgW,EAAcjZ,KAAK0Y,EApCnB,MAFEO,EAAcjZ,KAAK0Y,GAyCvB,OAAOO,CACT,CAKA,eAAMjB,GAEJ,OAAIjZ,KAAKqX,UACPrX,KAAKqX,SAASmD,aAAc,EAErB,eADWxa,KAAKuX,cAAgB,kBAAkBvX,KAAKsX,YAAc,cAActX,KAAKsX,gHAGlFtX,KAAKqX,SAASlB,4BAKxBnW,KAAKmV,MAAsB,KAAdnV,KAAKmV,KAKhB,uBADWnV,KAAKuX,cAAgB,kBAAkBvX,KAAKsX,YAAc,cAActX,KAAKsX,0BAGzFtX,KAAKmV,2BANF,EASX,CAKA,iBAAM+D,GAEJ,GAAIlZ,KAAK+X,WACP,MAAO,4BAA4B/X,KAAKgY,mDAI1C,GAAoB,OAAhBhY,KAAK8X,QAA0C,iBAAhB9X,KAAK8X,OACtC,MAAO,4BAA4B9X,KAAKgY,gBAAgBhY,KAAK8X,eAI/D,GAAI9X,KAAKkY,SAAWlY,KAAKkY,QAAQvR,OAAS,EAAG,CAC3C,MAAM8T,EAAcza,KAAKkY,QAAQ7S,IAAIqV,IACnC,MAAMC,EAAcD,EAAIE,QAAU,0BAA4B,GACxDC,EAAaH,EAAIX,OAAS,gBAAgBW,EAAIX,UAAY,GAC1De,EAASJ,EAAIvE,GAAK,OAAOuE,EAAIvE,MAAQ,GACrC4E,EAAeL,EAAIM,SAAW,WAAa,GAEjD,MAAO,6BACWN,EAAIhK,MAAQ,2CACPgK,EAAIlK,OAAS,uCACxBsK,wBACAH,wBACAE,wBACAE,mBACNL,EAAIjB,KAAO,gBAAgBiB,EAAIjB,kBAAoB,mBACnDiB,EAAIO,MAAQ,4CAGjBlC,KAAK,IAER,MAAO,4BAA4B/Y,KAAKgY,gBAAgByC,SAC1D,CAGA,MAAO,EACT,CAOA,WAAMS,CAAMC,EAAa,MACvB,IAAInb,KAAKob,UAAWpb,KAAKqb,UAAzB,CAKA,IAAKrb,KAAKsb,QACR,MAAM,IAAI/U,MAAM,uCAuBlB,aAnBMvG,KAAKub,iBAGevN,SAASC,cAAc,sBACJD,SAASmH,MACtCE,YAAYrV,KAAKsb,SAGjCtb,KAAKwb,aAGLxb,KAAKob,SAAU,QAGTpb,KAAKyb,eAGXzb,KAAK8C,KAAK,UAAW,CAAE4Y,KAAM1b,OAEtBA,IA3BP,CA4BF,CAKA,mBAAM2b,GAeJ,SAdMvF,MAAMuF,gBAGRlb,OAAOmb,OAAS5b,KAAKsb,SACJtb,KAAKsb,QAAQ5G,iBAAiB,YAClC/N,OAAS,GAEtBlG,OAAOmb,MAAMC,kBAAkB7b,KAAKsb,SAOpCtb,KAAK8W,SACP9W,KAAK8b,uBACP,GAAW9b,KAAK0X,UAAW,CACzB,MAAMqE,EAAY/b,KAAKsb,QAAQrN,cAAc,eACzC8N,IACFA,EAAU/G,MAAM0C,UAAY,GAAG1X,KAAK0X,cAGxC,CACF,CAKA,kBAAM+D,SACErF,MAAMqF,eAEU,oBAAXhb,QAA0BA,OAAOub,WAAavb,OAAOub,UAAUC,QAElD,WAAlBjc,KAAKoU,UACPpU,KAAKsb,QAAQY,aAAa,mBAAoB,UAE3Clc,KAAK+W,UACR/W,KAAKsb,QAAQY,aAAa,mBAAoB,SAIhDlc,KAAKqU,MAAQ,IAAI5T,OAAOub,UAAUC,MAAMjc,KAAKsb,QAAS,CACpDlH,SAAUpU,KAAKoU,SACf2C,SAAU/W,KAAK+W,SACf9E,MAAOjS,KAAKiS,QAIdjS,KAAKmc,sBAGDnc,KAAKwY,UACPxY,KAAKoc,KAAKpc,KAAKyY,eAGrB,CAKA,eAAAqD,GACO9b,KAAKsb,UAGVtb,KAAKsb,QAAQ5a,iBAAiB,iBAAkB,KAC9CV,KAAKqc,mBACJ,CAAEzV,MAAM,IAGXgC,WAAW,KACL5I,KAAKsc,WACPtc,KAAKqc,mBAEN,KACL,CAKA,eAAAA,GACE,GAAKrc,KAAKsb,QAEV,IACE,MAAMiB,EAAcvc,KAAKsb,QAAQrN,cAAc,iBACzCuO,EAAexc,KAAKsb,QAAQrN,cAAc,kBAC1C8N,EAAY/b,KAAKsb,QAAQrN,cAAc,eAE7C,IAAKsO,IAAgBC,IAAiBT,EAEpC,YADAvZ,QAAQC,KAAK,mDAKf,GAAIzC,KAAKqX,WAAarX,KAAKqX,SAASiE,QAElC,YADA1S,WAAW,IAAM5I,KAAKqc,kBAAmB,IAK3C,MAAMI,EAAiB,CACrBC,eAAgBH,EAAYvH,MAAM2H,SAClCC,YAAaL,EAAYvH,MAAM6H,MAC/BC,aAAcN,EAAaxH,MAAM6H,MACjCE,iBAAkBP,EAAaxH,MAAM0C,UACrCsF,mBAAoBT,EAAYxG,UAAUkH,SAAS,4BAIrDV,EAAYvH,MAAM2H,SAAW,OAC7BJ,EAAYvH,MAAM6H,MAAQ,OAC1BL,EAAaxH,MAAM6H,MAAQ,OAC3BL,EAAaxH,MAAM0C,UAAY,OAG/B8E,EAAaU,aAGb,MAAMC,EAAcX,EAAaY,wBAG3BC,EAAiB,GACjBV,EAAWtS,KAAKiT,IACpB7c,OAAO8c,WAAavd,KAAK2X,gBACzBlX,OAAO8c,WAAaF,GAEtB,IAAI3F,EAAYrN,KAAKiT,IACnB7c,OAAO+c,YAAcxd,KAAK4X,iBAC1BnX,OAAO+c,YAAcH,GAInBI,EAAepT,KAAKnC,IAAIlI,KAAKwX,SAAUnN,KAAKqT,KAAKP,EAAYN,MAAQ,KACrEc,EAAgBtT,KAAKnC,IAAIlI,KAAKyX,UAAWpN,KAAKqT,KAAKP,EAAYS,SAC/D5d,KAAK0X,YACPA,EAAYrN,KAAKiT,IAAItd,KAAK0X,UAAWA,GACrC6E,EAAYvH,MAAM0C,UAAY,GAAGA,OAGnC+F,EAAepT,KAAKiT,IAAIG,EAAcd,GACtC,MAAMkB,EAAmBV,EAAYS,OAASlG,EAG9C6E,EAAYvH,MAAM2H,SAAW,GAAGc,MAEhClB,EAAYvH,MAAM6H,MAAQ,GAAGY,MAGzBI,IAEGtB,EAAYxG,UAAUkH,SAAS,4BAClCV,EAAYxG,UAAUC,IAAI,2BAE5BwG,EAAaxH,MAAM0C,UAAY,GAAGA,MAClCiG,EAAgBjG,GAIlB1X,KAAK8d,eAAiBL,EACtBzd,KAAK+d,gBAAkBJ,EACvB3d,KAAKge,gBAAkBvB,CAEzB,OAASvY,GACP1B,QAAQ0B,MAAM,+BAAgCA,GAE9ClE,KAAKsb,QAAQrN,cAAc,iBAAiB+G,MAAM2H,SAAW,EAC/D,CACF,CAKA,eAAAsB,GACE,GAAKje,KAAK8W,UAAa9W,KAAKge,iBAAoBhe,KAAKsb,QAErD,IACE,MAAMiB,EAAcvc,KAAKsb,QAAQrN,cAAc,iBACzCuO,EAAexc,KAAKsb,QAAQrN,cAAc,kBAC1C8N,EAAY/b,KAAKsb,QAAQrN,cAAc,eAEzCsO,GAAeC,GAAgBT,IAEjCQ,EAAYvH,MAAM2H,SAAW3c,KAAKge,gBAAgBtB,gBAAkB,GACpEH,EAAYvH,MAAM6H,MAAQ7c,KAAKge,gBAAgBpB,aAAe,GAC9DJ,EAAaxH,MAAM6H,MAAQ7c,KAAKge,gBAAgBlB,cAAgB,GAChEN,EAAaxH,MAAM0C,UAAY1X,KAAKge,gBAAgBjB,kBAAoB,IAGnE/c,KAAKge,gBAAgBhB,oBAAsBT,EAAYxG,UAAUkH,SAAS,4BAC7EV,EAAYxG,UAAUE,OAAO,kCAIxBjW,KAAK8d,sBACL9d,KAAK+d,uBACL/d,KAAKge,gBAEhB,OAAS9Z,GACP1B,QAAQ0B,MAAM,sCAAuCA,EACvD,CACF,CAKA,mBAAAiY,GAEEnc,KAAKsb,QAAQ5a,iBAAiB,gBAAkB+P,IAE9C,MAAMyN,EAAahO,OAAO0E,aAAajO,OAEjCwX,EADajO,OAAOoE,2BACGD,MAAsB,GAAb6J,EACtCle,KAAKsb,QAAQtG,MAAMC,OAASkJ,EAG5Bne,KAAK8U,cAAgBqJ,EACrBne,KAAKoe,gBAAkBD,EAAY,GAEnCjO,OAAO0E,aAAa3T,KAAKjB,MAErBA,KAAKmY,QAAQnY,KAAKmY,OAAO1H,GAC7BzQ,KAAK8C,KAAK,OAAQ,CAChBub,OAAQre,KACRyY,cAAehI,EAAEgI,kBAKrBzY,KAAKsb,QAAQ5a,iBAAiB,iBAAmB+P,IAa/C,GAXA7H,WAAW,KACTsH,OAAOsE,0BACN,IAECxU,KAAKoY,SAASpY,KAAKoY,QAAQ3H,GAC/BzQ,KAAK8C,KAAK,QAAS,CACjBub,OAAQre,KACRyY,cAAehI,EAAEgI,gBAIfzY,KAAKiS,MAAO,CACd,MAAMqM,EAAate,KAAKsb,QAAQrN,cAAc,gDAC1CqQ,GACFA,EAAWrM,OAEf,IAIFjS,KAAKsb,QAAQ5a,iBAAiB,gBAAkB+P,IAE9C,MAAM8N,EAAiBve,KAAKsb,QAAQrN,cAAc,UAC9CsQ,GACFA,EAAerM,OAGblS,KAAKqY,SAEQ,IADArY,KAAKqY,OAAO5H,GAEzBA,EAAEtJ,iBAINnH,KAAK8C,KAAK,OAAQ,CAAEub,OAAQre,SAI9BA,KAAKsb,QAAQ5a,iBAAiB,kBAAoB+P,IAEhD,MAAMrN,EAAQ8M,OAAO0E,aAAa9N,QAAQ9G,MACtCoD,GAAQ,GACV8M,OAAO0E,aAAa7N,OAAO3D,EAAO,GAKhC8M,OAAO0E,aAAajO,OAAS,IAC/BqH,SAASmH,KAAKY,UAAUC,IAAI,cAE5BpN,WAAW,KACTsH,OAAOsE,0BACN,KAIDxU,KAAKwe,eAAiBxQ,SAASmH,KAAK8H,SAASjd,KAAKwe,gBACpDxe,KAAKwe,cAAcvM,QAGjBjS,KAAKsY,UAAUtY,KAAKsY,SAAS7H,GACjCzQ,KAAK8C,KAAK,SAAU,CAAEub,OAAQre,SAIhCA,KAAKsb,QAAQ5a,iBAAiB,yBAA2B+P,IACnDzQ,KAAKuY,iBAAiBvY,KAAKuY,gBAAgB9H,GAC/CzQ,KAAK8C,KAAK,gBAAiB,CAAEub,OAAQre,QAEzC,CAQA,IAAAoc,CAAK3D,EAAgB,MAEnBzY,KAAKwe,cAAgBxQ,SAASyQ,cAC9Bhe,OAAOie,WAAa1e,KAChBA,KAAKqU,OACPrU,KAAKqU,MAAM+H,KAAK3D,EAEpB,CAKA,IAAAkG,GAEE,MAAMJ,EAAiBve,KAAKsb,SAASrN,cAAc,UAC/CsQ,GACFA,EAAerM,OAGblS,KAAKqU,OACPrU,KAAKqU,MAAMsK,MAEf,CAMA,MAAAC,CAAOnG,EAAgB,MACjBzY,KAAKqU,OACPrU,KAAKqU,MAAMuK,OAAOnG,EAEtB,CAKA,aAAMrF,GAEJ,GAAIpT,KAAKqU,MAAO,CAEd,MAAMkK,EAAiBve,KAAKsb,SAASrN,cAAc,UAC/CsQ,GACFA,EAAerM,OAIjBlS,KAAKqU,MAAMwK,UACX7e,KAAKqU,MAAQ,IACf,CAGIrU,KAAKwe,eAAiBxQ,SAASmH,KAAK8H,SAASjd,KAAKwe,iBACpDxe,KAAKwe,cAAcvM,QACnBjS,KAAKwe,cAAgB,MAInBxe,KAAK8W,UACP9W,KAAKie,wBAID7H,MAAMhD,SACd,CAKA,YAAA0L,GACM9e,KAAKqU,OACPrU,KAAKqU,MAAMyK,cAEf,CAMA,gBAAMC,CAAW3H,GAEf,GAAIA,aAAmBlD,EAAAA,KAAM,CAEvBlU,KAAKqX,iBACDrX,KAAKqX,SAASjE,UACpBpT,KAAKgf,YAAYhf,KAAKqX,WAGxBrX,KAAKqX,SAAWD,EAChBpX,KAAKmV,KAAO,GACZnV,KAAK0Y,SAAS1Y,KAAKqX,UAEnB,MAAM4H,EAASjf,KAAKsb,SAASrN,cAAc,eACvCgR,IACFA,EAAO/Q,UAAY,SAEblO,KAAKqX,SAASjI,OAAO6P,GAE/B,KAAO,CAELjf,KAAKmV,KAAOiC,EACZ,MAAM6H,EAASjf,KAAKsb,SAASrN,cAAc,eACvCgR,IACFA,EAAO/Q,UAAYkJ,EAEvB,CAGApX,KAAK8e,cACP,CAKA,QAAAI,CAAS1N,GACPxR,KAAKwR,MAAQA,EACb,MAAM2N,EAAUnf,KAAKsb,SAASrN,cAAc,gBACxCkR,IACFA,EAAQrJ,YAActE,EAE1B,CAKA,UAAA4N,CAAW7S,GAAU,EAAM8C,EAAU,cACnC,MAAM4P,EAASjf,KAAKsb,SAASrN,cAAc,eACvCgR,IACE1S,EACF0S,EAAO/Q,UAAY,2NAKVmB,oCAGArP,KAAKqX,UACZ4H,EAAOI,gBAAgBrf,KAAKqX,SAASiE,SAG7C,CAKA,qBAAMgE,GAEAtf,KAAKqX,gBACDrX,KAAKqX,SAASjE,UAElBpT,KAAK+X,kBACD/X,KAAK+X,WAAW3E,gBAGlBgD,MAAMkJ,kBAERtf,KAAKqU,QACPrU,KAAKqU,MAAMwK,UACX7e,KAAKqU,MAAQ,KAEjB,CAKA,qBAAakL,CAASxf,EAAU,IAC9B,MAAMse,EAAS,IAAInO,OAAO,CACxBsB,MAAOzR,EAAQyR,OAAS,cACxBjB,KAAMxQ,EAAQwQ,MAAQ,KACtBsG,YAAY,EACZ1B,KAAMjF,OAAOsP,WAAWzf,EAAQ0f,KAAM1f,EAAQ2f,UAC9CxH,QAAS,CACP,CACE+C,KAAM,oBACNzK,MAAO,cACPiJ,KAAM,eACNM,OAAQ,QAEV,CACEkB,KAAM,QACNzK,MAAO,gBACPoK,SAAS,MAMfyD,EAAOjY,GAAG,cAAeuG,UACvB,GAAIgT,UAAUC,UACZ,UACQD,UAAUC,UAAUC,UAAU9f,EAAQ0f,MAC5CpB,EAAOyB,iBACT,OAASC,GACPvd,QAAQ0B,MAAM,kBAAmB6b,EACnC,IAKJ,MACM7K,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAiBtD,aAhBMkJ,EAAOjP,QAAO,EAAM8F,GAGtBzU,OAAOmb,OAASyC,EAAO/C,SACzB7a,OAAOmb,MAAMC,kBAAkBwC,EAAO/C,SAIxC+C,EAAOjC,OAGPiC,EAAOjY,GAAG,SAAU,KAClBiY,EAAOjL,UACPiL,EAAO/C,QAAQrF,WAGVoI,CACT,CAKA,iBAAOmB,CAAWC,EAAMC,EAAW,cACjC,IAAIM,EAKFA,EAFEvf,OAAOmb,OAASnb,OAAOmb,MAAMqE,UAAUP,GAEvBjf,OAAOmb,MAAMsE,UAAUT,EAAMhf,OAAOmb,MAAMqE,UAAUP,GAAWA,GAG/DD,EACfhe,QAAQ,KAAM,SACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,QACdA,QAAQ,KAAM,UACdA,QAAQ,KAAM,UAInB,MAAM0e,EAAa1f,OAAOmb,MAAQ,YAAY8D,IAAa,GAkB3D,MAAO,0+BAkBSS,+BAjCG,kaAajB1e,QAAQ,OAAQ,KAAK2e,kCAqBJD,0EAAmFH,8BAGxG,CAMA,0BAAOK,CAAoBjV,EAAY4C,UACjCvN,OAAOmb,OAASnb,OAAOmb,MAAMC,mBAC/Bpb,OAAOmb,MAAMC,kBAAkBzQ,EAEnC,CAKA,eAAA0U,GACE,MAAMpF,EAAM1a,KAAKsb,QAAQrN,cAAc,wBACvC,GAAIyM,EAAK,CACP,MAAM4F,EAAe5F,EAAIxM,UACzBwM,EAAIxM,UAAY,0CAChBwM,EAAI3E,UAAUE,OAAO,eACrByE,EAAI3E,UAAUC,IAAI,eAClB0E,EAAIM,UAAW,EAEfpS,WAAW,KACT8R,EAAIxM,UAAYoS,EAChB5F,EAAI3E,UAAUE,OAAO,eACrByE,EAAI3E,UAAUC,IAAI,eAClB0E,EAAIM,UAAW,GACd,IACL,CACF,CAeA,uBAAa1J,CAAWvR,EAAU,IAET,iBAAZA,IAITA,EAAU,IADGwgB,UAAU,IAAM,CAAA,EAG3BpL,KALcoL,UAAU,GAMxB/O,MALY+O,UAAU,IAAM,UAShC,MAAM/O,MACJA,EAAQ,SAAA4F,QACRA,EAAAjC,KACAA,EAAOiC,GAAW,GAAA7G,KAClBA,EAAO,KAAAqG,SACPA,GAAW,EAAAsB,QACXA,EAAU,CACR,CAAE+C,KAAM,KAAMzK,MAAO,cAAezM,OAAO,IACnDyc,gBACMA,GAAkB,KACfC,GACD1gB,EAGEse,EAAS,IAAInO,OAAO,CACxBsB,QACA2D,OACA5E,OACAqG,WACAsB,aACGuI,IAMCvL,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAItD,aAHMkJ,EAAOjP,QAAO,EAAM8F,GAGnB,IAAI3N,QAAQ,CAACC,EAASuB,KAC3B,IAAI2X,GAAW,EAGQrC,EAAO/C,QAAQ5G,iBAAiB,wBACxCxR,QAAQ,CAACyd,EAAYvd,KAClC,MAAMwd,EAAe1I,EAAQ9U,GACxBwd,GAELD,EAAWjgB,iBAAiB,QAASiM,MAAO8D,IAC1C,GAAIiQ,EAAU,OAEd,MAAMG,OACmB,IAAvBD,EAAa7c,MACT6c,EAAa7c,MACZ6c,EAAa7G,QAAU3W,EAI9B,GAAoC,mBAAzBwd,EAAaE,QACtB,IACE,MAAMtY,QAAeoY,EAAaE,QAAQ,CACxCzC,SACA0C,OAAQH,EACRxd,QACAiD,MAAOoK,IAIT,GAAe,OAAXjI,IAA8B,IAAXA,EACrB,OAIF,MAAMwY,GACQ,IAAXxY,QAA8B,IAAXA,EAChBqY,EACArY,EAENkY,GAAW,EAENE,EAAahG,SAChByD,EAAOM,OAETnX,EAAQwZ,EACV,OAASjB,GAGP,YAFAvd,QAAQ0B,MAAM,+BAAgC6b,EAGhD,MAGAW,GAAW,EACNE,EAAahG,SAChByD,EAAOM,OAETnX,EAAQqZ,OAMdxC,EAAOjY,GAAG,SAAU,KAEbsa,IACHA,GAAW,EACPF,EACFzX,EAAO,IAAIxC,MAAM,qBAEjBiB,EAAQ,OAIZoB,WAAW,KACTyV,EAAOjL,UACPiL,EAAO/C,QAAQrF,UACd,OAILoI,EAAOjC,QAEX,CAOA,kBAAa9L,CAAMvQ,EAAU,IAEJ,iBAAZA,IACTA,EAAU,CACRsP,QAAStP,EACTyR,MAAO,UAIX,MAAMnC,QACJA,EAAU,GAAAmC,MACVA,EAAQ,QAAAd,KACRA,EAAO,UACJ+P,GACD1gB,EAGJ,IAAI0Z,EAAO,GACPwH,EAAa,GACjB,OAAOvQ,GACL,IAAK,UACH+I,EAAO,4DACPwH,EAAa,eACb,MACF,IAAK,UACHxH,EAAO,oEACPwH,EAAa,eACb,MACF,IAAK,SACL,IAAK,QACHxH,EAAO,uDACPwH,EAAa,cACb,MACF,QACExH,EAAO,wDACPwH,EAAa,YAGjB,OAAO/Q,OAAOoB,WAAW,CACvBE,MAAO,gBAAgByP,MAAexH,IAAOjI,WAC7C2D,KAAM,MAAM9F,QACZkB,KAAM,KACNqG,UAAU,EACVsB,QAAS,CACP,CAAE+C,KAAM,KAAMzK,MAAO,cAAezM,OAAO,OAE1C0c,GAEP,CAKA,oBAAalP,CAAQlC,EAASmC,EAAQ,UAAWzR,EAAU,CAAA,GAClC,iBAAZsP,IAEPA,GADAtP,EAAUsP,GACQA,QAClBmC,EAAQzR,EAAQyR,OAASA,GAE7B,MAAM6M,EAAS,IAAInO,OAAO,CACxBsB,QACA2D,KAAM,MAAM9F,QACZkB,KAAMxQ,EAAQwQ,MAAQ,KACtBqG,UAAU,EACVxC,SAAU,SACV8D,QAAS,CACP,CAAE+C,KAAMlb,EAAQmhB,YAAc,SAAU1Q,MAAO,gBAAiBoK,SAAS,EAAMb,OAAQ,UACvF,CAAEkB,KAAMlb,EAAQohB,aAAe,UAAW3Q,MAAOzQ,EAAQqhB,cAAgB,cAAerH,OAAQ,eAE/Fha,IAKCmV,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAItD,aAHMkJ,EAAOjP,QAAO,EAAM8F,GAC1BmJ,EAAOjC,OAEA,IAAI7U,QAASC,IAClB,IAAIgB,GAAS,EAEb6V,EAAOjY,GAAG,iBAAkB,KAC1BoC,GAAS,EACT6V,EAAOM,SAGTN,EAAOjY,GAAG,SAAU,KAClBiY,EAAOjL,UACPiL,EAAO/C,QAAQrF,SACfzO,EAAQgB,MAGd,CAKA,mBAAa6Y,CAAOhS,EAASmC,EAAQ,QAASzR,EAAU,CAAA,GACtD,MAAMuhB,EAAU,gBAAgBzX,KAAKC,QAC/ByX,EAAexhB,EAAQwhB,cAAgB,GACvCC,EAAYzhB,EAAQyhB,WAAa,OACjCC,EAAc1hB,EAAQ0hB,aAAe,GAErCpD,EAAS,IAAInO,OAAO,CACxBsB,QACA2D,KAAM,gBACC9F,+BACUmS,+DAEFF,6BACGC,mCACME,cAExBlR,KAAMxQ,EAAQwQ,MAAQ,KACtBqG,UAAU,EACVxC,SAAU,SACV8D,QAAS,CACP,CAAE+C,KAAM,SAAUzK,MAAO,gBAAiBoK,SAAS,GACnD,CAAEK,KAAM,KAAMzK,MAAO,cAAeuJ,OAAQ,UAE3Cha,IAKCmV,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAatD,aAZMkJ,EAAOjP,QAAO,EAAM8F,GAC1BmJ,EAAOjC,OAGPiC,EAAOjY,GAAG,QAAS,KACjB,MAAM/C,EAAQgb,EAAO/C,QAAQrN,cAAc,IAAIqT,KAC3Cje,IACFA,EAAM4O,QACN5O,EAAMqe,YAIH,IAAIna,QAASC,IAClB,IAAIgB,EAAS,KAEb6V,EAAOjY,GAAG,YAAa,KACrB,MAAM/C,EAAQgb,EAAO/C,QAAQrN,cAAc,IAAIqT,KAC/C9Y,EAASnF,EAAQA,EAAMU,MAAQ,KAC/Bsa,EAAOM,SAGTN,EAAOjY,GAAG,SAAU,KAClBiY,EAAOjL,UACPiL,EAAO/C,QAAQrF,SACfzO,EAAQgB,MAGd,CAKA,QAAAmZ,GACE,OAAO3hB,KAAKqU,KACd,CAKA,OAAAiI,GACE,OAAOtc,KAAKsb,SAASvF,UAAUkH,SAAS,UAAW,CACrD,CAOA,qBAAa5L,CAAStR,EAAU,IAC9B,MAAMyR,MACJA,EAAQ,OAAAoQ,WACRA,EAAa,CAAA,EAAArR,KACbA,EAAO,KAAAqG,SACPA,GAAW,EAAAiL,WACXA,EAAa,SAAAX,WACbA,EAAa,YACVT,GACD1gB,EAIE+hB,EAAW,IAAIC,SADGxa,QAAAC,UAAA2I,KAAA,IAAA6R,QAAO,2BAAyB7R,KAAA8R,GAAAA,EAAAC,aAAG7R,SAC7B,CAC5B8R,aAAcpiB,EAAQoiB,cAAgB,SACtCnb,KAAMjH,EAAQiH,KACdob,SAAUriB,EAAQqiB,SAClBC,MAAOtiB,EAAQsiB,MACfT,WAAY,CACVU,OAAQV,EAAWU,QAAUviB,EAAQuiB,UAClCV,EACHW,cAAc,EACdC,aAAa,KAKXnE,EAAS,IAAInO,OAAO,CACxBsB,QACA2D,KAAM2M,EACNvR,OACAqG,WACAsB,QAAS,CACP,CACE+C,KAAMiG,EACN1Q,MAAO,gBACPuJ,OAAQ,UAEV,CACEkB,KAAM4G,EACNrR,MAAO,cACPuJ,OAAQ,cAGT0G,IAKCvL,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAMtD,aALMkJ,EAAOjP,QAAO,EAAM8F,GAG1BmJ,EAAOjC,OAEA,IAAI7U,QAASC,IAClB,IAAIkZ,GAAW,EAGfrC,EAAOjY,GAAG,gBAAiBuG,UACzB,IAAI+T,EAGJ,GAAKoB,EAASW,WAAd,CAKA,GAAI1iB,EAAQ2iB,UAAY3iB,EAAQsiB,MAAO,CACnChE,EAAOe,YAAW,GAClB,MAAM5W,QAAesZ,EAASa,YAC9B,IAAKna,EAAOoa,QAIR,OAHAvE,EAAOe,YAAW,GAClBf,EAAOjP,cACPiP,EAAOlE,SAAS0I,MAAM3e,MAAMsE,EAAO6G,SAGvCqR,GAAW,EACXrC,EAAOM,OACPnX,EAAQgB,EACZ,CAGA,IACE,MAAMsa,QAAiBhB,EAASiB,cAChCrC,GAAW,EACXrC,EAAOM,OACPnX,EAAQsb,EACV,OAAS5e,GACP1B,QAAQ0B,MAAM,8BAA+BA,GAC7C4d,EAASjU,UAAU,6BACrB,CAzBA,MAFEiU,EAASkB,oBA8Bb3E,EAAOjY,GAAG,gBAAiB,KACrBsa,IACJA,GAAW,EACXrC,EAAOM,OACPnX,EAAQ,SAIV6W,EAAOjY,GAAG,SAAU,KACbsa,IACHA,GAAW,EACXlZ,EAAQ,OAGVoB,WAAW,KACTkZ,EAAS1O,UACTiL,EAAOjL,WACN,QAGT,CAOA,0BAAahC,CAAcrR,EAAU,IACnC,MAAMyR,MACJA,EAAQ,OAAAoQ,WACRA,EAAa,CAAA,EAAArR,KACbA,EAAO,KAAAqG,SACPA,GAAW,EAAAiL,WACXA,EAAa,OAAAX,WACbA,EAAa,SAAAmB,MACbA,EAAAC,OACAA,KACG7B,GACD1gB,EAEJ,IAAKsiB,EACH,MAAM,IAAI9b,MAAM,kCAIlB,MACMub,EAAW,IAAIC,SADGxa,QAAAC,UAAA2I,KAAA,IAAA6R,QAAO,2BAAyB7R,KAAA8R,GAAAA,EAAAC,aAAG7R,SAC7B,CAC5B8R,aAAcpiB,EAAQoiB,cAAgB,SACtCE,QACArb,KAAMjH,EAAQiH,KACdob,SAAUriB,EAAQqiB,SAClBR,WAAY,CAEVU,OAAQA,GAAUV,EAAWU,QAAU,MACpCV,EACHW,cAAc,EACdC,aAAa,KAKXnE,EAAS,IAAInO,OAAO,CACxBsB,QACA2D,KAAM2M,EACNvR,OACAqG,WACAsB,QAAS,CACP,CACE+C,KAAMiG,EACN1Q,MAAO,gBACPuJ,OAAQ,UAEV,CACEkB,KAAM4G,EACNrR,MAAO,cACPuJ,OAAQ,cAGT0G,IAKCvL,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAMtD,aALMkJ,EAAOjP,QAAO,EAAM8F,GAG1BmJ,EAAOjC,OAEA,IAAI7U,QAASC,IAClB,IAAIkZ,GAAW,EAGfrC,EAAOjY,GAAG,gBAAiBuG,UACzB,IAAI+T,EAAJ,CAGArC,EAAOe,YAAW,EAAM,aAExB,IACE,MAAM5W,QAAesZ,EAASmB,eAC9B,GAAIza,EAAOoa,QACTlC,GAAW,EACXrC,EAAOM,OACPnX,EAAQgB,OACH,CAEL6V,EAAOe,YAAW,GAClB,IAAI8D,EAAS1a,EAAOtE,MAChBsE,EAAOxB,MAAQwB,EAAOxB,KAAK9C,QAC3Bgf,EAAS1a,EAAOxB,KAAK9C,OAEzBma,EAAOlE,SAAS0I,MAAM3e,MAAMgf,EAE9B,CACF,OAAShf,GACP1B,QAAQ0B,MAAM,qBAAsBA,SAE9Bma,EAAOU,WAAW+C,GACxBA,EAASjU,UAAU3J,EAAMmL,SAAW,iCACtC,CA1Bc,IA6BhBgP,EAAOjY,GAAG,gBAAiB,KACrBsa,IACJA,GAAW,EACXrC,EAAOM,OACPnX,EAAQ,SAIV6W,EAAOjY,GAAG,SAAU,KACbsa,IACHA,GAAW,EACXlZ,EAAQ,OAGVoB,WAAW,KACTkZ,EAAS1O,UACTiL,EAAOjL,WACN,QAGT,CAOA,qBAAa+P,CAASpjB,EAAU,IAC9B,MAAMyR,MACJA,EAAQ,YAAAxK,KACRA,EAAO,CAAA,EAAAqb,MACPA,EAAQ,KAAAC,OACRA,EAAS,GAAAc,QACTA,EAAU,EAAAC,WACVA,GAAa,EAAAC,gBACbA,GAAkB,EAAAC,eAClBA,EAAiB,IAAAhT,KACjBA,EAAO,KAAAqG,SACPA,GAAW,EAAA4M,UACXA,EAAY,WACT/C,GACD1gB,EAKE0jB,EAAW,IAAIC,SADGnc,QAAAC,UAAA2I,KAAA,IAAA6R,QAAO,4BAAiC3R,SAClC,CAC5BrJ,OACAqb,QACAC,SACAc,UACAC,aACAC,kBACAC,mBAIIlF,EAAS,IAAInO,OAAO,CACxBsB,QACA2D,KAAMsO,EACNlT,OACAqG,WACAsB,QAAS,CACP,CACE+C,KAAMuI,EACNhT,MAAO,gBACPzM,MAAO,aAGR0c,IAKCvL,EADoBlH,SAASC,cAAc,sBACJD,SAASmH,KAMtD,aALMkJ,EAAOjP,QAAO,EAAM8F,GAG1BmJ,EAAOjC,OAEA,IAAI7U,QAASC,IAClB,IAAIkZ,GAAW,EAGf,MAAMiD,EAAWtF,EAAO/C,QAAQrN,cAAc,wBAW9C0V,GAAUjjB,iBAAiB,QARP,KACdggB,IACJA,GAAW,EACXrC,EAAOM,OACPnX,GAAQ,MAOV6W,EAAOjY,GAAG,SAAU,KACbsa,IACHA,GAAW,EACXlZ,GAAQ,IAGVoB,WAAW,KACT6a,EAASrQ,UACTiL,EAAOjL,UACPiL,EAAO/C,QAAQrF,UACd,OAILwN,EAASrd,GAAG,cAAgBY,IAC1BqX,EAAOvb,KAAK,uBAAwBkE,KAGtCyc,EAASrd,GAAG,QAAUY,IACpBqX,EAAOvb,KAAK,iBAAkBkE,MAGpC,EAGFkJ,OAAO0T,YAAc1T,OAAOqB,QAC5BrB,OAAOrC,UAAYqC,OAAOI"}