web-mojo 2.1.936 → 2.1.955

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 (102) 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 +122 -229
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +7 -7
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +7 -7
  11. package/dist/chunks/ChatView-CTtQHvRP.js +2 -0
  12. package/dist/chunks/ChatView-CTtQHvRP.js.map +1 -0
  13. package/dist/chunks/{ChatView-DlSxjxah.js → ChatView-DLEStri1.js} +89 -574
  14. package/dist/chunks/ChatView-DLEStri1.js.map +1 -0
  15. package/dist/chunks/Collection-DD1_31eh.js +2 -0
  16. package/dist/chunks/Collection-DD1_31eh.js.map +1 -0
  17. package/dist/chunks/Collection-YRfGoT73.js +990 -0
  18. package/dist/chunks/Collection-YRfGoT73.js.map +1 -0
  19. package/dist/chunks/ContextMenu-By2g3KYY.js +1171 -0
  20. package/dist/chunks/ContextMenu-By2g3KYY.js.map +1 -0
  21. package/dist/chunks/ContextMenu-Cl0TRsIa.js +3 -0
  22. package/dist/chunks/ContextMenu-Cl0TRsIa.js.map +1 -0
  23. package/dist/chunks/DataView-CdDY9ijM.js +2 -0
  24. package/dist/chunks/{DataView-XJbTQ5q0.js.map → DataView-CdDY9ijM.js.map} +1 -1
  25. package/dist/chunks/{DataView-Vmjx4eCr.js → DataView-OUqaLmGB.js} +2 -2
  26. package/dist/chunks/{DataView-Vmjx4eCr.js.map → DataView-OUqaLmGB.js.map} +1 -1
  27. package/dist/chunks/{Dialog-D_rAf4gQ.js → Dialog-C2mRUxga.js} +8 -6
  28. package/dist/chunks/{Dialog-D_rAf4gQ.js.map → Dialog-C2mRUxga.js.map} +1 -1
  29. package/dist/chunks/Dialog-Cl6MN8if.js +2 -0
  30. package/dist/chunks/{Dialog-BinTQTfO.js.map → Dialog-Cl6MN8if.js.map} +1 -1
  31. package/dist/chunks/FormView-BSWaXDav.js +3 -0
  32. package/dist/chunks/{FormView-TPFsq8ZX.js.map → FormView-BSWaXDav.js.map} +1 -1
  33. package/dist/chunks/{FormView-CIriLDZY.js → FormView-HWvIdFkB.js} +10 -6
  34. package/dist/chunks/FormView-HWvIdFkB.js.map +1 -0
  35. package/dist/chunks/ListView-BMNhd5-B.js +492 -0
  36. package/dist/chunks/ListView-BMNhd5-B.js.map +1 -0
  37. package/dist/chunks/ListView-BRGiITfD.js +2 -0
  38. package/dist/chunks/ListView-BRGiITfD.js.map +1 -0
  39. package/dist/chunks/MetricsMiniChartWidget-BkTEO87S.js +2 -0
  40. package/dist/chunks/{MetricsMiniChartWidget-sONcM0pG.js.map → MetricsMiniChartWidget-BkTEO87S.js.map} +1 -1
  41. package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js → MetricsMiniChartWidget-Y70IHFIe.js} +3 -3
  42. package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js.map → MetricsMiniChartWidget-Y70IHFIe.js.map} +1 -1
  43. package/dist/chunks/PDFViewer-C0aMqGJL.js +2 -0
  44. package/dist/chunks/{PDFViewer-UBhinN8A.js.map → PDFViewer-C0aMqGJL.js.map} +1 -1
  45. package/dist/chunks/{PDFViewer-D6SKOl85.js → PDFViewer-DkbYnnoV.js} +3 -3
  46. package/dist/chunks/{PDFViewer-D6SKOl85.js.map → PDFViewer-DkbYnnoV.js.map} +1 -1
  47. package/dist/chunks/Page-CvbwEoLv.js +2 -0
  48. package/dist/chunks/{Page-CnvHhwLZ.js.map → Page-CvbwEoLv.js.map} +1 -1
  49. package/dist/chunks/{Page-B7L25Omb.js → Page-Deq4y2Kq.js} +2 -2
  50. package/dist/chunks/{Page-B7L25Omb.js.map → Page-Deq4y2Kq.js.map} +1 -1
  51. package/dist/chunks/Rest-BNYqGlnP.js +2 -0
  52. package/dist/chunks/Rest-BNYqGlnP.js.map +1 -0
  53. package/dist/chunks/{WebApp-El07OMHH.js → Rest-CS4jRCAs.js} +5 -1389
  54. package/dist/chunks/Rest-CS4jRCAs.js.map +1 -0
  55. package/dist/chunks/TopNav-A7NQ4viq.js +2 -0
  56. package/dist/chunks/{TopNav-Dcmcic-i.js.map → TopNav-A7NQ4viq.js.map} +1 -1
  57. package/dist/chunks/{TopNav-CPA884W7.js → TopNav-Dch6cZFa.js} +5 -5
  58. package/dist/chunks/{TopNav-CPA884W7.js.map → TopNav-Dch6cZFa.js.map} +1 -1
  59. package/dist/chunks/WebApp-CaOPY_k7.js +2 -0
  60. package/dist/chunks/WebApp-CaOPY_k7.js.map +1 -0
  61. package/dist/chunks/WebApp-RHtJ4hFZ.js +1388 -0
  62. package/dist/chunks/WebApp-RHtJ4hFZ.js.map +1 -0
  63. package/dist/css/web-mojo.css +2 -2
  64. package/dist/docit.cjs.js +1 -1
  65. package/dist/docit.cjs.js.map +1 -1
  66. package/dist/docit.es.js +12 -10
  67. package/dist/docit.es.js.map +1 -1
  68. package/dist/index.cjs.js +1 -1
  69. package/dist/index.cjs.js.map +1 -1
  70. package/dist/index.es.js +120 -116
  71. package/dist/index.es.js.map +1 -1
  72. package/dist/lightbox.cjs.js +1 -1
  73. package/dist/lightbox.cjs.js.map +1 -1
  74. package/dist/lightbox.es.js +121 -121
  75. package/dist/lightbox.es.js.map +1 -1
  76. package/dist/map.cjs.js +2 -0
  77. package/dist/map.cjs.js.map +1 -0
  78. package/dist/map.es.js +188 -0
  79. package/dist/map.es.js.map +1 -0
  80. package/dist/timeline.cjs.js +2 -0
  81. package/dist/timeline.cjs.js.map +1 -0
  82. package/dist/timeline.es.js +225 -0
  83. package/dist/timeline.es.js.map +1 -0
  84. package/package.json +9 -1
  85. package/dist/chunks/ChatView-DlSxjxah.js.map +0 -1
  86. package/dist/chunks/ChatView-DnqrGXMC.js +0 -2
  87. package/dist/chunks/ChatView-DnqrGXMC.js.map +0 -1
  88. package/dist/chunks/ContextMenu-CE77rUmn.js +0 -2155
  89. package/dist/chunks/ContextMenu-CE77rUmn.js.map +0 -1
  90. package/dist/chunks/ContextMenu-KVxd0Kgd.js +0 -3
  91. package/dist/chunks/ContextMenu-KVxd0Kgd.js.map +0 -1
  92. package/dist/chunks/DataView-XJbTQ5q0.js +0 -2
  93. package/dist/chunks/Dialog-BinTQTfO.js +0 -2
  94. package/dist/chunks/FormView-CIriLDZY.js.map +0 -1
  95. package/dist/chunks/FormView-TPFsq8ZX.js +0 -3
  96. package/dist/chunks/MetricsMiniChartWidget-sONcM0pG.js +0 -2
  97. package/dist/chunks/PDFViewer-UBhinN8A.js +0 -2
  98. package/dist/chunks/Page-CnvHhwLZ.js +0 -2
  99. package/dist/chunks/TopNav-Dcmcic-i.js +0 -2
  100. package/dist/chunks/WebApp-El07OMHH.js.map +0 -1
  101. package/dist/chunks/WebApp-b9DQWz1d.js +0 -2
  102. package/dist/chunks/WebApp-b9DQWz1d.js.map +0 -1
