web-mojo 2.2.57 → 2.2.59

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/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +1 -10105
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -588
  7. package/dist/auth.es.js.map +1 -1
  8. package/dist/charts.cjs.js +1 -1
  9. package/dist/charts.es.js +1 -571
  10. package/dist/charts.es.js.map +1 -1
  11. package/dist/chunks/ChatView-D4A9rIX3.js +2 -0
  12. package/dist/chunks/ChatView-D4A9rIX3.js.map +1 -0
  13. package/dist/chunks/ChatView-nxaq8aIo.js +2 -0
  14. package/dist/chunks/ChatView-nxaq8aIo.js.map +1 -0
  15. package/dist/chunks/Collection-1sPoIFvQ.js +2 -0
  16. package/dist/chunks/{Collection-DaiL0uGl.js.map → Collection-1sPoIFvQ.js.map} +1 -1
  17. package/dist/chunks/{Collection-CxbNKOas.js → Collection-DSBRXpwK.js} +2 -2
  18. package/dist/chunks/{Collection-CxbNKOas.js.map → Collection-DSBRXpwK.js.map} +1 -1
  19. package/dist/chunks/{ContextMenu-ClwHEbbD.js → ContextMenu-BWy7WqF4.js} +2 -2
  20. package/dist/chunks/{ContextMenu-ClwHEbbD.js.map → ContextMenu-BWy7WqF4.js.map} +1 -1
  21. package/dist/chunks/ContextMenu-BvniQz-N.js +3 -0
  22. package/dist/chunks/{ContextMenu-sgvgSACY.js.map → ContextMenu-BvniQz-N.js.map} +1 -1
  23. package/dist/chunks/DataView--nUWtq6r.js +2 -0
  24. package/dist/chunks/{DataView-Dzo0jbs2.js.map → DataView--nUWtq6r.js.map} +1 -1
  25. package/dist/chunks/{DataView-1xh3GFeC.js → DataView-CK3Z0TJH.js} +2 -2
  26. package/dist/chunks/{DataView-1xh3GFeC.js.map → DataView-CK3Z0TJH.js.map} +1 -1
  27. package/dist/chunks/Dialog-BcgSR01Z.js +2 -0
  28. package/dist/chunks/{Dialog-DOGDalUq.js.map → Dialog-BcgSR01Z.js.map} +1 -1
  29. package/dist/chunks/{Dialog-CQlTDhZS.js → Dialog-DwCTFV6O.js} +2 -2
  30. package/dist/chunks/{Dialog-CQlTDhZS.js.map → Dialog-DwCTFV6O.js.map} +1 -1
  31. package/dist/chunks/FormPlugins-DvQ-G5J5.js +2 -0
  32. package/dist/chunks/{FormPlugins-DY6e88YT.js.map → FormPlugins-DvQ-G5J5.js.map} +1 -1
  33. package/dist/chunks/{FormView-DaKA4Sys.js → FormView-CRmEReTC.js} +3 -3
  34. package/dist/chunks/{FormView-DaKA4Sys.js.map → FormView-CRmEReTC.js.map} +1 -1
  35. package/dist/chunks/FormView-OLA7t-yv.js +3 -0
  36. package/dist/chunks/{FormView-Dz3mYasQ.js.map → FormView-OLA7t-yv.js.map} +1 -1
  37. package/dist/chunks/ListView-6JQ6tRXs.js +2 -0
  38. package/dist/chunks/{ListView-X5w5jf51.js.map → ListView-6JQ6tRXs.js.map} +1 -1
  39. package/dist/chunks/{ListView-CDzKIpd8.js → ListView-DVStKiMi.js} +2 -2
  40. package/dist/chunks/{ListView-CDzKIpd8.js.map → ListView-DVStKiMi.js.map} +1 -1
  41. package/dist/chunks/{MetricsCountryMapView-Dx2cw7ya.js → MetricsCountryMapView-CnAEbUw_.js} +2 -2
  42. package/dist/chunks/{MetricsCountryMapView-Dx2cw7ya.js.map → MetricsCountryMapView-CnAEbUw_.js.map} +1 -1
  43. package/dist/chunks/MetricsCountryMapView-J067qrrt.js +2 -0
  44. package/dist/chunks/{MetricsCountryMapView-B2xz6zUw.js.map → MetricsCountryMapView-J067qrrt.js.map} +1 -1
  45. package/dist/chunks/{MetricsMiniChartWidget-CBuso0OE.js → MetricsMiniChartWidget-BeD1slGs.js} +2 -2
  46. package/dist/chunks/{MetricsMiniChartWidget-CBuso0OE.js.map → MetricsMiniChartWidget-BeD1slGs.js.map} +1 -1
  47. package/dist/chunks/MetricsMiniChartWidget-x2gFjHOU.js +2 -0
  48. package/dist/chunks/{MetricsMiniChartWidget-DvKd7Qrk.js.map → MetricsMiniChartWidget-x2gFjHOU.js.map} +1 -1
  49. package/dist/chunks/PDFViewer-CsyKn-gh.js +2 -0
  50. package/dist/chunks/{PDFViewer-EJ9cOfPF.js.map → PDFViewer-CsyKn-gh.js.map} +1 -1
  51. package/dist/chunks/{PDFViewer-ofMGdSaj.js → PDFViewer-DSa4BZCm.js} +2 -2
  52. package/dist/chunks/{PDFViewer-ofMGdSaj.js.map → PDFViewer-DSa4BZCm.js.map} +1 -1
  53. package/dist/chunks/Rest-DHbszkuP.js +2 -0
  54. package/dist/chunks/Rest-DHbszkuP.js.map +1 -0
  55. package/dist/chunks/Rest-Ds9e8tN8.js +2 -0
  56. package/dist/chunks/Rest-Ds9e8tN8.js.map +1 -0
  57. package/dist/chunks/TokenManager-D6SjKgPZ.js +2 -0
  58. package/dist/chunks/{TokenManager-DoN9e6q6.js.map → TokenManager-D6SjKgPZ.js.map} +1 -1
  59. package/dist/chunks/{TokenManager-Gqvj7SDX.js → TokenManager-REbha1Le.js} +2 -2
  60. package/dist/chunks/{TokenManager-Gqvj7SDX.js.map → TokenManager-REbha1Le.js.map} +1 -1
  61. package/dist/chunks/WebApp-CULZpO_0.js +2 -0
  62. package/dist/chunks/{WebApp-6qvqmOts.js.map → WebApp-CULZpO_0.js.map} +1 -1
  63. package/dist/chunks/{WebApp-_dgpwtFw.js → WebApp-DovLtA60.js} +2 -2
  64. package/dist/chunks/{WebApp-_dgpwtFw.js.map → WebApp-DovLtA60.js.map} +1 -1
  65. package/dist/chunks/WebSocketClient-B-wc3mez.js +2 -0
  66. package/dist/chunks/{WebSocketClient-DG2olXpH.js.map → WebSocketClient-B-wc3mez.js.map} +1 -1
  67. package/dist/chunks/{WebSocketClient-MFkFlSue.js → WebSocketClient-BdZ9QYll.js} +2 -2
  68. package/dist/chunks/{WebSocketClient-MFkFlSue.js.map → WebSocketClient-BdZ9QYll.js.map} +1 -1
  69. package/dist/chunks/version-C3dnl1bg.js +2 -0
  70. package/dist/chunks/version-C3dnl1bg.js.map +1 -0
  71. package/dist/chunks/{version-BVADfTA5.js → version-ioN546cp.js} +2 -2
  72. package/dist/chunks/{version-BVADfTA5.js.map → version-ioN546cp.js.map} +1 -1
  73. package/dist/css/web-mojo.css +1 -1
  74. package/dist/docit.cjs.js +1 -1
  75. package/dist/docit.es.js +1 -957
  76. package/dist/docit.es.js.map +1 -1
  77. package/dist/index.cjs.js +1 -1
  78. package/dist/index.es.js +1 -3252
  79. package/dist/index.es.js.map +1 -1
  80. package/dist/lightbox.cjs.js +1 -1
  81. package/dist/lightbox.es.js +1 -3737
  82. package/dist/lightbox.es.js.map +1 -1
  83. package/dist/loader.umd.js +2 -2
  84. package/dist/map.cjs.js +1 -1
  85. package/dist/map.es.js +1 -1032
  86. package/dist/map.es.js.map +1 -1
  87. package/dist/mojo-auth.es.js +338 -0
  88. package/dist/mojo-auth.umd.js +1 -0
  89. package/dist/timeline.cjs.js +1 -1
  90. package/dist/timeline.es.js +1 -224
  91. package/dist/timeline.es.js.map +1 -1
  92. package/dist/web-mojo.lite.iife.js +14 -3
  93. package/dist/web-mojo.lite.iife.js.map +1 -1
  94. package/dist/web-mojo.lite.iife.min.js +6 -6
  95. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  96. package/package.json +2 -2
  97. package/dist/chunks/ChatView-9k6xBWXk.js +0 -7632
  98. package/dist/chunks/ChatView-9k6xBWXk.js.map +0 -1
  99. package/dist/chunks/ChatView-CdtuCDYm.js +0 -2
  100. package/dist/chunks/ChatView-CdtuCDYm.js.map +0 -1
  101. package/dist/chunks/Collection-DaiL0uGl.js +0 -1014
  102. package/dist/chunks/ContextMenu-sgvgSACY.js +0 -1535
  103. package/dist/chunks/DataView-Dzo0jbs2.js +0 -862
  104. package/dist/chunks/Dialog-DOGDalUq.js +0 -1579
  105. package/dist/chunks/FormPlugins-DY6e88YT.js +0 -124
  106. package/dist/chunks/FormView-Dz3mYasQ.js +0 -8636
  107. package/dist/chunks/ListView-X5w5jf51.js +0 -495
  108. package/dist/chunks/MetricsCountryMapView-B2xz6zUw.js +0 -1054
  109. package/dist/chunks/MetricsMiniChartWidget-DvKd7Qrk.js +0 -3283
  110. package/dist/chunks/PDFViewer-EJ9cOfPF.js +0 -946
  111. package/dist/chunks/Rest-CgSjfMaU.js +0 -2
  112. package/dist/chunks/Rest-CgSjfMaU.js.map +0 -1
  113. package/dist/chunks/Rest-W-sPfGh9.js +0 -4375
  114. package/dist/chunks/Rest-W-sPfGh9.js.map +0 -1
  115. package/dist/chunks/TokenManager-DoN9e6q6.js +0 -1423
  116. package/dist/chunks/WebApp-6qvqmOts.js +0 -1386
  117. package/dist/chunks/WebSocketClient-DG2olXpH.js +0 -209
  118. package/dist/chunks/version-OyPGnx30.js +0 -38
  119. package/dist/chunks/version-OyPGnx30.js.map +0 -1
