web-mojo 2.1.995 → 2.1.1044

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