@@ -1,32 +1,3 @@
1
- const VERSION = "2.1.936";
2
- const VERSION_MAJOR = 2;
3
- const VERSION_MINOR = 1;
4
- const VERSION_REVISION = 936;
5
- const BUILD_TIME = "2025-10-16T13:13:09.014Z";
6
- const VERSION_INFO = {
7
- full: VERSION,
8
- major: VERSION_MAJOR,
9
- minor: VERSION_MINOR,
10
- revision: VERSION_REVISION,
11
- buildTime: BUILD_TIME,
12
- toString() {
13
- return this.full;
14
- },
15
- compare(other) {
16
- const parseVer = (v) => v.split(".").map(Number);
17
- const [a1, a2, a3] = parseVer(this.full);
18
- const [b1, b2, b3] = parseVer(other);
19
- if (a1 !== b1) return a1 - b1;
20
- if (a2 !== b2) return a2 - b2;
21
- return a3 - b3;
22
- }
23
- };
24
- if (typeof window !== "undefined") {
25
- window.MOJO = window.MOJO || {};
26
- window.MOJO.VERSION = VERSION;
27
- window.MOJO.VERSION_INFO = VERSION_INFO;
28
- window.MOJO.version = VERSION;
29
- }
30
1
  const objectToString = Object.prototype.toString;
31
2
  const isArray = Array.isArray || function(obj) {
32
3
  return objectToString.call(obj) === "[object Array]";
@@ -3353,223 +3324,6 @@ class View {
3353
3324
  }
3354
3325
  }
3355
3326
  Object.assign(View.prototype, EventEmitter);
