web-mojo 2.1.936 → 2.1.954
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.
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +122 -229
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +7 -7
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +7 -7
- package/dist/chunks/ChatView-BKa78eKF.js +2 -0
- package/dist/chunks/ChatView-BKa78eKF.js.map +1 -0
- package/dist/chunks/{ChatView-DlSxjxah.js → ChatView-Dcz7LkwL.js} +89 -574
- package/dist/chunks/ChatView-Dcz7LkwL.js.map +1 -0
- package/dist/chunks/Collection-OP7c9Zyu.js +989 -0
- package/dist/chunks/Collection-OP7c9Zyu.js.map +1 -0
- package/dist/chunks/Collection-aQF5eOUH.js +2 -0
- package/dist/chunks/Collection-aQF5eOUH.js.map +1 -0
- package/dist/chunks/ContextMenu-DV_wsr9B.js +3 -0
- package/dist/chunks/ContextMenu-DV_wsr9B.js.map +1 -0
- package/dist/chunks/ContextMenu-ixMyAUGS.js +1171 -0
- package/dist/chunks/ContextMenu-ixMyAUGS.js.map +1 -0
- package/dist/chunks/DataView-CdDY9ijM.js +2 -0
- package/dist/chunks/{DataView-XJbTQ5q0.js.map → DataView-CdDY9ijM.js.map} +1 -1
- package/dist/chunks/{DataView-Vmjx4eCr.js → DataView-OUqaLmGB.js} +2 -2
- package/dist/chunks/{DataView-Vmjx4eCr.js.map → DataView-OUqaLmGB.js.map} +1 -1
- package/dist/chunks/{Dialog-D_rAf4gQ.js → Dialog-C2mRUxga.js} +8 -6
- package/dist/chunks/{Dialog-D_rAf4gQ.js.map → Dialog-C2mRUxga.js.map} +1 -1
- package/dist/chunks/Dialog-Cl6MN8if.js +2 -0
- package/dist/chunks/{Dialog-BinTQTfO.js.map → Dialog-Cl6MN8if.js.map} +1 -1
- package/dist/chunks/FormView-BSWaXDav.js +3 -0
- package/dist/chunks/{FormView-TPFsq8ZX.js.map → FormView-BSWaXDav.js.map} +1 -1
- package/dist/chunks/{FormView-CIriLDZY.js → FormView-HWvIdFkB.js} +10 -6
- package/dist/chunks/FormView-HWvIdFkB.js.map +1 -0
- package/dist/chunks/ListView-Dcz0Gs6D.js +492 -0
- package/dist/chunks/ListView-Dcz0Gs6D.js.map +1 -0
- package/dist/chunks/ListView-eXgn0F0-.js +2 -0
- package/dist/chunks/ListView-eXgn0F0-.js.map +1 -0
- package/dist/chunks/MetricsMiniChartWidget-BkTEO87S.js +2 -0
- package/dist/chunks/{MetricsMiniChartWidget-sONcM0pG.js.map → MetricsMiniChartWidget-BkTEO87S.js.map} +1 -1
- package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js → MetricsMiniChartWidget-Y70IHFIe.js} +3 -3
- package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js.map → MetricsMiniChartWidget-Y70IHFIe.js.map} +1 -1
- package/dist/chunks/PDFViewer-C0aMqGJL.js +2 -0
- package/dist/chunks/{PDFViewer-UBhinN8A.js.map → PDFViewer-C0aMqGJL.js.map} +1 -1
- package/dist/chunks/{PDFViewer-D6SKOl85.js → PDFViewer-DkbYnnoV.js} +3 -3
- package/dist/chunks/{PDFViewer-D6SKOl85.js.map → PDFViewer-DkbYnnoV.js.map} +1 -1
- package/dist/chunks/Page-CvbwEoLv.js +2 -0
- package/dist/chunks/{Page-CnvHhwLZ.js.map → Page-CvbwEoLv.js.map} +1 -1
- package/dist/chunks/{Page-B7L25Omb.js → Page-Deq4y2Kq.js} +2 -2
- package/dist/chunks/{Page-B7L25Omb.js.map → Page-Deq4y2Kq.js.map} +1 -1
- package/dist/chunks/Rest-BNYqGlnP.js +2 -0
- package/dist/chunks/Rest-BNYqGlnP.js.map +1 -0
- package/dist/chunks/{WebApp-El07OMHH.js → Rest-CS4jRCAs.js} +5 -1389
- package/dist/chunks/Rest-CS4jRCAs.js.map +1 -0
- package/dist/chunks/{TopNav-CPA884W7.js → TopNav-DXLRdU0o.js} +5 -5
- package/dist/chunks/{TopNav-CPA884W7.js.map → TopNav-DXLRdU0o.js.map} +1 -1
- package/dist/chunks/TopNav-GyvI31l2.js +2 -0
- package/dist/chunks/{TopNav-Dcmcic-i.js.map → TopNav-GyvI31l2.js.map} +1 -1
- package/dist/chunks/WebApp-BAadsDpO.js +2 -0
- package/dist/chunks/WebApp-BAadsDpO.js.map +1 -0
- package/dist/chunks/WebApp-BVGZC2rj.js +1388 -0
- package/dist/chunks/WebApp-BVGZC2rj.js.map +1 -0
- package/dist/css/web-mojo.css +2 -2
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.cjs.js.map +1 -1
- package/dist/docit.es.js +12 -10
- package/dist/docit.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +120 -116
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.cjs.js.map +1 -1
- package/dist/lightbox.es.js +121 -121
- package/dist/lightbox.es.js.map +1 -1
- package/dist/map.cjs.js +2 -0
- package/dist/map.cjs.js.map +1 -0
- package/dist/map.es.js +188 -0
- package/dist/map.es.js.map +1 -0
- package/dist/timeline.cjs.js +2 -0
- package/dist/timeline.cjs.js.map +1 -0
- package/dist/timeline.es.js +225 -0
- package/dist/timeline.es.js.map +1 -0
- package/package.json +9 -1
- package/dist/chunks/ChatView-DlSxjxah.js.map +0 -1
- package/dist/chunks/ChatView-DnqrGXMC.js +0 -2
- package/dist/chunks/ChatView-DnqrGXMC.js.map +0 -1
- package/dist/chunks/ContextMenu-CE77rUmn.js +0 -2155
- package/dist/chunks/ContextMenu-CE77rUmn.js.map +0 -1
- package/dist/chunks/ContextMenu-KVxd0Kgd.js +0 -3
- package/dist/chunks/ContextMenu-KVxd0Kgd.js.map +0 -1
- package/dist/chunks/DataView-XJbTQ5q0.js +0 -2
- package/dist/chunks/Dialog-BinTQTfO.js +0 -2
- package/dist/chunks/FormView-CIriLDZY.js.map +0 -1
- package/dist/chunks/FormView-TPFsq8ZX.js +0 -3
- package/dist/chunks/MetricsMiniChartWidget-sONcM0pG.js +0 -2
- package/dist/chunks/PDFViewer-UBhinN8A.js +0 -2
- package/dist/chunks/Page-CnvHhwLZ.js +0 -2
- package/dist/chunks/TopNav-Dcmcic-i.js +0 -2
- package/dist/chunks/WebApp-El07OMHH.js.map +0 -1
- package/dist/chunks/WebApp-b9DQWz1d.js +0 -2
- 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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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
|
-
|
|
5414
|
-
|
|
5415
|
-
Router as R,
|
|
4037
|
+
EventEmitter as E,
|
|
4038
|
+
MOJOUtils as M,
|
|
5416
4039
|
View as V,
|
|
5417
|
-
|
|
5418
|
-
|
|
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=
|
|
4045
|
+
//# sourceMappingURL=Rest-CS4jRCAs.js.map
|