web-mojo 2.1.219 → 2.1.265

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 (75) 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 +261 -59
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/{ContextMenu-DjS0WgXA.js → ContextMenu-BqYO9pWG.js} +2 -2
  12. package/dist/chunks/{ContextMenu-DjS0WgXA.js.map → ContextMenu-BqYO9pWG.js.map} +1 -1
  13. package/dist/chunks/{ContextMenu-BGstYX-N.js → ContextMenu-CsMXWcoQ.js} +2 -2
  14. package/dist/chunks/{ContextMenu-BGstYX-N.js.map → ContextMenu-CsMXWcoQ.js.map} +1 -1
  15. package/dist/chunks/{DataView-Dmobu7T8.js → DataView-Cy9OMdVA.js} +2 -2
  16. package/dist/chunks/{DataView-Dmobu7T8.js.map → DataView-Cy9OMdVA.js.map} +1 -1
  17. package/dist/chunks/{DataView-LCk00Xma.js → DataView-Di2F5dQR.js} +2 -2
  18. package/dist/chunks/{DataView-LCk00Xma.js.map → DataView-Di2F5dQR.js.map} +1 -1
  19. package/dist/chunks/Dialog-BIOrX51Q.js +2 -0
  20. package/dist/chunks/Dialog-BIOrX51Q.js.map +1 -0
  21. package/dist/chunks/{Dialog-Bu9EGh-z.js → Dialog-ia-mdhjA.js} +41 -34
  22. package/dist/chunks/Dialog-ia-mdhjA.js.map +1 -0
  23. package/dist/chunks/{FilePreviewView-C-VswdQl.js → FilePreviewView-BsV1ur-F.js} +9 -6
  24. package/dist/chunks/FilePreviewView-BsV1ur-F.js.map +1 -0
  25. package/dist/chunks/FilePreviewView-C550ZqA1.js +2 -0
  26. package/dist/chunks/FilePreviewView-C550ZqA1.js.map +1 -0
  27. package/dist/chunks/{FormView-DWV5Lhc3.js → FormView-BxKY1Pua.js} +2 -2
  28. package/dist/chunks/{FormView-DWV5Lhc3.js.map → FormView-BxKY1Pua.js.map} +1 -1
  29. package/dist/chunks/{FormView-DhZi_-D_.js → FormView-Ieig2Ikt.js} +2 -2
  30. package/dist/chunks/{FormView-DhZi_-D_.js.map → FormView-Ieig2Ikt.js.map} +1 -1
  31. package/dist/chunks/{MetricsChart-CHQmuqGa.js → MetricsChart-BDhDGz4E.js} +2 -2
  32. package/dist/chunks/{MetricsChart-CHQmuqGa.js.map → MetricsChart-BDhDGz4E.js.map} +1 -1
  33. package/dist/chunks/{MetricsChart-BOSgjGoW.js → MetricsChart-Ch8QKUkf.js} +14 -8
  34. package/dist/chunks/{MetricsChart-BOSgjGoW.js.map → MetricsChart-Ch8QKUkf.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-C7_03lr6.js → PDFViewer-CzjtNiNr.js} +3 -3
  36. package/dist/chunks/{PDFViewer-C7_03lr6.js.map → PDFViewer-CzjtNiNr.js.map} +1 -1
  37. package/dist/chunks/{PDFViewer-CTQ_WArl.js → PDFViewer-DANtnv28.js} +2 -2
  38. package/dist/chunks/{PDFViewer-CTQ_WArl.js.map → PDFViewer-DANtnv28.js.map} +1 -1
  39. package/dist/chunks/{Page-7mRcLFeD.js → Page-BXT4XGsW.js} +2 -2
  40. package/dist/chunks/{Page-7mRcLFeD.js.map → Page-BXT4XGsW.js.map} +1 -1
  41. package/dist/chunks/{Page-CG1u27-d.js → Page-D-s2pJ4R.js} +2 -2
  42. package/dist/chunks/{Page-CG1u27-d.js.map → Page-D-s2pJ4R.js.map} +1 -1
  43. package/dist/chunks/{TopNav-CF1Ic6Ty.js → TopNav-E_ZvzFaL.js} +2 -2
  44. package/dist/chunks/{TopNav-CF1Ic6Ty.js.map → TopNav-E_ZvzFaL.js.map} +1 -1
  45. package/dist/chunks/{TopNav-CprY7p9c.js → TopNav-WEv4D1Jr.js} +2 -2
  46. package/dist/chunks/{TopNav-CprY7p9c.js.map → TopNav-WEv4D1Jr.js.map} +1 -1
  47. package/dist/chunks/{User-CgSxo-iW.js → User-BH8gosuT.js} +4 -2
  48. package/dist/chunks/{User-CgSxo-iW.js.map → User-BH8gosuT.js.map} +1 -1
  49. package/dist/chunks/{User-DPWffTVQ.js → User-CHIu2dzX.js} +2 -2
  50. package/dist/chunks/User-CHIu2dzX.js.map +1 -0
  51. package/dist/chunks/WebApp-AQ6Px5yi.js +2 -0
  52. package/dist/chunks/WebApp-AQ6Px5yi.js.map +1 -0
  53. package/dist/chunks/{WebApp-DDt4Ihy7.js → WebApp-Dr-gVl9H.js} +23 -12
  54. package/dist/chunks/WebApp-Dr-gVl9H.js.map +1 -0
  55. package/dist/css/web-mojo.css +1 -1
  56. package/dist/docit.cjs.js +1 -1
  57. package/dist/docit.es.js +6 -6
  58. package/dist/index.cjs.js +1 -1
  59. package/dist/index.cjs.js.map +1 -1
  60. package/dist/index.es.js +11 -11
  61. package/dist/index.es.js.map +1 -1
  62. package/dist/lightbox.cjs.js +1 -1
  63. package/dist/lightbox.es.js +4 -4
  64. package/dist/table.css +1 -8
  65. package/package.json +1 -1
  66. package/dist/chunks/Dialog-Bu9EGh-z.js.map +0 -1
  67. package/dist/chunks/Dialog-CxJjXDJ-.js +0 -2
  68. package/dist/chunks/Dialog-CxJjXDJ-.js.map +0 -1
  69. package/dist/chunks/FilePreviewView-C-VswdQl.js.map +0 -1
  70. package/dist/chunks/FilePreviewView-CxT-vXFS.js +0 -2
  71. package/dist/chunks/FilePreviewView-CxT-vXFS.js.map +0 -1
  72. package/dist/chunks/User-DPWffTVQ.js.map +0 -1
  73. package/dist/chunks/WebApp-D35UJNMm.js +0 -2
  74. package/dist/chunks/WebApp-D35UJNMm.js.map +0 -1
  75. package/dist/chunks/WebApp-DDt4Ihy7.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"User-DPWffTVQ.js","sources":["../../src/core/Model.js","../../src/core/Collection.js","../../src/core/models/Group.js","../../src/core/models/User.js","../../src/core/services/ToastService.js"],"sourcesContent":["/**\n * Model - Base class for models with REST API support\n * Provides CRUD operations for API resources with built-in event system\n *\n * Event System:\n * Uses EventEmitter mixin for instance-level events (emit, on, off, once)\n * Automatically emits 'change' events when data is modified via set()\n * Emits 'change:attributeName' for specific attribute changes\n *\n * Standard Events:\n * - 'change' - Emitted when any model data changes\n * - 'change:fieldName' - Emitted when specific field changes\n *\n * @example\n * const user = new User({ name: 'John', email: 'john@example.com' });\n *\n * // Listen for any changes\n * user.on('change', (model) => {\n * console.log('User model changed');\n * view.render();\n * });\n *\n * // Listen for specific field changes\n * user.on('change:name', (newName, model) => {\n * console.log('Name changed to:', newName);\n * });\n *\n * // Trigger events by changing data\n * user.set('name', 'Jane'); // Emits 'change' and 'change:name'\n * user.set({ name: 'Bob', email: 'bob@example.com' }); // Emits 'change' and individual field events\n */\n\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport EventEmitter from '@core/mixins/EventEmitter.js';\nimport rest from '@core/Rest.js';\n\nclass Model {\n constructor(data = {}, options = {}) {\n this.endpoint = options.endpoint || this.constructor.endpoint || '';\n this.id = data.id || null;\n this.attributes = { ...data };\n this._ = this.attributes;\n this.originalAttributes = { ...data };\n this.errors = {};\n this.loading = false;\n this.rest = rest;\n\n // Event system via EventEmitter mixin (applied to prototype)\n\n // Configuration options\n this.options = {\n idAttribute: 'id',\n timestamps: true,\n ...options\n };\n }\n\n getContextValue(key) {\n return this.get(key);\n }\n\n /**\n * Get attribute value with support for dot notation and pipe formatting\n * @param {string} key - Attribute key with optional pipes (e.g., \"name|uppercase\")\n * @returns {*} Attribute value, possibly formatted\n */\n get(key) {\n // Check if key exists as an instance field first (for 'id', 'endpoint', etc.)\n if (!key.includes('.') && !key.includes('|') && this[key] !== undefined) {\n // If it's a function, call it and return the result\n if (typeof this[key] === 'function') {\n return this[key]();\n }\n return this[key];\n }\n\n // Use MOJOUtils for all attribute access with pipes and dot notation\n return MOJOUtils.getContextData(this.attributes, key);\n }\n\n /**\n * Set attribute value(s)\n * @param {string|object} key - Attribute key or object of key-value pairs\n * @param {*} value - Attribute value (if key is string)\n * @param {object} options - Options (silent: true to not trigger change event)\n */\n set(key, value, options = {}) {\n const previousAttributes = { ...this.attributes };\n let hasChanged = false;\n\n if (typeof key === 'object') {\n // Set multiple attributes\n Object.assign(this.attributes, key);\n Object.assign(this, key);\n if (key.id !== undefined) {\n this.id = key.id;\n }\n hasChanged = JSON.stringify(previousAttributes) !== JSON.stringify(this.attributes);\n } else {\n // Set single attribute\n if (key === 'id') {\n this.id = value;\n hasChanged = true;\n } else {\n const oldValue = this.attributes[key];\n this.attributes[key] = value;\n this[key] = value;\n hasChanged = oldValue !== value;\n }\n }\n\n // Trigger change event if data changed and not silent\n if (hasChanged && !options.silent) {\n this.emit('change', this);\n\n // Trigger specific attribute change events\n if (typeof key === 'string') {\n this.emit(`change:${key}`, value, this);\n } else {\n for (const [attr, val] of Object.entries(key)) {\n if (previousAttributes[attr] !== val) {\n this.emit(`change:${attr}`, val, this);\n }\n }\n }\n }\n }\n\n getData() {\n return this.attributes;\n }\n\n getId() {\n return this.id;\n }\n\n /**\n * Fetch model data from API with request deduplication and cancellation\n * @param {object} options - Request options\n * @param {number} options.debounceMs - Optional debounce delay in milliseconds\n * @returns {Promise} Promise that resolves with REST response\n */\n async fetch(options = {}) {\n let url = options.url;\n if (!url) {\n const id = options.id || this.getId();\n if (!id && this.options.requiresId !== false) {\n throw new Error('Model: ID is required for fetching');\n }\n url = this.buildUrl(id);\n }\n const requestKey = JSON.stringify({url, params: options.params});\n\n // Handle debounced fetch\n if (options.debounceMs && options.debounceMs > 0) {\n return this._debouncedFetch(requestKey, options);\n }\n\n // CANCEL PREVIOUS REQUEST if it's different from current request\n if (this.currentRequest && this.currentRequestKey !== requestKey) {\n console.info('Model: Cancelling previous request for new parameters');\n this.abortController?.abort();\n this.currentRequest = null;\n }\n\n // REQUEST DEDUPLICATION - Return existing promise if identical request\n if (this.currentRequest && this.currentRequestKey === requestKey) {\n console.info('Model: Duplicate request in progress, returning existing promise');\n return this.currentRequest;\n }\n\n // RATE LIMITING - Prevent requests within 100ms of last request\n const now = Date.now();\n const minInterval = 100; // ms\n\n if (this.lastFetchTime && (now - this.lastFetchTime) < minInterval) {\n console.info('Model: Rate limited, skipping fetch');\n return this;\n }\n\n this.loading = true;\n this.errors = {};\n this.lastFetchTime = now;\n this.currentRequestKey = requestKey;\n\n // Create new AbortController for this request\n this.abortController = new AbortController();\n\n // Store the promise for deduplication\n this.currentRequest = this._performFetch(url, options, this.abortController);\n\n try {\n const result = await this.currentRequest;\n return result;\n } catch (error) {\n // Don't throw if request was cancelled\n if (error.name === 'AbortError') {\n console.info('Model: Request was cancelled');\n return this;\n }\n throw error;\n } finally {\n this.currentRequest = null;\n this.currentRequestKey = null;\n this.abortController = null;\n }\n }\n\n /**\n * Handle debounced fetch requests\n * @param {string} requestKey - Unique key for this request\n * @param {object} options - Fetch options\n * @returns {Promise} Promise that resolves with REST response\n */\n async _debouncedFetch(requestKey, options) {\n // Clear existing debounced fetch\n if (this.debouncedFetchTimeout) {\n clearTimeout(this.debouncedFetchTimeout);\n }\n\n // Cancel any active request since we're about to start a new one\n this.cancel();\n\n return new Promise((resolve, reject) => {\n this.debouncedFetchTimeout = setTimeout(async () => {\n try {\n const result = await this.fetch({ ...options, debounceMs: 0 });\n resolve(result);\n } catch (error) {\n reject(error);\n }\n }, options.debounceMs);\n });\n }\n\n /**\n * Internal method to perform the actual fetch\n * @param {string} url - API endpoint URL\n * @param {object} options - Request options\n * @param {AbortController} abortController - Controller for request cancellation\n * @returns {Promise} Promise that resolves with REST response\n */\n async _performFetch(url, options, abortController) {\n try {\n if (options.graph && (!options.params || !options.params.graph)) {\n if (!options.params) options.params = {};\n options.params.graph = options.graph;\n }\n const response = await this.rest.GET(url, options.params, {\n signal: abortController.signal\n });\n\n if (response.success) {\n if (response.data.status) {\n this.originalAttributes = { ...this.attributes };\n this.set(response.data.data);\n this.errors = {};\n } else {\n this.errors = response.data;\n }\n } else {\n this.errors = response.errors || {};\n }\n\n return response;\n } catch (error) {\n // Handle cancellation gracefully\n if (error.name === 'AbortError') {\n console.info('Model: Fetch was cancelled');\n throw error;\n }\n\n this.errors = { fetch: error.message };\n\n // Return error response for network/other errors\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n }\n }\n\n /**\n * Save model to API (create or update)\n * @param {object} data - Data to save to the model\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with REST response\n */\n async save(data, options = {}) {\n const isNew = !this.id;\n const method = isNew ? 'POST' : 'PUT';\n const url = isNew ? this.buildUrl() : this.buildUrl(this.id);\n\n this.loading = true;\n this.errors = {};\n\n try {\n const response = await this.rest[method](url, data, options.params);\n\n if (response.success) {\n if (response.data.status) {\n // Update model on success\n this.originalAttributes = { ...this.attributes };\n this.set(response.data.data);\n this.errors = {};\n } else {\n this.errors = response.data;\n }\n } else {\n this.errors = response.errors || {};\n }\n\n return response; // Always return the full response\n\n } catch (error) {\n // Return error response for network/other errors\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n }\n }\n\n\n /**\n * Delete model from API\n * @param {object} options - Request options\n * @returns {Promise} Promise that resolves with REST response\n */\n async destroy(options = {}) {\n if (!this.id) {\n this.errors = { destroy: 'Cannot destroy model without ID' };\n return {\n success: false,\n error: 'Cannot destroy model without ID',\n status: 400\n };\n }\n\n const url = this.buildUrl(this.id);\n this.loading = true;\n this.errors = {};\n\n try {\n const response = await this.rest.DELETE(url, options.params);\n\n if (response.success) {\n // Clear model data on success\n this.attributes = {};\n this.originalAttributes = {};\n this.id = null;\n this.errors = {};\n } else {\n this.errors = response.errors || {};\n }\n\n return response;\n\n } catch (error) {\n this.errors = { destroy: error.message };\n\n // Return error response for network/other errors\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n }\n }\n\n /**\n * Check if model has been modified\n * @returns {boolean} True if model has unsaved changes\n */\n isDirty() {\n return JSON.stringify(this.attributes) !== JSON.stringify(this.originalAttributes);\n }\n\n /**\n * Get attributes that have changed since last save\n * @returns {object} Object containing only changed attributes\n */\n getChangedAttributes() {\n const changed = {};\n\n for (const [key, value] of Object.entries(this.attributes)) {\n if (this.originalAttributes[key] !== value) {\n changed[key] = value;\n }\n }\n\n return changed;\n }\n\n /**\n * Reset model to original state\n */\n reset() {\n this.attributes = { ...this.originalAttributes };\n this._ = this.attributes;\n this.errors = {};\n }\n\n /**\n * Build URL for API requests\n * @param {string|number} id - Optional ID to append to URL\n * @returns {string} Complete API URL\n */\n buildUrl(id = null) {\n let url = this.endpoint;\n if (id) {\n url = url.endsWith('/') ? `${url}${id}` : `${url}/${id}`;\n }\n return url;\n }\n\n /**\n * Convert model to JSON\n * @returns {object} Model attributes as plain object\n */\n toJSON() {\n return {\n id: this.id,\n ...this.attributes\n };\n }\n\n /**\n * Validate model attributes\n * @returns {boolean} True if valid, false if validation errors exist\n */\n validate() {\n this.errors = {};\n\n // Override in subclasses for custom validation\n if (this.constructor.validations) {\n for (const [field, rules] of Object.entries(this.constructor.validations)) {\n this.validateField(field, rules);\n }\n }\n\n return Object.keys(this.errors).length === 0;\n }\n\n /**\n * Validate a single field\n * @param {string} field - Field name\n * @param {object|array} rules - Validation rules\n */\n validateField(field, rules) {\n const value = this.get(field);\n const rulesArray = Array.isArray(rules) ? rules : [rules];\n\n for (const rule of rulesArray) {\n if (typeof rule === 'function') {\n const result = rule(value, this);\n if (result !== true) {\n this.errors[field] = result || `${field} is invalid`;\n break;\n }\n } else if (typeof rule === 'object') {\n if (rule.required && (value === undefined || value === null || value === '')) {\n this.errors[field] = rule.message || `${field} is required`;\n break;\n }\n if (rule.minLength && value && value.length < rule.minLength) {\n this.errors[field] = rule.message || `${field} must be at least ${rule.minLength} characters`;\n break;\n }\n if (rule.maxLength && value && value.length > rule.maxLength) {\n this.errors[field] = rule.message || `${field} must be no more than ${rule.maxLength} characters`;\n break;\n }\n if (rule.pattern && value && !rule.pattern.test(value)) {\n this.errors[field] = rule.message || `${field} format is invalid`;\n break;\n }\n }\n }\n }\n\n // EventEmitter API: on, off, once, emit (from mixin).\n\n /**\n * Static method to create and fetch a model by ID\n * @param {string|number} id - Model ID\n * @param {object} options - Options\n * @returns {Promise<RestModel>} Promise that resolves with fetched model\n */\n static async find(id, options = {}) {\n const model = new this({}, options);\n await model.fetch({ id, ...options });\n return model;\n }\n\n /**\n * Static method to create a new model with data\n * @param {object} data - Model data\n * @param {object} options - Options\n * @returns {RestModel} New model instance\n */\n static create(data = {}, options = {}) {\n return new this(data, options);\n }\n\n /**\n * Cancel any active fetch request\n * @returns {boolean} True if a request was cancelled, false if no active request\n */\n cancel() {\n if (this.currentRequest && this.abortController) {\n console.info('Model: Manually cancelling active request');\n this.abortController.abort();\n return true;\n }\n\n // Cancel debounced fetch if exists\n if (this.debouncedFetchTimeout) {\n clearTimeout(this.debouncedFetchTimeout);\n this.debouncedFetchTimeout = null;\n return true;\n }\n\n return false;\n }\n\n /**\n * Check if model has an active fetch request\n * @returns {boolean} True if fetch is in progress\n */\n isFetching() {\n return !!this.currentRequest;\n }\n\n async showError(message) {\n await Dialog.alert(message, 'Error', {\n size: 'md',\n class: 'text-danger'\n });\n }\n}\n\nObject.assign(Model.prototype, EventEmitter);\n\nexport default Model;\n","/**\n * Collection - Class for managing arrays of Model instances\n * Provides methods for fetching and managing collections of models with built-in event system\n *\n * Event System:\n * Uses EventEmitter mixin for instance-level events (emit, on, off, once)\n * Automatically emits events when collection is modified\n *\n * Standard Events:\n * - 'add' - Emitted when models are added to the collection\n * - 'remove' - Emitted when models are removed from the collection\n * - 'update' - Emitted when collection is modified (after add/remove)\n * - 'reset' - Emitted when collection is reset (all models replaced)\n *\n * @example\n * const users = new UserCollection();\n *\n * // Listen for collection changes\n * users.on('add', ({ models, collection }) => {\n * console.log('Added', models.length, 'users');\n * updateUI();\n * });\n *\n * users.on('remove', ({ models, collection }) => {\n * console.log('Removed', models.length, 'users');\n * updateUI();\n * });\n *\n * // Add models - triggers 'add' and 'update' events\n * users.add([\n * new User({ name: 'John' }),\n * new User({ name: 'Jane' })\n * ]);\n *\n * Usage Examples:\n *\n * // Preloaded Data (no REST fetching)\n * const collection = new MyCollection({ preloaded: true });\n * collection.add(new MyModel({...}));\n * // collection.fetch() will be skipped if data already exists\n *\n * // REST Data (fetch from API)\n * const collection = new MyCollection({ preloaded: false }); // default\n * await collection.fetch(); // Will make API call\n */\n\nimport EventEmitter from '@core/mixins/EventEmitter.js';\nimport Model from '@core/Model.js';\nimport rest from '@core/Rest.js';\n\nclass Collection {\n constructor(options = {}, data = null) {\n // Handle case where first argument is data instead of ModelClass\n if (Array.isArray(options)) {\n // First argument is an array, treat it as data\n data = options;\n options = data || {};\n } else {\n data = data || options.data || [];\n }\n this.ModelClass = options.ModelClass || Model;\n this.models = [];\n this.loading = false;\n this.errors = {};\n this.meta = {};\n this.rest = rest;\n if (data) {\n this.add(data);\n }\n\n // Initialize params with defaults - single source of truth for query state\n this.params = {\n start: 0,\n size: options.size || 10,\n ...options.params\n };\n\n // Set up endpoint\n this.endpoint = options.endpoint || this.ModelClass.endpoint || '';\n if (!this.endpoint) {\n let tmp = new this.ModelClass();\n this.endpoint = tmp.endpoint;\n }\n\n // Automatic REST detection based on endpoint\n this.restEnabled = this.endpoint ? true : false;\n\n // Allow explicit override\n if (options.restEnabled !== undefined) {\n this.restEnabled = options.restEnabled;\n }\n\n // Configuration\n this.options = {\n parse: true,\n reset: true,\n preloaded: false,\n ...options\n };\n\n // Event system via EventEmitter mixin (applied to prototype)\n }\n\n getModelName() {\n return this.ModelClass.name;\n }\n\n /**\n * Fetch collection data from API\n * @param {object} additionalParams - Additional parameters to merge for this fetch only\n * @returns {Promise} Promise that resolves with REST response\n */\n async fetch(additionalParams = {}) {\n const requestKey = JSON.stringify({ ...this.params, ...additionalParams });\n\n // CANCEL PREVIOUS REQUEST if it's different from current request\n if (this.currentRequest && this.currentRequestKey !== requestKey) {\n console.info('Collection: Cancelling previous request for new parameters');\n this.abortController?.abort();\n this.currentRequest = null;\n }\n\n // REQUEST DEDUPLICATION - Return existing promise if identical request\n if (this.currentRequest && this.currentRequestKey === requestKey) {\n console.info('Collection: Duplicate request in progress, returning existing promise');\n return this.currentRequest;\n }\n\n // RATE LIMITING - Prevent requests within 100ms of last request\n const now = Date.now();\n const minInterval = 100; // ms\n\n if (this.options.rateLimiting && this.lastFetchTime && (now - this.lastFetchTime) < minInterval) {\n console.info('Collection: Rate limited, skipping fetch');\n return { success: true, message: 'Rate limited, skipping fetch', data: { data: this.toJSON() } };\n }\n\n // Skip fetching if not REST enabled\n if (!this.restEnabled) {\n console.info('Collection: REST disabled, skipping fetch');\n return { success: true, message: 'REST disabled, skipping fetch', data: { data: this.toJSON() } };\n }\n\n // Skip fetching if preloaded is true and we already have data\n if (this.options.preloaded && this.models.length > 0) {\n console.info('Collection: Using preloaded data, skipping fetch');\n return { success: true, message: 'Using preloaded data, skipping fetch', data: { data: this.toJSON() } };\n }\n\n const url = this.buildUrl();\n this.loading = true;\n this.errors = {};\n this.lastFetchTime = now;\n this.currentRequestKey = requestKey;\n\n // Create new AbortController for this request\n this.abortController = new AbortController();\n\n // Store the promise for deduplication\n this.currentRequest = this._performFetch(url, additionalParams, this.abortController);\n\n try {\n const result = await this.currentRequest;\n return result;\n } catch (error) {\n // Don't throw if request was cancelled\n if (error.name === 'AbortError') {\n console.info('Collection: Request was cancelled');\n return { success: false, error: 'Request cancelled', status: 0 };\n }\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.currentRequest = null;\n this.currentRequestKey = null;\n this.abortController = null;\n }\n }\n\n /**\n * Internal method to perform the actual fetch\n * @param {string} url - API endpoint URL\n * @param {object} additionalParams - Additional parameters\n * @param {AbortController} abortController - Controller for request cancellation\n * @returns {Promise} Promise that resolves with REST response\n */\n async _performFetch(url, additionalParams, abortController) {\n const fetchParams = { ...this.params, ...additionalParams };\n console.log('Fetching collection data from', url, fetchParams);\n try {\n this.emit(\"fetch:start\");\n const response = await this.rest.GET(url, fetchParams, {\n signal: abortController.signal\n });\n\n if (response.success && response.data.status) {\n const data = this.options.parse ? this.parse(response) : response.data;\n\n if (this.options.reset || additionalParams.reset !== false) {\n this.reset();\n }\n\n this.add(data, { silent: additionalParams.silent });\n this.errors = {};\n this.emit(\"fetch:success\");\n } else {\n if (response.data && response.data.error) {\n this.errors = response.data;\n this.emit(\"fetch:error\", { message: response.data.error, error: response.data });\n } else {\n this.errors = response.errors || {};\n this.emit(\"fetch:error\", { error: response.errors });\n }\n }\n\n return response;\n } catch (error) {\n // Handle cancellation gracefully\n if (error.name === 'AbortError') {\n console.info('Collection: Fetch was cancelled');\n return { success: false, error: 'Request cancelled', status: 0 };\n }\n\n this.errors = { fetch: error.message };\n this.emit(\"fetch:error\", { message: error.message, error: error });\n\n return {\n success: false,\n error: error.message,\n status: error.status || 500\n };\n } finally {\n this.loading = false;\n this.emit(\"fetch:end\");\n }\n }\n\n /**\n * Update collection parameters and optionally fetch new data\n * @param {object} newParams - Parameters to update\n * @param {boolean} autoFetch - Whether to automatically fetch after updating params\n * @param {number} debounceMs - Optional debounce delay in milliseconds\n * @returns {Promise} Promise that resolves with REST response if autoFetch=true, or collection if autoFetch=false\n */\n async updateParams(newParams, autoFetch = false, debounceMs = 0) {\n return await this.setParams({ ...this.params, ...newParams }, autoFetch, debounceMs);\n }\n\n async setParams(newParams, autoFetch = false, debounceMs = 0) {\n this.params = newParams;\n if (autoFetch && this.restEnabled) {\n if (debounceMs > 0) {\n // Clear existing debounced fetch\n if (this.debouncedFetchTimeout) {\n clearTimeout(this.debouncedFetchTimeout);\n }\n\n // Cancel any active request since we're about to start a new one\n this.cancel();\n\n return new Promise((resolve, reject) => {\n this.debouncedFetchTimeout = setTimeout(async () => {\n try {\n const result = await this.fetch();\n resolve(result);\n } catch (error) {\n reject(error);\n }\n }, debounceMs);\n });\n } else {\n // For immediate fetches, the fetch method will handle cancellation\n return this.fetch();\n }\n }\n\n return Promise.resolve(this);\n }\n\n /**\n * Fetch a single model by ID\n * @param {string|number} id - Model ID to fetch\n * @param {object} options - Additional fetch options\n * @returns {Promise<Model|null>} Promise that resolves with model instance or null if not found\n */\n async fetchOne(id, options = {}) {\n if (!id) {\n console.warn('Collection: fetchOne requires an ID');\n return null;\n }\n\n if (!this.restEnabled) {\n console.info('Collection: REST disabled, cannot fetch single item');\n return null;\n }\n\n try {\n // Create model instance with the ID and use its fetch method\n const model = new this.ModelClass({ id }, {\n endpoint: this.endpoint,\n collection: this\n });\n\n const response = await model.fetch(options);\n\n if (response.success) {\n // Optionally add to collection if not already present\n if (options.addToCollection === true) {\n const existingModel = this.get(model.id);\n if (!existingModel) {\n this.add(model, { silent: options.silent });\n } else if (options.merge !== false) {\n existingModel.set(model.attributes);\n }\n }\n\n return model;\n } else {\n console.warn('Collection: fetchOne failed -', response.error || 'Unknown error');\n return null;\n }\n } catch (error) {\n console.error('Collection: fetchOne error -', error.message);\n return null;\n }\n }\n\n /**\n * Download collection data in a specified format\n * @param {string} format - The format for the download (e.g., 'csv', 'json')\n * @param {object} options - Download options\n * @returns {Promise} Promise that resolves with the download result\n */\n async download(format = 'json', options = {}) {\n if (!this.restEnabled) {\n console.warn('Collection: REST is not enabled, cannot download from remote.');\n // Here we could implement local data export in the future.\n return { success: false, message: 'Remote downloads are not enabled for this collection.' };\n }\n\n const url = this.buildUrl();\n const downloadParams = { ...this.params };\n\n // Remove pagination params for full export\n delete downloadParams.start;\n delete downloadParams.size;\n\n // Add format param\n downloadParams.download_format = format;\n\n // Provide a default filename and content type\n const filename = `export-${this.getModelName().toLowerCase()}.${format}`;\n const contentTypes = {\n json: 'application/json',\n csv: 'text/csv'\n };\n const acceptHeader = contentTypes[format] || '*/*';\n\n return this.rest.download(url, downloadParams, {\n ...options,\n filename,\n headers: { 'Accept': acceptHeader }\n });\n }\n\n /**\n * Parse response data - override in subclasses for custom parsing\n * @param {object} response - API response\n * @returns {array} Array of model data objects\n */\n parse(response) {\n // Handle standard paginated responses with size/start/count\n if (response.data && Array.isArray(response.data.data)) {\n this.meta = {\n size: response.data.size || 10,\n start: response.data.start || 0,\n count: response.data.count || 0,\n status: response.data.status,\n graph: response.data.graph,\n ...response.meta\n };\n return response.data.data;\n }\n\n // Handle direct array responses\n if (Array.isArray(response.data)) {\n return response.data;\n }\n\n // Fallback - assume response itself is the data array\n return Array.isArray(response) ? response : [response];\n }\n\n /**\n * Add model(s) to the collection\n * @param {object|array} data - Model data or array of model data\n * @param {object} options - Options for adding models\n */\n add(data, options = {}) {\n const modelsData = Array.isArray(data) ? data : [data];\n const addedModels = [];\n\n for (const modelData of modelsData) {\n let model;\n\n if (modelData instanceof this.ModelClass) {\n model = modelData;\n } else {\n model = new this.ModelClass(modelData, {\n endpoint: this.endpoint,\n collection: this\n });\n }\n\n // Check for duplicates\n const existingIndex = this.models.findIndex(m => m.id === model.id);\n if (existingIndex !== -1) {\n if (options.merge !== false) {\n // Update existing model\n this.models[existingIndex].set(model.attributes);\n }\n } else {\n // Add new model\n this.models.push(model);\n addedModels.push(model);\n }\n }\n\n // Emit events if not silent\n if (!options.silent && addedModels.length > 0) {\n this.emit('add', { models: addedModels, collection: this });\n this.emit('update', { collection: this });\n }\n\n return addedModels;\n }\n\n /**\n * Remove model(s) from the collection\n * @param {Model|array|string|number} models - Model(s) to remove or ID(s)\n * @param {object} options - Options\n */\n remove(models, options = {}) {\n const modelsToRemove = Array.isArray(models) ? models : [models];\n const removedModels = [];\n\n for (const model of modelsToRemove) {\n let index = -1;\n\n if (typeof model === 'string' || typeof model === 'number') {\n // Remove by ID\n index = this.models.findIndex(m => m.id == model);\n } else {\n // Remove by model instance\n index = this.models.indexOf(model);\n }\n\n if (index !== -1) {\n const removedModel = this.models.splice(index, 1)[0];\n removedModels.push(removedModel);\n }\n }\n\n // Emit events if not silent\n if (!options.silent && removedModels.length > 0) {\n this.emit('remove', { models: removedModels, collection: this });\n this.emit('update', { collection: this });\n }\n\n return removedModels;\n }\n\n /**\n * Reset the collection (remove all models)\n * @param {array} models - Optional new models to set\n * @param {object} options - Options\n */\n reset(models = null, options = {}) {\n const previousModels = [...this.models];\n this.models = [];\n\n if (models) {\n this.add(models, { silent: true, ...options });\n }\n\n if (!options.silent) {\n this.emit('reset', {\n collection: this,\n previousModels\n });\n }\n\n return this;\n }\n\n /**\n * Get model by ID\n * @param {string|number} id - Model ID\n * @returns {Model|undefined} Model instance or undefined\n */\n get(id) {\n return this.models.find(model => model.id == id);\n }\n\n /**\n * Get model by index\n * @param {number} index - Model index\n * @returns {Model|undefined} Model instance or undefined\n */\n at(index) {\n return this.models[index];\n }\n\n /**\n * Get collection length\n * @returns {number} Number of models in collection\n */\n length() {\n return this.models.length;\n }\n\n /**\n * Check if collection is empty\n * @returns {boolean} True if collection has no models\n */\n isEmpty() {\n return this.models.length === 0;\n }\n\n /**\n * Find models matching criteria\n * @param {function|object} criteria - Filter function or object with key-value pairs\n * @returns {array} Array of matching models\n */\n where(criteria) {\n if (typeof criteria === 'function') {\n return this.models.filter(criteria);\n }\n\n if (typeof criteria === 'object') {\n return this.models.filter(model => {\n return Object.entries(criteria).every(([key, value]) => {\n return model.get(key) === value;\n });\n });\n }\n\n return [];\n }\n\n /**\n * Find first model matching criteria\n * @param {function|object} criteria - Filter function or object with key-value pairs\n * @returns {Model|undefined} First matching model or undefined\n */\n findWhere(criteria) {\n const results = this.where(criteria);\n return results.length > 0 ? results[0] : undefined;\n }\n\n /**\n * Iterate over each model in the collection\n * @param {function} callback - Function to execute for each model (model, index, collection)\n * @param {object} thisArg - Optional value to use as this when executing callback\n * @returns {Collection} Returns the collection for chaining\n */\n forEach(callback, thisArg) {\n if (typeof callback !== 'function') {\n throw new TypeError('Callback must be a function');\n }\n\n this.models.forEach((model, index) => {\n callback.call(thisArg, model, index, this);\n });\n\n return this;\n }\n\n /**\n * Sort collection by comparator function\n * @param {function|string} comparator - Comparison function or attribute name\n * @param {object} options - Sort options\n */\n sort(comparator, options = {}) {\n if (typeof comparator === 'string') {\n const attr = comparator;\n comparator = (a, b) => {\n const aVal = a.get(attr);\n const bVal = b.get(attr);\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n };\n }\n\n this.models.sort(comparator);\n\n if (!options.silent) {\n this.trigger('sort', { collection: this });\n }\n\n return this;\n }\n\n /**\n * Convert collection to JSON array\n * @returns {array} Array of model JSON representations\n */\n toJSON() {\n return this.models.map(model => model.toJSON());\n }\n\n /**\n * Cancel any active fetch request\n * @returns {boolean} True if a request was cancelled, false if no active request\n */\n cancel() {\n if (this.currentRequest && this.abortController) {\n console.info('Collection: Manually cancelling active request');\n this.abortController.abort();\n return true;\n }\n return false;\n }\n\n /**\n * Check if collection has an active fetch request\n * @returns {boolean} True if fetch is in progress\n */\n isFetching() {\n return !!this.currentRequest;\n }\n\n /**\n * Build URL for collection endpoint\n * @returns {string} Collection API URL\n */\n buildUrl() {\n return this.endpoint;\n }\n\n // EventEmitter API: on, off, once, emit (from mixin).\n\n /**\n * Iterator support for for...of loops\n */\n *[Symbol.iterator]() {\n for (const model of this.models) {\n yield model;\n }\n }\n\n /**\n * Static method to create collection from array data\n * @param {function} ModelClass - Model class constructor\n * @param {array} data - Array of model data\n * @param {object} options - Collection options\n * @returns {Collection} New collection instance\n */\n static fromArray(ModelClass, data = [], options = {}) {\n const collection = new this(ModelClass, options);\n collection.add(data, { silent: true });\n return collection;\n }\n}\n\nObject.assign(Collection.prototype, EventEmitter);\n\nexport default Collection;\n","\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\n/**\n * Group Model - Represents an organization, team, or group entity\n *\n * Features:\n * - Hierarchical group support (parent/child relationships)\n * - Member management\n * - Search and filtering capabilities\n * - Role-based permissions within groups\n * - Metadata and settings management\n */\nclass Group extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/group'\n });\n }\n}\n\n/**\n * GroupCollection - Enhanced collection for managing groups with advanced search and filtering\n */\nclass GroupList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Group,\n endpoint: '/api/group',\n size: 10,\n ...options\n });\n }\n}\n\n/**\n * Form configurations for group management\n */\nconst GroupForms = {\n create: {\n title: 'Create Group',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name'\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n options: [\n { value: 'org', label: 'Organization' },\n { value: 'team', label: 'Team' },\n { value: 'department', label: 'Department' },\n { value: 'merchant', label: 'Merchant' },\n { value: 'iso', label: 'ISO' },\n { value: 'group', label: 'Group' }\n ]\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n }\n ]\n },\n\n edit: {\n title: 'Edit Group',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name',\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n options: [\n { value: 'org', label: 'Organization' },\n { value: 'division', label: 'Division' },\n { value: 'department', label: 'Department' },\n { value: 'team', label: 'Team' },\n { value: 'merchant', label: 'Merchant' },\n { value: 'partner', label: 'Partner' },\n { value: 'client', label: 'Client' },\n { value: 'iso', label: 'ISO' },\n { value: 'location', label: 'Location' },\n { value: 'region', label: 'Region' },\n { value: 'route', label: 'Route' },\n { value: 'project', label: 'Project' },\n { value: 'role', label: 'Role' },\n { value: 'test', label: 'Testing' }\n ]\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n },\n {\n name: 'metadata.domain',\n type: 'text',\n label: 'Default Domain',\n placeholder: 'Enter Domain',\n },\n {\n name: 'metadata.portal',\n type: 'text',\n label: 'Default Portal',\n placeholder: 'Enter Portal URL',\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n cols: 4\n },\n ]\n },\n\n detailed: {\n title: 'Group Details',\n fields: [\n // Profile Header\n {\n type: 'header',\n text: 'Profile Information',\n level: 4,\n class: 'text-primary mb-3'\n },\n\n // Avatar and Basic Info\n {\n type: 'group',\n columns: { xs: 12, md: 4 },\n fields: [\n {\n type: 'image',\n name: 'avatar',\n size: 'lg',\n imageSize: { width: 200, height: 200 },\n placeholder: 'Upload your avatar',\n help: 'Square images work best',\n columns: 12\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n columns: 12\n },\n ]\n },\n\n // Profile Details\n {\n type: 'group',\n columns: { xs: 12, md: 8 },\n title: 'Details',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name',\n columns: 12\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n columns: 12,\n options: [\n { value: 'org', label: 'Organization' },\n { value: 'team', label: 'Team' },\n { value: 'department', label: 'Department' },\n { value: 'merchant', label: 'Merchant' },\n { value: 'iso', label: 'ISO' },\n { value: 'group', label: 'Group' }\n ]\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n columns: 12\n }\n ]\n },\n\n // Account Settings\n {\n type: 'group',\n columns: 12,\n title: 'Account Settings',\n class: \"pt-3\",\n fields: [\n {\n type: 'select',\n name: 'metadata.timezone',\n label: 'Timezone',\n columns: 6,\n options: [\n { value: 'America/New_York', text: 'Eastern Time' },\n { value: 'America/Chicago', text: 'Central Time' },\n { value: 'America/Denver', text: 'Mountain Time' },\n { value: 'America/Los_Angeles', text: 'Pacific Time' },\n { value: 'UTC', text: 'UTC' }\n ]\n },\n {\n type: 'select',\n name: 'metadata.language',\n label: 'Language',\n columns: 6,\n options: [\n { value: 'en', text: 'English' },\n { value: 'es', text: 'Spanish' },\n { value: 'fr', text: 'French' },\n { value: 'de', text: 'German' }\n ]\n },\n {\n type: 'switch',\n name: 'metadata.notify.email',\n label: 'Email Notifications',\n columns: 4\n },\n {\n type: 'switch',\n name: 'metadata.profile_public',\n label: 'Public Profile',\n columns: 4\n }\n ]\n }\n ]\n },\n};\n\nGroup.EDIT_FORM = GroupForms.edit;\nGroup.CREATE_FORM = GroupForms.create;\n\nexport { Group, GroupList, GroupForms };\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\nimport { GroupList } from './Group.js';\n\nclass User extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user'\n });\n }\n\n hasPermission(permission) {\n if (Array.isArray(permission)) {\n return permission.some(p => this.hasPermission(p));\n }\n\n // Check if permission has \"sys.\" prefix\n const isSysPermission = permission.startsWith('sys.');\n const permissionToCheck = isSysPermission ? permission.substring(4) : permission;\n\n if (this._hasPermission(permissionToCheck)) {\n return true;\n }\n\n // Only check member permissions if it's not a system permission\n if (!isSysPermission && this.member && this.member.hasPermission(permission)) {\n return true;\n }\n\n return false;\n }\n\n _hasPermission(permission) {\n const permissions = this.get(\"permissions\");\n if (!permissions) {\n return false;\n }\n return permissions[permission] == true;\n }\n\n hasPerm(p) {\n return this.hasPermission(p);\n }\n}\n\nclass UserList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: User,\n endpoint: '/api/user',\n ...options\n });\n }\n}\n\nUser.PERMISSIONS = [\n { name: \"manage_users\", label: \"Manage Users\" },\n { name: \"view_groups\", label: \"View Groups\" },\n { name: \"manage_groups\", label: \"Manage Groups\" },\n { name: \"view_metrics\", label: \"View System Metrics\" },\n { name: \"manage_metrics\", label: \"Manage System Metrics\" },\n { name: \"view_logs\", label: \"View Logs\" },\n { name: \"view_incidents\", label: \"View Incidents\" },\n { name: \"manage_incidents\", label: \"Manage Incidents\" },\n { name: \"view_admin\", label: \"View Admin\" },\n { name: \"view_jobs\", label: \"View Jobs\" },\n { name: \"manage_jobs\", label: \"Manage Jobs\" },\n { name: \"view_global\", label: \"View Global\" },\n { name: \"manage_notifications\", label: \"Manage Notifications\" },\n { name: \"manage_files\", label: \"Manage Files\" },\n { name: \"force_single_session\", label: \"Force Single Session\" },\n { name: \"file_vault\", label: \"Access File Vault\" },\n { name: \"manage_aws\", label: \"Manage AWS\" },\n { name: \"manage_docit\", label: \"Manage DocIt\" }\n];\n\nconst UserForms = {\n create: {\n title: 'Create User',\n fields: [\n { name: 'email', type: 'text', label: 'Email', required: true },\n { name: 'phone_number', type: 'text', label: 'Phone number', columns: 12 },\n { name: 'display_name', type: 'text', label: 'Display Name' }\n ]\n },\n edit: {\n title: 'Edit User',\n fields: [\n { name: 'email', type: 'email', label: 'Email', columns: 12 },\n { name: 'display_name', type: 'text', label: 'Display Name', columns: 12},\n { name: 'phone_number', type: 'text', label: 'Phone number', columns: 12 },\n { type: 'collection', name: 'org', label: 'Organization', Collection: GroupList, labelField: 'name', valueField: 'id', columns: 12 },\n ]\n },\n permissions: {\n title: 'Edit Permissions',\n fields: [\n ...User.PERMISSIONS.map(permission => ({\n name: `permissions.${permission.name}`,\n type: 'switch',\n label: permission.label,\n columns: 4\n }))\n ]\n }\n};\n\n\n// DataView configuration for User model\nconst UserDataView = {\n // Basic user profile view\n profile: {\n title: 'User Profile',\n columns: 2,\n fields: [\n {\n name: 'id',\n label: 'User ID',\n type: 'number',\n columns: 3\n },\n {\n name: 'username',\n label: 'Username',\n type: 'text',\n format: 'lowercase',\n columns: 9\n },\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n columns: 3\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n columns: 6\n },\n ...User.PERMISSIONS.map(permission => ({\n name: `permissions.${permission.name}`,\n label: permission.label,\n format: \"boolean('on', 'off')|badge\",\n columns: 4\n }))\n ]\n },\n\n // Activity tracking view\n activity: {\n title: 'User Activity',\n columns: 2,\n fields: [\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n }\n ]\n },\n\n // Comprehensive view with all data\n detailed: {\n title: 'Detailed User Information',\n columns: 2,\n showEmptyValues: true,\n emptyValueText: 'Not set',\n fields: [\n // Basic Info Section\n {\n name: 'id',\n label: 'User ID',\n type: 'number',\n colSize: 3\n },\n {\n name: 'display_name',\n label: 'Display Name',\n type: 'text',\n format: 'capitalize|default(\"Unnamed User\")',\n colSize: 9\n },\n {\n name: 'username',\n label: 'Username',\n type: 'text',\n format: 'lowercase',\n colSize: 6\n },\n {\n name: 'email',\n label: 'Email Address',\n type: 'email',\n colSize: 6\n },\n {\n name: 'phone_number',\n label: 'Phone Number',\n type: 'phone',\n format: 'phone|default(\"Not provided\")',\n colSize: 6\n },\n {\n name: 'is_active',\n label: 'Account Status',\n type: 'boolean',\n colSize: 6\n },\n\n // Activity Info\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n\n // Avatar Info\n {\n name: 'avatar.url',\n label: 'Avatar',\n type: 'url',\n colSize: 12\n },\n\n // Complex Data (will use full width automatically)\n {\n name: 'permissions',\n label: 'User Permissions',\n type: 'dataview',\n dataViewColumns: 2,\n showEmptyValues: false\n },\n {\n name: 'metadata',\n label: 'User Metadata',\n type: 'dataview',\n dataViewColumns: 1\n },\n {\n name: 'avatar',\n label: 'Avatar Details',\n type: 'dataview',\n dataViewColumns: 1\n }\n ]\n },\n\n // Permissions-focused view\n permissions: {\n title: 'User Permissions',\n columns: 1,\n fields: [\n {\n name: 'display_name',\n label: 'User',\n type: 'text',\n format: 'capitalize',\n columns: 12\n },\n {\n name: 'permissions',\n label: 'Assigned Permissions',\n type: 'dataview',\n dataViewColumns: 3,\n showEmptyValues: false,\n colSize: 12\n }\n ]\n },\n\n // Compact summary view\n summary: {\n title: 'User Summary',\n columns: 3,\n fields: [\n {\n name: 'display_name',\n label: 'Name',\n type: 'text',\n format: 'capitalize|truncate(30)'\n },\n {\n name: 'email',\n label: 'Email',\n type: 'email'\n },\n {\n name: 'is_active',\n label: 'Status',\n type: 'boolean'\n },\n {\n name: 'last_activity',\n label: 'Last Seen',\n type: 'datetime',\n format: 'relative',\n colSize: 12\n }\n ]\n }\n};\n\nUser.DATA_VIEW = UserDataView.detailed;\nUser.EDIT_FORM = UserForms.edit;\nUser.ADD_FORM = UserForms.create;\n\n/* =========================\n * UserDevice\n * ========================= */\nclass UserDevice extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user/device',\n });\n }\n\n static async getByDuid(duid) {\n const model = new UserDevice();\n const resp = await model.rest.GET('/api/user/device/lookup', { duid: duid });\n if (resp.success && resp.data && resp.data.data) {\n // A direct lookup should return a single object\n return new UserDevice(resp.data.data);\n }\n return null;\n }\n}\n\nclass UserDeviceList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: UserDevice,\n endpoint: '/api/user/device',\n ...options,\n });\n }\n}\n\n/* =========================\n * UserDeviceLocation\n * ========================= */\nclass UserDeviceLocation extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user/device/location',\n });\n }\n}\n\nclass UserDeviceLocationList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: UserDeviceLocation,\n endpoint: '/api/user/device/location',\n ...options,\n });\n }\n}\n\nexport { User, UserList, UserForms, UserDataView, UserDevice, UserDeviceList, UserDeviceLocation, UserDeviceLocationList };\n","/**\n * ToastService - Bootstrap 5 Toast Notification Service\n * Provides methods to display toast notifications with different types and options\n *\n * Features:\n * - Bootstrap 5 toast integration\n * - Multiple toast types (success, error, info, warning)\n * - Auto-dismiss with customizable delays\n * - Toast container management\n * - Event integration\n * - Proper cleanup and memory management\n *\n * @example\n * const toastService = new ToastService();\n * toastService.success('Operation completed successfully!');\n * toastService.error('Something went wrong');\n * toastService.info('FYI: This is informational');\n * toastService.warning('Please be careful');\n */\n\nclass ToastService {\n constructor(options = {}) {\n this.options = {\n containerId: 'toast-container',\n position: 'top-end', // top-start, top-center, top-end, middle-start, etc.\n autohide: true,\n defaultDelay: 5000, // 5 seconds\n maxToasts: 5, // Maximum number of toasts to show at once\n ...options\n };\n\n this.toasts = new Map(); // Track active toasts\n this.toastCounter = 0; // For unique IDs\n \n this.init();\n }\n\n /**\n * Initialize the toast service\n */\n init() {\n this.createContainer();\n }\n\n /**\n * Create the toast container if it doesn't exist\n */\n createContainer() {\n let container = document.getElementById(this.options.containerId);\n \n if (!container) {\n container = document.createElement('div');\n container.id = this.options.containerId;\n container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n container.style.zIndex = '1070'; // Bootstrap toast z-index\n container.setAttribute('aria-live', 'polite');\n container.setAttribute('aria-atomic', 'true');\n \n document.body.appendChild(container);\n }\n \n this.container = container;\n }\n\n /**\n * Get CSS classes for toast positioning\n */\n getPositionClasses() {\n const positionMap = {\n 'top-start': 'top-0 start-0 p-3',\n 'top-center': 'top-0 start-50 translate-middle-x p-3',\n 'top-end': 'top-0 end-0 p-3',\n 'middle-start': 'top-50 start-0 translate-middle-y p-3',\n 'middle-center': 'top-50 start-50 translate-middle p-3',\n 'middle-end': 'top-50 end-0 translate-middle-y p-3',\n 'bottom-start': 'bottom-0 start-0 p-3',\n 'bottom-center': 'bottom-0 start-50 translate-middle-x p-3',\n 'bottom-end': 'bottom-0 end-0 p-3'\n };\n \n return positionMap[this.options.position] || positionMap['top-end'];\n }\n\n\n\n /**\n * Show a success toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n success(message, options = {}) {\n return this.show(message, 'success', {\n icon: 'bi-check-circle-fill',\n ...options\n });\n }\n\n /**\n * Show an error toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n error(message, options = {}) {\n return this.show(message, 'error', {\n icon: 'bi-exclamation-triangle-fill',\n autohide: false, // Keep error toasts visible until manually dismissed\n ...options\n });\n }\n\n /**\n * Show an info toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n info(message, options = {}) {\n return this.show(message, 'info', {\n icon: 'bi-info-circle-fill',\n ...options\n });\n }\n\n /**\n * Show a warning toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n warning(message, options = {}) {\n return this.show(message, 'warning', {\n icon: 'bi-exclamation-triangle-fill',\n ...options\n });\n }\n\n /**\n * Show a plain toast without specific styling\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n plain(message, options = {}) {\n return this.show(message, 'plain', {\n ...options\n });\n }\n\n /**\n * Show a toast with specified type and options\n * @param {string} message - The message to display\n * @param {string} type - Toast type (success, error, info, warning)\n * @param {object} options - Additional options\n */\n show(message, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n \n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: this.getDefaultTitle(type),\n icon: this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createToastElement(toastId, message, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n message: message\n });\n\n // Setup cleanup on hide\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanup(toastId);\n });\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding toast:', error);\n }\n },\n dispose: () => this.cleanup(toastId),\n updateProgress: options.updateProgress || null\n };\n }\n\n /**\n * Show a toast with a View component in the body\n * @param {View} view - The View component to display\n * @param {string} type - Toast type (success, error, info, warning, plain)\n * @param {object} options - Additional options\n */\n showView(view, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n \n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: options.title || this.getDefaultTitle(type),\n icon: options.icon || this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createViewToastElement(toastId, view, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference with view\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n view: view,\n message: 'View toast'\n });\n\n // Setup cleanup on hide - dispose view properly\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanupView(toastId);\n });\n\n // Mount and render the view\n const bodyContainer = toastElement.querySelector('.toast-view-body');\n if (bodyContainer && view) {\n view.render(true, bodyContainer);\n }\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n view: view,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding view toast:', error);\n }\n },\n dispose: () => this.cleanupView(toastId),\n updateProgress: (progressInfo) => {\n if (view && typeof view.updateProgress === 'function') {\n view.updateProgress(progressInfo);\n }\n }\n };\n }\n\n /**\n * Create toast DOM element\n */\n createToastElement(id, message, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createToastBody(message, config.icon && !config.title);\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast DOM element for View component\n */\n createViewToastElement(id, view, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createViewToastBody();\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast body for View component\n */\n createViewToastBody() {\n return `\n <div class=\"toast-body p-0\">\n <div class=\"toast-view-body p-3\"></div>\n </div>\n `;\n }\n\n /**\n * Create toast header with title and icon\n */\n createToastHeader(config, _type) {\n const iconHtml = config.icon ? \n `<i class=\"${config.icon} toast-service-icon me-2\"></i>` : '';\n \n const titleHtml = config.title ? \n `<strong class=\"me-auto\">${iconHtml}${this.escapeHtml(config.title)}</strong>` : '';\n\n const timeHtml = config.showTime ? \n `<small class=\"text-muted\">${this.getTimeString()}</small>` : '';\n\n const closeButton = config.dismissible ? \n `<button type=\"button\" class=\"btn-close toast-service-close\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>` : '';\n\n if (!titleHtml && !timeHtml && !closeButton) {\n return '';\n }\n\n return `\n <div class=\"toast-header\">\n ${titleHtml}\n ${timeHtml}\n ${closeButton}\n </div>\n `;\n }\n\n /**\n * Create toast body with message\n */\n createToastBody(message, showIcon = false) {\n const iconHtml = showIcon ? \n `<i class=\"${this.getDefaultIcon('info')} toast-service-icon me-2\"></i>` : '';\n \n return `\n <div class=\"toast-body d-flex align-items-center\">\n ${iconHtml}\n <span>${this.escapeHtml(message)}</span>\n </div>\n `;\n }\n\n /**\n * Get default title for toast type\n */\n getDefaultTitle(type) {\n const titles = {\n success: 'Success',\n error: 'Error',\n warning: 'Warning',\n info: 'Information',\n plain: ''\n };\n return titles[type] || 'Notification';\n }\n\n /**\n * Get default icon for toast type\n */\n getDefaultIcon(type) {\n const icons = {\n success: 'bi-check-circle-fill',\n error: 'bi-exclamation-triangle-fill',\n warning: 'bi-exclamation-triangle-fill',\n info: 'bi-info-circle-fill',\n plain: ''\n };\n return icons[type] || 'bi-info-circle-fill';\n }\n\n /**\n * Enforce maximum number of toasts\n */\n enforceMaxToasts() {\n const activeToasts = Array.from(this.toasts.values());\n \n if (activeToasts.length >= this.options.maxToasts) {\n // Remove oldest toast\n const oldestId = this.toasts.keys().next().value;\n const oldest = this.toasts.get(oldestId);\n \n if (oldest) {\n oldest.bootstrap.hide();\n }\n }\n }\n\n /**\n * Clean up toast resources\n */\n cleanup(toastId) {\n const toast = this.toasts.get(toastId);\n \n if (toast) {\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n \n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n \n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Clean up view toast resources with proper view disposal\n */\n cleanupView(toastId) {\n const toast = this.toasts.get(toastId);\n \n if (toast) {\n // Dispose view first if it exists\n if (toast.view && typeof toast.view.dispose === 'function') {\n try {\n toast.view.dispose();\n } catch (e) {\n console.warn('Error disposing view in toast:', e);\n }\n }\n \n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n \n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n \n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Hide all active toasts\n */\n hideAll() {\n this.toasts.forEach((toast, _id) => {\n toast.bootstrap.hide();\n });\n }\n\n /**\n * Clear all toasts immediately\n */\n clearAll() {\n this.toasts.forEach((toast, id) => {\n this.cleanup(id);\n });\n }\n\n /**\n * Get current time string\n */\n getTimeString() {\n return new Date().toLocaleTimeString([], { \n hour: '2-digit', \n minute: '2-digit' \n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n /**\n * Dispose of the entire toast service\n */\n dispose() {\n this.clearAll();\n \n if (this.container && this.container.parentNode) {\n this.container.parentNode.removeChild(this.container);\n }\n }\n\n /**\n * Get statistics about active toasts\n */\n getStats() {\n const stats = {\n total: this.toasts.size,\n byType: {}\n };\n \n this.toasts.forEach(toast => {\n stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;\n });\n \n return stats;\n }\n\n /**\n * Set global options\n */\n setOptions(newOptions) {\n this.options = { ...this.options, ...newOptions };\n \n // Recreate container if position changed\n if (newOptions.position) {\n if (this.container) {\n this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n }\n }\n }\n}\n\nexport default ToastService;"],"names":["Model","constructor","data","options","this","endpoint","id","attributes","_","originalAttributes","errors","loading","rest","idAttribute","timestamps","getContextValue","key","get","includes","MOJOUtils","getContextData","set","value","previousAttributes","hasChanged","Object","assign","JSON","stringify","oldValue","silent","emit","attr","val","entries","getData","getId","fetch","url","requiresId","Error","buildUrl","requestKey","params","debounceMs","_debouncedFetch","currentRequest","currentRequestKey","console","info","abortController","abort","now","Date","lastFetchTime","AbortController","_performFetch","error","name","debouncedFetchTimeout","clearTimeout","cancel","Promise","resolve","reject","setTimeout","async","result","graph","response","GET","signal","success","status","message","save","isNew","method","destroy","DELETE","isDirty","getChangedAttributes","changed","reset","endsWith","toJSON","validate","validations","field","rules","validateField","keys","length","rulesArray","Array","isArray","rule","required","minLength","maxLength","pattern","test","find","model","create","isFetching","showError","Dialog","alert","size","class","prototype","EventEmitter","Collection","ModelClass","models","meta","add","start","tmp","restEnabled","parse","preloaded","getModelName","additionalParams","rateLimiting","fetchParams","log","updateParams","newParams","autoFetch","setParams","fetchOne","warn","collection","addToCollection","existingModel","merge","download","format","downloadParams","download_format","filename","toLowerCase","acceptHeader","json","csv","headers","Accept","count","modelsData","addedModels","modelData","existingIndex","findIndex","m","push","remove","modelsToRemove","removedModels","index","indexOf","removedModel","splice","previousModels","at","isEmpty","where","criteria","filter","every","findWhere","results","forEach","callback","thisArg","TypeError","call","sort","comparator","a","b","aVal","bVal","trigger","map","Symbol","iterator","fromArray","Group","super","GroupList","GroupForms","title","fields","type","label","placeholder","labelField","valueField","maxItems","emptyFetch","edit","cols","detailed","text","level","columns","xs","md","imageSize","width","height","help","EDIT_FORM","CREATE_FORM","User","hasPermission","permission","some","p","isSysPermission","startsWith","permissionToCheck","substring","_hasPermission","member","permissions","hasPerm","PERMISSIONS","UserForms","UserDataView","profile","activity","colSize","showEmptyValues","emptyValueText","dataViewColumns","summary","DATA_VIEW","ADD_FORM","UserDevice","getByDuid","duid","resp","UserDeviceLocation","containerId","position","autohide","defaultDelay","maxToasts","toasts","Map","toastCounter","init","createContainer","container","document","getElementById","createElement","className","getPositionClasses","style","zIndex","setAttribute","body","appendChild","positionMap","show","icon","warning","plain","enforceMaxToasts","toastId","config","getDefaultTitle","getDefaultIcon","delay","dismissible","toastElement","createToastElement","bootstrap","bsToast","Toast","element","addEventListener","cleanup","hide","dispose","updateProgress","showView","view","createViewToastElement","cleanupView","bodyContainer","querySelector","render","progressInfo","toast","header","createToastHeader","createToastBody","innerHTML","createViewToastBody","_type","iconHtml","titleHtml","escapeHtml","timeHtml","showTime","getTimeString","closeButton","showIcon","from","values","oldestId","next","oldest","e","parentNode","removeChild","delete","hideAll","_id","clearAll","toLocaleTimeString","hour","minute","str","div","textContent","getStats","stats","total","byType","setOptions","newOptions"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use strict";const e="2.1.219",t="2025-09-23T03:04:54.763Z",s={full:e,major:2,minor:1,revision:219,buildTime:t,toString(){return this.full},compare(e){const t=e=>e.split(".").map(Number),[s,r,n]=t(this.full),[i,o,a]=t(e);return s!==i?s-i:r!==o?r-o:n-a}};"undefined"!=typeof window&&(window.MOJO=window.MOJO||{},window.MOJO.VERSION=e,window.MOJO.VERSION_INFO=s,window.MOJO.version=e);const r=Object.prototype.toString,n=Array.isArray||function(e){return"[object Array]"===r.call(e)},i=function(e){return"function"==typeof e},o=function(e){return null!=e&&"object"==typeof e},a=function(e){const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(e).replace(/[&<>"'`=\/]/g,function(e){return t[e]})};class Scanner{constructor(e){this.string=e,this.tail=e,this.pos=0}eos(){return""===this.tail}scan(e){const t=this.tail.match(e);if(!t||0!==t.index)return"";const s=t[0];return this.tail=this.tail.substring(s.length),this.pos+=s.length,s}scanUntil(e){const t=this.tail.search(e);let s;switch(t){case-1:s=this.tail,this.tail="";break;case 0:s="";break;default:s=this.tail.substring(0,t),this.tail=this.tail.substring(t)}return this.pos+=s.length,s}}class Context{constructor(e,t){this.view=e,this.cache={".":this.view},this.parent=t,this.view?._cacheId||this.view&&"object"==typeof this.view&&(this.view._cacheId=Math.random().toString(36).substring(2))}push(e){return new Context(e,this)}lookup(e){if(this.renderCache&&this.view?._cacheId){const t=`${this.view._cacheId}:${e}`;if(this.renderCache.has(t))return this.renderCache.get(t)}if("."===e)return this.view;if(e&&e.startsWith(".")){let t=e.substring(1),s=!1;if(t.endsWith("|iter")&&(t=t.substring(0,t.length-5),s=!0),this.view&&"object"==typeof this.view){let e;if("function"==typeof this.view.getContextValue)try{e=this.view.getContextValue(t),void 0!==e&&i(e)&&(e=e.call(this.view))}catch(r){e=void 0}return void 0===e&&t in this.view&&(e=this.view[t],i(e)&&(e=e.call(this.view))),n(e)?s?e:e.length>0:o(e)?s?Object.entries(e).map(([e,t])=>({key:e,value:t})):Object.keys(e).length>0:e}return}const t=this.cache;let s;if(t.hasOwnProperty(e))s=t[e];else{let n,i,o,a=this,c=!1;for(;a;){if(a.view&&"function"==typeof a.view.getContextValue)try{n=a.view.getContextValue(e),void 0!==n&&(c=!0)}catch(r){c=!1}if(!c)if(e.indexOf(".")>0)for(n=a.view,i=e.split("."),o=0;null!=n&&o<i.length;)if(n&&"function"==typeof n.getContextValue&&o<i.length)try{const e=i.slice(o).join(".");n=n.getContextValue(e),o=i.length,void 0!==n&&(c=!0)}catch(r){o===i.length-1&&(c=h(n,i[o])||l(n,i[o])),n=n[i[o++]]}else o===i.length-1&&(c=h(n,i[o])||l(n,i[o])),n=n[i[o++]];else n=a.view[e],c=h(a.view,e);if(c){s=n;break}a=a.parent}t[e]=s}if(i(s)&&(s=s.call(this.view)),this.renderCache&&this.view?._cacheId){const t=`${this.view._cacheId}:${e}`;this.renderCache.set(t,s)}return s}}function h(e,t){return null!=e&&"object"==typeof e&&t in e}function l(e,t){return null!=e&&"object"!=typeof e&&e.hasOwnProperty&&e.hasOwnProperty(t)}class Writer{constructor(){this.templateCache=/* @__PURE__ */new Map}clearCache(){this.templateCache.clear()}parse(e,t){const s=e+":"+(t=t||["{{","}}"]).join(":");let r=this.templateCache.get(s);return null==r&&(r=this.parseTemplate(e,t),this.templateCache.set(s,r)),r}parseTemplate(e,t){if(!e)return[];const s=t[0],r=t[1],n=new Scanner(e),i=[];let o,a,h,l,u;const d=new RegExp(c(s)+"\\s*"),p=new RegExp("\\s*"+c(r)),g=new RegExp("\\s*"+c("}"+r));for(;!n.eos();){if(o=n.pos,h=n.scanUntil(d),h)for(let e=0;e<h.length;++e)l=h.charAt(e),i.push(["text",l]);if(!n.scan(d))break;if(a=n.scan(/[#^\/>{&=!]/),a||(a="name"),n.scan(/\s*/),"="===a?(h=n.scanUntil(/\s*=/),n.scan(/\s*=/),n.scanUntil(p)):"{"===a?(h=n.scanUntil(g),n.scan(g),a="&"):h=n.scanUntil(p),n.scan(p),"#"===a||"^"===a)u=[a,h,o,n.pos],i.push(u);else if("/"===a){let e;for(let t=i.length-1;t>=0;--t)if(("#"===i[t][0]||"^"===i[t][0])&&i[t][1]===h){e=i[t];break}e&&4===e.length&&e.push(n.pos),i.push([a,h,o,n.pos])}else i.push([a,h,o,n.pos])}return this.nestSections(this.squashTokens(i))}squashTokens(e){const t=[];let s,r;for(let n=0;n<e.length;++n)s=e[n],s&&("text"===s[0]&&r&&"text"===r[0]?(r[1]+=s[1],r[3]=s[3]):(t.push(s),r=s));return t}nestSections(e){const t=[];let s=t;const r=[];for(let n=0;n<e.length;++n){const i=e[n];switch(i[0]){case"#":case"^":const e=[i[0],i[1],i[2],i[3],[],i[4]||null];s.push(e),r.push(e),s=e[4];break;case"/":const n=r.pop();n&&(n[5]=i[2],s=r.length>0?r[r.length-1][4]:t);break;default:s.push(i)}}return t}render(e,t,s,r){const n=this.getConfigTags(r)||["{{","}}"],i=this.parse(e,n),o=/* @__PURE__ */new Map;return this.renderTokens(i,new Context(t),s,e,r,o)}renderTokens(e,t,s,r,o,h){h&&!t.renderCache&&(t.renderCache=h);let l="";for(let c=0;c<e.length;++c){const u=e[c];let d;switch(u[0]){case"#":if(d=t.lookup(u[1]),!d)continue;const e=u[4];if(!e||!n(e)){console.warn(`MUSTACHE WARNING - Section ${u[1]} has no child tokens:`,u);continue}if(n(d))for(let n=0;n<d.length;++n){const i=t.push(d[n]);t.renderCache&&(i.renderCache=t.renderCache),l+=this.renderTokens(e,i,s,r,o,h)}else if("object"==typeof d||"string"==typeof d||"number"==typeof d){const n=t.push(d);t.renderCache&&(n.renderCache=t.renderCache),l+=this.renderTokens(e,n,s,r,o,h)}else if(i(d)){const e=null==r?null:r.slice(u[3],u[5]);d=d.call(t.view,e,e=>this.render(e,t.view,s,o)),null!=d&&(l+=d)}else d&&(l+=this.renderTokens(e,t,s,r,o,h));break;case"^":if(d=t.lookup(u[1]),!d||n(d)&&0===d.length){const e=u[4];e&&n(e)&&(l+=this.renderTokens(e,t,s,r,o,h))}break;case">":if(!s)continue;d=i(s)?s(u[1]):s[u[1]],null!=d&&(l+=this.render(d,t.view,s,o));break;case"&":d=t.lookup(u[1]),null!=d&&(l+=d);break;case"name":d=t.lookup(u[1]),null!=d&&(l+=a(d));break;case"text":l+=u[1]}}return l}getConfigTags(e){return o(e)&&n(e.tags)?e.tags:null}}function c(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}const u=new Writer,d={name:"MOJO Mustache",version:"1.0.0",tags:["{{","}}"],Scanner:Scanner,Context:Context,Writer:Writer,escape:a,clearCache:()=>u.clearCache(),parse:(e,t)=>u.parse(e,t),render(e,t,s,r){if("string"!=typeof e)throw new TypeError('Invalid template! Template should be a "string"');return u.render(e,t,s,r)}},p=new class{constructor(){this.formatters=/* @__PURE__ */new Map,this.registerBuiltInFormatters()}escapeHtml(e){if(null==e)return"";const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};return String(e).replace(/[&<>"']/g,e=>t[e])}registerBuiltInFormatters(){this.register("date",this.date.bind(this)),this.register("time",this.time.bind(this)),this.register("datetime",this.datetime.bind(this)),this.register("relative",this.relative.bind(this)),this.register("fromNow",this.relative.bind(this)),this.register("iso",this.iso.bind(this)),this.register("epoch",e=>{if(null==e||""===e)return e;const t=parseFloat(e);return isNaN(t)?e:1e3*t}),this.register("number",this.number.bind(this)),this.register("currency",this.currency.bind(this)),this.register("percent",this.percent.bind(this)),this.register("filesize",this.filesize.bind(this)),this.register("ordinal",this.ordinal.bind(this)),this.register("compact",this.compact.bind(this)),this.register("uppercase",e=>String(e).toUpperCase()),this.register("lowercase",e=>String(e).toLowerCase()),this.register("capitalize",this.capitalize.bind(this)),this.register("truncate",this.truncate.bind(this)),this.register("truncate_middle",this.truncate_middle.bind(this)),this.register("slug",this.slug.bind(this)),this.register("initials",this.initials.bind(this)),this.register("mask",this.mask.bind(this)),this.register("email",this.email.bind(this)),this.register("phone",this.phone.bind(this)),this.register("url",this.url.bind(this)),this.register("badge",this.badge.bind(this)),this.register("status",this.status.bind(this)),this.register("boolean",this.boolean.bind(this)),this.register("bool",this.boolean.bind(this)),this.register("yesno",e=>this.boolean(e,"Yes","No")),this.register("yesnoicon",this.yesnoicon.bind(this)),this.register("icon",this.icon.bind(this)),this.register("avatar",this.avatar.bind(this)),this.register("image",this.image.bind(this)),this.register("default",this.default.bind(this)),this.register("json",this.json.bind(this)),this.register("raw",e=>e),this.register("custom",(e,t)=>"function"==typeof t?t(e):e),this.register("iter",this.iter.bind(this)),this.register("keys",e=>e&&"object"==typeof e&&!Array.isArray(e)?Object.keys(e):null),this.register("values",e=>e&&"object"==typeof e&&!Array.isArray(e)?Object.values(e):null),this.register("plural",this.plural.bind(this)),this.register("list",this.formatList.bind(this)),this.register("duration",this.duration.bind(this)),this.register("hash",this.hash.bind(this)),this.register("stripHtml",this.stripHtml.bind(this)),this.register("highlight",this.highlight.bind(this)),this.register("pre",e=>`<pre class="bg-light p-2 rounded border">${this.escapeHtml(String(e))}</pre>`)}register(e,t){if("function"!=typeof t)throw new Error("Formatter must be a function, got "+typeof t);return this.formatters.set(e.toLowerCase(),t),this}apply(e,t,...s){try{const r=this.formatters.get(e.toLowerCase());return r?r(t,...s):(console.warn(`Formatter '${e}' not found`),t)}catch(r){return console.error(`Error in formatter '${e}':`,r),t}}pipe(e,t){return t?this.parsePipeString(t).reduce((e,t)=>this.apply(t.name,e,...t.args),e):e}parsePipeString(e){const t=[],s=e.split("|").map(e=>e.trim());for(const r of s){const e=this.parseFormatter(r);e&&t.push(e)}return t}parseFormatter(e){const t=e.match(/^([a-zA-Z_]\w*)\s*(?:\((.*)\))?$/);if(!t)return null;const[,s,r]=t;return{name:s,args:r?this.parseArguments(r):[]}}parseArguments(e){const t=[];let s="",r=!1,n=null,i=0;for(let o=0;o<e.length;o++){const a=e[o];r||'"'!==a&&"'"!==a?r&&a===n&&"\\"!==e[o-1]?(r=!1,n=null,s+=a):r||"{"!==a?r||"}"!==a?r||0!==i||","!==a?s+=a:(t.push(this.parseValue(s.trim())),s=""):(i--,s+=a):(i++,s+=a):(r=!0,n=a,s+=a)}return s.trim()&&t.push(this.parseValue(s.trim())),t}parseValue(e){if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if("true"===e)return!0;if("false"===e)return!1;if("null"===e)return null;if("undefined"!==e){if(!isNaN(e)&&""!==e)return Number(e);if(e.startsWith("{")&&e.endsWith("}"))try{return JSON.parse(e)}catch(t){}return e}}date(e,t="MM/DD/YYYY"){if(!e)return"";let s;if((e=this.normalizeEpoch(e))instanceof Date)s=e;else if("string"==typeof e)if(/^\d{4}-\d{2}-\d{2}$/.test(e)){const[t,r,n]=e.split("-").map(Number);s=new Date(t,r-1,n)}else s=new Date(e);else s=new Date(e);if(isNaN(s.getTime()))return String(e);const r={YYYY:s.getFullYear(),YY:String(s.getFullYear()).slice(-2),MMMM:s.toLocaleDateString("en-US",{month:"long"}),MMM:s.toLocaleDateString("en-US",{month:"short"}),MM:String(s.getMonth()+1).padStart(2,"0"),M:s.getMonth()+1,dddd:s.toLocaleDateString("en-US",{weekday:"long"}),ddd:s.toLocaleDateString("en-US",{weekday:"short"}),DD:String(s.getDate()).padStart(2,"0"),D:s.getDate()};let n=t;const i=new RegExp(`(${Object.keys(r).join("|")})`,"g");return n=n.replace(i,e=>r[e]||e),n}time(e,t="HH:mm:ss"){if(!e)return"";const s=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const r=s.getHours(),n={HH:String(r).padStart(2,"0"),H:r,hh:String(r%12||12).padStart(2,"0"),h:r%12||12,mm:String(s.getMinutes()).padStart(2,"0"),m:s.getMinutes(),ss:String(s.getSeconds()).padStart(2,"0"),s:s.getSeconds(),A:r>=12?"PM":"AM",a:r>=12?"pm":"am"};let i=t;const o=Object.keys(n).sort((e,t)=>t.length-e.length);for(const a of o)i=i.replace(new RegExp(a,"g"),n[a]);return i}datetime(e,t="MM/DD/YYYY",s="HH:mm"){e=this.normalizeEpoch(e);const r=this.date(e,t),n=this.time(e,s);return r&&n?`${r} ${n}`:""}normalizeEpoch(e){if("number"!=typeof e&&(e=Number(e)),isNaN(e))return"";if(e<1e11)return 1e3*e;if(e>1e12&&e<1e13)return e;throw new Error("Value doesn't look like epoch seconds or ms")}relative(e,t=!1){if(!e)return"";const s=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);if(isNaN(s.getTime()))return String(e);const r=/* @__PURE__ */new Date-s,n=Math.floor(r/1e3),i=Math.floor(n/60),o=Math.floor(i/60),a=Math.floor(o/24);if(t)return a>365?Math.floor(a/365)+"y":a>30?Math.floor(a/30)+"mo":a>7?Math.floor(a/7)+"w":a>0?a+"d":o>0?o+"h":i>0?i+"m":"now";if(a>365){const e=Math.floor(a/365);return e+" year"+(e>1?"s":"")+" ago"}if(a>30){const e=Math.floor(a/30);return e+" month"+(e>1?"s":"")+" ago"}if(a>7){const e=Math.floor(a/7);return e+" week"+(e>1?"s":"")+" ago"}return 1===a?"yesterday":a>0?a+" days ago":o>0?o+" hour"+(o>1?"s":"")+" ago":i>0?i+" minute"+(i>1?"s":"")+" ago":n>30?n+" seconds ago":"just now"}iso(e,t=!1){if(!e)return"";const s=(e=this.normalizeEpoch(e))instanceof Date?e:new Date(e);return isNaN(s.getTime())?String(e):t?s.toISOString().split("T")[0]:s.toISOString()}number(e,t=2,s="en-US"){const r=parseFloat(e);return isNaN(r)?String(e):r.toLocaleString(s,{minimumFractionDigits:t,maximumFractionDigits:t})}currency(e,t="$",s=2){const r=parseInt(e);if(isNaN(r))return String(e);const n=Math.abs(r).toString(),i=r<0?"-":"";let o,a,h;return n.length<=2?(o="0",a=n.padStart(2,"0")):(o=n.slice(0,-2),a=n.slice(-2)),o=o.replace(/\B(?=(\d{3})+(?!\d))/g,","),0===s?(parseInt(a)>=50&&(o=(parseInt(o.replace(/,/g,""))+1).toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")),h=o):h=2===s?`${o}.${a}`:`${o}.${a.slice(0,s).padEnd(s,"0")}`,i+t+h}percent(e,t=0,s=!0){const r=parseFloat(e);if(isNaN(r))return String(e);const n=s?100*r:r;return this.number(n,t)+"%"}filesize(e,t=!1,s=1){const r=parseInt(e);if(isNaN(r))return String(e);const n=t?["B","KiB","MiB","GiB","TiB"]:["B","KB","MB","GB","TB"],i=t?1024:1e3;let o=r,a=0;for(;o>=i&&a<n.length-1;)o/=i,a++;const h=0===a?0:s;return`${o.toFixed(h)} ${n[a]}`}ordinal(e,t=!1){const s=parseInt(e);if(isNaN(s))return String(e);const r=s%10,n=s%100;let i="th";return 1===r&&11!==n?i="st":2===r&&12!==n?i="nd":3===r&&13!==n&&(i="rd"),t?i:s+i}compact(e,t=1){const s=parseFloat(e);if(isNaN(s))return String(e);const r=Math.abs(s),n=s<0?"-":"";return r>=1e9?n+(r/1e9).toFixed(t)+"B":r>=1e6?n+(r/1e6).toFixed(t)+"M":r>=1e3?n+(r/1e3).toFixed(t)+"K":String(s)}capitalize(e,t=!0){const s=String(e);return s?t?s.replace(/\b\w/g,e=>e.toUpperCase()):s.charAt(0).toUpperCase()+s.slice(1):""}truncate(e,t=50,s="..."){const r=String(e);return r.length<=t?r:r.substring(0,t)+s}truncate_middle(e,t=8,s="***"){const r=String(e);if(r.length<=t)return r;const n=Math.floor(t/2);return`${r.substring(0,n)}${s}${r.substring(r.length-n)}`}slug(e,t="-"){return String(e).toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,t).replace(new RegExp(`${t}+`,"g"),t).replace(new RegExp(`^${t}|${t}$`,"g"),"")}initials(e,t=2){return String(e).split(/\s+/).filter(e=>e.length>0).slice(0,t).map(e=>e.charAt(0).toUpperCase()).join("")}mask(e,t="*",s=4){const r=String(e);return r.length<=s?r:t.repeat(Math.max(0,r.length-s))+r.slice(-s)}email(e,t={}){const s=String(e).trim();return s?!1===t.link?s:`<a href="mailto:${s}${t.subject?`?subject=${encodeURIComponent(t.subject)}`:""}${t.body?`&body=${encodeURIComponent(t.body)}`:""}"${t.class?` class="${t.class}"`:""}>${s}</a>`:""}phone(e,t="US",s=!0){let r=String(e).replace(/\D/g,""),n=r;return"US"===t&&(10===r.length?n=`(${r.slice(0,3)}) ${r.slice(3,6)}-${r.slice(6)}`:11===r.length&&"1"===r[0]&&(n=`+1 (${r.slice(1,4)}) ${r.slice(4,7)}-${r.slice(7)}`)),s?`<a href="tel:${r}">${n}</a>`:n}url(e,t=null,s=!0){let r=String(e).trim();return r?(/^https?:\/\//.test(r)||(r="https://"+r),`<a href="${r}"${s?' target="_blank"':""}${s?' rel="noopener noreferrer"':""}>${t||r}</a>`):""}badge(e,t="auto"){if(Array.isArray(e))return e.map(e=>this.badge(e,t)).join(" ");const s=String(e),r="auto"===t?this.inferBadgeType(s):t;return`<span class="badge ${r?`bg-${r}`:"bg-secondary"}">${s}</span>`}inferBadgeType(e){const t=e.toLowerCase();return["active","success","complete","completed","approved","done","true","on","yes"].includes(t)?"success":["error","failed","rejected","deleted","cancelled","false","off","no","declined"].includes(t)?"danger":["warning","pending","review","processing","uploading"].includes(t)?"warning":["info","new","draft"].includes(t)?"info":(["inactive","disabled","archived","suspended"].includes(t),"secondary")}status(e,t={},s={}){const r=String(e).toLowerCase(),n=t[r]||{active:"✓",inactive:"✗",pending:"⏳",success:"✓",error:"✗",warning:"⚠"}[r]||"";return`<span class="text-${s[r]||{active:"success",inactive:"secondary",pending:"warning",success:"success",error:"danger",warning:"warning"}[r]||"secondary"}">${n}${n?" ":""}${e}</span>`}boolean(e,t="True",s="False"){return e?t:s}icon(e,t={}){const s=t[String(e).toLowerCase()]||"";return s?`<i class="${s}"></i>`:""}yesnoicon(e,t="bi bi-check-circle-fill text-success",s="bi bi-x-circle-fill text-danger"){return e?`<i class="${t}"></i>`:`<i class="${s}"></i>`}image(e,t="thumbnail",s="img-fluid",r=""){const n=this._extractImageUrl(e,t);return n?`<img src="${n}" class="${s}" alt="${r}" />`:""}avatar(e,t="md",s="rounded-circle",r=""){const n=this._extractImageUrl(e,"square_sm")||"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2NlZDRkYSI+PHBhdGggZD0iTTEyIDEyYzIuMjEgMCA0LTEuNzkgNC00cy0xLjc5LTQtNC00LTQgMS43OS00IDQgMS43OSA0IDQgNHptMCAyYy0yLjY3IDAtOCAxLjM0LTggNHYyaDE2di0yYzAtMi42Ni01LjMzLTQtOC00eiIvPjwvc3ZnPg==",i={xs:"width: 1.5rem; height: 1.5rem;",sm:"width: 2rem; height: 2rem;",md:"width: 3rem; height: 3rem;",lg:"width: 4rem; height: 4rem;",xl:"width: 5rem; height: 5rem;"},o=i[t]||i.md;return`<img src="${n}" class="${`object-fit-cover ${s}`.trim()}" style="${o}" alt="${r}" />`}_extractImageUrl(e,t="thumbnail"){if(!e)return null;if("string"==typeof e)return e;if("object"==typeof e){if(e.attributes&&(e=e.attributes),"thumbnail"===t&&e.thumbnail&&"string"==typeof e.thumbnail)return e.thumbnail;if(e.renditions&&"object"==typeof e.renditions){const s=e.renditions[t];if(s&&s.url)return s.url;const r=Object.values(e.renditions);if(r.length>0&&r[0].url)return r[0].url}if(e.url)return e.url}return null}default(e,t=""){return null==e||""===e?t:e}plural(e,t,s=null,r=!0){if(null==e||null==t)return r?`${e} ${t}`:t||"";const n=parseInt(e);if(isNaN(n))return r?`${e} ${t}`:t||"";const i=1===Math.abs(n)?t:s||t+"s";return r?`${n} ${i}`:i}formatList(e,t={}){if(!Array.isArray(e))return String(e);const{conjunction:s="and",limit:r=null,moreText:n="others"}=t;if(0===e.length)return"";if(1===e.length)return String(e[0]);let i=e.slice(),o=!1;if(r&&e.length>r&&(i=e.slice(0,r),o=!0),o){const t=e.length-r;return`${i.join(", ")}, ${s} ${t} ${n}`}return 2===i.length?`${i[0]} ${s} ${i[1]}`:`${i.slice(0,-1).join(", ")}, ${s} ${i[i.length-1]}`}duration(e,t={}){const{short:s=!1,precision:r=2}=t;if(null==e)return"";const n=parseInt(e);if(isNaN(n))return String(e);const i=[{name:"day",short:"d",value:864e5},{name:"hour",short:"h",value:36e5},{name:"minute",short:"m",value:6e4},{name:"second",short:"s",value:1e3}];if(0===n)return s?"0s":"0 seconds";const o=Math.abs(n),a=n<0?"-":"",h=[];let l=o;for(const c of i)if(l>=c.value){const e=Math.floor(l/c.value);l%=c.value;const t=s?c.short:1===e?c.name:c.name+"s";if(h.push(s?`${e}${t}`:`${e} ${t}`),h.length>=r)break}return 0===h.length?s?`${Math.round(o)}ms`:`${Math.round(o)} milliseconds`:a+(s?h.join(""):h.join(" "))}hash(e,t=8,s="",r="..."){if(null==e)return"";const n=String(e);return n.length<=t?s+n:s+n.substring(0,t)+r}stripHtml(e){return null==e?"":String(e).replace(/<[^>]*>/g,"")}highlight(e,t,s="highlight"){if(null==e||!t)return String(e||"");const r=String(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`(${r})`,"gi");return String(e).replace(n,`<mark class="${s}">$1</mark>`)}json(e,t=2){try{return JSON.stringify(e,null,t)}catch(s){return String(e)}}has(e){return this.formatters.has(e.toLowerCase())}unregister(e){return this.formatters.delete(e.toLowerCase())}listFormatters(){return Array.from(this.formatters.keys()).sort()}iter(e){return null==e?[]:Array.isArray(e)?e:"object"==typeof e?Object.entries(e).map(([e,t])=>({key:e,value:t})):[{key:"0",value:e}]}};window.dataFormatter=p;class MOJOUtils{static getContextData(e,t){if(!t||null==e)return;let s=t,r="",n=0,i=-1;for(let a=0;a<t.length;a++){const e=t[a];if("("===e)n++;else if(")"===e)n--;else if("|"===e&&0===n){i=a;break}}i>-1&&(s=t.substring(0,i).trim(),r=t.substring(i+1).trim());const o=this.getNestedValue(e,s);return r?p.pipe(o,r):o}static getNestedValue(e,t){if(!t||null==e)return;if(!t.includes(".")){if(t in e){const s=e[t];return"function"==typeof s?s.call(e):s}return}const s=t.split(".");let r=e;for(let n=0;n<s.length;n++){const t=s[n];if(null==r)return;if(0===n){if(!r.hasOwnProperty(t))return;{const s=r[t];r="function"==typeof s?s.call(e):s}}else{if(r&&"function"==typeof r.getContextValue){const e=s.slice(n).join(".");return r.getContextValue(e)}if(Array.isArray(r)&&!isNaN(t))r=r[parseInt(t)];else if(r.hasOwnProperty(t))r=r[t];else{if("function"!=typeof r[t])return;r=r[t].call(r)}}}return r}static isNullOrUndefined(e){return null==e}static deepClone(e){if(null===e||"object"!=typeof e)return e;if(e instanceof Date)return new Date(e.getTime());if(e instanceof Array)return e.map(e=>this.deepClone(e));if(e instanceof Object){const t={};for(const s in e)e.hasOwnProperty(s)&&(t[s]=this.deepClone(e[s]));return t}}static deepMerge(e,...t){if(!t.length)return e;const s=t.shift();if(this.isObject(e)&&this.isObject(s))for(const r in s)this.isObject(s[r])?(e[r]||Object.assign(e,{[r]:{}}),this.deepMerge(e[r],s[r])):Object.assign(e,{[r]:s[r]});return this.deepMerge(e,...t)}static isObject(e){return e&&"object"==typeof e&&!Array.isArray(e)}static debounce(e,t){let s;return function(...r){clearTimeout(s),s=setTimeout(()=>{clearTimeout(s),e(...r)},t)}}static throttle(e,t){let s;return function(...r){s||(e.apply(this,r),s=!0,setTimeout(()=>s=!1,t))}}static generateId(e=""){const t=Date.now().toString(36),s=Math.random().toString(36).substr(2,9);return e?`${e}_${t}_${s}`:`${t}_${s}`}static escapeHtml(e){const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return String(e).replace(/[&<>"'`=\/]/g,e=>t[e])}static checkPasswordStrength(e){if(!e||"string"!=typeof e)return{score:0,strength:"invalid",feedback:["Password must be a non-empty string"],details:{length:0,hasLowercase:!1,hasUppercase:!1,hasNumbers:!1,hasSpecialChars:!1,hasCommonPatterns:!1,isCommonPassword:!1}};const t=[],s={length:e.length,hasLowercase:/[a-z]/.test(e),hasUppercase:/[A-Z]/.test(e),hasNumbers:/[0-9]/.test(e),hasSpecialChars:/[^a-zA-Z0-9]/.test(e),hasCommonPatterns:!1,isCommonPassword:!1};let r=0;e.length<6?t.push("Password should be at least 6 characters long"):e.length<8?(r+=1,t.push("Consider using at least 8 characters for better security")):e.length<12?r+=2:r+=3,s.hasLowercase?r+=1:t.push("Include lowercase letters"),s.hasUppercase?r+=1:t.push("Include uppercase letters"),s.hasNumbers?r+=1:t.push("Include numbers"),s.hasSpecialChars?r+=2:t.push("Include special characters (!@#$%^&* etc.)");const n=[/123/,/abc/i,/qwerty/i,/asdf/i,/(.)\1{2,}/,/password/i,/admin/i,/user/i,/login/i];for(const o of n)if(o.test(e)){s.hasCommonPatterns=!0,r-=1,t.push("Avoid common patterns and dictionary words");break}let i;return["123456","password","123456789","12345678","12345","1234567","1234567890","qwerty","abc123","111111","123123","admin","letmein","welcome","monkey","password123","123qwe","qwerty123","000000","dragon","sunshine","princess","azerty","1234","iloveyou","trustno1","superman","shadow","master","jennifer"].includes(e.toLowerCase())&&(s.isCommonPassword=!0,r=Math.max(0,r-3),t.push("This password is too common and easily guessed")),i=r<2?"very-weak":r<4?"weak":r<6?"fair":r<8?"good":"strong",r>=7&&0===t.length?t.push("Strong password! Consider using a password manager."):r>=5&&t.length<=1&&t.push("Good password strength. Consider adding more variety."),{score:Math.max(0,r),strength:i,feedback:t,details:s}}static generatePassword(e={}){const t={length:12,includeLowercase:!0,includeUppercase:!0,includeNumbers:!0,includeSpecialChars:!0,customChars:"",excludeAmbiguous:!1,...e};if(t.length<4)throw new Error("Password length must be at least 4 characters");let s="abcdefghijklmnopqrstuvwxyz",r="ABCDEFGHIJKLMNOPQRSTUVWXYZ",n="0123456789",i="!@#$%^&*()_+-=[]{}|;:,.<>?";t.excludeAmbiguous&&(s=s.replace(/[il]/g,""),r=r.replace(/[IOL]/g,""),n=n.replace(/[01]/g,""),i=i.replace(/[|]/g,""));let o="";const a=[];if(t.customChars?o=t.customChars:(t.includeLowercase&&(o+=s,a.push(s[Math.floor(Math.random()*s.length)])),t.includeUppercase&&(o+=r,a.push(r[Math.floor(Math.random()*r.length)])),t.includeNumbers&&(o+=n,a.push(n[Math.floor(Math.random()*n.length)])),t.includeSpecialChars&&(o+=i,a.push(i[Math.floor(Math.random()*i.length)]))),!o)throw new Error("No character types selected for password generation");let h="";for(const l of a)h+=l;for(let l=h.length;l<t.length;l++)h+=o[Math.floor(Math.random()*o.length)];return h.split("").sort(()=>Math.random()-.5).join("")}static parseQueryString(e){const t={},s=new URLSearchParams(e);for(const[r,n]of s.entries())t[r]=n;return t}static toQueryString(e){return new URLSearchParams(e).toString()}static wrapData(e,t=null,s=3){return e&&"object"==typeof e?e instanceof Date||e instanceof RegExp||e instanceof Error||s<=0||"function"==typeof e.getContextValue?e:Array.isArray(e)?e.map(e=>e&&"object"==typeof e&&!e.getContextValue?new DataWrapper(e,t):e):new DataWrapper(e,t):e}}class DataWrapper{constructor(e,t=null){if(Object.defineProperty(this,"_data",{value:e,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(this,"_rootContext",{value:t,writable:!1,enumerable:!1,configurable:!1}),e&&"object"==typeof e)for(const s in e)if(e.hasOwnProperty(s)){const r=e[s];this[s]=MOJOUtils.wrapData(r,t)}}getContextValue(e){let t,s=e,r="",n=0,i=-1;for(let o=0;o<e.length;o++){const t=e[o];if("("===t)n++;else if(")"===t)n--;else if("|"===t&&0===n){i=o;break}}return i>-1&&(s=e.substring(0,i).trim(),r=e.substring(i+1).trim()),t=this._data&&this._data.hasOwnProperty(s)?s in this&&"_data"!==s&&"_rootContext"!==s?this[s]:MOJOUtils.getNestedValue(this._data,s):void 0,r&&void 0!==t?p.pipe(t,r):t}has(e){return this._data&&this._data.hasOwnProperty(e)}toJSON(){return this._data}}MOJOUtils.DataWrapper=DataWrapper,"undefined"!=typeof window&&(window.utils=MOJOUtils);class EventDelegate{constructor(e){this.view=e,this.domListeners=[],this.debounceTimers=/* @__PURE__ */new Map}bind(e){if(this.unbind(),!e)return;const t=async e=>{const t=e.target.closest("[data-action]");if(t&&this.shouldHandle(t,e)){const s=t.getAttribute("data-action");if(await this.dispatch(s,e,t))return e.preventDefault(),e.stopPropagation(),void(e.handledByChild=!0)}const s=e.target.closest("a[href], [data-page]");if(s&&!s.hasAttribute("data-action")&&this.shouldHandle(s,e)){if(e.ctrlKey||e.metaKey||e.shiftKey||1===e.button)return;if("A"===s.tagName){const e=s.getAttribute("href");if(e&&"#"!==e&&!e.startsWith("#")&&(this.view.isExternalLink(e)||s.hasAttribute("data-external")))return}e.preventDefault(),e.stopPropagation(),e.handledByChild=!0,s.hasAttribute("data-page")?await this.view.handlePageNavigation(s):await this.view.handleHrefNavigation(s)}},s=e=>{const t=e.target.closest("[data-change-action]");if(!t||!this.shouldHandle(t,e))return;const s=t.getAttribute("data-change-action");this.dispatchChange(s,e,t).then(t=>{t&&(e.stopPropagation(),e.handledByChild=!0)})},r=e=>{const t=e.target.closest("[data-change-action]");if(!t||!this.shouldHandle(t,e))return;if(!e.target.matches('[data-filter="live-search"]'))return;const s=t.getAttribute("data-change-action"),r=parseInt(t.getAttribute("data-filter-debounce"))||300,n=`${s}-${t.getAttribute("data-container")||"default"}`;this.debounceTimers.has(n)&&clearTimeout(this.debounceTimers.get(n));const i=setTimeout(()=>{this.debounceTimers.delete(n),this.dispatchChange(s,e,t).then(t=>{t&&(e.stopPropagation(),e.handledByChild=!0)})},r);this.debounceTimers.set(n,i)},n=e=>{if(e.target.matches('[data-filter="search"]'))return;const t=e.target.closest("[data-change-action]");if(!t||!this.shouldHandle(t,e))return;let s=["Enter"];if(t.getAttribute("data-change-keys")&&(s=t.getAttribute("data-change-keys").split(",").map(e=>e.trim())),s.includes(e.key)){e.preventDefault();const s=t.getAttribute("data-change-action");this.dispatch(s,e,t).then(t=>{t&&(e.stopPropagation(),e.handledByChild=!0)})}},i=e=>{const t=e.target.closest("form[data-action]");if(!t||!this.shouldHandle(t,e))return;e.preventDefault();const s=t.getAttribute("data-action");this.dispatch(s,e,t)};e.addEventListener("click",t),e.addEventListener("change",s),e.addEventListener("input",r),e.addEventListener("keydown",n),e.addEventListener("submit",i),this.domListeners.push({el:e,type:"click",fn:t},{el:e,type:"change",fn:s},{el:e,type:"input",fn:r},{el:e,type:"keydown",fn:n},{el:e,type:"submit",fn:i})}unbind(){for(const{el:e,type:t,fn:s}of this.domListeners)e.removeEventListener(t,s);this.domListeners=[];for(const e of this.debounceTimers.values())clearTimeout(e);this.debounceTimers.clear()}hideDropdown(e){const t=e.closest(".dropdown-menu").closest(".dropdown").querySelector('[data-bs-toggle="dropdown"]');if(t&&window.bootstrap?.Dropdown){const e=window.bootstrap.Dropdown.getInstance(t);e?.hide()}}async dispatch(e,t,s){const r=this.view,n=e=>e.includes("-")?e.split("-").map(e=>e[0].toUpperCase()+e.slice(1)).join(""):e[0].toUpperCase()+e.slice(1),i=`handleAction${n(e)}`;if("function"==typeof r[i])try{return t.preventDefault(),await r[i](t,s),!0}catch(h){return console.error(`Error in action ${e}:`,h),r.handleActionError(e,h,t,s),!0}const o=`onAction${n(e)}`;if("function"==typeof r[o])try{return!!(await r[o](t,s))&&(!!s.closest(".dropdown-menu")&&this.hideDropdown(s),t.preventDefault(),t.stopPropagation(),!0)}catch(h){return console.error(`Error in action ${e}:`,h),r.handleActionError(e,h,t,s),!0}const a=`onPassThruAction${n(e)}`;if("function"==typeof r[a])try{return await r[a](t,s),!1}catch(h){return console.error(`Error in action ${e}:`,h),r.handleActionError(e,h,t,s),!0}if("function"==typeof r.onActionDefault)try{return await r.onActionDefault(e,t,s)}catch(h){return console.error(`Error in default action handler for ${e}:`,h),r.handleActionError(e,h,t,s),!0}return r.emit?.(`action:${e}`,{action:e,event:t,element:s}),!1}async dispatchChange(e,t,s){const r=this.view,n=`onChange${i=e,i.includes("-")?i.split("-").map(e=>e[0].toUpperCase()+e.slice(1)).join(""):i[0].toUpperCase()+i.slice(1)}`;var i;if("function"==typeof r[n])try{return await r[n](t,s),!0}catch(o){return console.error(`Error in onChange ${e}:`,o),r.handleActionError?.(e,o,t,s),!0}return await this.dispatch(e,t,s)}shouldHandle(e,t){return!!this.owns(e)||!(!this.contains(e)||t.handledByChild)}owns(e){const t=this.view.element;if(!t||!t.contains(e))return!1;for(const s of Object.values(this.view.children))if(s.element&&s.element.contains(e))return!1;return!0}contains(e){return!!this.view.element&&this.view.element.contains(e)}}const g={on(e,t,s){this._listeners||(this._listeners={}),this._listeners[e]||(this._listeners[e]=[]);const r={callback:t,context:s,fn:s?t.bind(s):t};return this._listeners[e].push(r),this},off(e,t,s){return this._listeners&&this._listeners[e]?(t?(this._listeners[e]=this._listeners[e].filter(e=>e.callback!==t||3===arguments.length&&e.context!==s),0===this._listeners[e].length&&delete this._listeners[e]):delete this._listeners[e],this):this},once(e,t,s){const r=(...n)=>{this.off(e,r),(s?t.bind(s):t).apply(s||this,n)};return this.on(e,r),this},emit(e,...t){if(!this._listeners||!this._listeners[e])return this;const s=this._listeners[e].slice();for(const n of s)try{n.fn.apply(n.context||this,t)}catch(r){console&&console.error&&console.error(`Error in ${e} event handler:`,r)}return this}};"undefined"!=typeof window&&(window.Mustache=d);class View{constructor(e={}){this.tagName=e.tagName??"div",this.className=e.className??"mojo-view",this.style=e.style??null,this.id=e.id??View._genId(),this.containerId=e.containerId??null,this.container=e.container??null,"string"==typeof this.container&&(this.containerId=this.container,this.container=null),this.parent=e.parent??null,this.children=e.children??{},this.template=e.template||e.templateUrl||"",this.data=e.data??{},this.isRendering=!1,this.lastRenderTime=0,this.mounted=!1,this.debug=e.debug??!1,this.app=e.app??null,this.cacheTemplate=e.cacheTemplate??!0,this.options={...e},this.element=this._ensureElement(),this.events=new EventDelegate(this),e.model&&this.setModel(e.model)}async onInit(){}async onInitView(){this.initialized||(this.initialized=!0,await this.onInit())}async onBeforeRender(){}async onAfterRender(){}async onBeforeMount(){}async onAfterMount(){}async onBeforeUnmount(){}async onAfterUnmount(){}async onBeforeDestroy(){}async onAfterDestroy(){}setModel(e={}){let t=e!==this.model;if(!t)return this;this.model&&this.model.off&&this.model.off("change",this._onModelChange,this),this.model=e,this.model&&this.model.on&&this.model.on("change",this._onModelChange,this);for(const s in this.children){const t=this.children[s];t&&"function"==typeof t.setModel&&t.setModel(e)}return t&&this._onModelChange(),this}_onModelChange(){this.isMounted()&&this.render()}setTemplate(e){return this.template=e??"",this}addChild(e){try{if(!e||"object"!=typeof e)return this;e.parent=this,this.getApp()&&(e.app=this.app),this.children[e.id]=e}catch(t){View._warn("addChild error",t)}return e}removeChild(e){try{const t="string"==typeof e?e:e&&e.id;if(!t)return this;const s=this.children[t];s&&(Promise.resolve(s.destroy()).catch(e=>View._warn("removeChild destroy error",e)),delete this.children[t])}catch(t){View._warn("removeChild error",t)}return this}getChild(e){return this.children[e]}async updateData(e,t=!1){return Object.assign(this.data,e),t&&this.isMounted()&&await this.render(),this}toggleClass(e,t){return void 0===t&&(t=!this.element.classList.contains(e)),this.element.classList.toggle(e,t),this}addClass(e){return this.element.classList.add(e),this}setClass(e){return this.element.className=e,this}removeClass(e){return this.element.classList.remove(e),this}canRender(){if(this.options.renderCooldown>0&&now-this.lastRenderTime<this.options.renderCooldown)return View._warn(`View ${this.id}: Render called too quickly, cooldown active`),!1;if(this.options.noAppend&&this.parent){if(!this.parent.contains(this.containerId||this.container))return!1;if(this.containerId&&!document.getElementById(this.containerId))return!1;if(this.container&&!document.contains(this.container))return!1}return!0}async render(e=!0,t=null){const s=Date.now();if(!this.canRender())return this;this.isRendering=!0,this.lastRenderTime=s;try{this.initialized||await this.onInitView(),this.unbindEvents(),await this.onBeforeRender(),this.getViewData&&(this.data=await this.getViewData());const s=await this.renderTemplate();this.element.innerHTML=s,e&&!this.isMounted()&&await this.mount(t),await this._renderChildren(),await this.onAfterRender(),this.bindEvents()}catch(r){View._warn(`Render error in ${this.id}`,r)}finally{this.isRendering=!1}return this}async _renderChildren(){for(const e in this.children){const t=this.children[e];t&&(t.parent=this,await Promise.resolve(t.render()).catch(t=>View._warn(`Child render error (${e})`,t)))}}async _unmountChildren(){for(const e in this.children){const t=this.children[e];t&&t.unbindEvents()}}isMounted(){return this.element?.isConnected}getChildElementById(e,t=null){const s=e.startsWith("#")?e.substring(1):e;return t?t.querySelector(`#${s}`):this.element.querySelector(`#${s}`)}getChildElement(e){if(e.startsWith("#"))return this.getChildElementById(e);let t=this.element?.querySelector(`[data-container="${e}"]`);return t||this.getChildElementById(e)}getContainer(){return this.replaceById?this.parent?this.parent.getChildElementById(this.id):null:this.containerId?this.parent?this.parent.getChildElement(this.containerId):this.getChildElementById(this.containerId,document.body):null}async mount(e=null){await this.onBeforeMount(),e||(e=this.getContainer()),!this.containerId||e?(e&&this.replaceById?e.replaceWith(this.element):e?e.replaceChildren(this.element):!this.containerId&&this.parent?this.parent.element.appendChild(this.element):this.containerId||this.parent?console.error(`Container not found for ${this.containerId}`):document.body.appendChild(this.element),await this.onAfterMount(),this.mounted=!0):console.error(`Container not found for ${this.containerId}`)}async unmount(){this.element&&this.element.parentNode&&(await this.onBeforeUnmount(),await this._unmountChildren(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.events.unbind(),await this.onAfterUnmount(),this.mounted=!1)}async destroy(){try{this.events.unbind();for(const e in this.children){const t=this.children[e];t&&await Promise.resolve(t.destroy()).catch(t=>View._warn(`Child destroy error (${e})`,t))}this.mounted=!1,this.element&&this.element.parentNode&&(await this.onBeforeDestroy(),this.element.parentNode&&this.element.parentNode.removeChild(this.element),await this.onAfterDestroy())}catch(e){View._warn(`Destroy error in ${this.id}`,e)}}_ensureElement(){try{if(this.element&&this.element.tagName?.toLowerCase()===this.tagName)return this._syncAttrs(),this.element;const e=document.createElement(this.tagName);return this.element=e,this.el=e,this._syncAttrs(),e}catch(e){View._warn("ensureElement error",e);const t=document.createElement("div");return t.id=this.id||View._genId(),t}}_syncAttrs(){try{if(!this.element)return;this.id&&(this.element.id=this.id),this.element.className=this.className||"",null==this.style?this.element.removeAttribute("style"):this.element.style.cssText=String(this.style)}catch(e){View._warn("_syncAttrs error",e)}}bindEvents(){this.events.bind(this.element)}unbindEvents(){this.events.unbind()}async renderTemplate(){const e=await this.getTemplate();if(!e)return"";const t=this.getPartials();return d.render(e,this,t)}renderTemplateString(e,t,s){return d.render(e,t,s)}getPartials(){return{}}async getTemplate(){if(this._templateCache&&this.cacheTemplate)return this._templateCache;const e=this.template||this.templateUrl;if(!e)throw new Error("Template not found");let t="";if("string"==typeof e)if(e.includes("<")||e.includes("{"))t=e;else try{let s=e;this.app||(this.app=this.getApp()),!this.app||!this.app.basePath||s.startsWith("/")||s.startsWith("http://")||s.startsWith("https://")||(s=`${this.app.basePath.endsWith("/")?this.app.basePath.slice(0,-1):this.app.basePath}/${s}`);const r=await fetch(s);if(!r.ok)throw new Error(`HTTP ${r.status}: ${r.statusText}`);t=await r.text()}catch(s){View._warn(`Failed to load template from ${e}: ${s}`),this.showError?.(`Failed to load template from ${e}: ${s.message}`)}else"function"==typeof e&&(t=await this.template(this.data,this.state));return this.cacheTemplate&&t&&(this._templateCache=t),t}getContextValue(e){const t=MOJOUtils.getContextData(this,e);return e&&e.startsWith("data.")&&t&&"object"==typeof t?MOJOUtils.wrapData(t,this):e&&e.startsWith("model.")&&"model"!==e&&t&&"object"==typeof t&&"function"!=typeof t.getContextValue?MOJOUtils.wrapData(t,null):t}async handlePageNavigation(e){const t=e.getAttribute("data-page"),s=e.getAttribute("data-params");let r={};if(s)try{r=JSON.parse(s)}catch(o){console.warn("Invalid JSON in data-params:",s)}const n=this.getApp();if(n)return void n.showPage(t,r);const i=this.findRouter();i&&"function"==typeof i.navigateToPage?await i.navigateToPage(t,r):console.error(`No router found for page navigation to '${t}'`)}async handleHrefNavigation(e){const t=e.getAttribute("href");if(this.isExternalLink(t)||e.hasAttribute("data-external"))return;const s=this.findRouter();if(s){if(s.options&&"param"===s.options.mode&&t.startsWith("?")){const e="/"+t;return void(await s.navigate(e))}if(s.options&&"hash"===s.options.mode&&t.startsWith("#"))return void(await s.navigate(t));const e=this.hrefToRoutePath(t);await s.navigate(e)}else console.warn("No router found for navigation, using default behavior"),window.location.href=t}isExternalLink(e){return!e||(e.startsWith("/")&&this.getApp()?!e.startsWith(this.findRouter().basePath):e.startsWith("#")||e.startsWith("mailto:")||e.startsWith("tel:")||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("//"))}hrefToRoutePath(e){if(e.startsWith("/")){const t=this.findRouter();if(t&&t.options&&t.options.base){const s=t.options.base;if(e.startsWith(s))return e.substring(s.length)||"/"}return e}return e.startsWith("./")?e.substring(2):e}findRouter(){return this.getApp(),this.app?.router||null}getApp(){if(this.app)return this.app;const e=[window.__app__,window.MOJO?.app,window.APP,window.app,window.WebApp,window.matchUUID?window[window.matchUUID]():window[window.matchUUID]];return this.app=e.find(e=>e&&"function"==typeof e.showPage)||null,this.app}handleActionError(e,t,s,r){this.showError(`Action '${e}' failed: ${t}`,s,r)}escapeHtml(e){if("string"!=typeof e)return e;const t=document.createElement("div");return t.textContent=e,t.innerHTML}contains(e){if("string"==typeof e){if(!this.element)return!1;e=document.getElementById(e)}return!!e&&this.element.contains(e)}async showError(e){console.error(`View ${this.id} error:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showError?await t.showError(e):alert(`Error: ${e}`)}async showSuccess(e){this.debug&&console.log(`View ${this.id} success:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showSuccess?await t.showSuccess(e):alert(`Success: ${e}`)}async showInfo(e){console.info(`View ${this.id} info:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showInfo?await t.showInfo(e):alert(`Info: ${e}`)}async showWarning(e){console.warn(`View ${this.id} warning:`,e);const t=this.getApp?this.getApp():this.app||null;t&&"function"==typeof t.showWarning?await t.showWarning(e):alert(`Warning: ${e}`)}static _genId(){return`view_${Math.random().toString(36).substr(2,9)}`}static _warn(e,t){try{t?console.warn(`[View] ${e}:`,t):console.warn(`[View] ${e}`)}catch{}}}Object.assign(View.prototype,g);class Router{constructor(e={}){this.defaultRoute=e.defaultRoute||"home",this.routes=[],this.currentRoute=null,this.eventEmitter=e.eventEmitter||null,this.boundHandlePopState=this.handlePopState.bind(this)}start(){window.addEventListener("popstate",this.boundHandlePopState),this.handleCurrentLocation()}stop(){window.removeEventListener("popstate",this.boundHandlePopState)}addRoute(e,t){this.routes.push({pattern:this.normalizePattern(e),regex:this.patternToRegex(e),pageName:t,paramNames:this.extractParamNames(e)})}async navigate(e,t={}){const{replace:s=!1,state:r=null,trigger:n=!0}=t,{pageName:i,queryParams:o}=this.parseInput(e);n&&await this.handleRouteChange(i,o)}back(){window.history.back()}forward(){window.history.forward()}getCurrentRoute(){return this.currentRoute}getCurrentPath(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();return this.buildPublicUrl(e,t)}handlePopState(e){this.handleCurrentLocation()}async handleCurrentLocation(){const{pageName:e,queryParams:t}=this.parseCurrentUrl();await this.handleRouteChange(e,t)}async handleRouteChange(e,t){const s="/"+e,r=this.matchRoute(s),n=this.buildPublicUrl(e,t);return r?(this.currentRoute=r,this.eventEmitter&&this.eventEmitter.emit("route:changed",{path:n,pageName:r.pageName,params:r.params,query:t,route:r}),r):(console.log("No route matched for page:",e),this.eventEmitter&&this.eventEmitter.emit("route:notfound",{path:n}),null)}matchRoute(e){for(const t of this.routes){const s=e.match(t.regex);if(s){const r={};return t.paramNames.forEach((e,t)=>{r[e]=s[t+1]}),{...t,params:r,path:e}}}return null}parseInput(e){let t=this.defaultRoute,s={};if(!e)return{pageName:t,queryParams:s};try{if(e.includes("?")){const[r,n]=e.split("?",2),i=new URLSearchParams(n);if(i.has("page")){t=i.get("page")||this.defaultRoute;for(const[e,t]of i)"page"!==e&&(s[e]=t)}else{t=r.startsWith("/")?r.substring(1)||this.defaultRoute:r||this.defaultRoute;for(const[e,t]of i)s[e]=t}}else t=e.startsWith("/")?e.substring(1)||this.defaultRoute:e}catch(r){console.warn("Failed to parse input:",e,r),t=this.defaultRoute,s={}}return{pageName:t,queryParams:s}}parseCurrentUrl(){const e=new URLSearchParams(window.location.search),t=e.get("page")||this.defaultRoute,s={};for(const[r,n]of e)"page"!==r&&(s[r]=n);return{pageName:t,queryParams:s}}buildPublicUrl(e,t={}){const s=new URLSearchParams;return s.set("page",e),Object.entries(t).forEach(([e,t])=>{null!=t&&""!==t&&s.set(e,String(t))}),"?"+s.toString()}updateBrowserUrl(e,t,s,r){const n=new URL(window.location.origin+window.location.pathname);n.searchParams.set("page",e),Object.entries(t).forEach(([e,t])=>{null!=t&&""!==t&&n.searchParams.set(e,String(t))});const i=n.toString();s?window.history.replaceState(r,"",i):window.history.pushState(r,"",i)}patternToRegex(e){let t=e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&").replace(/\/:([^/?]+)\?/g,"(?:/([^/]+))?").replace(/:([^/]+)/g,"([^/]+)");return new RegExp(`^${t}$`)}extractParamNames(e){return(e.match(/:([^/?]+)\??/g)||[]).map(e=>e.replace(/[:?]/g,""))}normalizePattern(e){return e.startsWith("/")?e:`/${e}`}updateUrl(e={},t={}){const{replace:s=!1}=t,{pageName:r}=this.parseCurrentUrl();this.updateBrowserUrl(r,e,s)}buildUrl(e,t={}){return this.buildPublicUrl(e,t)}doRoutesMatch(e,t){if(!e||!t)return!1;const{pageName:s}=this.parseInput(e),{pageName:r}=this.parseInput(t);return s===r}}const f=new class{constructor(){this.config={baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"},trackDevice:!0,duidHeader:"X-Mojo-UID",duidTransport:"header"},this.interceptors={request:[],response:[]},this.duid=null,this.config.trackDevice&&this._initializeDuid()}_initializeDuid(){const e="mojo_device_uid";try{let t=localStorage.getItem(e);t?this.duid=t:(this.duid=this._generateDuid(),localStorage.setItem(e,this.duid))}catch(t){console.error("Could not access localStorage to get/set DUID.",t),this.duid=this._generateDuid()}}_generateDuid(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})}configure(e){e.baseUrl&&(e.baseURL=e.baseUrl);const t=this.config.trackDevice;this.config={...this.config,...e,headers:{...this.config.headers,...e.headers}},this.config.trackDevice&&!t&&this._initializeDuid()}addInterceptor(e,t){this.interceptors[e]&&this.interceptors[e].push(t)}buildUrl(e){return e.startsWith("http://")||e.startsWith("https://")?e:`${this.config.baseURL.endsWith("/")?this.config.baseURL.slice(0,-1):this.config.baseURL}${e.startsWith("/")?e:`/${e}`}`}categorizeError(e,t=0){if("TypeError"===e.name&&e.message.includes("fetch"))return{reason:"not_reachable",message:"Service is not reachable - please check your connection"};if("AbortError"===e.name)return{reason:"cancelled",message:"Request was cancelled"};if("TimeoutError"===e.name||e.message.includes("timeout"))return{reason:"timed_out",message:"Request timed out - please try again"};if(t>=400){if(400===t)return{reason:"bad_request",message:"Invalid request data"};if(401===t)return{reason:"unauthorized",message:"Authentication required"};if(403===t)return{reason:"forbidden",message:"Access denied"};if(404===t)return{reason:"not_found",message:"Resource not found"};if(409===t)return{reason:"conflict",message:"Resource conflict"};if(422===t)return{reason:"validation_error",message:"Validation failed"};if(429===t)return{reason:"rate_limited",message:"Too many requests - please wait"};if(t>=500)return{reason:"server_error",message:"Server error - please try again later"};if(t>=400)return{reason:"client_error",message:"Request error"}}return e.message.includes("CORS")?{reason:"cors_error",message:"Cross-origin request blocked"}:e.message.includes("DNS")||e.message.includes("ENOTFOUND")?{reason:"dns_error",message:"Unable to resolve server address"}:{reason:"unknown_error",message:`Network error: ${e.message}`}}buildQueryString(e={}){const t=new URLSearchParams;Object.entries(e).forEach(([e,s])=>{null!=s&&(Array.isArray(s)?s.forEach(s=>t.append(`${e}[]`,s)):t.append(e,s))});const s=t.toString();return s?`?${s}`:""}async processRequestInterceptors(e){let t={...e};for(const r of this.interceptors.request)try{t=await r(t)}catch(s){throw console.error("Request interceptor error:",s),s}return t}async processResponseInterceptors(e,t){let s={success:e.ok,status:e.status,statusText:e.statusText,headers:Object.fromEntries(e.headers.entries()),data:null,errors:null,message:null,reason:null};try{const t=e.headers.get("content-type");if(t&&t.includes("application/json")){const t=await e.json();if(s.data=t,!e.ok){const r=this.categorizeError(new Error("HTTP Error"),e.status);s.errors=t.errors||{},s.message=t.message||r.message,s.reason=r.reason}}else if(s.data=await e.text(),!e.ok){const t=this.categorizeError(new Error("HTTP Error"),e.status);s.message=t.message,s.reason=t.reason}}catch(r){s.errors={parse:"Failed to parse response"},s.message="Invalid response format"}for(const n of this.interceptors.response)try{s=await n(s,t)}catch(r){console.error("Response interceptor error:",r)}return s}async request(e,t,s=null,r={},n={}){let i={method:e.toUpperCase(),url:this.buildUrl(t)+this.buildQueryString(r),headers:{...this.config.headers,...n.headers},data:s,options:{timeout:this.config.timeout,...n}};if(i=await this.processRequestInterceptors(i),this.config.trackDevice&&this.duid)if("header"===this.config.duidTransport)i.headers[this.config.duidHeader]=this.duid;else if("GET"===i.method){const e=new URL(i.url);e.searchParams.append("duid",this.duid),i.url=e.toString()}else!i.data||"object"!=typeof i.data||i.data instanceof FormData||(i.data.duid=this.duid);const o={method:i.method,headers:i.headers},a=[];i.options.timeout&&a.push(AbortSignal.timeout(i.options.timeout)),i.options.signal&&a.push(i.options.signal),a.length>1?o.signal=AbortSignal.any?AbortSignal.any(a):a[0]:1===a.length&&(o.signal=a[0]),i.data&&["POST","PUT","PATCH"].includes(i.method)&&(i.data instanceof FormData?(o.body=i.data,delete o.headers["Content-Type"]):"object"==typeof i.data?o.body=JSON.stringify(i.data):o.body=i.data);try{const e=await fetch(i.url,o);return await this.processResponseInterceptors(e,i)}catch(h){if("AbortError"===h.name)throw h;const e=this.categorizeError(h),t={success:!1,status:0,statusText:"Network Error",headers:{},data:null,errors:{network:h.message},message:e.message,reason:e.reason},s={ok:!1,status:0,statusText:"Network Error",headers:new Headers,json:async()=>({}),text:async()=>""};return await this.processResponseInterceptors(s,i),t}}async GET(e,t={},s={}){return this.request("GET",e,null,t,s)}async POST(e,t={},s={},r={}){return this.request("POST",e,t,s,r)}async PUT(e,t={},s={},r={}){return this.request("PUT",e,t,s,r)}async PATCH(e,t={},s={},r={}){return this.request("PATCH",e,t,s,r)}async DELETE(e,t={},s={}){return this.request("DELETE",e,null,t,s)}async download(e,t={},s={}){const r={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete r.headers["Content-Type"];try{const e=await fetch(r.url,{method:r.method,headers:r.headers,signal:r.options.signal});if(!e.ok)throw new Error(`Download failed: ${e.status} ${e.statusText}`);const t=e.headers.get("content-disposition");let n=s.filename||"download";if(t){const e=t.match(/filename="?(.+)"?/);e&&e.length>1&&(n=e[1])}const i=e.body.getReader(),o=new ReadableStream({start:e=>function t(){return i.read().then(({done:s,value:r})=>{if(!s)return e.enqueue(r),t();e.close()})}()}),a=await new Response(o).blob(),h=window.URL.createObjectURL(a),l=document.createElement("a");return l.style.display="none",l.href=h,l.download=n,document.body.appendChild(l),l.click(),window.URL.revokeObjectURL(h),l.remove(),{success:!0,message:"Download initiated"}}catch(n){return console.error("Download error:",n),{success:!1,message:n.message}}}async downloadBlob(e,t={},s={}){const r={method:"GET",url:this.buildUrl(e)+this.buildQueryString(t),headers:{...this.config.headers,Accept:"*/*",...s.headers},options:{...s}};delete r.headers["Content-Type"];try{const e=await fetch(r.url,{method:r.method,headers:r.headers,signal:r.options.signal});if(!e.ok)throw new Error(`Download failed: ${e.status} ${e.statusText}`);const t=await e.blob(),n=e.headers.get("content-disposition");let i=s.filename||"download";if(n){const e=n.match(/filename="?(.+)"?/);e&&e.length>1&&(i=e[1])}const o=window.URL.createObjectURL(t),a=document.createElement("a");return a.style.display="none",a.href=o,a.download=i,document.body.appendChild(a),a.click(),window.URL.revokeObjectURL(o),a.remove(),{success:!0,message:"Download initiated"}}catch(n){return console.error("Download error:",n),{success:!1,message:n.message}}}async upload(e,t,s={}){return new Promise((r,n)=>{if(!(t instanceof File))return void n(new Error("Only single File objects are supported for legacy backend compatibility"));const i=new XMLHttpRequest;s.onProgress&&"function"==typeof s.onProgress&&(i.upload.onprogress=s.onProgress),i.onload=function(){i.status>=200&&i.status<300?r({data:i.response,status:i.status,statusText:i.statusText,xhr:i}):n(new Error(`Upload failed: ${i.status} ${i.statusText}`))},i.onerror=function(){n(new Error("Upload failed: Network error"))},i.ontimeout=function(){n(new Error("Upload failed: Timeout"))},i.open("PUT",e),i.setRequestHeader("Content-Type",t.type),s.timeout&&(i.timeout=s.timeout),i.send(t)})}async uploadMultipart(e,t,s={},r={}){const n=new FormData;if(t instanceof FileList)Array.from(t).forEach((e,t)=>{n.append(`file_${t}`,e)});else if(t instanceof File)n.append("file",t);else if(t instanceof FormData)return this.POST(e,t,{},r);return Object.entries(s).forEach(([e,t])=>{n.append(e,t)}),this.POST(e,n,{},r)}setAuthToken(e,t="Bearer"){e?this.config.headers.Authorization=`${t} ${e}`:delete this.config.headers.Authorization}clearAuth(){delete this.config.headers.Authorization}isRetryableError(e){return["not_reachable","timed_out","server_error","dns_error"].includes(e.reason)}requiresAuth(e){return"unauthorized"===e.reason}isNetworkError(e){return["not_reachable","timed_out","cancelled","cors_error","dns_error"].includes(e.reason)}getUserMessage(e){return e.message?e.message:{not_reachable:"Unable to connect to the server. Please check your internet connection.",timed_out:"The request took too long. Please try again.",cancelled:"The request was cancelled.",unauthorized:"Please log in to continue.",forbidden:"You don't have permission to perform this action.",not_found:"The requested resource was not found.",validation_error:"Please check your input and try again.",rate_limited:"Too many requests. Please wait a moment before trying again.",server_error:"Server error. Please try again later.",cors_error:"Access blocked by security policy.",dns_error:"Unable to reach the server.",unknown_error:"An unexpected error occurred."}[e.reason]||"An error occurred. Please try again."}};class EventBus{constructor(){this.listeners={},this.onceListeners={},this.maxListeners=100,this.debugMode=!1,this.eventStats={}}on(e,t){if("function"!=typeof t)throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(e=>this.on(e,t)),this):(this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].length>=this.maxListeners&&console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${e}`),this.listeners[e].push(t),this)}once(e,t){if("function"!=typeof t)throw new Error("Callback must be a function");return Array.isArray(e)?(e.forEach(e=>this.once(e,t)),this):(this.onceListeners[e]||(this.onceListeners[e]=[]),this.onceListeners[e].push(t),this)}off(e,t){if(Array.isArray(e))return e.forEach(e=>this.off(e,t)),this;if(!t)return delete this.listeners[e],delete this.onceListeners[e],this;if(this.listeners[e]){const s=this.listeners[e].indexOf(t);-1!==s&&(this.listeners[e].splice(s,1),0===this.listeners[e].length&&delete this.listeners[e])}if(this.onceListeners[e]){const s=this.onceListeners[e].indexOf(t);-1!==s&&(this.onceListeners[e].splice(s,1),0===this.onceListeners[e].length&&delete this.onceListeners[e])}return this}emit(e,t){this.updateEventStats(e),this.debugMode&&console.log(`[EventBus] Emitting: ${e}`,t);const s=[];return this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]),this.debugMode&&s.length>0&&console.log(`[EventBus] ${s.length} listener(s) for '${e}'`),s.forEach(s=>{try{s(t,e)}catch(r){console.error(`Error in event listener for '${e}':`,r),this.emitError(r,e,s)}}),this}async emitAsync(e,t){const s=[];this.listeners[e]&&s.push(...this.listeners[e]),this.listeners["*"]&&s.push(...this.listeners["*"]),this.onceListeners[e]&&(s.push(...this.onceListeners[e]),delete this.onceListeners[e]),this.onceListeners["*"]&&(s.push(...this.onceListeners["*"]),delete this.onceListeners["*"]);const r=s.map(s=>new Promise(r=>{try{r(s(t,e))}catch(n){console.error(`Error in async event listener for '${e}':`,n),this.emitError(n,e,s),r()}}));return await Promise.all(r),this}removeAllListeners(){return this.listeners={},this.onceListeners={},this}listenerCount(e){return(this.listeners[e]?this.listeners[e].length:0)+(this.onceListeners[e]?this.onceListeners[e].length:0)}eventNames(){const e=Object.keys(this.listeners),t=Object.keys(this.onceListeners);return[.../* @__PURE__ */new Set([...e,...t])]}setMaxListeners(e){if("number"!=typeof e||e<0)throw new Error("Max listeners must be a non-negative number");return this.maxListeners=e,this}namespace(e){const t=t=>`${e}:${t}`;return{on:(e,s)=>this.on(t(e),s),once:(e,s)=>this.once(t(e),s),off:(e,s)=>this.off(t(e),s),emit:(e,s)=>this.emit(t(e),s),emitAsync:(e,s)=>this.emitAsync(t(e),s)}}use(e){if("function"!=typeof e)throw new Error("Middleware must be a function");const t=this.emit;return this.emit=(s,r)=>{try{const n=e(s,r);if(!1===n)return this;const i=void 0!==n?n:r;return t.call(this,s,i)}catch(n){return console.error("Error in event middleware:",n),t.call(this,s,r)}},this}emitError(e,t,s){"error"!==t&&setTimeout(()=>{this.emit("error",{error:e,originalEvent:t,callback:s.toString()})},0)}waitFor(e,t=null){return new Promise((s,r)=>{let n=null;const i=e=>{n&&clearTimeout(n),s(e)};this.once(e,i),t&&(n=setTimeout(()=>{this.off(e,i),r(new Error(`Timeout waiting for event: ${e}`))},t))})}debug(e=!0){return this.debugMode=e,e?console.log("[EventBus] Debug mode enabled"):console.log("[EventBus] Debug mode disabled"),this}getStats(){const e=this.eventNames(),t={totalEvents:e.length,totalListeners:0,events:{},emissions:{...this.eventStats}};return e.forEach(e=>{const s=this.listenerCount(e);t.events[e]=s,t.totalListeners+=s}),t}updateEventStats(e){this.eventStats[e]||(this.eventStats[e]={count:0,firstEmission:Date.now(),lastEmission:null}),this.eventStats[e].count++,this.eventStats[e].lastEmission=Date.now()}getEventStats(e){const t=this.eventStats[e];return t?{...t,listenerCount:this.listenerCount(e),avgEmissionsPerMinute:this.calculateEmissionRate(t)}:null}calculateEmissionRate(e){if(!e.firstEmission||!e.lastEmission)return 0;const t=e.lastEmission-e.firstEmission;if(0===t)return 0;const s=t/6e4;return Math.round(e.count/s*100)/100}resetStats(){return this.eventStats={},this}getTopEvents(e=10){return Object.entries(this.eventStats).map(([e,t])=>({event:e,count:t.count,rate:this.calculateEmissionRate(t),listeners:this.listenerCount(e)})).sort((e,t)=>t.count-e.count).slice(0,e)}debugInfo(){console.group("[EventBus] Debug Information"),console.log("Debug Mode:",this.debugMode),console.log("Max Listeners:",this.maxListeners);const e=this.getStats();return console.log("Total Events:",e.totalEvents),console.log("Total Listeners:",e.totalListeners),Object.keys(this.eventStats).length>0&&console.log("Top Events:",this.getTopEvents(5)),console.groupEnd(),this}}class WebApp{constructor(e={}){this.config=e,this.initPluginRegistry(),this.name=e.name||"MOJO App",this.version=e.version||"1.0.0",this.debug=e.debug||!1,this.container=e.container||"#app",this.layoutType=e.layout||"portal",this.layoutConfig=e.layoutConfig||{},e.sidebar&&(this.layoutConfig.sidebarConfig=e.sidebar),e.topbar&&(this.layoutConfig.topbarConfig=e.topbar),this.layout=null,this.layoutConfig.containerId=this.container||this.containerId||"#app",this.pageContainer=e.pageContainer||"#page-container",this.basePath=e.basePath||"",this.routerMode=e.routerMode||e.router?.mode||"param",this.basePath=e.basePath||e.router?.base||"",this.defaultRoute=e.defaultRoute||"home",this.session=e.session||{},this.router=null,this.navigation=e.navigation||{},this.state={currentPage:null,previousPage:null,loading:!1},this.events=new EventBus,this.rest=f,e.api&&this.rest.configure(e.api),this.router=new Router({mode:"param"===this.routerMode?"params":this.routerMode,basePath:this.basePath,defaultRoute:this.defaultRoute,eventEmitter:this.events}),this.events.on("route:changed",async e=>{const{pageName:t,params:s,query:r}=e;await this.showPage(t,r,s,{fromRouter:!0})}),"undefined"!=typeof window&&(window.MOJO=window.MOJO||{},window.MOJO.router=this.router),this.setupFocusTracking(),this.pageCache=/* @__PURE__ */new Map,this.pageClasses=/* @__PURE__ */new Map,this.componentClasses=/* @__PURE__ */new Map,this.modelClasses=/* @__PURE__ */new Map,this.currentPage=null,this.isStarted=!1,window.matchUUID?window[window.matchUUID]=this:window.MOJO?window.MOJO.app=this:window.__app__=this,console.log(`WebApp initialized: ${this.name} v${this.version}`)}async start(){if(this.isStarted)console.warn("WebApp already started");else try{console.log(`Starting ${this.name}...`),this.setupPageContainer(),this.validateDefaultRoute(),console.log("Setting up router..."),await this.setupRouter(),this.isStarted=!0,this.events.emit("app:ready",{app:this}),console.log(`✅ ${this.name} started successfully`)}catch(e){throw console.error(`Failed to start ${this.name}:`,e),this.showError("Failed to start application"),e}}async setupRouter(){this.router?(this.events.on("route:notfound",async e=>{console.warn(`Route not found: ${e.path}`),this._show404(e.path)}),this.router.start(),console.log(`Router started in ${this.routerMode} mode`)):console.error("Router not initialized")}setupPageContainer(){const e="string"==typeof this.container?document.querySelector(this.container):this.container;e&&!e.querySelector("#page-container")&&(e.innerHTML='<div id="page-container"></div>'),this.pageContainer="#page-container"}registerPage(e,t,s={}){if("string"!=typeof e||!e)return console.error("registerPage: pageName must be a non-empty string"),this;if("function"!=typeof t)return console.error("registerPage: PageClass must be a constructor function"),this;if(s.containerId||(s.containerId=this.pageContainer),this.pageClasses.set(e,{PageClass:t,constructorOptions:s}),this.router){let t=s.route||`/${e}`;t.startsWith("/")||(t=`/${t}`),s.route=t,this.router.addRoute(t,e),console.log(`📝 Registered route: "${t}" -> ${e}`)}return this}getPage(e){return this.pageCache.get(e)}getOrCreatePage(e){if(this.pageCache.has(e))return this.pageCache.get(e);const t=this.pageClasses.get(e);if(!t)return console.error(`Page not registered: ${e}`),null;const{PageClass:s,constructorOptions:r}=t;try{const t=new s({pageName:e,...r,app:this});return r.route&&(t.route=r.route),this.pageCache.set(e,t),console.log(`Created page: ${e} with route: ${t.route}`),t}catch(n){return console.error(`Failed to create page ${e}:`,n),null}}async showPage(e,t={},s={},r={}){const{fromRouter:n=!1,replace:i=!1,force:o=!1}=r;try{let r,i;"string"==typeof e?(i=e,r=this.getOrCreatePage(e)):e&&"object"==typeof e&&(r=e,i=e.pageName);const o=this.currentPage;if(!r)return void this._show404(i,s,t,n);if(!r.canEnter())return void this._showDeniedPage(r,s,t,n);o&&o!==r&&await this._exitOldPage(o),await r.onParams(s,t),o!==r&&await r.onEnter(),r.syncUrl(),this.events.emit("page:show",{page:r,pageName:r.pageName,params:s,query:t,fromRouter:n}),await r.render(),this.currentPage=r,console.log(`✅ Showing page: ${r.pageName}`,{query:t,params:s})}catch(a){console.error("Error in showPage:",a),this.showError(`Failed to load page: ${a.message}`),"error"!==e&&await this.showPage("error",{},{error:a,originalPage:e},{fromRouter:n})}}async _show404(e,t,s,r){const n=this.getOrCreatePage("404");n&&(n.setInfo&&n.setInfo(e),await this._exitOldPage(this.currentPage),await n.render(),this.currentPage=n,this.events.emit("page:404",{page:null,pageName:e,params:t,query:s,fromRouter:r}))}async _showDeniedPage(e,t,s,r){const n=this.getOrCreatePage("denied");n.setDeniedPage&&n.setDeniedPage(e),await this._exitOldPage(this.currentPage),await n.render(),this.currentPage=n,this.events.emit("page:denied",{page:e,pageName:e.pageName,params:t,query:s,fromRouter:r})}async _exitOldPage(e){if(e)try{await e.onExit(),await e.unmount(),this.events.emit("page:hide",{page:e})}catch(t){console.error(`Error exiting page ${e.pageName}:`,t)}}async navigate(e,t={},s={}){if(!this.router)return void console.error("Router not initialized");console.log("🧭 WebApp.navigate:",{route:e,query:t,options:s});let r=e;if(Object.keys(t).length>0){const s=new URLSearchParams(t).toString();r+=(e.includes("?")?"&":"?")+s}return await this.router.navigate(r,s)}async navigateToDefault(e={}){return await this.showPage(this.defaultRoute,{},{},e)}back(){this.router?this.router.back():console.warn("Router not initialized")}forward(){this.router?this.router.forward():console.warn("Router not initialized")}getCurrentPage(){return this.currentPage}getPageContainer(){return this.layout&&this.layout.getPageContainer?this.layout.getPageContainer():"string"==typeof this.pageContainer?document.querySelector(this.pageContainer):this.pageContainer}async showError(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default;await t.alert(e,"Error",{size:"md",class:"text-danger"})}catch(t){this.events.emit("notification",{message:e,type:"error"}),"undefined"!=typeof window&&window?.console&&console.error("[WebApp] showError fallback:",t),"undefined"!=typeof window&&alert(`Error: ${e}`)}}async showSuccess(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default;await t.alert(e,"Success",{size:"md",class:"text-success"})}catch(t){this.events.emit("notification",{message:e,type:"success"}),"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] showSuccess fallback:",t),"undefined"!=typeof window&&alert(`Success: ${e}`)}}async showInfo(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default;await t.alert(e,"Information",{size:"md",class:"text-info"})}catch(t){this.events.emit("notification",{message:e,type:"info"}),"undefined"!=typeof window&&window?.console&&console.info("[WebApp] showInfo fallback:",t),"undefined"!=typeof window&&alert(`Info: ${e}`)}}async showWarning(e){try{const t=(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default;await t.alert(e,"Warning",{size:"md",class:"text-warning"})}catch(t){this.events.emit("notification",{message:e,type:"warning"}),"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] showWarning fallback:",t),"undefined"!=typeof window&&alert(`Warning: ${e}`)}}showNotification(e,t="info"){this.events.emit("notification",{message:e,type:t})}async showLoading(e={}){"string"==typeof e&&(e={message:e});try{(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default.showBusy(e)}catch(t){"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] showLoading fallback:",t,e),this.events.emit("notification",{message:e.message||"Loading...",type:"info"})}}async hideLoading(){try{(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default.hideBusy()}catch(e){"undefined"!=typeof window&&window?.console&&console.warn("[WebApp] hideLoading fallback:",e)}}async showModelForm(e={}){try{const t=(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default;return await t.showModelForm(e)}catch(t){throw"undefined"!=typeof window&&window?.console&&console.error("[WebApp] showModelForm failed:",t),t}}async showForm(e={}){try{const t=(await Promise.resolve().then(()=>require("./Dialog-CxJjXDJ-.js"))).default;return await t.showForm(e)}catch(t){throw"undefined"!=typeof window&&window?.console&&console.error("[WebApp] showForm failed:",t),t}}setupFocusTracking(){if("undefined"==typeof window)return;this.isFocused=!document.hidden;const e=()=>{const e=this.isFocused;this.isFocused=!document.hidden,e!==this.isFocused&&(this.isFocused?(console.log("🔥 Browser gained focus"),this.events.emit("browser:focus")):(console.log("💤 Browser lost focus"),this.events.emit("browser:blur")))},t=()=>{this.isFocused||(this.isFocused=!0,console.log("🔥 Browser gained focus"),this.events.emit("browser:focus"))},s=()=>{this.isFocused&&(this.isFocused=!1,console.log("💤 Browser lost focus"),this.events.emit("browser:blur"))};document.addEventListener("visibilitychange",e),window.addEventListener("focus",t),window.addEventListener("blur",s),this._focusHandlers={visibilitychange:e,focus:t,blur:s}}setupErrorHandling(){window.addEventListener("error",e=>{console.error("Global error:",e.error),this.debug&&this.showError(`Error: ${e.error?.message||"Unknown error"}`)}),window.addEventListener("unhandledrejection",e=>{console.error("Unhandled promise rejection:",e.reason),this.debug&&this.showError(`Promise rejected: ${e.reason?.message||"Unknown error"}`)})}escapeHtml(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}getState(e){return e?this.state[e]:this.state}setState(e){const t={...this.state};Object.assign(this.state,e),this.events.emit("state:changed",{oldState:t,newState:this.state,updates:e})}registerComponent(e,t){this.componentClasses.set(e,t)}getComponent(e){return this.componentClasses.get(e)}registerModel(e,t){this.modelClasses.set(e,t)}getModel(e){return this.modelClasses.get(e)}setupRest(){this.rest=f,f.configure(this.api)}async destroy(){console.log("Destroying WebApp..."),this.router&&this.router.stop(),this._focusHandlers&&"undefined"!=typeof window&&(document.removeEventListener("visibilitychange",this._focusHandlers.visibilitychange),window.removeEventListener("focus",this._focusHandlers.focus),window.removeEventListener("blur",this._focusHandlers.blur));const e=Array.from(this.pageCache.values());if(await Promise.allSettled(e.map(async e=>{try{e.destroy&&await e.destroy()}catch(t){console.error("Error destroying page:",t)}})),this.layout&&this.layout.destroy)try{await this.layout.destroy()}catch(t){console.error("Error destroying layout:",t)}this.pageCache.clear(),this.pageClasses.clear(),this.componentClasses.clear(),this.modelClasses.clear(),"undefined"!=typeof window&&window.MOJO&&delete window.MOJO.router,this.isStarted=!1,console.log(`✨ ${this.name} destroyed`)}buildPagePath(e,t,s){let r=e.route||`/${e.pageName.toLowerCase()}`;if(Object.keys(t).forEach(e=>{"string"!=typeof t[e]&&"number"!=typeof t[e]||(r=r.replace(`:${e}`,t[e]))}),s&&Object.keys(s).length>0){const e=new URLSearchParams(s).toString();r+=(r.includes("?")?"&":"?")+e}return r}validateDefaultRoute(){this.pageClasses.has(this.defaultRoute)?console.log(`✅ Default route '${this.defaultRoute}' is registered`):(console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`),console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`),console.warn(" Or change default route: new WebApp({ defaultRoute: 'your-page' });"))}findFallbackPage(){const e=["404","error","denied"];for(const[t]of this.pageClasses.entries())if(!e.includes(t))return t;return null}static create(e={}){return new WebApp(e)}initPluginRegistry(){"undefined"!=typeof window&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.app=this)}static registerPlugin(e,t){"undefined"!=typeof window&&(window.MOJO||(window.MOJO={}),window.MOJO.plugins||(window.MOJO.plugins={}),window.MOJO.plugins[e]=t)}}exports.BUILD_TIME=t,exports.DataWrapper=DataWrapper,exports.EventBus=EventBus,exports.EventDelegate=EventDelegate,exports.EventEmitter=g,exports.MOJOUtils=MOJOUtils,exports.Mustache=d,exports.Router=Router,exports.VERSION=e,exports.VERSION_INFO=s,exports.VERSION_MAJOR=2,exports.VERSION_MINOR=1,exports.VERSION_REVISION=219,exports.View=View,exports.WebApp=WebApp,exports.dataFormatter=p,exports.rest=f;
2
- //# sourceMappingURL=WebApp-D35UJNMm.js.map