@@ -1,1386 +0,0 @@
1
- import { r as rest } from "./Rest-W-sPfGh9.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
- const oldPage = this.currentPage;
843
- if (!pageInstance) {
844
- this._show404(pageName, params, query, fromRouter);
845
- return;
846
- }
847
- this.events.emit("page:showing", {
848
- page: pageInstance,
849
- pageName: pageInstance.pageName,
850
- params,
851
- query,
852
- fromRouter
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 Dialog = (await import("./Dialog-DOGDalUq.js")).default;
993
- await Dialog.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 Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1010
- await Dialog.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 Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1027
- await Dialog.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 Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1044
- await Dialog.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 Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1070
- Dialog.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 Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1084
- Dialog.hideBusy();
1085
- } catch (e) {
1086
- if (typeof window !== "undefined" && window?.console) {
1087
- console.warn("[WebApp] hideLoading fallback:", e);
1088
- }
1089
- }
1090
- }
1091
- async showModelView(model, options = {}) {
1092
- try {
1093
- const Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1094
- return await Dialog.showModelView(model, 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 showModelForm(options = {}) {
1103
- try {
1104
- const Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1105
- return await Dialog.showModelForm(options);
1106
- } catch (e) {
1107
- if (typeof window !== "undefined" && window?.console) {
1108
- console.error("[WebApp] showModelForm failed:", e);
1109
- }
1110
- throw e;
1111
- }
1112
- }
1113
- async showForm(options = {}) {
1114
- try {
1115
- const Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1116
- return await Dialog.showForm(options);
1117
- } catch (e) {
1118
- if (typeof window !== "undefined" && window?.console) {
1119
- console.error("[WebApp] showForm failed:", e);
1120
- }
1121
- throw e;
1122
- }
1123
- }
1124
- async showDialog(options = {}) {
1125
- try {
1126
- const Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1127
- return await Dialog.showDialog(options);
1128
- } catch (e) {
1129
- if (typeof window !== "undefined" && window?.console) {
1130
- console.error("[WebApp] showDialog failed:", e);
1131
- }
1132
- throw e;
1133
- }
1134
- }
1135
- async showAlert(options = {}) {
1136
- try {
1137
- const Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1138
- return await Dialog.showDialog(options);
1139
- } catch (e) {
1140
- if (typeof window !== "undefined" && window?.console) {
1141
- console.error("[WebApp] showDialog failed:", e);
1142
- }
1143
- throw e;
1144
- }
1145
- }
1146
- async confirm(message, title = "Confirm", options = {}) {
1147
- const Dialog = (await import("./Dialog-DOGDalUq.js")).default;
1148
- return await Dialog.confirm(message, title, options);
1149
- }
1150
- /**
1151
- * Setup browser focus/blur tracking
1152
- */
1153
- setupFocusTracking() {
1154
- if (typeof window === "undefined") return;
1155
- this.isFocused = !document.hidden;
1156
- const handleVisibilityChange = () => {
1157
- const wasFocused = this.isFocused;
1158
- this.isFocused = !document.hidden;
1159
- if (wasFocused !== this.isFocused) {
1160
- if (this.isFocused) {
1161
- this.events.emit("browser:focus");
1162
- } else {
1163
- this.events.emit("browser:blur");
1164
- }
1165
- }
1166
- };
1167
- const handleFocus = () => {
1168
- if (!this.isFocused) {
1169
- this.isFocused = true;
1170
- this.events.emit("browser:focus");
1171
- }
1172
- };
1173
- const handleBlur = () => {
1174
- if (this.isFocused) {
1175
- this.isFocused = false;
1176
- this.events.emit("browser:blur");
1177
- }
1178
- };
1179
- document.addEventListener("visibilitychange", handleVisibilityChange);
1180
- window.addEventListener("focus", handleFocus);
1181
- window.addEventListener("blur", handleBlur);
1182
- this._focusHandlers = {
1183
- visibilitychange: handleVisibilityChange,
1184
- focus: handleFocus,
1185
- blur: handleBlur
1186
- };
1187
- }
1188
- /**
1189
- * Setup global error handling
1190
- */
1191
- setupErrorHandling() {
1192
- window.addEventListener("error", (event) => {
1193
- console.error("Global error:", event.error);
1194
- if (this.debug) {
1195
- this.showError(`Error: ${event.error?.message || "Unknown error"}`);
1196
- }
1197
- });
1198
- window.addEventListener("unhandledrejection", (event) => {
1199
- console.error("Unhandled promise rejection:", event.reason);
1200
- if (this.debug) {
1201
- this.showError(`Promise rejected: ${event.reason?.message || "Unknown error"}`);
1202
- }
1203
- });
1204
- }
1205
- /**
1206
- * Escape HTML to prevent XSS
1207
- */
1208
- escapeHtml(unsafe) {
1209
- return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1210
- }
1211
- /**
1212
- * Get application state
1213
- */
1214
- getState(key) {
1215
- return key ? this.state[key] : this.state;
1216
- }
1217
- /**
1218
- * Set application state
1219
- */
1220
- setState(updates) {
1221
- const oldState = { ...this.state };
1222
- Object.assign(this.state, updates);
1223
- this.events.emit("state:changed", {
1224
- oldState,
1225
- newState: this.state,
1226
- updates
1227
- });
1228
- }
1229
- /**
1230
- * Register component class
1231
- */
1232
- registerComponent(name, ComponentClass) {
1233
- this.componentClasses.set(name, ComponentClass);
1234
- }
1235
- /**
1236
- * Get component class
1237
- */
1238
- getComponent(name) {
1239
- return this.componentClasses.get(name);
1240
- }
1241
- /**
1242
- * Register model class
1243
- */
1244
- registerModel(name, ModelClass) {
1245
- this.modelClasses.set(name, ModelClass);
1246
- }
1247
- /**
1248
- * Get model class
1249
- */
1250
- getModel(name) {
1251
- return this.modelClasses.get(name);
1252
- }
1253
- /**
1254
- * Setup REST configuration
1255
- */
1256
- setupRest() {
1257
- this.rest = rest;
1258
- rest.configure(this.api);
1259
- }
1260
- /**
1261
- * Destroy the application
1262
- */
1263
- async destroy() {
1264
- console.log("Destroying WebApp...");
1265
- if (this.router) {
1266
- this.router.stop();
1267
- }
1268
- if (this._focusHandlers && typeof window !== "undefined") {
1269
- document.removeEventListener("visibilitychange", this._focusHandlers.visibilitychange);
1270
- window.removeEventListener("focus", this._focusHandlers.focus);
1271
- window.removeEventListener("blur", this._focusHandlers.blur);
1272
- }
1273
- const pages = Array.from(this.pageCache.values());
1274
- await Promise.allSettled(
1275
- pages.map(async (page) => {
1276
- try {
1277
- if (page.destroy) {
1278
- await page.destroy();
1279
- }
1280
- } catch (error) {
1281
- console.error("Error destroying page:", error);
1282
- }
1283
- })
1284
- );
1285
- if (this.layout && this.layout.destroy) {
1286
- try {
1287
- await this.layout.destroy();
1288
- } catch (error) {
1289
- console.error("Error destroying layout:", error);
1290
- }
1291
- }
1292
- this.pageCache.clear();
1293
- this.pageClasses.clear();
1294
- this.componentClasses.clear();
1295
- this.modelClasses.clear();
1296
- if (typeof window !== "undefined" && window.MOJO) {
1297
- delete window.MOJO.router;
1298
- }
1299
- this.isStarted = false;
1300
- console.log(`✨ ${this.name} destroyed`);
1301
- }
1302
- /**
1303
- * Build page path with params and query
1304
- */
1305
- buildPagePath(page, params, query) {
1306
- let path = page.route || `/${page.pageName.toLowerCase()}`;
1307
- Object.keys(params).forEach((key) => {
1308
- if (typeof params[key] === "string" || typeof params[key] === "number") {
1309
- path = path.replace(`:${key}`, params[key]);
1310
- }
1311
- });
1312
- if (query && Object.keys(query).length > 0) {
1313
- const queryString = new URLSearchParams(query).toString();
1314
- path += (path.includes("?") ? "&" : "?") + queryString;
1315
- }
1316
- return path;
1317
- }
1318
- /**
1319
- * Static factory method
1320
- */
1321
- /**
1322
- * Validate that default route has a registered page
1323
- */
1324
- validateDefaultRoute() {
1325
- if (!this.pageClasses.has(this.defaultRoute)) {
1326
- console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`);
1327
- console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`);
1328
- console.warn(` Or change default route: new WebApp({ defaultRoute: 'your-page' });`);
1329
- } else {
1330
- console.log(`✅ Default route '${this.defaultRoute}' is registered`);
1331
- }
1332
- }
1333
- /**
1334
- * Find any registered page to use as emergency fallback
1335
- */
1336
- findFallbackPage() {
1337
- const systemPages = ["404", "error", "denied"];
1338
- for (const [pageName] of this.pageClasses.entries()) {
1339
- if (!systemPages.includes(pageName)) {
1340
- return pageName;
1341
- }
1342
- }
1343
- return null;
1344
- }
1345
- static create(config = {}) {
1346
- return new WebApp(config);
1347
- }
1348
- /**
1349
- * Initialize global plugin registry for extensions
1350
- * Allows extensions to register components that core can use optionally
1351
- */
1352
- initPluginRegistry() {
1353
- if (typeof window !== "undefined") {
1354
- if (!window.MOJO) {
1355
- window.MOJO = {};
1356
- }
1357
- if (!window.MOJO.plugins) {
1358
- window.MOJO.plugins = {};
1359
- }
1360
- window.MOJO.app = this;
1361
- }
1362
- }
1363
- /**
1364
- * Register a plugin component
1365
- * @param {string} name - Plugin component name (e.g., 'ImageCropView')
1366
- * @param {*} component - The component/class to register
1367
- */
1368
- static registerPlugin(name, component) {
1369
- if (typeof window !== "undefined") {
1370
- if (!window.MOJO) {
1371
- window.MOJO = {};
1372
- }
1373
- if (!window.MOJO.plugins) {
1374
- window.MOJO.plugins = {};
1375
- }
1376
- window.MOJO.plugins[name] = component;
1377
- console.debug(`MOJO Plugin registered: ${name}`);
1378
- }
1379
- }
1380
- }
1381
- export {
1382
- EventBus as E,
1383
- Router as R,
1384
- WebApp as W
1385
- };
1386
- //# sourceMappingURL=WebApp-6qvqmOts.js.map