web-mojo 2.1.1043 → 2.1.1087

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 (119) hide show
  1. package/README.md +19 -16
  2. package/dist/admin.cjs.js +1 -1
  3. package/dist/admin.cjs.js.map +1 -1
  4. package/dist/admin.es.js +16 -14
  5. package/dist/admin.es.js.map +1 -1
  6. package/dist/auth.cjs.js +1 -1
  7. package/dist/auth.cjs.js.map +1 -1
  8. package/dist/auth.css +305 -266
  9. package/dist/auth.es.js +537 -2175
  10. package/dist/auth.es.js.map +1 -1
  11. package/dist/charts.cjs.js +1 -1
  12. package/dist/charts.cjs.js.map +1 -1
  13. package/dist/charts.css +319 -0
  14. package/dist/charts.es.js +555 -2
  15. package/dist/charts.es.js.map +1 -1
  16. package/dist/chunks/ChatView-BnC15uoD.js +2 -0
  17. package/dist/chunks/{ChatView-CGBaudUc.js.map → ChatView-BnC15uoD.js.map} +1 -1
  18. package/dist/chunks/{ChatView-DguKw-gR.js → ChatView-D-5lHZ5H.js} +7 -8
  19. package/dist/chunks/{ChatView-DguKw-gR.js.map → ChatView-D-5lHZ5H.js.map} +1 -1
  20. package/dist/chunks/{Collection-DD1_31eh.js → Collection-B64LJ92k.js} +2 -2
  21. package/dist/chunks/{Collection-DD1_31eh.js.map → Collection-B64LJ92k.js.map} +1 -1
  22. package/dist/chunks/{Collection-YRfGoT73.js → Collection-CsAk0UhA.js} +2 -2
  23. package/dist/chunks/{Collection-YRfGoT73.js.map → Collection-CsAk0UhA.js.map} +1 -1
  24. package/dist/chunks/ContextMenu-CfMAB33c.js +3 -0
  25. package/dist/chunks/ContextMenu-CfMAB33c.js.map +1 -0
  26. package/dist/chunks/{ContextMenu-B4_YS0G8.js → ContextMenu-Cvls3QC_.js} +350 -3
  27. package/dist/chunks/ContextMenu-Cvls3QC_.js.map +1 -0
  28. package/dist/chunks/DataView-DESqBxT-.js +2 -0
  29. package/dist/chunks/DataView-DESqBxT-.js.map +1 -0
  30. package/dist/chunks/{DataView-OUqaLmGB.js → DataView-QXyfcg2M.js} +3 -2
  31. package/dist/chunks/DataView-QXyfcg2M.js.map +1 -0
  32. package/dist/chunks/Dialog-BfXN-fFA.js +2 -0
  33. package/dist/chunks/Dialog-BfXN-fFA.js.map +1 -0
  34. package/dist/chunks/{Dialog-BiVgKzSK.js → Dialog-DHUsZ92-.js} +1375 -7
  35. package/dist/chunks/Dialog-DHUsZ92-.js.map +1 -0
  36. package/dist/chunks/{FormView-BClEkzmE.js → FormView-DGRmcKUG.js} +282 -123
  37. package/dist/chunks/FormView-DGRmcKUG.js.map +1 -0
  38. package/dist/chunks/FormView-KGvr68ju.js +3 -0
  39. package/dist/chunks/FormView-KGvr68ju.js.map +1 -0
  40. package/dist/chunks/{ListView-BMNhd5-B.js → ListView-BGJG4GYH.js} +3 -3
  41. package/dist/chunks/{ListView-BMNhd5-B.js.map → ListView-BGJG4GYH.js.map} +1 -1
  42. package/dist/chunks/{ListView-BRGiITfD.js → ListView-BpGEatee.js} +2 -2
  43. package/dist/chunks/{ListView-BRGiITfD.js.map → ListView-BpGEatee.js.map} +1 -1
  44. package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js → MetricsMiniChartWidget-BKbFGvXG.js} +4 -4
  45. package/dist/chunks/{MetricsMiniChartWidget-CCroU6BZ.js.map → MetricsMiniChartWidget-BKbFGvXG.js.map} +1 -1
  46. package/dist/chunks/MetricsMiniChartWidget-BNdGuSZV.js +2 -0
  47. package/dist/chunks/{MetricsMiniChartWidget-Esvv-lFp.js.map → MetricsMiniChartWidget-BNdGuSZV.js.map} +1 -1
  48. package/dist/chunks/{PDFViewer-NeL91Gon.js → PDFViewer-BIBNhuWY.js} +3 -3
  49. package/dist/chunks/{PDFViewer-NeL91Gon.js.map → PDFViewer-BIBNhuWY.js.map} +1 -1
  50. package/dist/chunks/{PDFViewer-D4uo3oiA.js → PDFViewer-nZAQQScE.js} +2 -2
  51. package/dist/chunks/{PDFViewer-D4uo3oiA.js.map → PDFViewer-nZAQQScE.js.map} +1 -1
  52. package/dist/chunks/Rest-BpDyhFfG.js +2 -0
  53. package/dist/chunks/Rest-BpDyhFfG.js.map +1 -0
  54. package/dist/chunks/{Rest-CS4jRCAs.js → Rest-DpbPbmra.js} +96 -5
  55. package/dist/chunks/Rest-DpbPbmra.js.map +1 -0
  56. package/dist/chunks/TokenManager-BWc_pRpg.js +2 -0
  57. package/dist/chunks/TokenManager-BWc_pRpg.js.map +1 -0
  58. package/dist/chunks/{TopNav-DC8oGpHp.js → TokenManager-N3e5wDu1.js} +369 -6
  59. package/dist/chunks/TokenManager-N3e5wDu1.js.map +1 -0
  60. package/dist/chunks/{WebSocketClient-D-5DJoMX.js → WebSocketClient-DghNkEyO.js} +2 -2
  61. package/dist/chunks/{WebSocketClient-D-5DJoMX.js.map → WebSocketClient-DghNkEyO.js.map} +1 -1
  62. package/dist/chunks/{WebSocketClient-DzcqAmho.js → WebSocketClient-E08hfP5f.js} +2 -2
  63. package/dist/chunks/{WebSocketClient-DzcqAmho.js.map → WebSocketClient-E08hfP5f.js.map} +1 -1
  64. package/dist/chunks/version-CKPqwcQJ.js +2 -0
  65. package/dist/chunks/version-CKPqwcQJ.js.map +1 -0
  66. package/dist/chunks/version-Dtwh-YkD.js +38 -0
  67. package/dist/chunks/version-Dtwh-YkD.js.map +1 -0
  68. package/dist/css/web-mojo.css +1 -17
  69. package/dist/docit.cjs.js +1 -1
  70. package/dist/docit.cjs.js.map +1 -1
  71. package/dist/docit.es.js +6 -8
  72. package/dist/docit.es.js.map +1 -1
  73. package/dist/index.cjs.js +1 -1
  74. package/dist/index.cjs.js.map +1 -1
  75. package/dist/index.es.js +34 -36
  76. package/dist/index.es.js.map +1 -1
  77. package/dist/lightbox.cjs.js +1 -1
  78. package/dist/lightbox.cjs.js.map +1 -1
  79. package/dist/lightbox.es.js +5 -4
  80. package/dist/lightbox.es.js.map +1 -1
  81. package/dist/map.cjs.js +1 -1
  82. package/dist/map.cjs.js.map +1 -1
  83. package/dist/map.es.js +82 -3
  84. package/dist/map.es.js.map +1 -1
  85. package/dist/timeline.cjs.js +1 -1
  86. package/dist/timeline.es.js +4 -4
  87. package/package.json +1 -1
  88. package/dist/chunks/ChatView-CGBaudUc.js +0 -2
  89. package/dist/chunks/ContextMenu-B4_YS0G8.js.map +0 -1
  90. package/dist/chunks/ContextMenu-DcLhcYMp.js +0 -3
  91. package/dist/chunks/ContextMenu-DcLhcYMp.js.map +0 -1
  92. package/dist/chunks/DataView-CdDY9ijM.js +0 -2
  93. package/dist/chunks/DataView-CdDY9ijM.js.map +0 -1
  94. package/dist/chunks/DataView-OUqaLmGB.js.map +0 -1
  95. package/dist/chunks/Dialog-BiVgKzSK.js.map +0 -1
  96. package/dist/chunks/Dialog-DmIPK_Bi.js +0 -2
  97. package/dist/chunks/Dialog-DmIPK_Bi.js.map +0 -1
  98. package/dist/chunks/FormView-BClEkzmE.js.map +0 -1
  99. package/dist/chunks/FormView-nulck4nL.js +0 -3
  100. package/dist/chunks/FormView-nulck4nL.js.map +0 -1
  101. package/dist/chunks/MetricsMiniChartWidget-Esvv-lFp.js +0 -2
  102. package/dist/chunks/Page-CvbwEoLv.js +0 -2
  103. package/dist/chunks/Page-CvbwEoLv.js.map +0 -1
  104. package/dist/chunks/Page-Deq4y2Kq.js +0 -351
  105. package/dist/chunks/Page-Deq4y2Kq.js.map +0 -1
  106. package/dist/chunks/Rest-BNYqGlnP.js +0 -2
  107. package/dist/chunks/Rest-BNYqGlnP.js.map +0 -1
  108. package/dist/chunks/Rest-CS4jRCAs.js.map +0 -1
  109. package/dist/chunks/TokenManager-CAZNcCMs.js +0 -366
  110. package/dist/chunks/TokenManager-CAZNcCMs.js.map +0 -1
  111. package/dist/chunks/TokenManager-CJBYcVqs.js +0 -2
  112. package/dist/chunks/TokenManager-CJBYcVqs.js.map +0 -1
  113. package/dist/chunks/TopNav-23B5R-dl.js +0 -2
  114. package/dist/chunks/TopNav-23B5R-dl.js.map +0 -1
  115. package/dist/chunks/TopNav-DC8oGpHp.js.map +0 -1
  116. package/dist/chunks/WebApp-C1vcdSuu.js +0 -1388
  117. package/dist/chunks/WebApp-C1vcdSuu.js.map +0 -1
  118. package/dist/chunks/WebApp-CpxtmTk0.js +0 -2
  119. package/dist/chunks/WebApp-CpxtmTk0.js.map +0 -1