3356
- class Router {
3357
- constructor(options = {}) {
3358
- this.defaultRoute = options.defaultRoute || "home";
3359
- this.routes = [];
3360
- this.currentRoute = null;
3361
- this.eventEmitter = options.eventEmitter || null;
3362
- this.boundHandlePopState = this.handlePopState.bind(this);
3363
- }
3364
- start() {
3365
- window.addEventListener("popstate", this.boundHandlePopState);
3366
- this.handleCurrentLocation();
3367
- }
3368
- stop() {
3369
- window.removeEventListener("popstate", this.boundHandlePopState);
3370
- }
3371
- addRoute(pattern, pageName) {
3372
- this.routes.push({
3373
- pattern: this.normalizePattern(pattern),
3374
- regex: this.patternToRegex(pattern),
3375
- pageName,
3376
- paramNames: this.extractParamNames(pattern)
3377
- });
3378
- }
3379
- async navigate(path, options = {}) {
3380
- const { replace = false, state = null, trigger = true } = options;
3381
- const { pageName, queryParams } = this.parseInput(path);
3382
- if (trigger) {
3383
- await this.handleRouteChange(pageName, queryParams);
3384
- }
3385
- }
3386
- // Browser navigation
3387
- back() {
3388
- window.history.back();
3389
- }
3390
- forward() {
3391
- window.history.forward();
3392
- }
3393
- // Get current route info
3394
- getCurrentRoute() {
3395
- return this.currentRoute;
3396
- }
3397
- getCurrentPath() {
3398
- const { pageName, queryParams } = this.parseCurrentUrl();
3399
- return this.buildPublicUrl(pageName, queryParams);
3400
- }
3401
- // Private methods
3402
- handlePopState(_event) {
3403
- if (this.allowPopState) {
3404
- this.handleCurrentLocation();
3405
- } else {
3406
- console.warn("PopStateEvent is not allowed");
3407
- }
3408
- }
3409
- async handleCurrentLocation() {
3410
- const { pageName, queryParams } = this.parseCurrentUrl();
3411
- await this.handleRouteChange(pageName, queryParams);
3412
- }
3413
- async handleRouteChange(pageName, queryParams) {
3414
- const routePath = "/" + pageName;
3415
- const route = this.matchRoute(routePath);
3416
- const publicUrl = this.buildPublicUrl(pageName, queryParams);
3417
- if (!route) {
3418
- console.log("No route matched for page:", pageName);
3419
- if (this.eventEmitter) {
3420
- this.eventEmitter.emit("route:notfound", { path: publicUrl });
3421
- }
3422
- return null;
3423
- }
3424
- this.currentRoute = route;
3425
- if (this.eventEmitter) {
3426
- this.eventEmitter.emit("route:changed", {
3427
- path: publicUrl,
3428
- pageName: route.pageName,
3429
- params: route.params,
3430
- query: queryParams,
3431
- route
3432
- });
3433
- }
3434
- return route;
3435
- }
3436
- matchRoute(path) {
3437
- for (const route of this.routes) {
3438
- const match = path.match(route.regex);
3439
- if (match) {
3440
- const params = {};
3441
- route.paramNames.forEach((name, index) => {
3442
- params[name] = match[index + 1];
3443
- });
3444
- return {
3445
- ...route,
3446
- params,
3447
- path
3448
- };
3449
- }
3450
- }
3451
- return null;
3452
- }
3453
- // Parse any input format and extract page name + query params
3454
- parseInput(input) {
3455
- let pageName = this.defaultRoute;
3456
- let queryParams = {};
3457
- if (!input) {
3458
- return { pageName, queryParams };
3459
- }
3460
- try {
3461
- if (input.includes("?")) {
3462
- const [pathPart, queryPart] = input.split("?", 2);
3463
- const urlParams = new URLSearchParams(queryPart);
3464
- if (urlParams.has("page")) {
3465
- pageName = urlParams.get("page") || this.defaultRoute;
3466
- for (const [key, value] of urlParams) {
3467
- if (key !== "page") {
3468
- queryParams[key] = value;
3469
- }
3470
- }
3471
- } else {
3472
- if (pathPart.startsWith("/")) {
3473
- pageName = pathPart.substring(1) || this.defaultRoute;
3474
- } else {
3475
- pageName = pathPart || this.defaultRoute;
3476
- }
3477
- for (const [key, value] of urlParams) {
3478
- queryParams[key] = value;
3479
- }
3480
- }
3481
- } else if (input.startsWith("/")) {
3482
- pageName = input.substring(1) || this.defaultRoute;
3483
- } else {
3484
- pageName = input;
3485
- }
3486
- } catch (error) {
3487
- console.warn("Failed to parse input:", input, error);
3488
- pageName = this.defaultRoute;
3489
- queryParams = {};
3490
- }
3491
- return { pageName, queryParams };
3492
- }
3493
- // Parse current browser URL
3494
- parseCurrentUrl() {
3495
- const urlParams = new URLSearchParams(window.location.search);
3496
- const pageName = urlParams.get("page") || this.defaultRoute;
3497
- const queryParams = {};
3498
- for (const [key, value] of urlParams) {
3499
- if (key !== "page") {
3500
- queryParams[key] = value;
3501
- }
3502
- }
3503
- return { pageName, queryParams };
3504
- }
3505
- // Build public URL format: ?page=admin&group=123
3506
- buildPublicUrl(pageName, queryParams = {}) {
3507
- const urlParams = new URLSearchParams();
3508
- urlParams.set("page", pageName);
3509
- Object.entries(queryParams).forEach(([key, value]) => {
3510
- if (value !== null && value !== void 0 && value !== "") {
3511
- urlParams.set(key, String(value));
3512
- }
3513
- });
3514
- return "?" + urlParams.toString();
3515
- }
3516
- // Update browser URL
3517
- updateBrowserUrl(pageName, queryParams, replace, state) {
3518
- const currentUrl = new URL(window.location.origin + window.location.pathname);
3519
- currentUrl.searchParams.set("page", pageName);
3520
- Object.entries(queryParams).forEach(([key, value]) => {
3521
- if (value !== null && value !== void 0 && value !== "") {
3522
- currentUrl.searchParams.set(key, String(value));
3523
- }
3524
- });
3525
- const url = currentUrl.toString();
3526
- if (replace) {
3527
- window.history.replaceState(state, "", url);
3528
- } else {
3529
- window.history.pushState(state, "", url);
3530
- }
3531
- }
3532
- // Route pattern utilities
3533
- patternToRegex(pattern) {
3534
- let regexPattern = pattern.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&").replace(/\/:([^/?]+)\?/g, "(?:/([^/]+))?").replace(/:([^/]+)/g, "([^/]+)");
3535
- return new RegExp(`^${regexPattern}$`);
3536
- }
3537
- extractParamNames(pattern) {
3538
- const matches = pattern.match(/:([^/?]+)\??/g) || [];
3539
- return matches.map((match) => match.replace(/[:?]/g, ""));
3540
- }
3541
- normalizePattern(pattern) {
3542
- return pattern.startsWith("/") ? pattern : `/${pattern}`;
3543
- }
3544
- /**
3545
- * Update URL parameters without triggering navigation
3546
- * @param {object} params - Parameters to update in URL
3547
- * @param {object} options - Options like { replace: true }
3548
- */
3549
- updateUrl(params = {}, options = {}) {
3550
- const { replace = false } = options;
3551
- const { pageName } = this.parseCurrentUrl();
3552
- this.updateBrowserUrl(pageName, params, replace);
3553
- }
3554
- /**
3555
- * Build URL for given page and query parameters
3556
- */
3557
- buildUrl(page, query = {}) {
3558
- return this.buildPublicUrl(page, query);
3559
- }
3560
- /**
3561
- * Check if two routes match (ignoring query parameters)
3562
- * @param {string} route1 - First route in any format ("/admin", "?page=admin", "admin")
3563
- * @param {string} route2 - Second route in any format
3564
- * @returns {boolean} - True if routes point to the same page
3565
- */
3566
- doRoutesMatch(route1, route2) {
3567
- if (!route1 || !route2) return false;
3568
- const { pageName: pageName1 } = this.parseInput(route1);
3569
- const { pageName: pageName2 } = this.parseInput(route2);
3570
- return pageName1 === pageName2;
3571
- }
3572
- }
3573
3327
  class Rest {
3574
3328
  constructor() {
3575
3329
  this.config = {
@@ -4278,1152 +4032,14 @@ class Rest {
4278
4032
  }
4279
4033
  }
4280
4034
  const rest = new Rest();
4281
- class EventBus {
4282
- constructor() {
4283
- this.listeners = {};
4284
- this.onceListeners = {};
4285
- this.maxListeners = 100;
4286
- this.debugMode = false;
4287
- this.eventStats = {};
4288
- }
4289
- /**
4290
- * Add event listener
4291
- * @param {string|Array<string>} event - Event name or array of event names
4292
- * @param {function} callback - Event callback
4293
- * @returns {EventBus} This instance for chaining
4294
- */
4295
- on(event, callback) {
4296
- if (typeof callback !== "function") {
4297
- throw new Error("Callback must be a function");
4298
- }
4299
- if (Array.isArray(event)) {
4300
- event.forEach((eventName) => this.on(eventName, callback));
4301
- return this;
4302
- }
4303
- if (!this.listeners[event]) {
4304
- this.listeners[event] = [];
4305
- }
4306
- if (this.listeners[event].length >= this.maxListeners) {
4307
- console.warn(`Max listeners (${this.maxListeners}) exceeded for event: ${event}`);
4308
- }
4309
- this.listeners[event].push(callback);
4310
- return this;
4311
- }
4312
- /**
4313
- * Add one-time event listener
4314
- * @param {string|Array<string>} event - Event name or array of event names
4315
- * @param {function} callback - Event callback
4316
- * @returns {EventBus} This instance for chaining
4317
- */
4318
- once(event, callback) {
4319
- if (typeof callback !== "function") {
4320
- throw new Error("Callback must be a function");
4321
- }
4322
- if (Array.isArray(event)) {
4323
- event.forEach((eventName) => this.once(eventName, callback));
4324
- return this;
4325
- }
4326
- if (!this.onceListeners[event]) {
4327
- this.onceListeners[event] = [];
4328
- }
4329
- this.onceListeners[event].push(callback);
4330
- return this;
4331
- }
4332
- /**
4333
- * Remove event listener
4334
- * @param {string|Array<string>} event - Event name or array of event names
4335
- * @param {function} callback - Event callback to remove
4336
- * @returns {EventBus} This instance for chaining
4337
- */
4338
- off(event, callback) {
4339
- if (Array.isArray(event)) {
4340
- event.forEach((eventName) => this.off(eventName, callback));
4341
- return this;
4342
- }
4343
- if (!callback) {
4344
- delete this.listeners[event];
4345
- delete this.onceListeners[event];
4346
- return this;
4347
- }
4348
- if (this.listeners[event]) {
4349
- const index = this.listeners[event].indexOf(callback);
4350
- if (index !== -1) {
4351
- this.listeners[event].splice(index, 1);
4352
- if (this.listeners[event].length === 0) {
4353
- delete this.listeners[event];
4354
- }
4355
- }
4356
- }
4357
- if (this.onceListeners[event]) {
4358
- const index = this.onceListeners[event].indexOf(callback);
4359
- if (index !== -1) {
4360
- this.onceListeners[event].splice(index, 1);
4361
- if (this.onceListeners[event].length === 0) {
4362
- delete this.onceListeners[event];
4363
- }
4364
- }
4365
- }
4366
- return this;
4367
- }
4368
- /**
4369
- * Emit event to all listeners
4370
- * @param {string} event - Event name
4371
- * @param {*} data - Event data
4372
- * @returns {EventBus} This instance for chaining
4373
- */
4374
- emit(event, data) {
4375
- this.updateEventStats(event);
4376
- if (this.debugMode) {
4377
- console.log(`[EventBus] Emitting: ${event}`, data);
4378
- }
4379
- const listeners = [];
4380
- if (this.listeners[event]) {
4381
- listeners.push(...this.listeners[event]);
4382
- }
4383
- if (this.listeners["*"]) {
4384
- listeners.push(...this.listeners["*"]);
4385
- }
4386
- if (this.onceListeners[event]) {
4387
- listeners.push(...this.onceListeners[event]);
4388
- delete this.onceListeners[event];
4389
- }
4390
- if (this.onceListeners["*"]) {
4391
- listeners.push(...this.onceListeners["*"]);
4392
- delete this.onceListeners["*"];
4393
- }
4394
- if (this.debugMode && listeners.length > 0) {
4395
- console.log(`[EventBus] ${listeners.length} listener(s) for '${event}'`);
4396
- }
4397
- listeners.forEach((callback) => {
4398
- try {
4399
- if (callback(data, event)) {
4400
- if (event.stopPropagation) {
4401
- event.stopPropagation();
4402
- }
4403
- if (event.preventDefault) {
4404
- event.preventDefault();
4405
- }
4406
- }
4407
- } catch (error) {
4408
- console.error(`Error in event listener for '${event}':`, error);
4409
- this.emitError(error, event, callback);
4410
- }
4411
- });
4412
- return this;
4413
- }
4414
- /**
4415
- * Emit event asynchronously
4416
- * @param {string} event - Event name
4417
- * @param {*} data - Event data
4418
- * @returns {Promise<EventBus>} Promise that resolves with this instance
4419
- */
4420
- async emitAsync(event, data) {
4421
- const listeners = [];
4422
- if (this.listeners[event]) {
4423
- listeners.push(...this.listeners[event]);
4424
- }
4425
- if (this.listeners["*"]) {
4426
- listeners.push(...this.listeners["*"]);
4427
- }
4428
- if (this.onceListeners[event]) {
4429
- listeners.push(...this.onceListeners[event]);
4430
- delete this.onceListeners[event];
4431
- }
4432
- if (this.onceListeners["*"]) {
4433
- listeners.push(...this.onceListeners["*"]);
4434
- delete this.onceListeners["*"];
4435
- }
4436
- const promises = listeners.map((callback) => {
4437
- return new Promise((resolve) => {
4438
- try {
4439
- const result = callback(data, event);
4440
- resolve(result);
4441
- } catch (error) {
4442
- console.error(`Error in async event listener for '${event}':`, error);
4443
- this.emitError(error, event, callback);
4444
- resolve();
4445
- }
4446
- });
4447
- });
4448
- await Promise.all(promises);
4449
- return this;
4450
- }
4451
- /**
4452
- * Remove all listeners for all events
4453
- * @returns {EventBus} This instance for chaining
4454
- */
4455
- removeAllListeners() {
4456
- this.listeners = {};
4457
- this.onceListeners = {};
4458
- return this;
4459
- }
4460
- /**
4461
- * Get listener count for an event
4462
- * @param {string} event - Event name
4463
- * @returns {number} Number of listeners
4464
- */
4465
- listenerCount(event) {
4466
- const regularCount = this.listeners[event] ? this.listeners[event].length : 0;
4467
- const onceCount = this.onceListeners[event] ? this.onceListeners[event].length : 0;
4468
- return regularCount + onceCount;
4469
- }
4470
- /**
4471
- * Get all event names that have listeners
4472
- * @returns {Array<string>} Array of event names
4473
- */
4474
- eventNames() {
4475
- const regularEvents = Object.keys(this.listeners);
4476
- const onceEvents = Object.keys(this.onceListeners);
4477
- return [.../* @__PURE__ */ new Set([...regularEvents, ...onceEvents])];
4478
- }
4479
- /**
4480
- * Set maximum number of listeners per event
4481
- * @param {number} max - Maximum listeners
4482
- * @returns {EventBus} This instance for chaining
4483
- */
4484
- setMaxListeners(max) {
4485
- if (typeof max !== "number" || max < 0) {
4486
- throw new Error("Max listeners must be a non-negative number");
4487
- }
4488
- this.maxListeners = max;
4489
- return this;
4490
- }
4491
- /**
4492
- * Create a namespaced event bus
4493
- * @param {string} namespace - Namespace prefix
4494
- * @returns {object} Namespaced event bus methods
4495
- */
4496
- namespace(namespace) {
4497
- const prefixEvent = (event) => `${namespace}:${event}`;
4498
- return {
4499
- on: (event, callback) => this.on(prefixEvent(event), callback),
4500
- once: (event, callback) => this.once(prefixEvent(event), callback),
4501
- off: (event, callback) => this.off(prefixEvent(event), callback),
4502
- emit: (event, data) => this.emit(prefixEvent(event), data),
4503
- emitAsync: (event, data) => this.emitAsync(prefixEvent(event), data)
4504
- };
4505
- }
4506
- /**
4507
- * Add middleware to intercept events
4508
- * @param {function} middleware - Middleware function
4509
- * @returns {EventBus} This instance for chaining
4510
- */
4511
- use(middleware) {
4512
- if (typeof middleware !== "function") {
4513
- throw new Error("Middleware must be a function");
4514
- }
4515
- const originalEmit = this.emit;
4516
- this.emit = (event, data) => {
4517
- try {
4518
- const result = middleware(event, data);
4519
- if (result === false) {
4520
- return this;
4521
- }
4522
- const finalData = result !== void 0 ? result : data;
4523
- return originalEmit.call(this, event, finalData);
4524
- } catch (error) {
4525
- console.error("Error in event middleware:", error);
4526
- return originalEmit.call(this, event, data);
4527
- }
4528
- };
4529
- return this;
4530
- }
4531
- /**
4532
- * Emit error event for listener errors
4533
- * @private
4534
- * @param {Error} error - The error that occurred
4535
- * @param {string} originalEvent - The event that caused the error
4536
- * @param {function} callback - The callback that threw the error
4537
- */
4538
- emitError(error, originalEvent, callback) {
4539
- if (originalEvent === "error") {
4540
- return;
4541
- }
4542
- setTimeout(() => {
4543
- this.emit("error", {
4544
- error,
4545
- originalEvent,
4546
- callback: callback.toString()
4547
- });
4548
- }, 0);
4549
- }
4550
- /**
4551
- * Create a promise that resolves when an event is emitted
4552
- * @param {string} event - Event name
4553
- * @param {number} timeout - Optional timeout in milliseconds
4554
- * @returns {Promise} Promise that resolves with event data
4555
- */
4556
- waitFor(event, timeout = null) {
4557
- return new Promise((resolve, reject) => {
4558
- let timeoutId = null;
4559
- const cleanup = () => {
4560
- if (timeoutId) {
4561
- clearTimeout(timeoutId);
4562
- }
4563
- };
4564
- const listener = (data) => {
4565
- cleanup();
4566
- resolve(data);
4567
- };
4568
- this.once(event, listener);
4569
- if (timeout) {
4570
- timeoutId = setTimeout(() => {
4571
- this.off(event, listener);
4572
- reject(new Error(`Timeout waiting for event: ${event}`));
4573
- }, timeout);
4574
- }
4575
- });
4576
- }
4577
- /**
4578
- * Enable/disable debug mode for detailed event logging
4579
- * @param {boolean} enable - Whether to enable debug mode
4580
- * @returns {EventBus} This instance for chaining
4581
- */
4582
- debug(enable = true) {
4583
- this.debugMode = enable;
4584
- if (enable) {
4585
- console.log("[EventBus] Debug mode enabled");
4586
- } else {
4587
- console.log("[EventBus] Debug mode disabled");
4588
- }
4589
- return this;
4590
- }
4591
- /**
4592
- * Get statistics about the event bus
4593
- * @returns {object} Statistics object
4594
- */
4595
- getStats() {
4596
- const eventNames = this.eventNames();
4597
- const stats = {
4598
- totalEvents: eventNames.length,
4599
- totalListeners: 0,
4600
- events: {},
4601
- emissions: { ...this.eventStats }
4602
- };
4603
- eventNames.forEach((event) => {
4604
- const count = this.listenerCount(event);
4605
- stats.events[event] = count;
4606
- stats.totalListeners += count;
4607
- });
4608
- return stats;
4609
- }
4610
- /**
4611
- * Update event emission statistics
4612
- * @private
4613
- * @param {string} event - Event name
4614
- */
4615
- updateEventStats(event) {
4616
- if (!this.eventStats[event]) {
4617
- this.eventStats[event] = {
4618
- count: 0,
4619
- firstEmission: Date.now(),
4620
- lastEmission: null
4621
- };
4622
- }
4623
- this.eventStats[event].count++;
4624
- this.eventStats[event].lastEmission = Date.now();
4625
- }
4626
- /**
4627
- * Get detailed statistics for a specific event
4628
- * @param {string} event - Event name
4629
- * @returns {Object} Event statistics
4630
- */
4631
- getEventStats(event) {
4632
- const stats = this.eventStats[event];
4633
- if (!stats) {
4634
- return null;
4635
- }
4636
- return {
4637
- ...stats,
4638
- listenerCount: this.listenerCount(event),
4639
- avgEmissionsPerMinute: this.calculateEmissionRate(stats)
4640
- };
4641
- }
4642
- /**
4643
- * Calculate emission rate for an event
4644
- * @private
4645
- * @param {Object} stats - Event statistics
4646
- * @returns {number} Emissions per minute
4647
- */
4648
- calculateEmissionRate(stats) {
4649
- if (!stats.firstEmission || !stats.lastEmission) {
4650
- return 0;
4651
- }
4652
- const durationMs = stats.lastEmission - stats.firstEmission;
4653
- if (durationMs === 0) {
4654
- return 0;
4655
- }
4656
- const durationMinutes = durationMs / (1e3 * 60);
4657
- return Math.round(stats.count / durationMinutes * 100) / 100;
4658
- }
4659
- /**
4660
- * Reset event statistics
4661
- * @returns {EventBus} This instance for chaining
4662
- */
4663
- resetStats() {
4664
- this.eventStats = {};
4665
- return this;
4666
- }
4667
- /**
4668
- * Get a summary of the most active events
4669
- * @param {number} limit - Number of events to return (default: 10)
4670
- * @returns {Array} Array of event statistics sorted by emission count
4671
- */
4672
- getTopEvents(limit = 10) {
4673
- return Object.entries(this.eventStats).map(([event, stats]) => ({
4674
- event,
4675
- count: stats.count,
4676
- rate: this.calculateEmissionRate(stats),
4677
- listeners: this.listenerCount(event)
4678
- })).sort((a, b) => b.count - a.count).slice(0, limit);
4679
- }
4680
- /**
4681
- * Log comprehensive debug information
4682
- * @returns {EventBus} This instance for chaining
4683
- */
4684
- debugInfo() {
4685
- console.group("[EventBus] Debug Information");
4686
- console.log("Debug Mode:", this.debugMode);
4687
- console.log("Max Listeners:", this.maxListeners);
4688
- const stats = this.getStats();
4689
- console.log("Total Events:", stats.totalEvents);
4690
- console.log("Total Listeners:", stats.totalListeners);
4691
- if (Object.keys(this.eventStats).length > 0) {
4692
- console.log("Top Events:", this.getTopEvents(5));
4693
- }
4694
- console.groupEnd();
4695
- return this;
4696
- }
4697
- }
4698
- class WebApp {
4699
- constructor(config = {}) {
4700
- this.config = config;
4701
- this.initPluginRegistry();
4702
- this.name = config.name || "MOJO App";
4703
- this.version = config.version || "1.0.0";
4704
- this.debug = config.debug || false;
4705
- this.container = config.container || "#app";
4706
- this.layoutType = config.layout || "portal";
4707
- this.layoutConfig = config.layoutConfig || {};
4708
- if (config.sidebar) {
4709
- this.layoutConfig.sidebarConfig = config.sidebar;
4710
- }
4711
- if (config.topbar) {
4712
- this.layoutConfig.topbarConfig = config.topbar;
4713
- }
4714
- this.layout = null;
4715
- this.layoutConfig.containerId = this.container || this.containerId || "#app";
4716
- this.pageContainer = config.pageContainer || "#page-container";
4717
- this.basePath = config.basePath || "";
4718
- this.routerMode = config.routerMode || config.router?.mode || "param";
4719
- this.basePath = config.basePath || config.router?.base || "";
4720
- this.defaultRoute = config.defaultRoute || "home";
4721
- this.session = config.session || {};
4722
- this.router = null;
4723
- this.navigation = config.navigation || {};
4724
- this.state = {
4725
- currentPage: null,
4726
- previousPage: null,
4727
- loading: false
4728
- };
4729
- this.events = new EventBus();
4730
- this.rest = rest;
4731
- if (config.api) {
4732
- this.rest.configure(config.api);
4733
- }
4734
- this.router = new Router({
4735
- mode: this.routerMode === "param" ? "params" : this.routerMode,
4736
- basePath: this.basePath,
4737
- defaultRoute: this.defaultRoute,
4738
- eventEmitter: this.events
4739
- });
4740
- this.events.on("route:changed", async (routeInfo) => {
4741
- const { pageName, params, query } = routeInfo;
4742
- await this.showPage(pageName, query, params, { fromRouter: true });
4743
- });
4744
- if (typeof window !== "undefined") {
4745
- window.MOJO = window.MOJO || {};
4746
- window.MOJO.router = this.router;
4747
- }
4748
- this.setupFocusTracking();
4749
- this.pageCache = /* @__PURE__ */ new Map();
4750
- this.pageClasses = /* @__PURE__ */ new Map();
4751
- this.componentClasses = /* @__PURE__ */ new Map();
4752
- this.modelClasses = /* @__PURE__ */ new Map();
4753
- this.currentPage = null;
4754
- this.isStarted = false;
4755
- if (window.matchUUID) {
4756
- window[window.matchUUID] = this;
4757
- } else if (window.MOJO) {
4758
- window.MOJO.app = this;
4759
- } else {
4760
- window.__app__ = this;
4761
- }
4762
- }
4763
- /**
4764
- * Start the application
4765
- */
4766
- async start() {
4767
- if (this.isStarted) {
4768
- console.warn("WebApp already started");
4769
- return;
4770
- }
4771
- try {
4772
- this.setupPageContainer();
4773
- this.validateDefaultRoute();
4774
- await this.setupRouter();
4775
- this.isStarted = true;
4776
- this.router.allowPopState = false;
4777
- this.events.emit("app:ready", { app: this });
4778
- } catch (error) {
4779
- console.error(`Failed to start ${this.name}:`, error);
4780
- this.showError("Failed to start application");
4781
- throw error;
4782
- }
4783
- }
4784
- /**
4785
- * Setup router with configuration
4786
- */
4787
- async setupRouter() {
4788
- if (!this.router) {
4789
- console.error("Router not initialized");
4790
- return;
4791
- }
4792
- this.events.on("route:notfound", async (info) => {
4793
- console.warn(`Route not found: ${info.path}`);
4794
- this._show404(info.path);
4795
- });
4796
- this.router.start();
4797
- console.log(`Router started in ${this.routerMode} mode`);
4798
- }
4799
- /**
4800
- * Setup layout based on configuration
4801
- */
4802
- setupPageContainer() {
4803
- const container = typeof this.container === "string" ? document.querySelector(this.container) : this.container;
4804
- if (container && !container.querySelector("#page-container")) {
4805
- container.innerHTML = '<div id="page-container"></div>';
4806
- }
4807
- this.pageContainer = "#page-container";
4808
- }
4809
- /**
4810
- * Register a page with the app
4811
- */
4812
- registerPage(pageName, PageClass, options = {}) {
4813
- if (typeof pageName !== "string" || !pageName) {
4814
- console.error("registerPage: pageName must be a non-empty string");
4815
- return this;
4816
- }
4817
- if (typeof PageClass !== "function") {
4818
- console.error("registerPage: PageClass must be a constructor function");
4819
- return this;
4820
- }
4821
- if (!options.containerId) options.containerId = this.pageContainer;
4822
- this.pageClasses.set(pageName, {
4823
- PageClass,
4824
- constructorOptions: options
4825
- });
4826
- if (this.router) {
4827
- let route = options.route || `/${pageName}`;
4828
- if (!route.startsWith("/")) {
4829
- route = `/${route}`;
4830
- }
4831
- options.route = route;
4832
- this.router.addRoute(route, pageName);
4833
- }
4834
- return this;
4835
- }
4836
- /**
4837
- * Get page instance (cached)
4838
- */
4839
- getPage(pageName) {
4840
- return this.pageCache.get(pageName);
4841
- }
4842
- getPagePermissions(pageName) {
4843
- if (this.pageCache.has(pageName)) {
4844
- return this.pageCache.get(pageName).permissions;
4845
- }
4846
- const pageInfo = this.pageClasses.get(pageName);
4847
- if (!pageInfo) return null;
4848
- const { PageClass, constructorOptions } = pageInfo;
4849
- if (!constructorOptions) return null;
4850
- return constructorOptions.permissions;
4851
- }
4852
- /**
4853
- * Get or create page instance (with caching)
4854
- */
4855
- getOrCreatePage(pageName) {
4856
- if (this.pageCache.has(pageName)) {
4857
- return this.pageCache.get(pageName);
4858
- }
4859
- const pageInfo = this.pageClasses.get(pageName);
4860
- if (!pageInfo) {
4861
- console.error(`Page not registered: ${pageName}`);
4862
- return null;
4863
- }
4864
- const { PageClass, constructorOptions } = pageInfo;
4865
- try {
4866
- const pageOptions = {
4867
- pageName,
4868
- ...constructorOptions,
4869
- app: this
4870
- };
4871
- const page = new PageClass(pageOptions);
4872
- if (constructorOptions.route) {
4873
- page.route = constructorOptions.route;
4874
- }
4875
- this.pageCache.set(pageName, page);
4876
- console.log(`Created page: ${pageName} with route: ${page.route}`);
4877
- return page;
4878
- } catch (error) {
4879
- console.error(`Failed to create page ${pageName}:`, error);
4880
- return null;
4881
- }
4882
- }
4883
- /**
4884
- * Show a page
4885
- */
4886
- /**
4887
- * SIMPLIFIED UNIFIED PAGE SHOWING METHOD
4888
- * @param {string|object} page - Page name or page instance
4889
- * @param {object} query - URL query parameters (URL-safe)
4890
- * @param {object} params - Any data to pass to page (can include objects)
4891
- * @param {object} options - Options { fromRouter, replace, force }
4892
- */
4893
- async showPage(page, query = {}, params = {}, options = {}) {
4894
- const { fromRouter = false, replace = false, force = false } = options;
4895
- try {
4896
- let pageInstance, pageName;
4897
- if (typeof page === "string") {
4898
- pageName = page;
4899
- pageInstance = this.getOrCreatePage(page);
4900
- } else if (page && typeof page === "object") {
4901
- pageInstance = page;
4902
- pageName = page.pageName;
4903
- }
4904
- this.events.emit("page:showing", {
4905
- page: pageInstance,
4906
- pageName: pageInstance.pageName,
4907
- params,
4908
- query,
4909
- fromRouter
4910
- });
4911
- const oldPage = this.currentPage;
4912
- if (!pageInstance) {
4913
- this._show404(pageName, params, query, fromRouter);
4914
- return;
4915
- }
4916
- if (!pageInstance.canEnter()) {
4917
- this._showDeniedPage(pageInstance, params, query, fromRouter);
4918
- return;
4919
- }
4920
- if (oldPage && oldPage !== pageInstance) {
4921
- await this._exitOldPage(oldPage);
4922
- }
4923
- await pageInstance.onParams(params, query);
4924
- if (oldPage !== pageInstance) {
4925
- await pageInstance.onEnter();
4926
- }
4927
- pageInstance.syncUrl();
4928
- this.events.emit("page:show", {
4929
- page: pageInstance,
4930
- pageName: pageInstance.pageName,
4931
- params,
4932
- query,
4933
- fromRouter
4934
- });
4935
- await pageInstance.render();
4936
- this.currentPage = pageInstance;
4937
- console.log(`✅ Showing page: ${pageInstance.pageName}`, { query, params });
4938
- } catch (error) {
4939
- console.error("Error in showPage:", error);
4940
- this.showError(`Failed to load page: ${error.message}`);
4941
- if (page !== "error") {
4942
- await this.showPage("error", {}, { error, originalPage: page }, { fromRouter });
4943
- }
4944
- }
4945
- }
4946
- async _show404(pageName, params, query, fromRouter) {
4947
- const notFoundPage = this.getOrCreatePage("404");
4948
- if (!notFoundPage) return;
4949
- if (notFoundPage.setInfo) {
4950
- notFoundPage.setInfo(pageName);
4951
- }
4952
- await this._exitOldPage(this.currentPage);
4953
- await notFoundPage.render();
4954
- this.currentPage = notFoundPage;
4955
- this.events.emit("page:404", {
4956
- page: null,
4957
- pageName,
4958
- params,
4959
- query,
4960
- fromRouter
4961
- });
4962
- }
4963
- async _showDeniedPage(pageInstance, params, query, fromRouter) {
4964
- const deniedPage = this.getOrCreatePage("denied");
4965
- if (deniedPage.setDeniedPage) {
4966
- deniedPage.setDeniedPage(pageInstance);
4967
- }
4968
- await this._exitOldPage(this.currentPage);
4969
- await deniedPage.render();
4970
- this.currentPage = deniedPage;
4971
- this.events.emit("page:denied", {
4972
- page: pageInstance,
4973
- pageName: pageInstance.pageName,
4974
- params,
4975
- query,
4976
- fromRouter
4977
- });
4978
- }
4979
- async _exitOldPage(oldPage) {
4980
- if (!oldPage) return;
4981
- try {
4982
- await oldPage.onExit();
4983
- await oldPage.unmount();
4984
- this.events.emit("page:hide", { page: oldPage });
4985
- } catch (error) {
4986
- console.error(`Error exiting page ${oldPage.pageName}:`, error);
4987
- }
4988
- }
4989
- /**
4990
- * SIMPLIFIED NAVIGATION - delegates to router
4991
- * @param {string} route - Route like "/users" or "/users/123"
4992
- * @param {object} query - Query string params { filter: 'active' }
4993
- * @param {object} options - Navigation options { replace, force }
4994
- */
4995
- async navigate(route, query = {}, options = {}) {
4996
- if (!this.router) {
4997
- console.error("Router not initialized");
4998
- return;
4999
- }
5000
- let fullPath = route;
5001
- if (Object.keys(query).length > 0) {
5002
- const queryString = new URLSearchParams(query).toString();
5003
- fullPath += (route.includes("?") ? "&" : "?") + queryString;
5004
- }
5005
- return await this.router.navigate(fullPath, options);
5006
- }
5007
- /**
5008
- * Navigate to the default route
5009
- */
5010
- async navigateToDefault(options = {}) {
5011
- return await this.showPage(this.defaultRoute, {}, {}, options);
5012
- }
5013
- /**
5014
- * Navigate back
5015
- */
5016
- back() {
5017
- if (this.router) {
5018
- this.router.back();
5019
- } else {
5020
- console.warn("Router not initialized");
5021
- }
5022
- }
5023
- /**
5024
- * Navigate forward
5025
- */
5026
- forward() {
5027
- if (this.router) {
5028
- this.router.forward();
5029
- } else {
5030
- console.warn("Router not initialized");
5031
- }
5032
- }
5033
- /**
5034
- * Get current page
5035
- */
5036
- getCurrentPage() {
5037
- return this.currentPage;
5038
- }
5039
- /**
5040
- * Get page container element
5041
- */
5042
- getPageContainer() {
5043
- if (this.layout && this.layout.getPageContainer) {
5044
- return this.layout.getPageContainer();
5045
- }
5046
- const container = typeof this.pageContainer === "string" ? document.querySelector(this.pageContainer) : this.pageContainer;
5047
- return container;
5048
- }
5049
- /**
5050
- * Show error notification
5051
- */
5052
- async showError(message) {
5053
- try {
5054
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5055
- await Dialog.alert(message, "Error", { size: "md", class: "text-danger" });
5056
- } catch (e) {
5057
- this.events.emit("notification", { message, type: "error" });
5058
- if (typeof window !== "undefined" && window?.console) {
5059
- console.error("[WebApp] showError fallback:", e);
5060
- }
5061
- if (typeof window !== "undefined") {
5062
- alert(`Error: ${message}`);
5063
- }
5064
- }
5065
- }
5066
- /**
5067
- * Show success notification
5068
- */
5069
- async showSuccess(message) {
5070
- try {
5071
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5072
- await Dialog.alert(message, "Success", { size: "md", class: "text-success" });
5073
- } catch (e) {
5074
- this.events.emit("notification", { message, type: "success" });
5075
- if (typeof window !== "undefined" && window?.console) {
5076
- console.warn("[WebApp] showSuccess fallback:", e);
5077
- }
5078
- if (typeof window !== "undefined") {
5079
- alert(`Success: ${message}`);
5080
- }
5081
- }
5082
- }
5083
- /**
5084
- * Show info notification
5085
- */
5086
- async showInfo(message) {
5087
- try {
5088
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5089
- await Dialog.alert(message, "Information", { size: "md", class: "text-info" });
5090
- } catch (e) {
5091
- this.events.emit("notification", { message, type: "info" });
5092
- if (typeof window !== "undefined" && window?.console) {
5093
- console.info("[WebApp] showInfo fallback:", e);
5094
- }
5095
- if (typeof window !== "undefined") {
5096
- alert(`Info: ${message}`);
5097
- }
5098
- }
5099
- }
5100
- /**
5101
- * Show warning notification
5102
- */
5103
- async showWarning(message) {
5104
- try {
5105
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5106
- await Dialog.alert(message, "Warning", { size: "md", class: "text-warning" });
5107
- } catch (e) {
5108
- this.events.emit("notification", { message, type: "warning" });
5109
- if (typeof window !== "undefined" && window?.console) {
5110
- console.warn("[WebApp] showWarning fallback:", e);
5111
- }
5112
- if (typeof window !== "undefined") {
5113
- alert(`Warning: ${message}`);
5114
- }
5115
- }
5116
- }
5117
- /**
5118
- * Show generic notification
5119
- */
5120
- showNotification(message, type = "info") {
5121
- this.events.emit("notification", { message, type });
5122
- }
5123
- /**
5124
- * Show loading indicator
5125
- */
5126
- async showLoading(opts = {}) {
5127
- if (typeof opts === "string") {
5128
- opts = { message: opts };
5129
- }
5130
- try {
5131
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5132
- Dialog.showBusy(opts);
5133
- } catch (e) {
5134
- if (typeof window !== "undefined" && window?.console) {
5135
- console.warn("[WebApp] showLoading fallback:", e, opts);
5136
- }
5137
- this.events.emit("notification", { message: opts.message || "Loading...", type: "info" });
5138
- }
5139
- }
5140
- /**
5141
- * Hide loading indicator
5142
- */
5143
- async hideLoading() {
5144
- try {
5145
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5146
- Dialog.hideBusy();
5147
- } catch (e) {
5148
- if (typeof window !== "undefined" && window?.console) {
5149
- console.warn("[WebApp] hideLoading fallback:", e);
5150
- }
5151
- }
5152
- }
5153
- async showModelForm(options = {}) {
5154
- try {
5155
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5156
- return await Dialog.showModelForm(options);
5157
- } catch (e) {
5158
- if (typeof window !== "undefined" && window?.console) {
5159
- console.error("[WebApp] showModelForm failed:", e);
5160
- }
5161
- throw e;
5162
- }
5163
- }
5164
- async showForm(options = {}) {
5165
- try {
5166
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5167
- return await Dialog.showForm(options);
5168
- } catch (e) {
5169
- if (typeof window !== "undefined" && window?.console) {
5170
- console.error("[WebApp] showForm failed:", e);
5171
- }
5172
- throw e;
5173
- }
5174
- }
5175
- async confirm(message, title = "Confirm", options = {}) {
5176
- const Dialog = (await import("./Dialog-D_rAf4gQ.js")).default;
5177
- return await Dialog.confirm(message, title, options);
5178
- }
5179
- /**
5180
- * Setup browser focus/blur tracking
5181
- */
5182
- setupFocusTracking() {
5183
- if (typeof window === "undefined") return;
5184
- this.isFocused = !document.hidden;
5185
- const handleVisibilityChange = () => {
5186
- const wasFocused = this.isFocused;
5187
- this.isFocused = !document.hidden;
5188
- if (wasFocused !== this.isFocused) {
5189
- if (this.isFocused) {
5190
- this.events.emit("browser:focus");
5191
- } else {
5192
- this.events.emit("browser:blur");
5193
- }
5194
- }
5195
- };
5196
- const handleFocus = () => {
5197
- if (!this.isFocused) {
5198
- this.isFocused = true;
5199
- this.events.emit("browser:focus");
5200
- }
5201
- };
5202
- const handleBlur = () => {
5203
- if (this.isFocused) {
5204
- this.isFocused = false;
5205
- this.events.emit("browser:blur");
5206
- }
5207
- };
5208
- document.addEventListener("visibilitychange", handleVisibilityChange);
5209
- window.addEventListener("focus", handleFocus);
5210
- window.addEventListener("blur", handleBlur);
5211
- this._focusHandlers = {
5212
- visibilitychange: handleVisibilityChange,
5213
- focus: handleFocus,
5214
- blur: handleBlur
5215
- };
5216
- }
5217
- /**
5218
- * Setup global error handling
5219
- */
5220
- setupErrorHandling() {
5221
- window.addEventListener("error", (event) => {
5222
- console.error("Global error:", event.error);
5223
- if (this.debug) {
5224
- this.showError(`Error: ${event.error?.message || "Unknown error"}`);
5225
- }
5226
- });
5227
- window.addEventListener("unhandledrejection", (event) => {
5228
- console.error("Unhandled promise rejection:", event.reason);
5229
- if (this.debug) {
5230
- this.showError(`Promise rejected: ${event.reason?.message || "Unknown error"}`);
5231
- }
5232
- });
5233
- }
5234
- /**
5235
- * Escape HTML to prevent XSS
5236
- */
5237
- escapeHtml(unsafe) {
5238
- return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
5239
- }
5240
- /**
5241
- * Get application state
5242
- */
5243
- getState(key) {
5244
- return key ? this.state[key] : this.state;
5245
- }
5246
- /**
5247
- * Set application state
5248
- */
5249
- setState(updates) {
5250
- const oldState = { ...this.state };
5251
- Object.assign(this.state, updates);
5252
- this.events.emit("state:changed", {
5253
- oldState,
5254
- newState: this.state,
5255
- updates
5256
- });
5257
- }
5258
- /**
5259
- * Register component class
5260
- */
5261
- registerComponent(name, ComponentClass) {
5262
- this.componentClasses.set(name, ComponentClass);
5263
- }
5264
- /**
5265
- * Get component class
5266
- */
5267
- getComponent(name) {
5268
- return this.componentClasses.get(name);
5269
- }
5270
- /**
5271
- * Register model class
5272
- */
5273
- registerModel(name, ModelClass) {
5274
- this.modelClasses.set(name, ModelClass);
5275
- }
5276
- /**
5277
- * Get model class
5278
- */
5279
- getModel(name) {
5280
- return this.modelClasses.get(name);
5281
- }
5282
- /**
5283
- * Setup REST configuration
5284
- */
5285
- setupRest() {
5286
- this.rest = rest;
5287
- rest.configure(this.api);
5288
- }
5289
- /**
5290
- * Destroy the application
5291
- */
5292
- async destroy() {
5293
- console.log("Destroying WebApp...");
5294
- if (this.router) {
5295
- this.router.stop();
5296
- }
5297
- if (this._focusHandlers && typeof window !== "undefined") {
5298
- document.removeEventListener("visibilitychange", this._focusHandlers.visibilitychange);
5299
- window.removeEventListener("focus", this._focusHandlers.focus);
5300
- window.removeEventListener("blur", this._focusHandlers.blur);
5301
- }
5302
- const pages = Array.from(this.pageCache.values());
5303
- await Promise.allSettled(
5304
- pages.map(async (page) => {
5305
- try {
5306
- if (page.destroy) {
5307
- await page.destroy();
5308
- }
5309
- } catch (error) {
5310
- console.error("Error destroying page:", error);
5311
- }
5312
- })
5313
- );
5314
- if (this.layout && this.layout.destroy) {
5315
- try {
5316
- await this.layout.destroy();
5317
- } catch (error) {
5318
- console.error("Error destroying layout:", error);
5319
- }
5320
- }
5321
- this.pageCache.clear();
5322
- this.pageClasses.clear();
5323
- this.componentClasses.clear();
5324
- this.modelClasses.clear();
5325
- if (typeof window !== "undefined" && window.MOJO) {
5326
- delete window.MOJO.router;
5327
- }
5328
- this.isStarted = false;
5329
- console.log(`✨ ${this.name} destroyed`);
5330
- }
5331
- /**
5332
- * Build page path with params and query
5333
- */
5334
- buildPagePath(page, params, query) {
5335
- let path = page.route || `/${page.pageName.toLowerCase()}`;
5336
- Object.keys(params).forEach((key) => {
5337
- if (typeof params[key] === "string" || typeof params[key] === "number") {
5338
- path = path.replace(`:${key}`, params[key]);
5339
- }
5340
- });
5341
- if (query && Object.keys(query).length > 0) {
5342
- const queryString = new URLSearchParams(query).toString();
5343
- path += (path.includes("?") ? "&" : "?") + queryString;
5344
- }
5345
- return path;
5346
- }
5347
- /**
5348
- * Static factory method
5349
- */
5350
- /**
5351
- * Validate that default route has a registered page
5352
- */
5353
- validateDefaultRoute() {
5354
- if (!this.pageClasses.has(this.defaultRoute)) {
5355
- console.warn(`⚠️ Default route '${this.defaultRoute}' is not registered!`);
5356
- console.warn(` Please register a page: app.registerPage('${this.defaultRoute}', YourPageClass);`);
5357
- console.warn(` Or change default route: new WebApp({ defaultRoute: 'your-page' });`);
5358
- } else {
5359
- console.log(`✅ Default route '${this.defaultRoute}' is registered`);
5360
- }
5361
- }
5362
- /**
5363
- * Find any registered page to use as emergency fallback
5364
- */
5365
- findFallbackPage() {
5366
- const systemPages = ["404", "error", "denied"];
5367
- for (const [pageName] of this.pageClasses.entries()) {
5368
- if (!systemPages.includes(pageName)) {
5369
- return pageName;
5370
- }
5371
- }
5372
- return null;
5373
- }
5374
- static create(config = {}) {
5375
- return new WebApp(config);
5376
- }
5377
- /**
5378
- * Initialize global plugin registry for extensions
5379
- * Allows extensions to register components that core can use optionally
5380
- */
5381
- initPluginRegistry() {
5382
- if (typeof window !== "undefined") {
5383
- if (!window.MOJO) {
5384
- window.MOJO = {};
5385
- }
5386
- if (!window.MOJO.plugins) {
5387
- window.MOJO.plugins = {};
5388
- }
5389
- window.MOJO.app = this;
5390
- }
5391
- }
5392
- /**
5393
- * Register a plugin component
5394
- * @param {string} name - Plugin component name (e.g., 'ImageCropView')
5395
- * @param {*} component - The component/class to register
5396
- */
5397
- static registerPlugin(name, component) {
5398
- if (typeof window !== "undefined") {
5399
- if (!window.MOJO) {
5400
- window.MOJO = {};
5401
- }
5402
- if (!window.MOJO.plugins) {
5403
- window.MOJO.plugins = {};
5404
- }
5405
- window.MOJO.plugins[name] = component;
5406
- console.debug(`MOJO Plugin registered: ${name}`);
5407
- }
5408
- }
5409
- }
5410
4035
  export {
5411
- BUILD_TIME as B,
5412
4036
  DataWrapper as D,
5413
- EventDelegate as E,
5414
- Mustache as M,
5415
- Router as R,
4037
+ EventEmitter as E,
4038
+ MOJOUtils as M,
5416
4039
  View as V,
5417
- WebApp as W,
5418
- VERSION_INFO as a,
5419
- VERSION as b,
5420
- VERSION_MAJOR as c,
4040
+ Mustache as a,
4041
+ EventDelegate as b,
5421
4042
  dataFormatter as d,
5422
- VERSION_MINOR as e,
5423
- VERSION_REVISION as f,
5424
- EventBus as g,
5425
- MOJOUtils as h,
5426
- EventEmitter as i,
5427
4043
  rest as r
5428
4044
  };
5429
- //# sourceMappingURL=WebApp-El07OMHH.js.map
4045
+ //# sourceMappingURL=Rest-CS4jRCAs.js.map