@@ -1,4 +1,1361 @@
1
- import { V as View } from "./Rest-CS4jRCAs.js";
1
+ import { r as rest, V as View } from "./Rest-DpbPbmra.js";
2
+ class Router {
3
+ constructor(options = {}) {
4
+ this.defaultRoute = options.defaultRoute || "home";
5
+ this.routes = [];
6
+ this.currentRoute = null;
7
+ this.eventEmitter = options.eventEmitter || null;
8
+ this.boundHandlePopState = this.handlePopState.bind(this);
9
+ }
10
+ start() {
11
+ window.addEventListener("popstate", this.boundHandlePopState);
12
+ this.handleCurrentLocation();
13
+ }
14
+ stop() {
15
+ window.removeEventListener("popstate", this.boundHandlePopState);
16
+ }
17
+ addRoute(pattern, pageName) {
18
+ this.routes.push({
19
+ pattern: this.normalizePattern(pattern),
20
+ regex: this.patternToRegex(pattern),
21
+ pageName,
22
+ paramNames: this.extractParamNames(pattern)
23
+ });
24
+ }
25
+ async navigate(path, options = {}) {
26
+ const { replace = false, state = null, trigger = true } = options;
27
+ const { pageName, queryParams } = this.parseInput(path);
28
+ if (trigger) {
29
+ await this.handleRouteChange(pageName, queryParams);
30
+ }
31
+ }
32
+ // Browser navigation
33
+ back() {
34
+ window.history.back();
35
+ }
36
+ forward() {
37
+ window.history.forward();
38
+ }
39
+ // Get current route info
40
+ getCurrentRoute() {
41
+ return this.currentRoute;
42
+ }
43
+ getCurrentPath() {
44
+ const { pageName, queryParams } = this.parseCurrentUrl();
45
+ return this.buildPublicUrl(pageName, queryParams);
46
+ }
47
+ // Private methods
48
+ handlePopState(_event) {
49
+ if (this.allowPopState) {
50
+ this.handleCurrentLocation();
51
+ } else {
52
+ console.warn("PopStateEvent is not allowed");
53
+ }
54
+ }
55
+ async handleCurrentLocation() {
56
+ const { pageName, queryParams } = this.parseCurrentUrl();
57
+ await this.handleRouteChange(pageName, queryParams);
58
+ }
59
+ async handleRouteChange(pageName, queryParams) {
60
+ const routePath = "/" + pageName;
61
+ const route = this.matchRoute(routePath);
62
+ const publicUrl = this.buildPublicUrl(pageName, queryParams);
63
+ if (!route) {
64
+ console.log("No route matched for page:", pageName);
65
+ if (this.eventEmitter) {
66
+ this.eventEmitter.emit("route:notfound", { path: publicUrl });
67
+ }
68
+ return null;
69
+ }
70
+ this.currentRoute = route;
71
+ if (this.eventEmitter) {
72
+ this.eventEmitter.emit("route:changed", {
73
+ path: publicUrl,
74
+ pageName: route.pageName,
75
+ params: route.params,
76
+ query: queryParams,
77
+ route
78
+ });
79
+ }
80
+ return route;
81
+ }
82
+ matchRoute(path) {
83
+ for (const route of this.routes) {
84
+ const match = path.match(route.regex);
85
+ if (match) {
86
+ const params = {};
87
+ route.paramNames.forEach((name, index) => {
88
+ params[name] = match[index + 1];
89
+ });
90
+ return {
91
+ ...route,
92
+ params,
93
+ path
94
+ };
95
+ }
96
+ }
97
+ return null;
98
+ }
99
+ // Parse any input format and extract page name + query params
100
+ parseInput(input) {
101
+ let pageName = this.defaultRoute;
102
+ let queryParams = {};
103
+ if (!input) {
104
+ return { pageName, queryParams };
105
+ }
106
+ try {
107
+ if (input.includes("?")) {
108
+ const [pathPart, queryPart] = input.split("?", 2);
109
+ const urlParams = new URLSearchParams(queryPart);
110
+ if (urlParams.has("page")) {
111
+ pageName = urlParams.get("page") || this.defaultRoute;
112
+ for (const [key, value] of urlParams) {
113
+ if (key !== "page") {
114
+ queryParams[key] = value;
115
+ }
116
+ }
117
+ } else {
118
+ if (pathPart.startsWith("/")) {
119
+ pageName = pathPart.substring(1) || this.defaultRoute;
120
+ } else {
121
+ pageName = pathPart || this.defaultRoute;
122
+ }
123
+ for (const [key, value] of urlParams) {
124
+ queryParams[key] = value;
125
+ }
126
+ }
127
+ } else if (input.startsWith("/")) {
128
+ pageName = input.substring(1) || this.defaultRoute;
129
+ } else {
130
+ pageName = input;
131
+ }
132
+ } catch (error) {
133
+ console.warn("Failed to parse input:", input, error);
134
+ pageName = this.defaultRoute;
135
+ queryParams = {};
136
+ }
137
+ return { pageName, queryParams };
138
+ }
139
+ // Parse current browser URL
140
+ parseCurrentUrl() {
141
+ const urlParams = new URLSearchParams(window.location.search);
142
+ const pageName = urlParams.get("page") || this.defaultRoute;
143
+ const queryParams = {};
144
+ for (const [key, value] of urlParams) {
145
+ if (key !== "page") {
146
+ queryParams[key] = value;
147
+ }
148
+ }
149
+ return { pageName, queryParams };
150
+ }
151
+ // Build public URL format: ?page=admin&group=123
152
+ buildPublicUrl(pageName, queryParams = {}) {
153
+ const urlParams = new URLSearchParams();
154
+ urlParams.set("page", pageName);
155
+ Object.entries(queryParams).forEach(([key, value]) => {
156
+ if (value !== null && value !== void 0 && value !== "") {
157
+ urlParams.set(key, String(value));
158
+ }
159
+ });
160
+ return "?" + urlParams.toString();
161
+ }
162
+ // Update browser URL
163
+ updateBrowserUrl(pageName, queryParams, replace, state) {
164
+ const currentUrl = new URL(window.location.origin + window.location.pathname);
165
+ currentUrl.searchParams.set("page", pageName);
166
+ Object.entries(queryParams).forEach(([key, value]) => {
167
+ if (value !== null && value !== void 0 && value !== "") {
168
+ currentUrl.searchParams.set(key, String(value));
169
+ }
170
+ });
171
+ const url = currentUrl.toString();
172
+ if (replace) {
173
+ window.history.replaceState(state, "", url);
174
+ } else {
175
+ window.history.pushState(state, "", url);
176
+ }
177
+ }
178
+ // Route pattern utilities
179
+ patternToRegex(pattern) {
180
+ let regexPattern = pattern.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&").replace(/\/:([^/?]+)\?/g, "(?:/([^/]+))?").replace(/:([^/]+)/g, "([^/]+)");
181
+ return new RegExp(`^${regexPattern}$`);
182
+ }
183
+ extractParamNames(pattern) {
184
+ const matches = pattern.match(/:([^/?]+)\??/g) || [];
185
+ return matches.map((match) => match.replace(/[:?]/g, ""));
186
+ }
187
+ normalizePattern(pattern) {
188
+ return pattern.startsWith("/") ? pattern : `/${pattern}`;
189
+ }
190
+ /**
191
+ * Update URL parameters without triggering navigation
192
+ * @param {object} params - Parameters to update in URL
193
+ * @param {object} options - Options like { replace: true }
194
+ */
195
+ updateUrl(params = {}, options = {}) {
196
+ const { replace = false } = options;
197
+ const { pageName } = this.parseCurrentUrl();
198
+ this.updateBrowserUrl(pageName, params, replace);
199
+ }
200
+ /**
201
+ * Build URL for given page and query parameters
202
+ */
203
+ buildUrl(page, query = {}) {
204
+ return this.buildPublicUrl(page, query);
205
+ }
206
+ /**
207
+ * Check if two routes match (ignoring query parameters)
208
+ * @param {string} route1 - First route in any format ("/admin", "?page=admin", "admin")
209
+ * @param {string} route2 - Second route in any format
210
+ * @returns {boolean} - True if routes point to the same page
211
+ */
212
+ doRoutesMatch(route1, route2) {
213
+ if (!route1 || !route2) return false;
214
+ const { pageName: pageName1 } = this.parseInput(route1);
215
+ const { pageName: pageName2 } = this.parseInput(route2);
216
+ return pageName1 === pageName2;
217
+ }
218
+ }
219
+ class EventBus {
220
+ constructor() {
221
+ this.listeners = {};
222
+ this.onceListeners = {};
223
+ this.maxListeners = 100;
224
+ this.debugMode = false;
225
+ this.eventStats = {};
226
+ }
227
+ /**
228
+ * Add event listener
229
+ * @param {string|Array<string>} event - Event name or array of event names
230
+ * @param {function} callback - Event callback
231
+ * @returns {EventBus} This instance for chaining
232
+ */
233
+ on(event, callback) {
234
+ if (typeof callback !== "function") {
235
+ throw new Error("Callback must be a function");
236
+ }
237
+ if (Array.isArray(event)) {
238
+ event.forEach((eventName) => this.on(eventName, callback));
239
+ return this;
240
+ }
241
+ if (!this.listeners[event]) {
242
+ this.listeners[event] = [];
243
+ }
244
+ if (this.listeners[event].length >= this.maxListeners) {
245
+ console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${event}`);
246
+ }
247
+ this.listeners[event].push(callback);
248
+ return this;
249
+ }
250
+ /**
251
+ * Add one-time event listener
252
+ * @param {string|Array<string>} event - Event name or array of event names
253
+ * @param {function} callback - Event callback
254
+ * @returns {EventBus} This instance for chaining
255
+ */
256
+ once(event, callback) {
257
+ if (typeof callback !== "function") {
258
+ throw new Error("Callback must be a function");
259
+ }
260
+ if (Array.isArray(event)) {
261
+ event.forEach((eventName) => this.once(eventName, callback));
262
+ return this;
263
+ }
264
+ if (!this.onceListeners[event]) {
265
+ this.onceListeners[event] = [];
266
+ }
267
+ this.onceListeners[event].push(callback);
268
+ return this;
269
+ }
270
+ /**
271
+ * Remove event listener
272
+ * @param {string|Array<string>} event - Event name or array of event names
273
+ * @param {function} callback - Event callback to remove
274
+ * @returns {EventBus} This instance for chaining
275
+ */
276
+ off(event, callback) {
277
+ if (Array.isArray(event)) {
278
+ event.forEach((eventName) => this.off(eventName, callback));
279
+ return this;
280
+ }
281
+ if (!callback) {
282
+ delete this.listeners[event];
283
+ delete this.onceListeners[event];
284
+ return this;
285
+ }
286
+ if (this.listeners[event]) {
287
+ const index = this.listeners[event].indexOf(callback);
288
+ if (index !== -1) {
289
+ this.listeners[event].splice(index, 1);
290
+ if (this.listeners[event].length === 0) {
291
+ delete this.listeners[event];
292
+ }
293
+ }
294
+ }
295
+ if (this.onceListeners[event]) {
296
+ const index = this.onceListeners[event].indexOf(callback);
297
+ if (index !== -1) {
298
+ this.onceListeners[event].splice(index, 1);
299
+ if (this.onceListeners[event].length === 0) {
300
+ delete this.onceListeners[event];
301
+ }
302
+ }
303
+ }
304
+ return this;
305
+ }
306
+ /**
307
+ * Emit event to all listeners
308
+ * @param {string} event - Event name
309
+ * @param {*} data - Event data
310
+ * @returns {EventBus} This instance for chaining
311
+ */
312
+ emit(event, data) {
313
+ this.updateEventStats(event);
314
+ if (this.debugMode) {
315
+ console.log(`[EventBus] Emitting: ${event}`, data);
316
+ }
317
+ const listeners = [];
318
+ if (this.listeners[event]) {
319
+ listeners.push(...this.listeners[event]);
320
+ }
321
+ if (this.listeners["*"]) {
322
+ listeners.push(...this.listeners["*"]);
323
+ }
324
+ if (this.onceListeners[event]) {
325
+ listeners.push(...this.onceListeners[event]);
326
+ delete this.onceListeners[event];
327
+ }
328
+ if (this.onceListeners["*"]) {
329
+ listeners.push(...this.onceListeners["*"]);
330
+ delete this.onceListeners["*"];
331
+ }
332
+ if (this.debugMode && listeners.length > 0) {
333
+ console.log(`[EventBus] ${listeners.length} listener(s) for '${event}'`);
334
+ }
335
+ listeners.forEach((callback) => {
336
+ try {
337
+ if (callback(data, event)) {
338
+ if (event.stopPropagation) {
339
+ event.stopPropagation();
340
+ }
341
+ if (event.preventDefault) {
342
+ event.preventDefault();
343
+ }
344
+ }
345
+ } catch (error) {
346
+ console.error(`Error in event listener for '${event}':`, error);
347
+ this.emitError(error, event, callback);
348
+ }
349
+ });
350
+ return this;
351
+ }
352
+ /**
353
+ * Emit event asynchronously
354
+ * @param {string} event - Event name
355
+ * @param {*} data - Event data
356
+ * @returns {Promise<EventBus>} Promise that resolves with this instance
357
+ */
358
+ async emitAsync(event, data) {
359
+ const listeners = [];
360
+ if (this.listeners[event]) {
361
+ listeners.push(...this.listeners[event]);
362
+ }
363
+ if (this.listeners["*"]) {
364
+ listeners.push(...this.listeners["*"]);
365
+ }
366
+ if (this.onceListeners[event]) {
367
+ listeners.push(...this.onceListeners[event]);
368
+ delete this.onceListeners[event];
369
+ }
370
+ if (this.onceListeners["*"]) {
371
+ listeners.push(...this.onceListeners["*"]);
372
+ delete this.onceListeners["*"];
373
+ }
374
+ const promises = listeners.map((callback) => {
375
+ return new Promise((resolve) => {
376
+ try {
377
+ const result = callback(data, event);
378
+ resolve(result);
379
+ } catch (error) {
380
+ console.error(`Error in async event listener for '${event}':`, error);
381
+ this.emitError(error, event, callback);
382
+ resolve();
383
+ }
384
+ });
385
+ });
386
+ await Promise.all(promises);
387
+ return this;
388
+ }
389
+ /**
390
+ * Remove all listeners for all events
391
+ * @returns {EventBus} This instance for chaining
392
+ */
393
+ removeAllListeners() {
394
+ this.listeners = {};
395
+ this.onceListeners = {};
396
+ return this;
397
+ }
398
+ /**
399
+ * Get listener count for an event
400
+ * @param {string} event - Event name
401
+ * @returns {number} Number of listeners
402
+ */
403
+ listenerCount(event) {
404
+ const regularCount = this.listeners[event] ? this.listeners[event].length : 0;
405
+ const onceCount = this.onceListeners[event] ? this.onceListeners[event].length : 0;
406
+ return regularCount + onceCount;
407
+ }
408
+ /**
409
+ * Get all event names that have listeners
410
+ * @returns {Array<string>} Array of event names
411
+ */
412
+ eventNames() {
413
+ const regularEvents = Object.keys(this.listeners);
414
+ const onceEvents = Object.keys(this.onceListeners);
415
+ return [.../* @__PURE__ */ new Set([...regularEvents, ...onceEvents])];
416
+ }
417
+ /**
418
+ * Set maximum number of listeners per event
419
+ * @param {number} max - Maximum listeners
420
+ * @returns {EventBus} This instance for chaining
421
+ */
422
+ setMaxListeners(max) {
423
+ if (typeof max !== "number" || max < 0) {
424
+ throw new Error("Max listeners must be a non-negative number");
425
+ }
426
+ this.maxListeners = max;
427
+ return this;
428
+ }
429
+ /**
430
+ * Create a namespaced event bus
431
+ * @param {string} namespace - Namespace prefix
432
+ * @returns {object} Namespaced event bus methods
433
+ */
434
+ namespace(namespace) {
435
+ const prefixEvent = (event) => `${namespace}:${event}`;
436
+ return {
437
+ on: (event, callback) => this.on(prefixEvent(event), callback),
438
+ once: (event, callback) => this.once(prefixEvent(event), callback),
439
+ off: (event, callback) => this.off(prefixEvent(event), callback),
440
+ emit: (event, data) => this.emit(prefixEvent(event), data),
441
+ emitAsync: (event, data) => this.emitAsync(prefixEvent(event), data)
442
+ };
443
+ }
444
+ /**
445
+ * Add middleware to intercept events
446
+ * @param {function} middleware - Middleware function
447
+ * @returns {EventBus} This instance for chaining
448
+ */
449
+ use(middleware) {
450
+ if (typeof middleware !== "function") {
451
+ throw new Error("Middleware must be a function");
452
+ }
453
+ const originalEmit = this.emit;
454
+ this.emit = (event, data) => {
455
+ try {
456
+ const result = middleware(event, data);
457
+ if (result === false) {
458
+ return this;
459
+ }
460
+ const finalData = result !== void 0 ? result : data;
461
+ return originalEmit.call(this, event, finalData);
462
+ } catch (error) {
463
+ console.error("Error in event middleware:", error);
464
+ return originalEmit.call(this, event, data);
465
+ }
466
+ };
467
+ return this;
468
+ }
469
+ /**
470
+ * Emit error event for listener errors
471
+ * @private
472
+ * @param {Error} error - The error that occurred
473
+ * @param {string} originalEvent - The event that caused the error
474
+ * @param {function} callback - The callback that threw the error
475
+ */
476
+ emitError(error, originalEvent, callback) {
477
+ if (originalEvent === "error") {
478
+ return;
479
+ }
480
+ setTimeout(() => {
481
+ this.emit("error", {
482
+ error,
483
+ originalEvent,
484
+ callback: callback.toString()
485
+ });
486
+ }, 0);
487
+ }
488
+ /**
489
+ * Create a promise that resolves when an event is emitted
490
+ * @param {string} event - Event name
491
+ * @param {number} timeout - Optional timeout in milliseconds
492
+ * @returns {Promise} Promise that resolves with event data
493
+ */
494
+ waitFor(event, timeout = null) {
495
+ return new Promise((resolve, reject) => {
496
+ let timeoutId = null;
497
+ const cleanup = () => {
498
+ if (timeoutId) {
499
+ clearTimeout(timeoutId);
500
+ }
501
+ };
502
+ const listener = (data) => {
503
+ cleanup();
504
+ resolve(data);
505
+ };
506
+ this.once(event, listener);
507
+ if (timeout) {
508
+ timeoutId = setTimeout(() => {
509
+ this.off(event, listener);
510
+ reject(new Error(`Timeout waiting for event: ${event}`));
511
+ }, timeout);
512
+ }
513
+ });
514
+ }
515
+ /**
516
+ * Enable/disable debug mode for detailed event logging
517
+ * @param {boolean} enable - Whether to enable debug mode
518
+ * @returns {EventBus} This instance for chaining
519
+ */
520
+ debug(enable = true) {
521
+ this.debugMode = enable;
522
+ if (enable) {
523
+ console.log("[EventBus] Debug mode enabled");
524
+ } else {
525
+ console.log("[EventBus] Debug mode disabled");
526
+ }
527
+ return this;
528
+ }
529
+ /**
530
+ * Get statistics about the event bus
531
+ * @returns {object} Statistics object
532
+ */
533
+ getStats() {
534
+ const eventNames = this.eventNames();
535
+ const stats = {
536
+ totalEvents: eventNames.length,
537
+ totalListeners: 0,
538
+ events: {},
539
+ emissions: { ...this.eventStats }
540
+ };
541
+ eventNames.forEach((event) => {
542
+ const count = this.listenerCount(event);
543
+ stats.events[event] = count;
544
+ stats.totalListeners += count;
545
+ });
546
+ return stats;
547
+ }
548
+ /**
549
+ * Update event emission statistics
550
+ * @private
551
+ * @param {string} event - Event name
552
+ */
553
+ updateEventStats(event) {
554
+ if (!this.eventStats[event]) {
555
+ this.eventStats[event] = {
556
+ count: 0,
557
+ firstEmission: Date.now(),
558
+ lastEmission: null
559
+ };
560
+ }
561
+ this.eventStats[event].count++;
562
+ this.eventStats[event].lastEmission = Date.now();
563
+ }
564
+ /**
565
+ * Get detailed statistics for a specific event
566
+ * @param {string} event - Event name
567
+ * @returns {Object} Event statistics
568
+ */
569
+ getEventStats(event) {
570
+ const stats = this.eventStats[event];
571
+ if (!stats) {
572
+ return null;
573
+ }
574
+ return {
575
+ ...stats,
576
+ listenerCount: this.listenerCount(event),
577
+ avgEmissionsPerMinute: this.calculateEmissionRate(stats)
578
+ };
579
+ }
580
+ /**
581
+ * Calculate emission rate for an event
582
+ * @private
583
+ * @param {Object} stats - Event statistics
584
+ * @returns {number} Emissions per minute
585
+ */
586
+ calculateEmissionRate(stats) {
587
+ if (!stats.firstEmission || !stats.lastEmission) {
588
+ return 0;
589
+ }
590
+ const durationMs = stats.lastEmission - stats.firstEmission;
591
+ if (durationMs === 0) {
592
+ return 0;
593
+ }
594
+ const durationMinutes = durationMs / (1e3 * 60);
595
+ return Math.round(stats.count / durationMinutes * 100) / 100;
596
+ }
597
+ /**
598
+ * Reset event statistics
599
+ * @returns {EventBus} This instance for chaining
600
+ */
601
+ resetStats() {
602
+ this.eventStats = {};
603
+ return this;
604
+ }
605
+ /**
606
+ * Get a summary of the most active events
607
+ * @param {number} limit - Number of events to return (default: 10)
608
+ * @returns {Array} Array of event statistics sorted by emission count
609
+ */
610
+ getTopEvents(limit = 10) {
611
+ return Object.entries(this.eventStats).map(([event, stats]) => ({
612
+ event,
613
+ count: stats.count,
614
+ rate: this.calculateEmissionRate(stats),
615
+ listeners: this.listenerCount(event)
616
+ })).sort((a, b) => b.count - a.count).slice(0, limit);
617
+ }
618
+ /**
619
+ * Log comprehensive debug information
620
+ * @returns {EventBus} This instance for chaining
621
+ */
622
+ debugInfo() {
623
+ console.group("[EventBus] Debug Information");
624
+ console.log("Debug Mode:", this.debugMode);
625
+ console.log("Max Listeners:", this.maxListeners);
626
+ const stats = this.getStats();
627
+ console.log("Total Events:", stats.totalEvents);
628
+ console.log("Total Listeners:", stats.totalListeners);
629
+ if (Object.keys(this.eventStats).length > 0) {
630
+ console.log("Top Events:", this.getTopEvents(5));
631
+ }
632
+ console.groupEnd();
633
+ return this;
634
+ }
635
+ }
636
+ class WebApp {
637
+ constructor(config = {}) {
638
+ this.config = config;
639
+ this.initPluginRegistry();
640
+ this.name = config.name || "MOJO App";
641
+ this.version = config.version || "1.0.0";
642
+ this.debug = config.debug || false;
643
+ this.container = config.container || "#app";
644
+ this.layoutType = config.layout || "portal";
645
+ this.layoutConfig = config.layoutConfig || {};
646
+ if (config.sidebar) {
647
+ this.layoutConfig.sidebarConfig = config.sidebar;
648
+ }
649
+ if (config.topbar) {
650
+ this.layoutConfig.topbarConfig = config.topbar;
651
+ }
652
+ this.layout = null;
653
+ this.layoutConfig.containerId = this.container || this.containerId || "#app";
654
+ this.pageContainer = config.pageContainer || "#page-container";
655
+ this.basePath = config.basePath || "";
656
+ this.routerMode = config.routerMode || config.router?.mode || "param";
657
+ this.basePath = config.basePath || config.router?.base || "";
658
+ this.defaultRoute = config.defaultRoute || "home";
659
+ this.session = config.session || {};
660
+ this.router = null;
661
+ this.navigation = config.navigation || {};
662
+ this.state = {
663
+ currentPage: null,
664
+ previousPage: null,
665
+ loading: false
666
+ };
667
+ this.events = new EventBus();
668
+ this.rest = rest;
669
+ if (config.api) {
670
+ this.rest.configure(config.api);
671
+ }
672
+ this.router = new Router({
673
+ mode: this.routerMode === "param" ? "params" : this.routerMode,
674
+ basePath: this.basePath,
675
+ defaultRoute: this.defaultRoute,
676
+ eventEmitter: this.events
677
+ });
678
+ this.events.on("route:changed", async (routeInfo) => {
679
+ const { pageName, params, query } = routeInfo;
680
+ await this.showPage(pageName, query, params, { fromRouter: true });
681
+ });
682
+ if (typeof window !== "undefined") {
683
+ window.MOJO = window.MOJO || {};
684
+ window.MOJO.router = this.router;
685
+ }
686
+ this.setupFocusTracking();
687
+ this.pageCache = /* @__PURE__ */ new Map();
688
+ this.pageClasses = /* @__PURE__ */ new Map();
689
+ this.componentClasses = /* @__PURE__ */ new Map();
690
+ this.modelClasses = /* @__PURE__ */ new Map();
691
+ this.currentPage = null;
692
+ this.isStarted = false;
693
+ if (window.matchUUID) {
694
+ window[window.matchUUID] = this;
695
+ } else if (window.MOJO) {
696
+ window.MOJO.app = this;
697
+ } else {
698
+ window.__app__ = this;
699
+ }
700
+ }
701
+ /**
702
+ * Start the application
703
+ */
704
+ async start() {
705
+ if (this.isStarted) {
706
+ console.warn("WebApp already started");
707
+ return;
708
+ }
709
+ try {
710
+ this.setupPageContainer();
711
+ this.validateDefaultRoute();
712
+ await this.setupRouter();
713
+ this.isStarted = true;
714
+ this.router.allowPopState = false;
715
+ this.events.emit("app:ready", { app: this });
716
+ } catch (error) {
717
+ console.error(`Failed to start ${this.name}:`, error);
718
+ this.showError("Failed to start application");
719
+ throw error;
720
+ }
721
+ }
722
+ /**
723
+ * Setup router with configuration
724
+ */
725
+ async setupRouter() {
726
+ if (!this.router) {
727
+ console.error("Router not initialized");
728
+ return;
729
+ }
730
+ this.events.on("route:notfound", async (info) => {
731
+ console.warn(`Route not found: ${info.path}`);
732
+ this._show404(info.path);
733
+ });
734
+ this.router.start();
735
+ console.log(`Router started in ${this.routerMode} mode`);
736
+ }
737
+ /**
738
+ * Setup layout based on configuration
739
+ */
740
+ setupPageContainer() {
741
+ const container = typeof this.container === "string" ? document.querySelector(this.container) : this.container;
742
+ if (container && !container.querySelector("#page-container")) {
743
+ container.innerHTML = '<div id="page-container"></div>';
744
+ }
745
+ this.pageContainer = "#page-container";
746
+ }
747
+ /**
748
+ * Register a page with the app
749
+ */
750
+ registerPage(pageName, PageClass, options = {}) {
751
+ if (typeof pageName !== "string" || !pageName) {
752
+ console.error("registerPage: pageName must be a non-empty string");
753
+ return this;
754
+ }
755
+ if (typeof PageClass !== "function") {
756
+ console.error("registerPage: PageClass must be a constructor function");
757
+ return this;
758
+ }
759
+ if (!options.containerId) options.containerId = this.pageContainer;
760
+ this.pageClasses.set(pageName, {
761
+ PageClass,
762
+ constructorOptions: options
763
+ });
764
+ if (this.router) {
765
+ let route = options.route || `/${pageName}`;
766
+ if (!route.startsWith("/")) {
767
+ route = `/${route}`;
768
+ }
769
+ options.route = route;
770
+ this.router.addRoute(route, pageName);
771
+ }
772
+ return this;
773
+ }
774
+ /**
775
+ * Get page instance (cached)
776
+ */
777
+ getPage(pageName) {
778
+ return this.pageCache.get(pageName);
779
+ }
780
+ getPagePermissions(pageName) {
781
+ if (this.pageCache.has(pageName)) {
782
+ return this.pageCache.get(pageName).permissions;
783
+ }
784
+ const pageInfo = this.pageClasses.get(pageName);
785
+ if (!pageInfo) return null;
786
+ const { PageClass, constructorOptions } = pageInfo;
787
+ if (!constructorOptions) return null;
788
+ return constructorOptions.permissions;
789
+ }
790
+ /**
791
+ * Get or create page instance (with caching)
792
+ */
793
+ getOrCreatePage(pageName) {
794
+ if (this.pageCache.has(pageName)) {
795
+ return this.pageCache.get(pageName);
796
+ }
797
+ const pageInfo = this.pageClasses.get(pageName);
798
+ if (!pageInfo) {
799
+ console.error(`Page not registered: ${pageName}`);
800
+ return null;
801
+ }
802
+ const { PageClass, constructorOptions } = pageInfo;
803
+ try {
804
+ const pageOptions = {
805
+ pageName,
806
+ ...constructorOptions,
807
+ app: this
808
+ };
809
+ const page = new PageClass(pageOptions);
810
+ if (constructorOptions.route) {
811
+ page.route = constructorOptions.route;
812
+ }
813
+ this.pageCache.set(pageName, page);
814
+ console.log(`Created page: ${pageName} with route: ${page.route}`);
815
+ return page;
816
+ } catch (error) {
817
+ console.error(`Failed to create page ${pageName}:`, error);
818
+ return null;
819
+ }
820
+ }
821
+ /**
822
+ * Show a page
823
+ */
824
+ /**
825
+ * SIMPLIFIED UNIFIED PAGE SHOWING METHOD
826
+ * @param {string|object} page - Page name or page instance
827
+ * @param {object} query - URL query parameters (URL-safe)
828
+ * @param {object} params - Any data to pass to page (can include objects)
829
+ * @param {object} options - Options { fromRouter, replace, force }
830
+ */
831
+ async showPage(page, query = {}, params = {}, options = {}) {
832
+ const { fromRouter = false, replace = false, force = false } = options;
833
+ try {
834
+ let pageInstance, pageName;
835
+ if (typeof page === "string") {
836
+ pageName = page;
837
+ pageInstance = this.getOrCreatePage(page);
838
+ } else if (page && typeof page === "object") {
839
+ pageInstance = page;
840
+ pageName = page.pageName;
841
+ }
842
+ this.events.emit("page:showing", {
843
+ page: pageInstance,
844
+ pageName: pageInstance.pageName,
845
+ params,
846
+ query,
847
+ fromRouter
848
+ });
849
+ const oldPage = this.currentPage;
850
+ if (!pageInstance) {
851
+ this._show404(pageName, params, query, fromRouter);
852
+ return;
853
+ }
854
+ if (!pageInstance.canEnter()) {
855
+ this._showDeniedPage(pageInstance, params, query, fromRouter);
856
+ return;
857
+ }
858
+ if (oldPage && oldPage !== pageInstance) {
859
+ await this._exitOldPage(oldPage);
860
+ }
861
+ await pageInstance.onParams(params, query);
862
+ if (oldPage !== pageInstance) {
863
+ await pageInstance.onEnter();
864
+ }
865
+ pageInstance.syncUrl();
866
+ this.events.emit("page:show", {
867
+ page: pageInstance,
868
+ pageName: pageInstance.pageName,
869
+ params,
870
+ query,
871
+ fromRouter
872
+ });
873
+ await pageInstance.render();
874
+ this.currentPage = pageInstance;
875
+ console.log(`✅ Showing page: ${pageInstance.pageName}`, { query, params });
876
+ } catch (error) {
877
+ console.error("Error in showPage:", error);
878
+ this.showError(`Failed to load page: ${error.message}`);
879
+ if (page !== "error") {
880
+ await this.showPage("error", {}, { error, originalPage: page }, { fromRouter });
881
+ }
882
+ }
883
+ }
884
+ async _show404(pageName, params, query, fromRouter) {
885
+ const notFoundPage = this.getOrCreatePage("404");
886
+ if (!notFoundPage) return;
887
+ if (notFoundPage.setInfo) {
888
+ notFoundPage.setInfo(pageName);
889
+ }
890
+ await this._exitOldPage(this.currentPage);
891
+ await notFoundPage.render();
892
+ this.currentPage = notFoundPage;
893
+ this.events.emit("page:404", {
894
+ page: null,
895
+ pageName,
896
+ params,
897
+ query,
898
+ fromRouter
899
+ });
900
+ }
901
+ async _showDeniedPage(pageInstance, params, query, fromRouter) {
902
+ const deniedPage = this.getOrCreatePage("denied");
903
+ if (deniedPage.setDeniedPage) {
904
+ deniedPage.setDeniedPage(pageInstance);
905
+ }
906
+ await this._exitOldPage(this.currentPage);
907
+ await deniedPage.render();
908
+ this.currentPage = deniedPage;
909
+ this.events.emit("page:denied", {
910
+ page: pageInstance,
911
+ pageName: pageInstance.pageName,
912
+ params,
913
+ query,
914
+ fromRouter
915
+ });
916
+ }
917
+ async _exitOldPage(oldPage) {
918
+ if (!oldPage) return;
919
+ try {
920
+ await oldPage.onExit();
921
+ await oldPage.unmount();
922
+ this.events.emit("page:hide", { page: oldPage });
923
+ } catch (error) {
924
+ console.error(`Error exiting page ${oldPage.pageName}:`, error);
925
+ }
926
+ }
927
+ /**
928
+ * SIMPLIFIED NAVIGATION - delegates to router
929
+ * @param {string} route - Route like "/users" or "/users/123"
930
+ * @param {object} query - Query string params { filter: 'active' }
931
+ * @param {object} options - Navigation options { replace, force }
932
+ */
933
+ async navigate(route, query = {}, options = {}) {
934
+ if (!this.router) {
935
+ console.error("Router not initialized");
936
+ return;
937
+ }
938
+ let fullPath = route;
939
+ if (Object.keys(query).length > 0) {
940
+ const queryString = new URLSearchParams(query).toString();
941
+ fullPath += (route.includes("?") ? "&" : "?") + queryString;
942
+ }
943
+ return await this.router.navigate(fullPath, options);
944
+ }
945
+ /**
946
+ * Navigate to the default route
947
+ */
948
+ async navigateToDefault(options = {}) {
949
+ return await this.showPage(this.defaultRoute, {}, {}, options);
950
+ }
951
+ /**
952
+ * Navigate back
953
+ */
954
+ back() {
955
+ if (this.router) {
956
+ this.router.back();
957
+ } else {
958
+ console.warn("Router not initialized");
959
+ }
960
+ }
961
+ /**
962
+ * Navigate forward
963
+ */
964
+ forward() {
965
+ if (this.router) {
966
+ this.router.forward();
967
+ } else {
968
+ console.warn("Router not initialized");
969
+ }
970
+ }
971
+ /**
972
+ * Get current page
973
+ */
974
+ getCurrentPage() {
975
+ return this.currentPage;
976
+ }
977
+ /**
978
+ * Get page container element
979
+ */
980
+ getPageContainer() {
981
+ if (this.layout && this.layout.getPageContainer) {
982
+ return this.layout.getPageContainer();
983
+ }
984
+ const container = typeof this.pageContainer === "string" ? document.querySelector(this.pageContainer) : this.pageContainer;
985
+ return container;
986
+ }
987
+ /**
988
+ * Show error notification
989
+ */
990
+ async showError(message) {
991
+ try {
992
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
993
+ await Dialog2.alert(message, "Error", { size: "md", class: "text-danger" });
994
+ } catch (e) {
995
+ this.events.emit("notification", { message, type: "error" });
996
+ if (typeof window !== "undefined" && window?.console) {
997
+ console.error("[WebApp] showError fallback:", e);
998
+ }
999
+ if (typeof window !== "undefined") {
1000
+ alert(`Error: ${message}`);
1001
+ }
1002
+ }
1003
+ }
1004
+ /**
1005
+ * Show success notification
1006
+ */
1007
+ async showSuccess(message) {
1008
+ try {
1009
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1010
+ await Dialog2.alert(message, "Success", { size: "md", class: "text-success" });
1011
+ } catch (e) {
1012
+ this.events.emit("notification", { message, type: "success" });
1013
+ if (typeof window !== "undefined" && window?.console) {
1014
+ console.warn("[WebApp] showSuccess fallback:", e);
1015
+ }
1016
+ if (typeof window !== "undefined") {
1017
+ alert(`Success: ${message}`);
1018
+ }
1019
+ }
1020
+ }
1021
+ /**
1022
+ * Show info notification
1023
+ */
1024
+ async showInfo(message) {
1025
+ try {
1026
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1027
+ await Dialog2.alert(message, "Information", { size: "md", class: "text-info" });
1028
+ } catch (e) {
1029
+ this.events.emit("notification", { message, type: "info" });
1030
+ if (typeof window !== "undefined" && window?.console) {
1031
+ console.info("[WebApp] showInfo fallback:", e);
1032
+ }
1033
+ if (typeof window !== "undefined") {
1034
+ alert(`Info: ${message}`);
1035
+ }
1036
+ }
1037
+ }
1038
+ /**
1039
+ * Show warning notification
1040
+ */
1041
+ async showWarning(message) {
1042
+ try {
1043
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1044
+ await Dialog2.alert(message, "Warning", { size: "md", class: "text-warning" });
1045
+ } catch (e) {
1046
+ this.events.emit("notification", { message, type: "warning" });
1047
+ if (typeof window !== "undefined" && window?.console) {
1048
+ console.warn("[WebApp] showWarning fallback:", e);
1049
+ }
1050
+ if (typeof window !== "undefined") {
1051
+ alert(`Warning: ${message}`);
1052
+ }
1053
+ }
1054
+ }
1055
+ /**
1056
+ * Show generic notification
1057
+ */
1058
+ showNotification(message, type = "info") {
1059
+ this.events.emit("notification", { message, type });
1060
+ }
1061
+ /**
1062
+ * Show loading indicator
1063
+ */
1064
+ async showLoading(opts = {}) {
1065
+ if (typeof opts === "string") {
1066
+ opts = { message: opts };
1067
+ }
1068
+ try {
1069
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1070
+ Dialog2.showBusy(opts);
1071
+ } catch (e) {
1072
+ if (typeof window !== "undefined" && window?.console) {
1073
+ console.warn("[WebApp] showLoading fallback:", e, opts);
1074
+ }
1075
+ this.events.emit("notification", { message: opts.message || "Loading...", type: "info" });
1076
+ }
1077
+ }
1078
+ /**
1079
+ * Hide loading indicator
1080
+ */
1081
+ async hideLoading() {
1082
+ try {
1083
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1084
+ Dialog2.hideBusy();
1085
+ } catch (e) {
1086
+ if (typeof window !== "undefined" && window?.console) {
1087
+ console.warn("[WebApp] hideLoading fallback:", e);
1088
+ }
1089
+ }
1090
+ }
1091
+ async showModelForm(options = {}) {
1092
+ try {
1093
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1094
+ return await Dialog2.showModelForm(options);
1095
+ } catch (e) {
1096
+ if (typeof window !== "undefined" && window?.console) {
1097
+ console.error("[WebApp] showModelForm failed:", e);
1098
+ }
1099
+ throw e;
1100
+ }
1101
+ }
1102
+ async showForm(options = {}) {
1103
+ try {
1104
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1105
+ return await Dialog2.showForm(options);
1106
+ } catch (e) {
1107
+ if (typeof window !== "undefined" && window?.console) {
1108
+ console.error("[WebApp] showForm failed:", e);
1109
+ }
1110
+ throw e;
1111
+ }
1112
+ }
1113
+ async showDialog(options = {}) {
1114
+ try {
1115
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1116
+ return await Dialog2.showDialog(options);
1117
+ } catch (e) {
1118
+ if (typeof window !== "undefined" && window?.console) {
1119
+ console.error("[WebApp] showDialog failed:", e);
1120
+ }
1121
+ throw e;
1122
+ }
1123
+ }
1124
+ async confirm(message, title = "Confirm", options = {}) {
1125
+ const Dialog2 = (await Promise.resolve().then(() => Dialog$1)).default;
1126
+ return await Dialog2.confirm(message, title, options);
1127
+ }
1128
+ /**
1129
+ * Setup browser focus/blur tracking
1130
+ */
1131
+ setupFocusTracking() {
1132
+ if (typeof window === "undefined") return;
1133
+ this.isFocused = !document.hidden;
1134
+ const handleVisibilityChange = () => {
1135
+ const wasFocused = this.isFocused;
1136
+ this.isFocused = !document.hidden;
1137
+ if (wasFocused !== this.isFocused) {
1138
+ if (this.isFocused) {
1139
+ this.events.emit("browser:focus");
1140
+ } else {
1141
+ this.events.emit("browser:blur");
1142
+ }
1143
+ }
1144
+ };
1145
+ const handleFocus = () => {
1146
+ if (!this.isFocused) {
1147
+ this.isFocused = true;
1148
+ this.events.emit("browser:focus");
1149
+ }
1150
+ };
1151
+ const handleBlur = () => {
1152
+ if (this.isFocused) {
1153
+ this.isFocused = false;
1154
+ this.events.emit("browser:blur");
1155
+ }
1156
+ };
1157
+ document.addEventListener("visibilitychange", handleVisibilityChange);
1158
+ window.addEventListener("focus", handleFocus);
1159
+ window.addEventListener("blur", handleBlur);
1160
+ this._focusHandlers = {
1161
+ visibilitychange: handleVisibilityChange,
1162
+ focus: handleFocus,
1163
+ blur: handleBlur
1164
+ };
1165
+ }
1166
+ /**
1167
+ * Setup global error handling
1168
+ */
1169
+ setupErrorHandling() {
1170
+ window.addEventListener("error", (event) => {
1171
+ console.error("Global error:", event.error);
1172
+ if (this.debug) {
1173
+ this.showError(`Error: ${event.error?.message || "Unknown error"}`);
1174
+ }
1175
+ });
1176
+ window.addEventListener("unhandledrejection", (event) => {
1177
+ console.error("Unhandled promise rejection:", event.reason);
1178
+ if (this.debug) {
1179
+ this.showError(`Promise rejected: ${event.reason?.message || "Unknown error"}`);
1180
+ }
1181
+ });
1182
+ }
1183
+ /**
1184
+ * Escape HTML to prevent XSS
1185
+ */
1186
+ escapeHtml(unsafe) {
1187
+ return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1188
+ }
1189
+ /**
1190
+ * Get application state
1191
+ */
1192
+ getState(key) {
1193
+ return key ? this.state[key] : this.state;
1194
+ }
1195
+ /**
1196
+ * Set application state
1197
+ */
1198
+ setState(updates) {
1199
+ const oldState = { ...this.state };
1200
+ Object.assign(this.state, updates);
1201
+ this.events.emit("state:changed", {
1202
+ oldState,
1203
+ newState: this.state,
1204
+ updates
1205
+ });
1206
+ }
1207
+ /**
1208
+ * Register component class
1209
+ */
1210
+ registerComponent(name, ComponentClass) {
1211
+ this.componentClasses.set(name, ComponentClass);
1212
+ }
1213
+ /**
1214
+ * Get component class
1215
+ */
1216
+ getComponent(name) {
1217
+ return this.componentClasses.get(name);
1218
+ }
1219
+ /**
1220
+ * Register model class
1221
+ */
1222
+ registerModel(name, ModelClass) {
1223
+ this.modelClasses.set(name, ModelClass);
1224
+ }
1225
+ /**
1226
+ * Get model class
1227
+ */
1228
+ getModel(name) {
1229
+ return this.modelClasses.get(name);
1230
+ }
1231
+ /**
1232
+ * Setup REST configuration
1233
+ */
1234
+ setupRest() {
1235
+ this.rest = rest;
1236
+ rest.configure(this.api);
1237
+ }
1238
+ /**
1239
+ * Destroy the application
1240
+ */
1241
+ async destroy() {
1242
+ console.log("Destroying WebApp...");
1243
+ if (this.router) {
1244
+ this.router.stop();
1245
+ }
1246
+ if (this._focusHandlers && typeof window !== "undefined") {
1247
+ document.removeEventListener("visibilitychange", this._focusHandlers.visibilitychange);
1248
+ window.removeEventListener("focus", this._focusHandlers.focus);
1249
+ window.removeEventListener("blur", this._focusHandlers.blur);
1250
+ }
1251
+ const pages = Array.from(this.pageCache.values());
1252
+ await Promise.allSettled(
1253
+ pages.map(async (page) => {
1254
+ try {
1255
+ if (page.destroy) {
1256
+ await page.destroy();
1257
+ }
1258
+ } catch (error) {
1259
+ console.error("Error destroying page:", error);
1260
+ }
1261
+ })
1262
+ );
1263
+ if (this.layout && this.layout.destroy) {
1264
+ try {
1265
+ await this.layout.destroy();
1266
+ } catch (error) {
1267
+ console.error("Error destroying layout:", error);
1268
+ }
1269
+ }
1270
+ this.pageCache.clear();
1271
+ this.pageClasses.clear();
1272
+ this.componentClasses.clear();
1273
+ this.modelClasses.clear();
1274
+ if (typeof window !== "undefined" && window.MOJO) {
1275
+ delete window.MOJO.router;
1276
+ }
1277
+ this.isStarted = false;
1278
+ console.log(`✨ ${this.name} destroyed`);
1279
+ }
1280
+ /**
1281
+ * Build page path with params and query
1282
+ */
1283
+ buildPagePath(page, params, query) {
1284
+ let path = page.route || `/${page.pageName.toLowerCase()}`;
1285
+ Object.keys(params).forEach((key) => {
1286
+ if (typeof params[key] === "string" || typeof params[key] === "number") {
1287
+ path = path.replace(`:${key}`, params[key]);
1288
+ }
1289
+ });
1290
+ if (query && Object.keys(query).length > 0) {
1291
+ const queryString = new URLSearchParams(query).toString();
1292
+ path += (path.includes("?") ? "&" : "?") + queryString;
1293
+ }
1294
+ return path;
1295
+ }
1296
+ /**
1297
+ * Static factory method
1298
+ */
1299
+ /**
1300
+ * Validate that default route has a registered page
1301
+ */
1302
+ validateDefaultRoute() {
1303
+ if (!this.pageClasses.has(this.defaultRoute)) {
1304
+ console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`);
1305
+ console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`);
1306
+ console.warn(` Or change default route: new WebApp({ defaultRoute: 'your-page' });`);
1307
+ } else {
1308
+ console.log(`✅ Default route '${this.defaultRoute}' is registered`);
1309
+ }
1310
+ }
1311
+ /**
1312
+ * Find any registered page to use as emergency fallback
1313
+ */
1314
+ findFallbackPage() {
1315
+ const systemPages = ["404", "error", "denied"];
1316
+ for (const [pageName] of this.pageClasses.entries()) {
1317
+ if (!systemPages.includes(pageName)) {
1318
+ return pageName;
1319
+ }
1320
+ }
1321
+ return null;
1322
+ }
1323
+ static create(config = {}) {
1324
+ return new WebApp(config);
1325
+ }
1326
+ /**
1327
+ * Initialize global plugin registry for extensions
1328
+ * Allows extensions to register components that core can use optionally
1329
+ */
1330
+ initPluginRegistry() {
1331
+ if (typeof window !== "undefined") {
1332
+ if (!window.MOJO) {
1333
+ window.MOJO = {};
1334
+ }
1335
+ if (!window.MOJO.plugins) {
1336
+ window.MOJO.plugins = {};
1337
+ }
1338
+ window.MOJO.app = this;
1339
+ }
1340
+ }
1341
+ /**
1342
+ * Register a plugin component
1343
+ * @param {string} name - Plugin component name (e.g., 'ImageCropView')
1344
+ * @param {*} component - The component/class to register
1345
+ */
1346
+ static registerPlugin(name, component) {
1347
+ if (typeof window !== "undefined") {
1348
+ if (!window.MOJO) {
1349
+ window.MOJO = {};
1350
+ }
1351
+ if (!window.MOJO.plugins) {
1352
+ window.MOJO.plugins = {};
1353
+ }
1354
+ window.MOJO.plugins[name] = component;
1355
+ console.debug(`MOJO Plugin registered: ${name}`);
1356
+ }
1357
+ }
1358
+ }
2
1359
  class Dialog extends View {
3
1360
  static _openDialogs = [];
4
1361
  static _baseZIndex = {
@@ -248,8 +1605,11 @@ class Dialog extends View {
248
1605
  if (this.size && this.size !== "auto") {
249
1606
  if (this.size.startsWith("fullscreen")) {
250
1607
  dialogClasses.push(`modal-${this.size}`);
251
- } else if (["sm", "lg", "xl"].includes(this.size)) {
1608
+ } else if (["sm", "lg", "xl", "xxl"].includes(this.size)) {
252
1609
  dialogClasses.push(`modal-${this.size}`);
1610
+ if (["lg", "xl", "xxl"].includes(this.size)) {
1611
+ dialogClasses.push("modal-fullscreen-sm-down");
1612
+ }
253
1613
  }
254
1614
  }
255
1615
  if (this.centered) {
@@ -1165,7 +2525,7 @@ class Dialog extends View {
1165
2525
  cancelText = "Cancel",
1166
2526
  ...dialogOptions
1167
2527
  } = options;
1168
- const FormView = (await import("./FormView-BClEkzmE.js").then((n) => n.b)).default;
2528
+ const FormView = (await import("./FormView-DGRmcKUG.js").then((n) => n.b)).default;
1169
2529
  const formView = new FormView({
1170
2530
  fileHandling: options.fileHandling || "base64",
1171
2531
  data: options.data,
@@ -1270,7 +2630,7 @@ class Dialog extends View {
1270
2630
  if (!model) {
1271
2631
  throw new Error("showModelForm requires a model");
1272
2632
  }
1273
- const FormView = (await import("./FormView-BClEkzmE.js").then((n) => n.b)).default;
2633
+ const FormView = (await import("./FormView-DGRmcKUG.js").then((n) => n.b)).default;
1274
2634
  const formView = new FormView({
1275
2635
  fileHandling: options.fileHandling || "base64",
1276
2636
  model,
@@ -1370,7 +2730,7 @@ class Dialog extends View {
1370
2730
  closeText = "Close",
1371
2731
  ...dialogOptions
1372
2732
  } = options;
1373
- const DataView = (await import("./DataView-OUqaLmGB.js")).default;
2733
+ const DataView = (await import("./DataView-QXyfcg2M.js")).default;
1374
2734
  const dataView = new DataView({
1375
2735
  data,
1376
2736
  model,
@@ -1430,7 +2790,15 @@ class Dialog extends View {
1430
2790
  }
1431
2791
  Dialog.showConfirm = Dialog.confirm;
1432
2792
  Dialog.showError = Dialog.alert;
2793
+ const Dialog$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2794
+ __proto__: null,
2795
+ default: Dialog
2796
+ }, Symbol.toStringTag, { value: "Module" }));
1433
2797
  export {
1434
- Dialog as default
2798
+ Dialog as D,
2799
+ EventBus as E,
2800
+ Router as R,
2801
+ WebApp as W,
2802
+ Dialog$1 as a
1435
2803
  };
1436
- //# sourceMappingURL=Dialog-BiVgKzSK.js.map
2804
+ //# sourceMappingURL=Dialog-DHUsZ92-.js.map