@lark.js/mvc 0.0.9 → 0.0.11
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/README.md +10 -32
- package/dist/chunk-RIV4NK3K.js +108 -0
- package/dist/compiler.cjs +733 -193
- package/dist/compiler.d.cts +35 -35
- package/dist/compiler.d.ts +35 -35
- package/dist/compiler.js +730 -193
- package/dist/devtool.cjs +3421 -0
- package/dist/devtool.d.cts +83 -0
- package/dist/devtool.d.ts +83 -0
- package/dist/devtool.js +3333 -0
- package/dist/index.cjs +811 -319
- package/dist/index.d.cts +247 -491
- package/dist/index.d.ts +247 -491
- package/dist/index.js +808 -280
- package/dist/rspack.cjs +15978 -0
- package/dist/rspack.d.cts +122 -0
- package/dist/rspack.d.ts +122 -0
- package/dist/{chunk-3HSA7OHB.js → rspack.js} +809 -193
- package/dist/runtime.cjs +35 -10
- package/dist/runtime.d.cts +22 -13
- package/dist/runtime.d.ts +22 -13
- package/dist/runtime.js +13 -34
- package/dist/vite.cjs +753 -195
- package/dist/vite.d.cts +3 -0
- package/dist/vite.d.ts +3 -0
- package/dist/vite.js +15917 -10
- package/dist/webpack.cjs +798 -201
- package/dist/webpack.d.cts +60 -4
- package/dist/webpack.d.ts +60 -4
- package/dist/webpack.js +15962 -14
- package/package.json +25 -2
package/dist/devtool.cjs
ADDED
|
@@ -0,0 +1,3421 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/devtool.ts
|
|
21
|
+
var devtool_exports = {};
|
|
22
|
+
__export(devtool_exports, {
|
|
23
|
+
FrameDevtoolBridge: () => FrameDevtoolBridge,
|
|
24
|
+
installFrameDevtoolBridge: () => installFrameDevtoolBridge,
|
|
25
|
+
serializeFrameTree: () => serializeFrameTree
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(devtool_exports);
|
|
28
|
+
|
|
29
|
+
// src/common.ts
|
|
30
|
+
var globalCounter = 0;
|
|
31
|
+
var SPLITTER = String.fromCharCode(30);
|
|
32
|
+
var RouterEvents = {
|
|
33
|
+
CHANGE: "change",
|
|
34
|
+
CHANGED: "changed",
|
|
35
|
+
PAGE_UNLOAD: "page_unload"
|
|
36
|
+
};
|
|
37
|
+
var LARK_VIEW = "v-lark";
|
|
38
|
+
var EVENT_METHOD_REGEXP = new RegExp(
|
|
39
|
+
`(?:([\\w-]+)${SPLITTER})?([^(]+)\\(([\\s\\S]*?)?\\)`
|
|
40
|
+
);
|
|
41
|
+
var VIEW_EVENT_METHOD_REGEXP = /^(\$?)([\w]*)<(.*?)>(?:<([\w ,]*)>)?$/;
|
|
42
|
+
var URL_TRIM_HASH_REGEXP = /(?:^.*\/\/[^/]+|#.*$)/gi;
|
|
43
|
+
var URL_TRIM_QUERY_REGEXP = /^[^#]*#?!?/;
|
|
44
|
+
var URL_PARAM_REGEXP = /([^=&?/#]+)=?([^&#?]*)/g;
|
|
45
|
+
var IS_URL_PARAMS = /(?!^)=|&/;
|
|
46
|
+
var URL_QUERY_HASH_REGEXP = /[#?].*$/;
|
|
47
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
48
|
+
var MATH_NS = "http://www.w3.org/1998/Math/MathML";
|
|
49
|
+
var TAG_NAME_REGEXP = /<([a-z][^/\0>\x20\t\r\n\f]+)/i;
|
|
50
|
+
var V_TEXT_NODE = 0;
|
|
51
|
+
var VDOM_NS_MAP = {
|
|
52
|
+
svg: SVG_NS,
|
|
53
|
+
math: MATH_NS
|
|
54
|
+
};
|
|
55
|
+
function nextCounter() {
|
|
56
|
+
return ++globalCounter;
|
|
57
|
+
}
|
|
58
|
+
var HTML_ENT_MAP = {
|
|
59
|
+
"&": "amp",
|
|
60
|
+
"<": "lt",
|
|
61
|
+
">": "gt",
|
|
62
|
+
'"': "#34",
|
|
63
|
+
"'": "#39",
|
|
64
|
+
"`": "#96"
|
|
65
|
+
};
|
|
66
|
+
var HTML_ENT_REGEXP = /[&<>"'`]/g;
|
|
67
|
+
function strSafe(v) {
|
|
68
|
+
return String(v == null ? "" : v);
|
|
69
|
+
}
|
|
70
|
+
function encodeHTML(v) {
|
|
71
|
+
return String(v == null ? "" : v).replace(
|
|
72
|
+
HTML_ENT_REGEXP,
|
|
73
|
+
(m) => "&" + HTML_ENT_MAP[m] + ";"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
var URI_ENT_MAP = {
|
|
77
|
+
"!": "%21",
|
|
78
|
+
"'": "%27",
|
|
79
|
+
"(": "%28",
|
|
80
|
+
")": "%29",
|
|
81
|
+
"*": "%2A"
|
|
82
|
+
};
|
|
83
|
+
var URI_ENT_REGEXP = /[!')(*]/g;
|
|
84
|
+
function encodeURIExtra(v) {
|
|
85
|
+
return encodeURIComponent(strSafe(v)).replace(
|
|
86
|
+
URI_ENT_REGEXP,
|
|
87
|
+
(m) => URI_ENT_MAP[m]
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
var QUOTE_ENT_REGEXP = /['"\\]/g;
|
|
91
|
+
function encodeQuote(v) {
|
|
92
|
+
return strSafe(v).replace(QUOTE_ENT_REGEXP, "\\$&");
|
|
93
|
+
}
|
|
94
|
+
function refFn(ref, value, key) {
|
|
95
|
+
const counter = ref[SPLITTER];
|
|
96
|
+
for (let i = counter; --i; ) {
|
|
97
|
+
key = SPLITTER + i;
|
|
98
|
+
if (ref[key] === value) return key;
|
|
99
|
+
}
|
|
100
|
+
key = SPLITTER + ref[SPLITTER]++;
|
|
101
|
+
ref[key] = value;
|
|
102
|
+
return key;
|
|
103
|
+
}
|
|
104
|
+
function isRefToken(s) {
|
|
105
|
+
if (s.length < 2 || s[0] !== SPLITTER) return false;
|
|
106
|
+
for (let i = 1; i < s.length; i++) {
|
|
107
|
+
const c = s.charCodeAt(i);
|
|
108
|
+
if (c < "0".charCodeAt(0) || c > "9".charCodeAt(0)) return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/utils.ts
|
|
114
|
+
var CALL_BREAK_TIME = 9;
|
|
115
|
+
var callQueue = [];
|
|
116
|
+
var callScheduled = false;
|
|
117
|
+
var schedulerYield = (() => {
|
|
118
|
+
try {
|
|
119
|
+
if (typeof globalThis?.scheduler?.yield === "function") {
|
|
120
|
+
return globalThis.scheduler.yield.bind(globalThis.scheduler);
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
return void 0;
|
|
125
|
+
})();
|
|
126
|
+
async function startCall() {
|
|
127
|
+
callScheduled = false;
|
|
128
|
+
const startTime = performance.now();
|
|
129
|
+
while (callQueue.length > 0) {
|
|
130
|
+
const task = callQueue.shift();
|
|
131
|
+
try {
|
|
132
|
+
task();
|
|
133
|
+
} catch (e) {
|
|
134
|
+
console.error("scheduler task error:", e);
|
|
135
|
+
}
|
|
136
|
+
if (callQueue.length > 0 && performance.now() - startTime > CALL_BREAK_TIME) {
|
|
137
|
+
if (schedulerYield) {
|
|
138
|
+
await schedulerYield();
|
|
139
|
+
} else {
|
|
140
|
+
scheduleNextChunk();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function scheduleNextChunk() {
|
|
147
|
+
setTimeout(() => startCall(), 0);
|
|
148
|
+
callScheduled = true;
|
|
149
|
+
}
|
|
150
|
+
function callFunction(fn, args) {
|
|
151
|
+
callQueue.push(() => fn(...args));
|
|
152
|
+
if (!callScheduled) {
|
|
153
|
+
scheduleNextChunk();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function isPlainObject(value) {
|
|
157
|
+
if (typeof value !== "object" || value === null) return false;
|
|
158
|
+
const proto = Object.getPrototypeOf(value);
|
|
159
|
+
return proto === null || proto === Object.prototype;
|
|
160
|
+
}
|
|
161
|
+
function isRecord(value) {
|
|
162
|
+
return typeof value === "object" && value !== null;
|
|
163
|
+
}
|
|
164
|
+
function asRecord(value) {
|
|
165
|
+
if (isRecord(value)) {
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
console.error("fallback to Object.fromEntries, even an empty object {}.");
|
|
169
|
+
if (Array.isArray(value)) {
|
|
170
|
+
return Object.fromEntries(value.entries());
|
|
171
|
+
}
|
|
172
|
+
return {};
|
|
173
|
+
}
|
|
174
|
+
function isPrimitiveOrFunc(value) {
|
|
175
|
+
return !value || typeof value !== "object" && typeof value !== "function";
|
|
176
|
+
}
|
|
177
|
+
function isPrimitive(value) {
|
|
178
|
+
return !value || typeof value !== "object";
|
|
179
|
+
}
|
|
180
|
+
var _localCounter = 0;
|
|
181
|
+
function generateId(prefix) {
|
|
182
|
+
return (prefix || "lark_") + _localCounter++;
|
|
183
|
+
}
|
|
184
|
+
function noop() {
|
|
185
|
+
}
|
|
186
|
+
function hasOwnProperty(owner, prop) {
|
|
187
|
+
return owner != null && Object.prototype.hasOwnProperty.call(owner, prop);
|
|
188
|
+
}
|
|
189
|
+
function assign(target, ...sources) {
|
|
190
|
+
for (const source of sources) {
|
|
191
|
+
if (source) {
|
|
192
|
+
for (const p in source) {
|
|
193
|
+
if (hasOwnProperty(source, p)) {
|
|
194
|
+
target[p] = source[p];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return target;
|
|
200
|
+
}
|
|
201
|
+
function funcWithTry(fns, args, context, configError) {
|
|
202
|
+
const fnArray = Array.isArray(fns) ? fns : [fns];
|
|
203
|
+
let ret;
|
|
204
|
+
for (const fn of fnArray) {
|
|
205
|
+
try {
|
|
206
|
+
ret = fn.apply(context, args);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
configError?.(e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return ret;
|
|
212
|
+
}
|
|
213
|
+
var EMPTY_STRING_SET = /* @__PURE__ */ new Set();
|
|
214
|
+
function setData(newData, oldData, changedKeys, excludes) {
|
|
215
|
+
let changed = false;
|
|
216
|
+
for (const p in newData) {
|
|
217
|
+
if (hasOwnProperty(newData, p)) {
|
|
218
|
+
const now = newData[p];
|
|
219
|
+
const old = oldData[p];
|
|
220
|
+
if ((!isPrimitiveOrFunc(now) || old !== now) && !excludes.has(p)) {
|
|
221
|
+
changedKeys.add(p);
|
|
222
|
+
changed = true;
|
|
223
|
+
}
|
|
224
|
+
oldData[p] = now;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return changed;
|
|
228
|
+
}
|
|
229
|
+
function translateData(data, value) {
|
|
230
|
+
if (isPrimitive(value)) {
|
|
231
|
+
const prop = String(value);
|
|
232
|
+
if (isRefToken(prop) && hasOwnProperty(data, prop)) {
|
|
233
|
+
return data[prop];
|
|
234
|
+
}
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
if (isPlainObject(value) || Array.isArray(value)) {
|
|
238
|
+
for (const p in value) {
|
|
239
|
+
if (hasOwnProperty(value, p)) {
|
|
240
|
+
const val = value[p];
|
|
241
|
+
const newVal = translateData(data, val);
|
|
242
|
+
value[p] = newVal;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
return value;
|
|
248
|
+
}
|
|
249
|
+
function getById(id) {
|
|
250
|
+
if (!id) return null;
|
|
251
|
+
if (typeof id === "object") return id;
|
|
252
|
+
return document.getElementById(id);
|
|
253
|
+
}
|
|
254
|
+
function getAttribute(element, attr) {
|
|
255
|
+
return Element.prototype.getAttribute.call(element, attr) ?? "";
|
|
256
|
+
}
|
|
257
|
+
function ensureElementId(element, prefix) {
|
|
258
|
+
const id = element.getAttribute("id");
|
|
259
|
+
if (id) return id;
|
|
260
|
+
element.autoId = 1;
|
|
261
|
+
const newId = generateId(prefix);
|
|
262
|
+
element.id = newId;
|
|
263
|
+
return newId;
|
|
264
|
+
}
|
|
265
|
+
function parseUri(uri) {
|
|
266
|
+
const params = {};
|
|
267
|
+
const path = uri.replace(URL_QUERY_HASH_REGEXP, "");
|
|
268
|
+
const pathname = path;
|
|
269
|
+
const actualPath = uri === pathname && IS_URL_PARAMS.test(pathname) ? "" : pathname;
|
|
270
|
+
uri.replace(actualPath, "").replace(URL_PARAM_REGEXP, (_match, name, value) => {
|
|
271
|
+
try {
|
|
272
|
+
params[name] = decodeURIComponent(value || "");
|
|
273
|
+
} catch {
|
|
274
|
+
params[name] = value || "";
|
|
275
|
+
}
|
|
276
|
+
return "";
|
|
277
|
+
});
|
|
278
|
+
return { path: actualPath, params };
|
|
279
|
+
}
|
|
280
|
+
function toUri(path, params, keepEmpty) {
|
|
281
|
+
const pairs = [];
|
|
282
|
+
let hasParams = false;
|
|
283
|
+
for (const p in params) {
|
|
284
|
+
if (hasOwnProperty(params, p)) {
|
|
285
|
+
const v = String(params[p] ?? "");
|
|
286
|
+
if (!keepEmpty || v || keepEmpty.has(p)) {
|
|
287
|
+
pairs.push(`${p}=${encodeURIComponent(v)}`);
|
|
288
|
+
hasParams = true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (hasParams) {
|
|
293
|
+
path += (path && (~path.indexOf("?") ? "&" : "?")) + pairs.join("&");
|
|
294
|
+
}
|
|
295
|
+
return path;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/event-emitter.ts
|
|
299
|
+
var EventEmitter = class {
|
|
300
|
+
/** Event listeners: prefixed key -> listener array */
|
|
301
|
+
listeners = /* @__PURE__ */ new Map();
|
|
302
|
+
/** Number of `fire()` calls currently on the stack (re-entrancy depth). */
|
|
303
|
+
firingDepth = 0;
|
|
304
|
+
/** Keys whose listener list needs compaction after firing settles. */
|
|
305
|
+
pendingCompaction;
|
|
306
|
+
/**
|
|
307
|
+
* Bind event listener.
|
|
308
|
+
*/
|
|
309
|
+
on(event, handler) {
|
|
310
|
+
const key = SPLITTER + event;
|
|
311
|
+
let list = this.listeners.get(key);
|
|
312
|
+
if (!list) {
|
|
313
|
+
list = [];
|
|
314
|
+
this.listeners.set(key, list);
|
|
315
|
+
}
|
|
316
|
+
list.push({ handler, executing: 0 });
|
|
317
|
+
return this;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Unbind event listener.
|
|
321
|
+
* If handler is provided, removes only that handler.
|
|
322
|
+
* If no handler, removes all handlers for the event.
|
|
323
|
+
*/
|
|
324
|
+
off(event, handler) {
|
|
325
|
+
const key = SPLITTER + event;
|
|
326
|
+
if (handler) {
|
|
327
|
+
const list = this.listeners.get(key);
|
|
328
|
+
if (!list) return this;
|
|
329
|
+
if (this.firingDepth > 0) {
|
|
330
|
+
for (const listener of list) {
|
|
331
|
+
if (listener.handler === handler) {
|
|
332
|
+
listener.handler = noop;
|
|
333
|
+
(this.pendingCompaction ??= /* @__PURE__ */ new Set()).add(key);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
for (let i = 0; i < list.length; i++) {
|
|
339
|
+
if (list[i].handler === handler) {
|
|
340
|
+
list.splice(i, 1);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (list.length === 0) this.listeners.delete(key);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
this.listeners.delete(key);
|
|
348
|
+
Reflect.deleteProperty(
|
|
349
|
+
this,
|
|
350
|
+
`on${event[0].toUpperCase() + event.slice(1)}`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Fire event, execute all bound handlers. Safe for re-entrant `off()` calls
|
|
357
|
+
* during dispatch: removed handlers are replaced with noop and compacted
|
|
358
|
+
* after the outermost fire returns.
|
|
359
|
+
*
|
|
360
|
+
* @param event - Event name
|
|
361
|
+
* @param data - Event data (type property added automatically)
|
|
362
|
+
* @param remove - Whether to remove all handlers after firing
|
|
363
|
+
* @param lastToFirst - Whether to execute handlers in reverse order
|
|
364
|
+
*/
|
|
365
|
+
fire(event, data, remove, lastToFirst) {
|
|
366
|
+
const key = SPLITTER + event;
|
|
367
|
+
const list = this.listeners.get(key);
|
|
368
|
+
if (!data) {
|
|
369
|
+
data = {};
|
|
370
|
+
}
|
|
371
|
+
data["type"] = event;
|
|
372
|
+
this.firingDepth++;
|
|
373
|
+
try {
|
|
374
|
+
if (list) {
|
|
375
|
+
const len = list.length;
|
|
376
|
+
for (let i = 0; i < len; i++) {
|
|
377
|
+
const idx = lastToFirst ? len - 1 - i : i;
|
|
378
|
+
const listener = list[idx];
|
|
379
|
+
if (!listener) continue;
|
|
380
|
+
if (listener.handler === noop) continue;
|
|
381
|
+
listener.executing = 1;
|
|
382
|
+
funcWithTry([listener.handler], [data], this, noop);
|
|
383
|
+
listener.executing = "";
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const onMethodName = `on${event[0].toUpperCase() + event.slice(1)}`;
|
|
387
|
+
const onMethod = this[onMethodName];
|
|
388
|
+
if (typeof onMethod === "function") {
|
|
389
|
+
funcWithTry([onMethod], [data], this, noop);
|
|
390
|
+
}
|
|
391
|
+
if (remove) {
|
|
392
|
+
this.off(event);
|
|
393
|
+
}
|
|
394
|
+
} finally {
|
|
395
|
+
this.firingDepth--;
|
|
396
|
+
if (this.firingDepth === 0 && this.pendingCompaction) {
|
|
397
|
+
for (const k of this.pendingCompaction) {
|
|
398
|
+
const l = this.listeners.get(k);
|
|
399
|
+
if (!l) continue;
|
|
400
|
+
for (let i = l.length - 1; i >= 0; i--) {
|
|
401
|
+
if (l[i].handler === noop) l.splice(i, 1);
|
|
402
|
+
}
|
|
403
|
+
if (l.length === 0) this.listeners.delete(k);
|
|
404
|
+
}
|
|
405
|
+
this.pendingCompaction = void 0;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return this;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// src/mark.ts
|
|
413
|
+
var hostStore = /* @__PURE__ */ new WeakMap();
|
|
414
|
+
function unmark(host) {
|
|
415
|
+
const record = hostStore.get(host);
|
|
416
|
+
if (record) {
|
|
417
|
+
record.deleted = true;
|
|
418
|
+
record.signs.clear();
|
|
419
|
+
} else {
|
|
420
|
+
hostStore.set(host, { signs: /* @__PURE__ */ new Map(), deleted: true });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/cache.ts
|
|
425
|
+
function sortCacheEntries(a, b) {
|
|
426
|
+
return b.frequency - a.frequency || b.lastTimestamp - a.lastTimestamp;
|
|
427
|
+
}
|
|
428
|
+
var Cache = class {
|
|
429
|
+
/** Cache entries array */
|
|
430
|
+
entries = [];
|
|
431
|
+
/** Fast lookup: prefixed key -> entry */
|
|
432
|
+
lookup = /* @__PURE__ */ new Map();
|
|
433
|
+
/** Buffer size for eviction */
|
|
434
|
+
bufferSize;
|
|
435
|
+
/** Maximum cache size */
|
|
436
|
+
maxSize;
|
|
437
|
+
/** Total capacity (maxSize + bufferSize) */
|
|
438
|
+
capacity;
|
|
439
|
+
/** Callback when entry is removed */
|
|
440
|
+
onRemove;
|
|
441
|
+
/** Sort comparator */
|
|
442
|
+
comparator;
|
|
443
|
+
constructor(options = {}) {
|
|
444
|
+
this.maxSize = options.maxSize ?? 20;
|
|
445
|
+
this.bufferSize = options.bufferSize ?? 5;
|
|
446
|
+
this.capacity = this.maxSize + this.bufferSize;
|
|
447
|
+
this.onRemove = options.onRemove;
|
|
448
|
+
this.comparator = options.sortComparator ?? sortCacheEntries;
|
|
449
|
+
}
|
|
450
|
+
/** Prefix a key with SPLITTER for namespace isolation */
|
|
451
|
+
prefixKey(key) {
|
|
452
|
+
return SPLITTER + key;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Get a cached value by key.
|
|
456
|
+
* Updates frequency and timestamp for cache sorting.
|
|
457
|
+
*/
|
|
458
|
+
get(key) {
|
|
459
|
+
const prefixedKey = this.prefixKey(key);
|
|
460
|
+
const entry = this.lookup.get(prefixedKey);
|
|
461
|
+
if (!entry) return void 0;
|
|
462
|
+
entry.frequency++;
|
|
463
|
+
entry.lastTimestamp = nextCounter();
|
|
464
|
+
return entry.value;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Iterate all cached values.
|
|
468
|
+
*/
|
|
469
|
+
forEach(callback) {
|
|
470
|
+
for (const entry of this.entries) {
|
|
471
|
+
callback(entry.value);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Set or update a cached value.
|
|
476
|
+
* If key already exists, updates value and increments frequency.
|
|
477
|
+
* If cache exceeds capacity, triggers eviction.
|
|
478
|
+
*/
|
|
479
|
+
set(key, value) {
|
|
480
|
+
const prefixedKey = this.prefixKey(key);
|
|
481
|
+
const existing = this.lookup.get(prefixedKey);
|
|
482
|
+
if (existing) {
|
|
483
|
+
existing.value = value;
|
|
484
|
+
existing.frequency++;
|
|
485
|
+
existing.lastTimestamp = nextCounter();
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (this.entries.length >= this.capacity) {
|
|
489
|
+
this.evictEntries();
|
|
490
|
+
}
|
|
491
|
+
const entry = {
|
|
492
|
+
originalKey: key,
|
|
493
|
+
value,
|
|
494
|
+
frequency: 1,
|
|
495
|
+
lastTimestamp: nextCounter()
|
|
496
|
+
};
|
|
497
|
+
this.entries.push(entry);
|
|
498
|
+
this.lookup.set(prefixedKey, entry);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Delete a cached entry. Removes it immediately from both the lookup map
|
|
502
|
+
* and the entries array so the GC can reclaim the value without waiting
|
|
503
|
+
* for the next eviction sweep.
|
|
504
|
+
*/
|
|
505
|
+
del(key) {
|
|
506
|
+
const prefixedKey = this.prefixKey(key);
|
|
507
|
+
const entry = this.lookup.get(prefixedKey);
|
|
508
|
+
if (!entry) return;
|
|
509
|
+
this.lookup.delete(prefixedKey);
|
|
510
|
+
const idx = this.entries.indexOf(entry);
|
|
511
|
+
if (idx !== -1) this.entries.splice(idx, 1);
|
|
512
|
+
if (this.onRemove) {
|
|
513
|
+
this.onRemove(key);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Check if a key exists in cache.
|
|
518
|
+
*/
|
|
519
|
+
has(key) {
|
|
520
|
+
return this.lookup.has(this.prefixKey(key));
|
|
521
|
+
}
|
|
522
|
+
/** Get current cache size */
|
|
523
|
+
get size() {
|
|
524
|
+
return this.entries.length;
|
|
525
|
+
}
|
|
526
|
+
/** Clear all entries */
|
|
527
|
+
clear() {
|
|
528
|
+
if (this.onRemove) {
|
|
529
|
+
for (const entry of this.entries) {
|
|
530
|
+
this.onRemove(entry.originalKey);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
this.entries = [];
|
|
534
|
+
this.lookup.clear();
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Evict the `bufferSize` worst entries from the cache.
|
|
538
|
+
*
|
|
539
|
+
* Uses single-pass partial selection (O(n·k)) instead of sorting the entire
|
|
540
|
+
* `entries` array (O(n log n)). For the typical `bufferSize = 5` this is
|
|
541
|
+
* effectively a linear scan with at most 5 in-bucket comparisons per
|
|
542
|
+
* iteration — and it avoids mutating the rest of `entries`.
|
|
543
|
+
*/
|
|
544
|
+
evictEntries() {
|
|
545
|
+
const entries = this.entries;
|
|
546
|
+
const k = this.bufferSize;
|
|
547
|
+
if (k <= 0 || entries.length === 0) return;
|
|
548
|
+
if (entries.length <= k) {
|
|
549
|
+
for (const e of entries) {
|
|
550
|
+
this.lookup.delete(this.prefixKey(e.originalKey));
|
|
551
|
+
if (this.onRemove) this.onRemove(e.originalKey);
|
|
552
|
+
}
|
|
553
|
+
this.entries = [];
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const cmp = this.comparator;
|
|
557
|
+
const worst = [];
|
|
558
|
+
for (const entry of entries) {
|
|
559
|
+
if (worst.length < k) {
|
|
560
|
+
let i = worst.length;
|
|
561
|
+
while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
|
|
562
|
+
worst.splice(i, 0, entry);
|
|
563
|
+
} else if (cmp(entry, worst[k - 1]) > 0) {
|
|
564
|
+
worst.pop();
|
|
565
|
+
let i = worst.length;
|
|
566
|
+
while (i > 0 && cmp(entry, worst[i - 1]) > 0) i--;
|
|
567
|
+
worst.splice(i, 0, entry);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const evictSet = new Set(worst);
|
|
571
|
+
for (const e of worst) {
|
|
572
|
+
this.lookup.delete(this.prefixKey(e.originalKey));
|
|
573
|
+
if (this.onRemove) this.onRemove(e.originalKey);
|
|
574
|
+
}
|
|
575
|
+
this.entries = entries.filter((e) => !evictSet.has(e));
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/event-delegator.ts
|
|
580
|
+
var rootEvents = {};
|
|
581
|
+
var selectorEvents = {};
|
|
582
|
+
var rangeEvents = {};
|
|
583
|
+
var rangeFrames = {};
|
|
584
|
+
var elementGuid = 0;
|
|
585
|
+
var eventInfoCache = new Cache({
|
|
586
|
+
maxSize: 30,
|
|
587
|
+
bufferSize: 10
|
|
588
|
+
});
|
|
589
|
+
var frameGetter;
|
|
590
|
+
function parseEventInfo(eventInfo) {
|
|
591
|
+
const cached = eventInfoCache.get(eventInfo);
|
|
592
|
+
if (cached) {
|
|
593
|
+
return assign({}, cached, { value: eventInfo });
|
|
594
|
+
}
|
|
595
|
+
const match = eventInfo.match(EVENT_METHOD_REGEXP) || [];
|
|
596
|
+
const result = {
|
|
597
|
+
id: match[1] || "",
|
|
598
|
+
name: match[2] || "",
|
|
599
|
+
params: match[3] || ""
|
|
600
|
+
};
|
|
601
|
+
eventInfoCache.set(eventInfo, result);
|
|
602
|
+
return assign({}, result, { value: eventInfo });
|
|
603
|
+
}
|
|
604
|
+
function findFrameInfo(current, eventType) {
|
|
605
|
+
const eventInfos = [];
|
|
606
|
+
const info = current.getAttribute(`@${eventType}`);
|
|
607
|
+
const hasSelectorEvents = !!selectorEvents[eventType];
|
|
608
|
+
if (!info && !hasSelectorEvents) {
|
|
609
|
+
return eventInfos;
|
|
610
|
+
}
|
|
611
|
+
let begin = current;
|
|
612
|
+
let match;
|
|
613
|
+
if (info) {
|
|
614
|
+
match = parseEventInfo(info);
|
|
615
|
+
}
|
|
616
|
+
if (match && !match.id || hasSelectorEvents) {
|
|
617
|
+
let selectorFrameId = "#";
|
|
618
|
+
let backtrace = 0;
|
|
619
|
+
while (begin && begin !== document.body) {
|
|
620
|
+
const beginId = begin.id;
|
|
621
|
+
if (beginId && frameGetter?.(beginId)) {
|
|
622
|
+
selectorFrameId = beginId;
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
begin = begin.parentElement;
|
|
626
|
+
}
|
|
627
|
+
const currentId = current.id;
|
|
628
|
+
if (currentId && frameGetter?.(currentId)) {
|
|
629
|
+
backtrace = 1;
|
|
630
|
+
selectorFrameId = currentId;
|
|
631
|
+
}
|
|
632
|
+
let frameId = selectorFrameId;
|
|
633
|
+
do {
|
|
634
|
+
const frame = frameId ? frameGetter?.(frameId) : void 0;
|
|
635
|
+
if (frame) {
|
|
636
|
+
const view = frame.view;
|
|
637
|
+
if (view) {
|
|
638
|
+
const selectorEntry = view.eventSelectorMap[eventType];
|
|
639
|
+
if (selectorEntry) {
|
|
640
|
+
for (const selectorName of selectorEntry.selectors) {
|
|
641
|
+
const entry = {
|
|
642
|
+
value: selectorName,
|
|
643
|
+
id: frameId,
|
|
644
|
+
name: selectorName,
|
|
645
|
+
params: ""
|
|
646
|
+
};
|
|
647
|
+
if (selectorName) {
|
|
648
|
+
if (!backtrace && elementMatchesSelector(current, selectorName)) {
|
|
649
|
+
eventInfos.push(entry);
|
|
650
|
+
}
|
|
651
|
+
} else if (backtrace) {
|
|
652
|
+
eventInfos.unshift(entry);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (view.template && !backtrace) {
|
|
657
|
+
if (match && !match.id) {
|
|
658
|
+
match.id = frameId;
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
backtrace = 0;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (frame) {
|
|
666
|
+
frameId = frame.parentId || "";
|
|
667
|
+
} else {
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
} while (frameId);
|
|
671
|
+
}
|
|
672
|
+
if (match) {
|
|
673
|
+
eventInfos.push({
|
|
674
|
+
id: match.id,
|
|
675
|
+
value: match.value,
|
|
676
|
+
name: match.name,
|
|
677
|
+
params: match.params
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
return eventInfos;
|
|
681
|
+
}
|
|
682
|
+
function elementMatchesSelector(element, selector) {
|
|
683
|
+
try {
|
|
684
|
+
return element.matches?.(selector) ?? false;
|
|
685
|
+
} catch {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function domEventProcessor(domEvent) {
|
|
690
|
+
const target = domEvent.target;
|
|
691
|
+
const eventType = domEvent.type;
|
|
692
|
+
let lastFrameId = "";
|
|
693
|
+
let current = target;
|
|
694
|
+
while (current && current !== document.body) {
|
|
695
|
+
const eventInfos = findFrameInfo(current, eventType);
|
|
696
|
+
if (eventInfos.length) {
|
|
697
|
+
for (const info of eventInfos) {
|
|
698
|
+
const { id: frameId, name: handlerName, params } = info;
|
|
699
|
+
if (lastFrameId !== frameId) {
|
|
700
|
+
if (lastFrameId && domEvent.isPropagationStopped?.()) {
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
lastFrameId = frameId;
|
|
704
|
+
}
|
|
705
|
+
const frame = frameId ? frameGetter?.(frameId) : void 0;
|
|
706
|
+
const view = frame?.view;
|
|
707
|
+
if (view) {
|
|
708
|
+
const eventName = handlerName + SPLITTER + eventType;
|
|
709
|
+
const fn = Reflect.get(view, eventName);
|
|
710
|
+
if (fn) {
|
|
711
|
+
const extendedEvent = domEvent;
|
|
712
|
+
extendedEvent.eventTarget = target;
|
|
713
|
+
extendedEvent.params = params ? parseUri(params).params : {};
|
|
714
|
+
funcWithTry(fn, [extendedEvent], view, noop);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const rangeFrameId = current.getAttribute("data-range-fid");
|
|
720
|
+
const rangeGuid = current.getAttribute("data-range-guid");
|
|
721
|
+
if (rangeFrameId && rangeGuid) {
|
|
722
|
+
const rangeMap = rangeEvents[rangeFrameId];
|
|
723
|
+
if (rangeMap?.[rangeGuid]?.[eventType]) {
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (domEvent.isPropagationStopped?.()) {
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
current = current.parentElement;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
var EventDelegator = {
|
|
734
|
+
/**
|
|
735
|
+
* Bind a DOM event type to document body.
|
|
736
|
+
*/
|
|
737
|
+
bind(eventType, hasSelector = false) {
|
|
738
|
+
const counter = rootEvents[eventType] || 0;
|
|
739
|
+
if (counter === 0) {
|
|
740
|
+
document.body.addEventListener(eventType, domEventProcessor, true);
|
|
741
|
+
}
|
|
742
|
+
rootEvents[eventType] = counter + 1;
|
|
743
|
+
if (hasSelector) {
|
|
744
|
+
selectorEvents[eventType] = (selectorEvents[eventType] || 0) + 1;
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
/**
|
|
748
|
+
* Unbind a DOM event type from document body.
|
|
749
|
+
*/
|
|
750
|
+
unbind(eventType, hasSelector = false) {
|
|
751
|
+
const counter = rootEvents[eventType] || 0;
|
|
752
|
+
if (counter <= 1) {
|
|
753
|
+
document.body.removeEventListener(eventType, domEventProcessor, true);
|
|
754
|
+
Reflect.deleteProperty(rootEvents, eventType);
|
|
755
|
+
} else {
|
|
756
|
+
rootEvents[eventType] = counter - 1;
|
|
757
|
+
}
|
|
758
|
+
if (hasSelector) {
|
|
759
|
+
const selectorCounter = selectorEvents[eventType] || 0;
|
|
760
|
+
if (selectorCounter <= 1) {
|
|
761
|
+
Reflect.deleteProperty(selectorEvents, eventType);
|
|
762
|
+
} else {
|
|
763
|
+
selectorEvents[eventType] = selectorCounter - 1;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
/**
|
|
768
|
+
* Clean up range events for a destroyed view.
|
|
769
|
+
*/
|
|
770
|
+
clearRangeEvents(viewId) {
|
|
771
|
+
Reflect.deleteProperty(rangeEvents, viewId);
|
|
772
|
+
Reflect.deleteProperty(rangeFrames, viewId);
|
|
773
|
+
},
|
|
774
|
+
/**
|
|
775
|
+
* Set the frame getter function (called by Framework.boot).
|
|
776
|
+
*/
|
|
777
|
+
setFrameGetter(getter) {
|
|
778
|
+
frameGetter = getter;
|
|
779
|
+
},
|
|
780
|
+
/**
|
|
781
|
+
* Get next element GUID.
|
|
782
|
+
*/
|
|
783
|
+
nextElementGuid() {
|
|
784
|
+
return ++elementGuid;
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
// src/safeguard.ts
|
|
789
|
+
var proxiesPool = /* @__PURE__ */ new Map();
|
|
790
|
+
var SAFEGUARD_SENTINEL = "_safe_";
|
|
791
|
+
function safeguard(data, getter, setter, isRoot) {
|
|
792
|
+
if (typeof window.__lark_Debug === "undefined" || !window.__lark_Debug) {
|
|
793
|
+
return data;
|
|
794
|
+
}
|
|
795
|
+
if (typeof Proxy === "undefined") {
|
|
796
|
+
return data;
|
|
797
|
+
}
|
|
798
|
+
if (isPrimitive(data)) {
|
|
799
|
+
return data;
|
|
800
|
+
}
|
|
801
|
+
const build = (prefix, obj) => {
|
|
802
|
+
const cacheKey = (getter || "") + "" + (setter || "");
|
|
803
|
+
const cached = proxiesPool.get(obj);
|
|
804
|
+
if (cached && cached.cacheKey === cacheKey) {
|
|
805
|
+
return cached.entity;
|
|
806
|
+
}
|
|
807
|
+
if (Reflect.get(obj, SAFEGUARD_SENTINEL)) {
|
|
808
|
+
return obj;
|
|
809
|
+
}
|
|
810
|
+
const entity = new Proxy(obj, {
|
|
811
|
+
set(target, property, value) {
|
|
812
|
+
if (!setter && !prefix) {
|
|
813
|
+
throw new Error(
|
|
814
|
+
"Avoid write back, key: " + prefix + property + " value:" + value + " more: https://github.com/hangtiancheng/lark"
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
Reflect.set(target, property, value);
|
|
818
|
+
if (setter) {
|
|
819
|
+
setter(prefix + property, value);
|
|
820
|
+
}
|
|
821
|
+
return true;
|
|
822
|
+
},
|
|
823
|
+
get(target, property) {
|
|
824
|
+
if (property === SAFEGUARD_SENTINEL) {
|
|
825
|
+
return true;
|
|
826
|
+
}
|
|
827
|
+
const out = Reflect.get(target, property);
|
|
828
|
+
if (!prefix && getter) {
|
|
829
|
+
getter(property);
|
|
830
|
+
}
|
|
831
|
+
if (!isRoot && hasOwnProperty(target, property) && (Array.isArray(out) || isPlainObject(out))) {
|
|
832
|
+
return build(prefix + property + ".", out);
|
|
833
|
+
}
|
|
834
|
+
return out;
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
proxiesPool.set(obj, { cacheKey, entity });
|
|
838
|
+
return entity;
|
|
839
|
+
};
|
|
840
|
+
return build("", data);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/module-loader.ts
|
|
844
|
+
var config = {
|
|
845
|
+
rootId: "root",
|
|
846
|
+
routeMode: "history",
|
|
847
|
+
hashbang: "#!",
|
|
848
|
+
error: (error) => {
|
|
849
|
+
throw error;
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
function use(names, callback) {
|
|
853
|
+
const nameList = typeof names === "string" ? [names] : names;
|
|
854
|
+
const loadPromise = (() => {
|
|
855
|
+
if (config.require) {
|
|
856
|
+
const result = config.require(nameList);
|
|
857
|
+
if (result && typeof result.then === "function") {
|
|
858
|
+
return result;
|
|
859
|
+
}
|
|
860
|
+
return Promise.resolve([]);
|
|
861
|
+
}
|
|
862
|
+
return Promise.all(
|
|
863
|
+
nameList.map((name) => {
|
|
864
|
+
const importPath = name.startsWith(".") || name.startsWith("/") ? name : `./${name}`;
|
|
865
|
+
return import(
|
|
866
|
+
/* @vite-ignore */
|
|
867
|
+
/* webpackIgnore: true */
|
|
868
|
+
importPath
|
|
869
|
+
).then((mod) => {
|
|
870
|
+
return mod && (mod["__esModule"] || // For Webpack
|
|
871
|
+
typeof mod["default"] === "function") ? mod["default"] : mod;
|
|
872
|
+
}).catch((err) => {
|
|
873
|
+
const errorHandler = config.error;
|
|
874
|
+
if (errorHandler) {
|
|
875
|
+
errorHandler(err instanceof Error ? err : new Error(String(err)));
|
|
876
|
+
}
|
|
877
|
+
return void 0;
|
|
878
|
+
});
|
|
879
|
+
})
|
|
880
|
+
);
|
|
881
|
+
})();
|
|
882
|
+
if (callback) {
|
|
883
|
+
loadPromise.then((modules) => {
|
|
884
|
+
callback(...modules);
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return loadPromise;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/dom.ts
|
|
891
|
+
var wrapMeta = {
|
|
892
|
+
option: [1, "<select multiple>"],
|
|
893
|
+
thead: [1, "<table>"],
|
|
894
|
+
col: [2, "<table><colgroup>"],
|
|
895
|
+
tr: [2, "<table><tbody>"],
|
|
896
|
+
td: [3, "<table><tbody><tr>"],
|
|
897
|
+
area: [1, "<map>"],
|
|
898
|
+
param: [1, "<object>"],
|
|
899
|
+
svg: [1, '<svg xmlns="' + SVG_NS + '">'],
|
|
900
|
+
math: [1, '<math xmlns="' + MATH_NS + '">'],
|
|
901
|
+
_: [0, ""]
|
|
902
|
+
};
|
|
903
|
+
wrapMeta["optgroup"] = wrapMeta["option"];
|
|
904
|
+
wrapMeta["tbody"] = wrapMeta["tfoot"] = wrapMeta["colgroup"] = wrapMeta["caption"] = wrapMeta["thead"];
|
|
905
|
+
wrapMeta["th"] = wrapMeta["td"];
|
|
906
|
+
var VDoc = document.implementation.createHTMLDocument("");
|
|
907
|
+
var VBase = VDoc.createElement("base");
|
|
908
|
+
VBase.href = document.location.href;
|
|
909
|
+
VDoc.head.appendChild(VBase);
|
|
910
|
+
var DomSpecials = {
|
|
911
|
+
INPUT: ["value", "checked"],
|
|
912
|
+
TEXTAREA: ["value"],
|
|
913
|
+
OPTION: ["selected"]
|
|
914
|
+
};
|
|
915
|
+
function domUnmountFrames(frame, node) {
|
|
916
|
+
if (!(node instanceof Element)) return;
|
|
917
|
+
const id = node.getAttribute("id");
|
|
918
|
+
if (!id) return;
|
|
919
|
+
frame.unmountZone(id);
|
|
920
|
+
if (frame.children().includes(id)) {
|
|
921
|
+
frame.unmountFrame(id);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function domGetNode(html, refNode) {
|
|
925
|
+
const tmp = VDoc.createElement("div");
|
|
926
|
+
const ns = refNode.namespaceURI;
|
|
927
|
+
let tag;
|
|
928
|
+
if (ns === SVG_NS) {
|
|
929
|
+
tag = "svg";
|
|
930
|
+
} else if (ns === MATH_NS) {
|
|
931
|
+
tag = "math";
|
|
932
|
+
} else {
|
|
933
|
+
const match = TAG_NAME_REGEXP.exec(html);
|
|
934
|
+
tag = match ? match[1] : "";
|
|
935
|
+
}
|
|
936
|
+
const wrap = wrapMeta[tag] || wrapMeta["_"];
|
|
937
|
+
tmp.innerHTML = wrap[1] + html;
|
|
938
|
+
let j = wrap[0];
|
|
939
|
+
while (j--) {
|
|
940
|
+
const last = tmp.lastChild;
|
|
941
|
+
if (last) tmp.replaceChildren(last);
|
|
942
|
+
}
|
|
943
|
+
return tmp;
|
|
944
|
+
}
|
|
945
|
+
function domGetCompareKey(node) {
|
|
946
|
+
if (node.nodeType !== 1) return void 0;
|
|
947
|
+
const el = node;
|
|
948
|
+
if (el.compareKeyCached) {
|
|
949
|
+
return el.cachedCompareKey;
|
|
950
|
+
}
|
|
951
|
+
let key = el.autoId ? "" : el.getAttribute("id") || void 0;
|
|
952
|
+
if (!key) {
|
|
953
|
+
const larkView = el.getAttribute(LARK_VIEW);
|
|
954
|
+
if (larkView) {
|
|
955
|
+
key = parseUri(larkView).path || void 0;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
el.compareKeyCached = 1;
|
|
959
|
+
el.cachedCompareKey = key || "";
|
|
960
|
+
return key;
|
|
961
|
+
}
|
|
962
|
+
function domSpecialDiff(oldNode, newNode) {
|
|
963
|
+
const specials = DomSpecials[oldNode.nodeName];
|
|
964
|
+
if (!specials) return 0;
|
|
965
|
+
let result = 0;
|
|
966
|
+
for (const prop of specials) {
|
|
967
|
+
if (Reflect.get(oldNode, prop) !== Reflect.get(newNode, prop)) {
|
|
968
|
+
result = 1;
|
|
969
|
+
Reflect.set(oldNode, prop, Reflect.get(newNode, prop));
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return result;
|
|
973
|
+
}
|
|
974
|
+
function domSetAttributes(oldNode, newNode, ref, keepId) {
|
|
975
|
+
const oldEl = oldNode;
|
|
976
|
+
Reflect.deleteProperty(oldEl, "compareKeyCached");
|
|
977
|
+
const oldAttrs = oldNode.attributes;
|
|
978
|
+
const newAttrs = newNode.attributes;
|
|
979
|
+
for (let i = oldAttrs.length; i--; ) {
|
|
980
|
+
const name = oldAttrs[i].name;
|
|
981
|
+
if (!newNode.hasAttribute(name)) {
|
|
982
|
+
if (name === "id") {
|
|
983
|
+
if (!keepId) {
|
|
984
|
+
ref.idUpdates.push([oldNode, ""]);
|
|
985
|
+
}
|
|
986
|
+
} else {
|
|
987
|
+
ref.hasChanged = 1;
|
|
988
|
+
oldNode.removeAttribute(name);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
for (let i = newAttrs.length; i--; ) {
|
|
993
|
+
const attr = newAttrs[i];
|
|
994
|
+
const key = attr.name;
|
|
995
|
+
const value = attr.value;
|
|
996
|
+
if (oldNode.getAttribute(key) !== value) {
|
|
997
|
+
if (key === "id") {
|
|
998
|
+
ref.idUpdates.push([oldNode, value]);
|
|
999
|
+
} else {
|
|
1000
|
+
ref.hasChanged = 1;
|
|
1001
|
+
oldNode.setAttribute(key, value);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function domSetChildNodes(oldParent, newParent, ref, frame, keys_) {
|
|
1007
|
+
let oldNode = oldParent.lastChild;
|
|
1008
|
+
let newNode = newParent.firstChild;
|
|
1009
|
+
let extra = 0;
|
|
1010
|
+
const keyedNodes = /* @__PURE__ */ new Map();
|
|
1011
|
+
const newKeyedNodes = /* @__PURE__ */ new Map();
|
|
1012
|
+
while (oldNode) {
|
|
1013
|
+
extra++;
|
|
1014
|
+
const nodeKey = domGetCompareKey(oldNode);
|
|
1015
|
+
if (nodeKey) {
|
|
1016
|
+
let bucket = keyedNodes.get(nodeKey);
|
|
1017
|
+
if (!bucket) {
|
|
1018
|
+
bucket = [];
|
|
1019
|
+
keyedNodes.set(nodeKey, bucket);
|
|
1020
|
+
}
|
|
1021
|
+
bucket.push(oldNode);
|
|
1022
|
+
}
|
|
1023
|
+
oldNode = oldNode.previousSibling;
|
|
1024
|
+
}
|
|
1025
|
+
while (newNode) {
|
|
1026
|
+
const nodeKey = domGetCompareKey(newNode);
|
|
1027
|
+
if (nodeKey) {
|
|
1028
|
+
newKeyedNodes.set(nodeKey, (newKeyedNodes.get(nodeKey) ?? 0) + 1);
|
|
1029
|
+
}
|
|
1030
|
+
newNode = newNode.nextSibling;
|
|
1031
|
+
}
|
|
1032
|
+
newNode = newParent.firstChild;
|
|
1033
|
+
oldNode = oldParent.firstChild;
|
|
1034
|
+
while (newNode) {
|
|
1035
|
+
extra--;
|
|
1036
|
+
const tempNew = newNode;
|
|
1037
|
+
newNode = newNode.nextSibling;
|
|
1038
|
+
const nodeKey = domGetCompareKey(tempNew);
|
|
1039
|
+
let foundNode = nodeKey ? keyedNodes.get(nodeKey) : void 0;
|
|
1040
|
+
if (foundNode && (foundNode = foundNode.slice()) && foundNode.length) {
|
|
1041
|
+
const matched = foundNode.pop();
|
|
1042
|
+
while (matched !== oldNode) {
|
|
1043
|
+
if (!oldNode) break;
|
|
1044
|
+
const next = oldNode.nextSibling;
|
|
1045
|
+
oldParent.appendChild(oldNode);
|
|
1046
|
+
oldNode = next;
|
|
1047
|
+
}
|
|
1048
|
+
oldNode = matched.nextSibling;
|
|
1049
|
+
if (nodeKey) {
|
|
1050
|
+
const c = newKeyedNodes.get(nodeKey);
|
|
1051
|
+
if (c) newKeyedNodes.set(nodeKey, c - 1);
|
|
1052
|
+
}
|
|
1053
|
+
domSetNode(matched, tempNew, oldParent, ref, frame, keys_);
|
|
1054
|
+
} else if (oldNode) {
|
|
1055
|
+
const tempOld2 = oldNode;
|
|
1056
|
+
const oldKey = domGetCompareKey(tempOld2);
|
|
1057
|
+
if (oldKey && keyedNodes.has(oldKey) && newKeyedNodes.get(oldKey)) {
|
|
1058
|
+
extra++;
|
|
1059
|
+
ref.hasChanged = 1;
|
|
1060
|
+
ref.domOps.push([8, oldParent, tempNew, tempOld2]);
|
|
1061
|
+
} else {
|
|
1062
|
+
oldNode = oldNode.nextSibling;
|
|
1063
|
+
domSetNode(tempOld2, tempNew, oldParent, ref, frame, keys_);
|
|
1064
|
+
}
|
|
1065
|
+
} else {
|
|
1066
|
+
ref.hasChanged = 1;
|
|
1067
|
+
ref.domOps.push([1, oldParent, tempNew]);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
let tempOld = oldParent.lastChild;
|
|
1071
|
+
while (extra-- > 0) {
|
|
1072
|
+
if (tempOld) {
|
|
1073
|
+
domUnmountFrames(frame, tempOld);
|
|
1074
|
+
ref.domOps.push([2, oldParent, tempOld]);
|
|
1075
|
+
tempOld = tempOld.previousSibling;
|
|
1076
|
+
ref.hasChanged = 1;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
function domSetNode(oldNode, newNode, oldParent, ref, frame, keys_) {
|
|
1081
|
+
const oldAsEl = oldNode instanceof Element ? oldNode : null;
|
|
1082
|
+
const newAsEl = newNode instanceof Element ? newNode : null;
|
|
1083
|
+
const equalAsNodes = oldAsEl !== null && newAsEl !== null && oldAsEl.isEqualNode && oldAsEl.isEqualNode(newAsEl);
|
|
1084
|
+
if (domSpecialDiff(oldNode, newNode) || !equalAsNodes) {
|
|
1085
|
+
if (oldNode.nodeType === newNode.nodeType && oldNode.nodeName === newNode.nodeName) {
|
|
1086
|
+
if (oldAsEl !== null && newAsEl !== null) {
|
|
1087
|
+
const oldEl = oldAsEl;
|
|
1088
|
+
const newEl = newAsEl;
|
|
1089
|
+
const newLarkView = newEl.getAttribute(LARK_VIEW);
|
|
1090
|
+
let updateChildren = true;
|
|
1091
|
+
if (newLarkView) {
|
|
1092
|
+
const oldFrameId = oldEl.getAttribute("id") || "";
|
|
1093
|
+
const newViewPath = parseUri(newLarkView).path;
|
|
1094
|
+
const oldLarkView = oldEl.getAttribute(LARK_VIEW);
|
|
1095
|
+
const oldViewPath = oldLarkView ? parseUri(oldLarkView).path : "";
|
|
1096
|
+
if (oldFrameId && newViewPath === oldViewPath) {
|
|
1097
|
+
updateChildren = false;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
domSetAttributes(oldEl, newEl, ref, !!newLarkView);
|
|
1101
|
+
if (updateChildren) {
|
|
1102
|
+
domSetChildNodes(oldEl, newEl, ref, frame, keys_);
|
|
1103
|
+
}
|
|
1104
|
+
} else if (oldNode.nodeValue !== newNode.nodeValue) {
|
|
1105
|
+
ref.hasChanged = 1;
|
|
1106
|
+
oldNode.nodeValue = newNode.nodeValue;
|
|
1107
|
+
}
|
|
1108
|
+
} else {
|
|
1109
|
+
ref.hasChanged = 1;
|
|
1110
|
+
domUnmountFrames(frame, oldNode);
|
|
1111
|
+
ref.domOps.push([4, oldParent, newNode, oldNode]);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function createDomRef() {
|
|
1116
|
+
return {
|
|
1117
|
+
idUpdates: [],
|
|
1118
|
+
views: [],
|
|
1119
|
+
domOps: [],
|
|
1120
|
+
hasChanged: 0
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
function applyDomOps(ops) {
|
|
1124
|
+
for (const op of ops) {
|
|
1125
|
+
switch (op[0]) {
|
|
1126
|
+
case 1:
|
|
1127
|
+
op[1].appendChild(op[2]);
|
|
1128
|
+
break;
|
|
1129
|
+
case 2:
|
|
1130
|
+
op[1].removeChild(op[2]);
|
|
1131
|
+
break;
|
|
1132
|
+
case 4:
|
|
1133
|
+
op[1].replaceChild(op[2], op[3]);
|
|
1134
|
+
break;
|
|
1135
|
+
case 8:
|
|
1136
|
+
op[1].insertBefore(op[2], op[3]);
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
function applyIdUpdates(updates) {
|
|
1142
|
+
for (const [element, newId] of updates) {
|
|
1143
|
+
if (newId) {
|
|
1144
|
+
element.setAttribute("id", newId);
|
|
1145
|
+
} else {
|
|
1146
|
+
element.removeAttribute("id");
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// src/vdom.ts
|
|
1152
|
+
var DOM_SPECIALS = {
|
|
1153
|
+
INPUT: ["value", "checked"],
|
|
1154
|
+
TEXTAREA: ["value"],
|
|
1155
|
+
OPTION: ["selected"]
|
|
1156
|
+
};
|
|
1157
|
+
function isSameVDomNode(a, b) {
|
|
1158
|
+
return a.compareKey && b.compareKey === a.compareKey || !a.compareKey && !b.compareKey && a.tag === b.tag || a.tag === SPLITTER || b.tag === SPLITTER;
|
|
1159
|
+
}
|
|
1160
|
+
function getKeyNodes(list, nodes, start, end, realEnd) {
|
|
1161
|
+
const keyedNodes = {};
|
|
1162
|
+
for (let i = end, re = realEnd; i >= start; i--, re--) {
|
|
1163
|
+
const oc = list[i];
|
|
1164
|
+
const cKey = oc.compareKey;
|
|
1165
|
+
if (cKey) {
|
|
1166
|
+
const bucket = keyedNodes[cKey] || (keyedNodes[cKey] = []);
|
|
1167
|
+
bucket.push(nodes[re]);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return keyedNodes;
|
|
1171
|
+
}
|
|
1172
|
+
function vdomCreateNode(vnode, owner, ref) {
|
|
1173
|
+
const tag = vnode.tag;
|
|
1174
|
+
if (tag === V_TEXT_NODE) {
|
|
1175
|
+
return document.createTextNode(vnode.html);
|
|
1176
|
+
}
|
|
1177
|
+
const sTag = typeof tag === "string" ? tag : tag.toString();
|
|
1178
|
+
const ns = VDOM_NS_MAP[sTag] || owner.namespaceURI;
|
|
1179
|
+
const el = document.createElementNS(ns, sTag);
|
|
1180
|
+
vdomSetAttributes(el, vnode, ref);
|
|
1181
|
+
el.innerHTML = vnode.html;
|
|
1182
|
+
return el;
|
|
1183
|
+
}
|
|
1184
|
+
function vdomSetAttributes(realNode, newVDom, ref, lastVDom) {
|
|
1185
|
+
let changed = 0;
|
|
1186
|
+
const nMap = newVDom.attrsMap || {};
|
|
1187
|
+
const nsMap = newVDom.attrsSpecials || {};
|
|
1188
|
+
if (lastVDom) {
|
|
1189
|
+
const oMap = lastVDom.attrsMap || {};
|
|
1190
|
+
const osMap = lastVDom.attrsSpecials || {};
|
|
1191
|
+
for (const key in oMap) {
|
|
1192
|
+
if (!hasOwnProperty(nMap, key)) {
|
|
1193
|
+
changed = 1;
|
|
1194
|
+
const sValue = osMap[key];
|
|
1195
|
+
if (sValue) {
|
|
1196
|
+
if (ref) {
|
|
1197
|
+
ref.nodeProps.push([realNode, sValue, ""]);
|
|
1198
|
+
} else {
|
|
1199
|
+
Reflect.set(realNode, sValue, "");
|
|
1200
|
+
}
|
|
1201
|
+
} else {
|
|
1202
|
+
realNode.removeAttribute(key);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
for (const key in nMap) {
|
|
1208
|
+
const value = nMap[key];
|
|
1209
|
+
const sKey = nsMap[key];
|
|
1210
|
+
if (sKey) {
|
|
1211
|
+
if (Reflect.get(realNode, sKey) !== value) {
|
|
1212
|
+
changed = 1;
|
|
1213
|
+
if (ref) {
|
|
1214
|
+
ref.nodeProps.push([realNode, sKey, value]);
|
|
1215
|
+
} else {
|
|
1216
|
+
Reflect.set(realNode, sKey, value);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
} else {
|
|
1220
|
+
const oldMap = lastVDom?.attrsMap;
|
|
1221
|
+
if (!oldMap || oldMap[key] !== value) {
|
|
1222
|
+
changed = 1;
|
|
1223
|
+
realNode.setAttribute(key, String(value ?? ""));
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return changed;
|
|
1228
|
+
}
|
|
1229
|
+
function vdomSyncFormState(realNode, newVDom) {
|
|
1230
|
+
const specials = DOM_SPECIALS[realNode.nodeName];
|
|
1231
|
+
if (!specials) return 0;
|
|
1232
|
+
const nMap = newVDom.attrsMap || {};
|
|
1233
|
+
let result = 0;
|
|
1234
|
+
for (const prop of specials) {
|
|
1235
|
+
const newVal = nMap[prop];
|
|
1236
|
+
if (newVal !== void 0 && Reflect.get(realNode, prop) !== newVal) {
|
|
1237
|
+
result = 1;
|
|
1238
|
+
Reflect.set(realNode, prop, newVal);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return result;
|
|
1242
|
+
}
|
|
1243
|
+
function vdomSetNode(realNode, oldParent, lastVDom, newVDom, ref, frame, keys, rootView, ready) {
|
|
1244
|
+
const lastTag = lastVDom.tag;
|
|
1245
|
+
const newTag = newVDom.tag;
|
|
1246
|
+
if (lastTag === V_TEXT_NODE || newTag === V_TEXT_NODE) {
|
|
1247
|
+
if (lastTag === newTag) {
|
|
1248
|
+
if (lastVDom.html !== newVDom.html) {
|
|
1249
|
+
ref.changed = 1;
|
|
1250
|
+
realNode.nodeValue = newVDom.html;
|
|
1251
|
+
}
|
|
1252
|
+
} else {
|
|
1253
|
+
ref.changed = 1;
|
|
1254
|
+
domUnmountFrames(frame, realNode);
|
|
1255
|
+
oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
|
|
1256
|
+
}
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
if (lastTag === newTag) {
|
|
1260
|
+
const lastAMap = lastVDom.attrsMap || {};
|
|
1261
|
+
const newAMap = newVDom.attrsMap || {};
|
|
1262
|
+
if (lastVDom.compareKey && lastVDom.compareKey === newVDom.compareKey && !lastAMap["id"] && !newAMap["id"]) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
let attrChanged = 0;
|
|
1266
|
+
if (lastVDom.attrs !== newVDom.attrs || newVDom.hasSpecials) {
|
|
1267
|
+
attrChanged = vdomSetAttributes(
|
|
1268
|
+
realNode,
|
|
1269
|
+
newVDom,
|
|
1270
|
+
ref,
|
|
1271
|
+
lastVDom
|
|
1272
|
+
);
|
|
1273
|
+
if (attrChanged) ref.changed = 1;
|
|
1274
|
+
}
|
|
1275
|
+
let updateChildren = true;
|
|
1276
|
+
if (newVDom.isLarkView) {
|
|
1277
|
+
const oldFrameId = realNode.getAttribute("id") || "";
|
|
1278
|
+
const newViewPath = newVDom.isLarkView;
|
|
1279
|
+
const oldViewPath = lastVDom.isLarkView || "";
|
|
1280
|
+
if (oldFrameId && newViewPath === oldViewPath) {
|
|
1281
|
+
updateChildren = false;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
vdomSyncFormState(realNode, newVDom);
|
|
1285
|
+
if (updateChildren && !newVDom.selfClose) {
|
|
1286
|
+
vdomSetChildNodes(
|
|
1287
|
+
realNode,
|
|
1288
|
+
lastVDom,
|
|
1289
|
+
newVDom,
|
|
1290
|
+
ref,
|
|
1291
|
+
frame,
|
|
1292
|
+
keys,
|
|
1293
|
+
rootView,
|
|
1294
|
+
ready
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
} else {
|
|
1298
|
+
ref.changed = 1;
|
|
1299
|
+
domUnmountFrames(frame, realNode);
|
|
1300
|
+
oldParent.replaceChild(vdomCreateNode(newVDom, oldParent, ref), realNode);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function vdomSetChildNodes(realNode, lastVDom, newVDom, ref, frame, keys, view, ready) {
|
|
1304
|
+
if (!lastVDom) {
|
|
1305
|
+
ref.changed = 1;
|
|
1306
|
+
realNode.innerHTML = newVDom.html;
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (lastVDom.html === newVDom.html) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
const oldChildren = lastVDom.children;
|
|
1313
|
+
const newChildren = newVDom.children;
|
|
1314
|
+
const oldLen = oldChildren?.length || 0;
|
|
1315
|
+
const newLen = newChildren?.length || 0;
|
|
1316
|
+
if (oldLen === 0 && newLen === 0) return;
|
|
1317
|
+
const nodes = realNode.childNodes;
|
|
1318
|
+
let oldStart = 0;
|
|
1319
|
+
let oldEnd = oldLen - 1;
|
|
1320
|
+
let newStart = 0;
|
|
1321
|
+
let newEnd = newLen - 1;
|
|
1322
|
+
let realStart = oldStart;
|
|
1323
|
+
let realEnd = oldEnd;
|
|
1324
|
+
let keyedNodes;
|
|
1325
|
+
const oldReusedTotal = lastVDom.reusedTotal || 0;
|
|
1326
|
+
const newReusedTotal = newVDom.reusedTotal || 0;
|
|
1327
|
+
let oldStartNode = oldChildren?.[oldStart];
|
|
1328
|
+
let oldEndNode = oldChildren?.[oldEnd];
|
|
1329
|
+
let newStartNode = newChildren?.[newStart];
|
|
1330
|
+
let newEndNode = newChildren?.[newEnd];
|
|
1331
|
+
while (oldStart <= oldEnd && newStart <= newEnd) {
|
|
1332
|
+
if (!oldStartNode) {
|
|
1333
|
+
oldStartNode = oldChildren?.[++oldStart];
|
|
1334
|
+
realStart++;
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
if (!oldEndNode) {
|
|
1338
|
+
oldEndNode = oldChildren?.[--oldEnd];
|
|
1339
|
+
realEnd--;
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (isSameVDomNode(newStartNode, oldStartNode)) {
|
|
1343
|
+
if (newStartNode.tag === SPLITTER || oldStartNode.tag === SPLITTER) {
|
|
1344
|
+
ref.changed = 1;
|
|
1345
|
+
domUnmountFrames(frame, realNode);
|
|
1346
|
+
if (newStartNode.tag === SPLITTER) {
|
|
1347
|
+
realNode.innerHTML = newStartNode.html;
|
|
1348
|
+
} else {
|
|
1349
|
+
realNode.innerHTML = "";
|
|
1350
|
+
realNode.appendChild(vdomCreateNode(newStartNode, realNode, ref));
|
|
1351
|
+
}
|
|
1352
|
+
} else {
|
|
1353
|
+
vdomSetNode(
|
|
1354
|
+
nodes[realStart],
|
|
1355
|
+
realNode,
|
|
1356
|
+
oldStartNode,
|
|
1357
|
+
newStartNode,
|
|
1358
|
+
ref,
|
|
1359
|
+
frame,
|
|
1360
|
+
keys,
|
|
1361
|
+
view,
|
|
1362
|
+
ready
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
reduceCached(keyedNodes, oldStartNode, nodes[realStart]);
|
|
1366
|
+
realStart++;
|
|
1367
|
+
oldStartNode = oldChildren?.[++oldStart];
|
|
1368
|
+
newStartNode = newChildren?.[++newStart];
|
|
1369
|
+
} else if (isSameVDomNode(newEndNode, oldEndNode)) {
|
|
1370
|
+
if (newEndNode.tag === SPLITTER || oldEndNode.tag === SPLITTER) {
|
|
1371
|
+
ref.changed = 1;
|
|
1372
|
+
domUnmountFrames(frame, realNode);
|
|
1373
|
+
realNode.innerHTML = newEndNode.tag === SPLITTER ? newEndNode.html : "";
|
|
1374
|
+
if (newEndNode.tag !== SPLITTER) {
|
|
1375
|
+
realNode.appendChild(vdomCreateNode(newEndNode, realNode, ref));
|
|
1376
|
+
}
|
|
1377
|
+
} else {
|
|
1378
|
+
vdomSetNode(
|
|
1379
|
+
nodes[realEnd],
|
|
1380
|
+
realNode,
|
|
1381
|
+
oldEndNode,
|
|
1382
|
+
newEndNode,
|
|
1383
|
+
ref,
|
|
1384
|
+
frame,
|
|
1385
|
+
keys,
|
|
1386
|
+
view,
|
|
1387
|
+
ready
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
reduceCached(keyedNodes, oldEndNode, nodes[realEnd]);
|
|
1391
|
+
realEnd--;
|
|
1392
|
+
oldEndNode = oldChildren?.[--oldEnd];
|
|
1393
|
+
newEndNode = newChildren?.[--newEnd];
|
|
1394
|
+
} else if (isSameVDomNode(newEndNode, oldStartNode)) {
|
|
1395
|
+
if (newEndNode.tag === SPLITTER || oldStartNode.tag === SPLITTER) {
|
|
1396
|
+
ref.changed = 1;
|
|
1397
|
+
domUnmountFrames(frame, realNode);
|
|
1398
|
+
realNode.innerHTML = newEndNode.tag === SPLITTER ? newEndNode.html : "";
|
|
1399
|
+
if (newEndNode.tag !== SPLITTER) {
|
|
1400
|
+
realNode.appendChild(vdomCreateNode(newEndNode, realNode, ref));
|
|
1401
|
+
}
|
|
1402
|
+
} else {
|
|
1403
|
+
const oi = nodes[realStart];
|
|
1404
|
+
realNode.insertBefore(oi, nodes[realEnd + 1] || null);
|
|
1405
|
+
vdomSetNode(
|
|
1406
|
+
oi,
|
|
1407
|
+
realNode,
|
|
1408
|
+
oldStartNode,
|
|
1409
|
+
newEndNode,
|
|
1410
|
+
ref,
|
|
1411
|
+
frame,
|
|
1412
|
+
keys,
|
|
1413
|
+
view,
|
|
1414
|
+
ready
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
reduceCached(keyedNodes, oldStartNode, nodes[realStart]);
|
|
1418
|
+
realStart++;
|
|
1419
|
+
oldStartNode = oldChildren?.[++oldStart];
|
|
1420
|
+
newEndNode = newChildren?.[--newEnd];
|
|
1421
|
+
} else if (isSameVDomNode(newStartNode, oldEndNode)) {
|
|
1422
|
+
if (newStartNode.tag === SPLITTER || oldEndNode.tag === SPLITTER) {
|
|
1423
|
+
ref.changed = 1;
|
|
1424
|
+
domUnmountFrames(frame, realNode);
|
|
1425
|
+
realNode.innerHTML = newStartNode.tag === SPLITTER ? newStartNode.html : "";
|
|
1426
|
+
if (newStartNode.tag !== SPLITTER) {
|
|
1427
|
+
realNode.appendChild(vdomCreateNode(newStartNode, realNode, ref));
|
|
1428
|
+
}
|
|
1429
|
+
} else {
|
|
1430
|
+
const oi = nodes[realEnd];
|
|
1431
|
+
realNode.insertBefore(oi, nodes[realStart]);
|
|
1432
|
+
vdomSetNode(
|
|
1433
|
+
oi,
|
|
1434
|
+
realNode,
|
|
1435
|
+
oldEndNode,
|
|
1436
|
+
newStartNode,
|
|
1437
|
+
ref,
|
|
1438
|
+
frame,
|
|
1439
|
+
keys,
|
|
1440
|
+
view,
|
|
1441
|
+
ready
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
reduceCached(keyedNodes, oldEndNode, nodes[realEnd]);
|
|
1445
|
+
realEnd--;
|
|
1446
|
+
oldEndNode = oldChildren?.[--oldEnd];
|
|
1447
|
+
newStartNode = newChildren?.[++newStart];
|
|
1448
|
+
} else {
|
|
1449
|
+
if (!keyedNodes && newReusedTotal > 0 && oldReusedTotal > 0) {
|
|
1450
|
+
keyedNodes = getKeyNodes(
|
|
1451
|
+
oldChildren,
|
|
1452
|
+
nodes,
|
|
1453
|
+
oldStart,
|
|
1454
|
+
oldEnd,
|
|
1455
|
+
realEnd
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
const cKey = newStartNode.compareKey;
|
|
1459
|
+
let found;
|
|
1460
|
+
let compareKey;
|
|
1461
|
+
if (cKey && keyedNodes) {
|
|
1462
|
+
found = keyedNodes[cKey];
|
|
1463
|
+
compareKey = void 0;
|
|
1464
|
+
while (found && found.length > 0) {
|
|
1465
|
+
compareKey = found.pop();
|
|
1466
|
+
if (compareKey) break;
|
|
1467
|
+
}
|
|
1468
|
+
if (found && found.length === 0) delete keyedNodes[cKey];
|
|
1469
|
+
}
|
|
1470
|
+
if (compareKey) {
|
|
1471
|
+
if (compareKey !== nodes[realStart]) {
|
|
1472
|
+
for (let j = oldStart + 1; j <= oldEnd; j++) {
|
|
1473
|
+
const oc = oldChildren?.[j];
|
|
1474
|
+
if (oc && nodes[realStart + (j - oldStart)] === compareKey) {
|
|
1475
|
+
oldChildren[j] = void 0;
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
realNode.insertBefore(compareKey, nodes[realStart]);
|
|
1480
|
+
}
|
|
1481
|
+
vdomSetNode(
|
|
1482
|
+
compareKey,
|
|
1483
|
+
realNode,
|
|
1484
|
+
oldStartNode,
|
|
1485
|
+
newStartNode,
|
|
1486
|
+
ref,
|
|
1487
|
+
frame,
|
|
1488
|
+
keys,
|
|
1489
|
+
view,
|
|
1490
|
+
ready
|
|
1491
|
+
);
|
|
1492
|
+
} else if (oldStartNode.compareKey && lastVDom.reused?.[oldStartNode.compareKey] && newVDom.reused?.[oldStartNode.compareKey] || nodes[realStart]?.id && realNode.querySelectorAll?.(
|
|
1493
|
+
`#${nodes[realStart].id}`
|
|
1494
|
+
)?.length && !newStartNode.isLarkView) {
|
|
1495
|
+
ref.changed = 1;
|
|
1496
|
+
const newNode = vdomCreateNode(newStartNode, realNode, ref);
|
|
1497
|
+
realNode.insertBefore(newNode, nodes[realStart]);
|
|
1498
|
+
realStart--;
|
|
1499
|
+
realEnd++;
|
|
1500
|
+
} else {
|
|
1501
|
+
vdomSetNode(
|
|
1502
|
+
nodes[realStart],
|
|
1503
|
+
realNode,
|
|
1504
|
+
oldStartNode,
|
|
1505
|
+
newStartNode,
|
|
1506
|
+
ref,
|
|
1507
|
+
frame,
|
|
1508
|
+
keys,
|
|
1509
|
+
view,
|
|
1510
|
+
ready
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
realStart++;
|
|
1514
|
+
oldStartNode = oldChildren?.[++oldStart];
|
|
1515
|
+
newStartNode = newChildren?.[++newStart];
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
if (newStart <= newEnd) {
|
|
1519
|
+
const refNode = nodes[realEnd + 1] || null;
|
|
1520
|
+
for (let i = newStart; i <= newEnd; i++) {
|
|
1521
|
+
ref.changed = 1;
|
|
1522
|
+
const nc = newChildren[i];
|
|
1523
|
+
if (nc.tag === SPLITTER) {
|
|
1524
|
+
domUnmountFrames(frame, realNode);
|
|
1525
|
+
realNode.innerHTML = nc.html;
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
const newNode = vdomCreateNode(nc, realNode, ref);
|
|
1529
|
+
realNode.insertBefore(newNode, refNode);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
if (oldStart <= oldEnd) {
|
|
1533
|
+
for (let i = realEnd; i >= realStart; i--) {
|
|
1534
|
+
const node = nodes[i];
|
|
1535
|
+
if (node) {
|
|
1536
|
+
domUnmountFrames(frame, node);
|
|
1537
|
+
ref.changed = 1;
|
|
1538
|
+
realNode.removeChild(node);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
if (ref.asyncCount === 0) {
|
|
1543
|
+
callFunction(ready, []);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
function reduceCached(keyedNodes, node, compared) {
|
|
1547
|
+
if (!keyedNodes || !node.compareKey) return;
|
|
1548
|
+
const bucket = keyedNodes[node.compareKey];
|
|
1549
|
+
if (bucket) {
|
|
1550
|
+
for (let i = bucket.length; i--; ) {
|
|
1551
|
+
if (bucket[i] === compared) {
|
|
1552
|
+
bucket[i] = void 0;
|
|
1553
|
+
break;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
function createVDomRef(viewId) {
|
|
1559
|
+
return {
|
|
1560
|
+
viewId,
|
|
1561
|
+
viewRenders: [],
|
|
1562
|
+
nodeProps: [],
|
|
1563
|
+
asyncCount: 0,
|
|
1564
|
+
changed: 0,
|
|
1565
|
+
domOps: []
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// src/updater.ts
|
|
1570
|
+
var Updater = class {
|
|
1571
|
+
/** View ID (same as owner frame ID) */
|
|
1572
|
+
viewId;
|
|
1573
|
+
/** Current data object */
|
|
1574
|
+
data;
|
|
1575
|
+
/** Ref data for template rendering */
|
|
1576
|
+
refData;
|
|
1577
|
+
/** Changed keys in current digest cycle */
|
|
1578
|
+
changedKeys = /* @__PURE__ */ new Set();
|
|
1579
|
+
/** Whether data has changed since last digest */
|
|
1580
|
+
hasChangedFlag = 0;
|
|
1581
|
+
/**
|
|
1582
|
+
* Digesting queue: supports re-digest during digest.
|
|
1583
|
+
* Holds pending callbacks; `null` is used as a sentinel marking the start
|
|
1584
|
+
* of an active digest cycle, so `runDigest` can detect re-entrant calls.
|
|
1585
|
+
*/
|
|
1586
|
+
digestingQueue = [];
|
|
1587
|
+
/** Monotonically increasing version, bumped each time data actually changes. */
|
|
1588
|
+
version = 0;
|
|
1589
|
+
/** Snapshot of `version` taken by `snapshot()`, used by `altered()`. */
|
|
1590
|
+
snapshotVersion;
|
|
1591
|
+
/** Last rendered VDOM tree (only used when virtualDom is enabled) */
|
|
1592
|
+
vdom;
|
|
1593
|
+
constructor(viewId) {
|
|
1594
|
+
this.viewId = viewId;
|
|
1595
|
+
this.data = { vId: viewId };
|
|
1596
|
+
const refCounter = {};
|
|
1597
|
+
refCounter[SPLITTER] = 1;
|
|
1598
|
+
this.refData = refCounter;
|
|
1599
|
+
this.hasChangedFlag = 1;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Get data by key.
|
|
1603
|
+
* Returns entire data object if key is omitted.
|
|
1604
|
+
*/
|
|
1605
|
+
get(key) {
|
|
1606
|
+
let result = this.data;
|
|
1607
|
+
if (key) {
|
|
1608
|
+
result = this.data[key];
|
|
1609
|
+
}
|
|
1610
|
+
if (typeof window !== "undefined" && window.__lark_Debug) {
|
|
1611
|
+
return safeguard(result);
|
|
1612
|
+
}
|
|
1613
|
+
return result;
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Set data, tracking changed keys.
|
|
1617
|
+
* Returns this for chaining.
|
|
1618
|
+
*/
|
|
1619
|
+
set(data, excludes) {
|
|
1620
|
+
const changed = setData(
|
|
1621
|
+
data,
|
|
1622
|
+
this.data,
|
|
1623
|
+
this.changedKeys,
|
|
1624
|
+
excludes || EMPTY_STRING_SET
|
|
1625
|
+
);
|
|
1626
|
+
if (changed) {
|
|
1627
|
+
this.version++;
|
|
1628
|
+
this.hasChangedFlag = 1;
|
|
1629
|
+
}
|
|
1630
|
+
return this;
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Detect changes and trigger DOM re-render.
|
|
1634
|
+
*
|
|
1635
|
+
* The core rendering pipeline:
|
|
1636
|
+
* 1. Set data if provided
|
|
1637
|
+
* 2. If changed, run DOM diff (template → new DOM → diff against old DOM)
|
|
1638
|
+
* 3. Apply DOM operations
|
|
1639
|
+
* 4. Apply ID updates
|
|
1640
|
+
* 5. Call endUpdate on views that need re-rendering
|
|
1641
|
+
* 6. Support re-digest during digest via queue
|
|
1642
|
+
*/
|
|
1643
|
+
digest(data, excludes, callback) {
|
|
1644
|
+
if (data) {
|
|
1645
|
+
this.set(data, excludes);
|
|
1646
|
+
}
|
|
1647
|
+
const digesting = this.digestingQueue;
|
|
1648
|
+
if (callback) {
|
|
1649
|
+
digesting.push(callback);
|
|
1650
|
+
}
|
|
1651
|
+
if (digesting.length > 0 && digesting[0] === null) {
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
this.runDigest(digesting);
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Core digest execution.
|
|
1658
|
+
*/
|
|
1659
|
+
runDigest(digesting) {
|
|
1660
|
+
const startIndex = digesting.length;
|
|
1661
|
+
digesting.push(null);
|
|
1662
|
+
const keys = this.changedKeys;
|
|
1663
|
+
const changed = this.hasChangedFlag;
|
|
1664
|
+
this.hasChangedFlag = 0;
|
|
1665
|
+
this.changedKeys = /* @__PURE__ */ new Set();
|
|
1666
|
+
const frame = Frame.get(this.viewId);
|
|
1667
|
+
const view = frame?.view;
|
|
1668
|
+
const node = getById(this.viewId);
|
|
1669
|
+
if (changed && view && node && view.signature > 0 && frame) {
|
|
1670
|
+
const template = view.template;
|
|
1671
|
+
if (typeof template === "function") {
|
|
1672
|
+
if (config.virtualDom) {
|
|
1673
|
+
const vdomTemplate = template;
|
|
1674
|
+
const newVDom = vdomTemplate(this.data, this.viewId, this.refData);
|
|
1675
|
+
const ref = createVDomRef(this.viewId);
|
|
1676
|
+
const ready = () => {
|
|
1677
|
+
this.vdom = newVDom;
|
|
1678
|
+
if (ref.changed || !view.rendered) {
|
|
1679
|
+
view.endUpdate(this.viewId);
|
|
1680
|
+
}
|
|
1681
|
+
for (const [el, prop, val] of ref.nodeProps) {
|
|
1682
|
+
Reflect.set(el, prop, val);
|
|
1683
|
+
}
|
|
1684
|
+
for (const v of ref.viewRenders) {
|
|
1685
|
+
if (v.render) {
|
|
1686
|
+
funcWithTry(v.render, [], v, noop);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
vdomSetChildNodes(
|
|
1691
|
+
node,
|
|
1692
|
+
this.vdom,
|
|
1693
|
+
newVDom,
|
|
1694
|
+
ref,
|
|
1695
|
+
frame,
|
|
1696
|
+
keys,
|
|
1697
|
+
view,
|
|
1698
|
+
ready
|
|
1699
|
+
);
|
|
1700
|
+
if (ref.asyncCount === 0) {
|
|
1701
|
+
ready();
|
|
1702
|
+
}
|
|
1703
|
+
} else {
|
|
1704
|
+
const html = template(
|
|
1705
|
+
this.data,
|
|
1706
|
+
this.viewId,
|
|
1707
|
+
this.refData,
|
|
1708
|
+
encodeHTML,
|
|
1709
|
+
strSafe,
|
|
1710
|
+
encodeURIExtra,
|
|
1711
|
+
refFn,
|
|
1712
|
+
encodeQuote
|
|
1713
|
+
);
|
|
1714
|
+
const newDom = domGetNode(html, node);
|
|
1715
|
+
const ref = createDomRef();
|
|
1716
|
+
domSetChildNodes(node, newDom, ref, frame, keys);
|
|
1717
|
+
applyIdUpdates(ref.idUpdates);
|
|
1718
|
+
applyDomOps(ref.domOps);
|
|
1719
|
+
for (const v of ref.views) {
|
|
1720
|
+
if (v.render) {
|
|
1721
|
+
funcWithTry(v.render, [], v, noop);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
if (ref.hasChanged || !view.rendered) {
|
|
1725
|
+
view.endUpdate(this.viewId);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
if (digesting.length > startIndex + 1) {
|
|
1731
|
+
this.runDigest(digesting);
|
|
1732
|
+
} else {
|
|
1733
|
+
const callbacks = digesting.slice();
|
|
1734
|
+
digesting.length = 0;
|
|
1735
|
+
for (const cb of callbacks) {
|
|
1736
|
+
if (cb) cb();
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Save a snapshot of the current data version for `altered()` detection.
|
|
1742
|
+
* Cheap O(1) — records the current monotonic version, no serialization.
|
|
1743
|
+
*/
|
|
1744
|
+
snapshot() {
|
|
1745
|
+
this.snapshotVersion = this.version;
|
|
1746
|
+
return this;
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Check whether data has changed since the last snapshot.
|
|
1750
|
+
* Returns undefined when no snapshot has been taken yet.
|
|
1751
|
+
*/
|
|
1752
|
+
altered() {
|
|
1753
|
+
if (this.snapshotVersion === void 0) return void 0;
|
|
1754
|
+
return this.version !== this.snapshotVersion;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Translate a refData reference back to its original value.
|
|
1758
|
+
*
|
|
1759
|
+
* The ref protocol is `SPLITTER` + ascii decimal digits — the exact format
|
|
1760
|
+
* emitted by `refFn`. We require that exact shape so a user-supplied
|
|
1761
|
+
* string that merely begins with SPLITTER is never accidentally resolved
|
|
1762
|
+
* (or mishandled as a "missing ref").
|
|
1763
|
+
*/
|
|
1764
|
+
translate(data) {
|
|
1765
|
+
if (typeof data !== "string" || !isRefToken(data)) return data;
|
|
1766
|
+
return hasOwnProperty(this.refData, data) ? this.refData[data] : data;
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Resolve a dotted property path against refData.
|
|
1770
|
+
*
|
|
1771
|
+
* Only safe property-path syntax is supported: `a`, `a.b`, `a.b.c`.
|
|
1772
|
+
* Numeric literals (e.g. `1`, `1.5`) are returned as numbers. Anything else
|
|
1773
|
+
* returns `undefined` — we no longer evaluate arbitrary JavaScript via
|
|
1774
|
+
* `new Function`, so the method is CSP-safe and cannot be used as an
|
|
1775
|
+
* injection vector.
|
|
1776
|
+
*/
|
|
1777
|
+
parse(expr) {
|
|
1778
|
+
const trimmed = expr.trim();
|
|
1779
|
+
if (!trimmed) return void 0;
|
|
1780
|
+
if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
1781
|
+
return Number(trimmed);
|
|
1782
|
+
}
|
|
1783
|
+
if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(trimmed)) {
|
|
1784
|
+
return void 0;
|
|
1785
|
+
}
|
|
1786
|
+
let cur = this.refData;
|
|
1787
|
+
for (const segment of trimmed.split(".")) {
|
|
1788
|
+
if (cur == null || typeof cur !== "object") return void 0;
|
|
1789
|
+
cur = cur[segment];
|
|
1790
|
+
}
|
|
1791
|
+
return cur;
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Get the set of keys changed since the last digest (for external inspection).
|
|
1795
|
+
*/
|
|
1796
|
+
getChangedKeys() {
|
|
1797
|
+
return this.changedKeys;
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
|
|
1801
|
+
// src/router.ts
|
|
1802
|
+
var emitter = new EventEmitter();
|
|
1803
|
+
var hrefCache = new Cache();
|
|
1804
|
+
var changedCache = new Cache();
|
|
1805
|
+
var lastLocation = createEmptyLocation();
|
|
1806
|
+
var lastChanged;
|
|
1807
|
+
var silent = 0;
|
|
1808
|
+
var booted = false;
|
|
1809
|
+
var cachedRoutes;
|
|
1810
|
+
var cachedUnmatchedView;
|
|
1811
|
+
var cachedDefaultView;
|
|
1812
|
+
var cachedDefaultPath;
|
|
1813
|
+
var cachedRewrite;
|
|
1814
|
+
var defaultTitle;
|
|
1815
|
+
var frameworkConfig;
|
|
1816
|
+
var routeMode = "history";
|
|
1817
|
+
var beforeEachGuards = [];
|
|
1818
|
+
function createEmptyLocation() {
|
|
1819
|
+
return {
|
|
1820
|
+
href: "",
|
|
1821
|
+
srcQuery: "",
|
|
1822
|
+
srcHash: "",
|
|
1823
|
+
query: { path: "", params: {} },
|
|
1824
|
+
hash: { path: "", params: {} },
|
|
1825
|
+
params: {},
|
|
1826
|
+
get: (_key, defaultValue) => defaultValue ?? ""
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
function getParam(key, defaultValue) {
|
|
1830
|
+
return this["params"][key] || (defaultValue !== void 0 ? defaultValue : "");
|
|
1831
|
+
}
|
|
1832
|
+
function attachViewAndPath(loc) {
|
|
1833
|
+
if (!frameworkConfig) return;
|
|
1834
|
+
if (!cachedRoutes) {
|
|
1835
|
+
cachedRoutes = frameworkConfig.routes || {};
|
|
1836
|
+
cachedUnmatchedView = frameworkConfig.unmatchedView;
|
|
1837
|
+
cachedDefaultView = frameworkConfig.defaultView;
|
|
1838
|
+
cachedDefaultPath = frameworkConfig.defaultPath || "/";
|
|
1839
|
+
cachedRewrite = frameworkConfig.rewrite;
|
|
1840
|
+
}
|
|
1841
|
+
if (!loc.view) {
|
|
1842
|
+
const rawPath = routeMode === "history" ? loc.query["path"] || loc.hash["path"] : loc.hash["path"];
|
|
1843
|
+
let path = rawPath || cachedDefaultPath || "/";
|
|
1844
|
+
if (!cachedRoutes[path] && path === "/" && cachedDefaultPath && cachedDefaultPath !== "/") {
|
|
1845
|
+
path = cachedDefaultPath;
|
|
1846
|
+
}
|
|
1847
|
+
if (cachedRewrite) {
|
|
1848
|
+
path = cachedRewrite(
|
|
1849
|
+
path,
|
|
1850
|
+
loc["params"],
|
|
1851
|
+
cachedRoutes
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
const viewEntry = cachedRoutes[path] || cachedUnmatchedView || cachedDefaultView;
|
|
1855
|
+
loc["path"] = path;
|
|
1856
|
+
loc.view = typeof viewEntry === "string" ? viewEntry : viewEntry?.view || "";
|
|
1857
|
+
if (typeof viewEntry === "object" && viewEntry) {
|
|
1858
|
+
assign(loc, viewEntry);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
function getChanged(oldLoc, newLoc) {
|
|
1863
|
+
const oKey = oldLoc.href;
|
|
1864
|
+
const nKey = newLoc.href;
|
|
1865
|
+
const tKey = oKey + SPLITTER + nKey;
|
|
1866
|
+
const cached = changedCache.get(tKey);
|
|
1867
|
+
if (cached) {
|
|
1868
|
+
return cached;
|
|
1869
|
+
}
|
|
1870
|
+
let hasChanged = false;
|
|
1871
|
+
const changedParams = {};
|
|
1872
|
+
const setDiff = (key, oldVal, newVal) => {
|
|
1873
|
+
const from = oldVal || "";
|
|
1874
|
+
const to = newVal || "";
|
|
1875
|
+
if (from !== to) {
|
|
1876
|
+
changedParams[key] = { from, to };
|
|
1877
|
+
hasChanged = true;
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
const allParamKeys = /* @__PURE__ */ new Set([
|
|
1881
|
+
...Object.keys(oldLoc["params"]),
|
|
1882
|
+
...Object.keys(newLoc["params"])
|
|
1883
|
+
]);
|
|
1884
|
+
for (const key of allParamKeys) {
|
|
1885
|
+
setDiff(key, oldLoc["params"][key], newLoc["params"][key]);
|
|
1886
|
+
}
|
|
1887
|
+
const result = {
|
|
1888
|
+
["params"]: changedParams,
|
|
1889
|
+
force: !oKey,
|
|
1890
|
+
changed: hasChanged
|
|
1891
|
+
};
|
|
1892
|
+
setDiff("path", oldLoc["path"], newLoc["path"]);
|
|
1893
|
+
if (changedParams["path"]) {
|
|
1894
|
+
result["path"] = changedParams["path"];
|
|
1895
|
+
result.changed = true;
|
|
1896
|
+
}
|
|
1897
|
+
const viewKey = "view";
|
|
1898
|
+
setDiff(viewKey, oldLoc.view, newLoc.view);
|
|
1899
|
+
if (changedParams[viewKey]) {
|
|
1900
|
+
result.view = changedParams[viewKey];
|
|
1901
|
+
result.changed = true;
|
|
1902
|
+
}
|
|
1903
|
+
const finalResult = {
|
|
1904
|
+
changed: hasChanged,
|
|
1905
|
+
diff: result
|
|
1906
|
+
};
|
|
1907
|
+
changedCache.set(tKey, finalResult);
|
|
1908
|
+
return finalResult;
|
|
1909
|
+
}
|
|
1910
|
+
function updateBrowserUrl(path, replace) {
|
|
1911
|
+
if (routeMode === "history") {
|
|
1912
|
+
const url = path || "/";
|
|
1913
|
+
const currentUrl = window.location.pathname + window.location.search;
|
|
1914
|
+
if (url === currentUrl) {
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
if (replace) {
|
|
1918
|
+
window.history.replaceState(null, "", url);
|
|
1919
|
+
} else {
|
|
1920
|
+
window.history.pushState(null, "", url);
|
|
1921
|
+
}
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
const hashbang = frameworkConfig?.hashbang || "#!";
|
|
1925
|
+
const fullPath = path === "" ? "" : hashbang + path;
|
|
1926
|
+
if (replace) {
|
|
1927
|
+
window.location.replace(fullPath);
|
|
1928
|
+
} else {
|
|
1929
|
+
window.location.hash = fullPath;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
function updateUrl(path, params, loc, replace, silentFlag, lQuery) {
|
|
1933
|
+
path = toUri(path, params, lQuery);
|
|
1934
|
+
const currentSrc = routeMode === "history" ? loc.srcQuery : loc.srcHash;
|
|
1935
|
+
if (path !== currentSrc) {
|
|
1936
|
+
silent = silentFlag ? 1 : 0;
|
|
1937
|
+
updateBrowserUrl(path, replace);
|
|
1938
|
+
if (routeMode === "history" && Router.notify) {
|
|
1939
|
+
Router.notify();
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
var Router = {
|
|
1944
|
+
/**
|
|
1945
|
+
* Parse href into Location object.
|
|
1946
|
+
* Defaults to window.location.href.
|
|
1947
|
+
*/
|
|
1948
|
+
parse(href) {
|
|
1949
|
+
href = href || window.location.href;
|
|
1950
|
+
const cached = hrefCache.get(href);
|
|
1951
|
+
if (cached) {
|
|
1952
|
+
return cached;
|
|
1953
|
+
}
|
|
1954
|
+
let srcQuery;
|
|
1955
|
+
let srcHash;
|
|
1956
|
+
let query;
|
|
1957
|
+
let hash;
|
|
1958
|
+
if (routeMode === "history") {
|
|
1959
|
+
try {
|
|
1960
|
+
const urlObj = new URL(href, window.location.origin);
|
|
1961
|
+
srcQuery = urlObj.pathname + urlObj.search;
|
|
1962
|
+
srcHash = urlObj.hash ? urlObj.hash.replace(/^#!?/, "") : "";
|
|
1963
|
+
query = parseUri(srcQuery);
|
|
1964
|
+
hash = srcHash ? parseUri(srcHash) : { path: "", params: {} };
|
|
1965
|
+
} catch {
|
|
1966
|
+
srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
|
|
1967
|
+
srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
|
|
1968
|
+
query = parseUri(srcQuery);
|
|
1969
|
+
hash = parseUri(srcHash);
|
|
1970
|
+
}
|
|
1971
|
+
} else {
|
|
1972
|
+
srcQuery = href.replace(URL_TRIM_HASH_REGEXP, "");
|
|
1973
|
+
srcHash = href.replace(URL_TRIM_QUERY_REGEXP, "");
|
|
1974
|
+
query = parseUri(srcQuery);
|
|
1975
|
+
hash = parseUri(srcHash);
|
|
1976
|
+
}
|
|
1977
|
+
const params = assign({}, query["params"], hash["params"]);
|
|
1978
|
+
const location = {
|
|
1979
|
+
href,
|
|
1980
|
+
srcQuery,
|
|
1981
|
+
srcHash,
|
|
1982
|
+
query: { path: query.path, params: query["params"] },
|
|
1983
|
+
hash: { path: hash.path, params: hash["params"] },
|
|
1984
|
+
["params"]: params,
|
|
1985
|
+
get: getParam
|
|
1986
|
+
};
|
|
1987
|
+
if (booted) {
|
|
1988
|
+
attachViewAndPath(location);
|
|
1989
|
+
hrefCache.set(href, location);
|
|
1990
|
+
}
|
|
1991
|
+
if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug) {
|
|
1992
|
+
location["params"] = safeguard(location["params"]);
|
|
1993
|
+
}
|
|
1994
|
+
return location;
|
|
1995
|
+
},
|
|
1996
|
+
/**
|
|
1997
|
+
* Compute diff between current and previous location.
|
|
1998
|
+
* Fires 'changed' event if location changed and not silent.
|
|
1999
|
+
*/
|
|
2000
|
+
diff() {
|
|
2001
|
+
const location = Router.parse();
|
|
2002
|
+
const changed = getChanged(lastLocation, location);
|
|
2003
|
+
lastLocation = location;
|
|
2004
|
+
if (!silent && changed.changed) {
|
|
2005
|
+
lastChanged = changed.diff;
|
|
2006
|
+
if (lastChanged["path"]) {
|
|
2007
|
+
document.title = defaultTitle || document.title;
|
|
2008
|
+
}
|
|
2009
|
+
emitter.fire(RouterEvents.CHANGED, asRecord(lastChanged));
|
|
2010
|
+
}
|
|
2011
|
+
silent = 0;
|
|
2012
|
+
if (typeof window.__lark_Debug !== "undefined" && window.__lark_Debug && lastChanged) {
|
|
2013
|
+
lastChanged = safeguard(lastChanged);
|
|
2014
|
+
}
|
|
2015
|
+
return lastChanged;
|
|
2016
|
+
},
|
|
2017
|
+
/**
|
|
2018
|
+
* Navigate to new URL.
|
|
2019
|
+
*
|
|
2020
|
+
* @param pathOrParams - Path string or params object
|
|
2021
|
+
* @param params - Query parameters (if pathOrParams is string)
|
|
2022
|
+
* @param replace - Whether to replace current history entry
|
|
2023
|
+
* @param silentFlag - Whether to silently update without firing events
|
|
2024
|
+
*/
|
|
2025
|
+
to(pathOrParams, params, replace, silentFlag) {
|
|
2026
|
+
let tPath = "";
|
|
2027
|
+
let tParams;
|
|
2028
|
+
if (!params && typeof pathOrParams === "object") {
|
|
2029
|
+
tParams = pathOrParams;
|
|
2030
|
+
} else {
|
|
2031
|
+
const parsed = parseUri(pathOrParams);
|
|
2032
|
+
tPath = parsed.path;
|
|
2033
|
+
tParams = { ...parsed["params"] };
|
|
2034
|
+
if (params) {
|
|
2035
|
+
assign(tParams, params);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
const lPath = lastLocation["path"] || "";
|
|
2039
|
+
const lParams = lastLocation["params"];
|
|
2040
|
+
const lQuery = /* @__PURE__ */ new Set();
|
|
2041
|
+
for (const k in lastLocation.query["params"]) {
|
|
2042
|
+
if (hasOwnProperty(lastLocation.query["params"], k)) {
|
|
2043
|
+
lQuery.add(k);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
if (tPath) {
|
|
2047
|
+
if (routeMode === "hash" && !hasOwnProperty(window, "history")) {
|
|
2048
|
+
for (const qKey of lQuery) {
|
|
2049
|
+
if (!hasOwnProperty(tParams, qKey)) {
|
|
2050
|
+
tParams[qKey] = "";
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
} else if (lParams) {
|
|
2055
|
+
tPath = lPath;
|
|
2056
|
+
tParams = assign({}, lParams, tParams);
|
|
2057
|
+
}
|
|
2058
|
+
updateUrl(tPath, tParams, lastLocation, replace, silentFlag, lQuery);
|
|
2059
|
+
},
|
|
2060
|
+
/**
|
|
2061
|
+
* Register an async-friendly navigation guard. See `RouterInterface.beforeEach`.
|
|
2062
|
+
*/
|
|
2063
|
+
beforeEach(guard) {
|
|
2064
|
+
beforeEachGuards.push(guard);
|
|
2065
|
+
return () => {
|
|
2066
|
+
const idx = beforeEachGuards.indexOf(guard);
|
|
2067
|
+
if (idx !== -1) beforeEachGuards.splice(idx, 1);
|
|
2068
|
+
};
|
|
2069
|
+
},
|
|
2070
|
+
/**
|
|
2071
|
+
* Join multiple path segments into a single path.
|
|
2072
|
+
*/
|
|
2073
|
+
join(...paths) {
|
|
2074
|
+
let result = paths.join("/");
|
|
2075
|
+
result = result.replace(/\/\.\//g, "/");
|
|
2076
|
+
const doubleDotRegExp = /\/[^/]+\/\.\.\//;
|
|
2077
|
+
while (doubleDotRegExp.test(result)) {
|
|
2078
|
+
result = result.replace(doubleDotRegExp, "/");
|
|
2079
|
+
}
|
|
2080
|
+
result = result.replace(/\/{2,}/g, "/");
|
|
2081
|
+
return result;
|
|
2082
|
+
},
|
|
2083
|
+
/** Bind event listener */
|
|
2084
|
+
on(event, handler) {
|
|
2085
|
+
emitter.on(event, handler);
|
|
2086
|
+
return Router;
|
|
2087
|
+
},
|
|
2088
|
+
/** Unbind event listener */
|
|
2089
|
+
off(event, handler) {
|
|
2090
|
+
emitter.off(event, handler);
|
|
2091
|
+
return Router;
|
|
2092
|
+
},
|
|
2093
|
+
/** Fire event */
|
|
2094
|
+
fire(event, data, remove) {
|
|
2095
|
+
emitter.fire(event, data, remove);
|
|
2096
|
+
return Router;
|
|
2097
|
+
},
|
|
2098
|
+
/**
|
|
2099
|
+
* Internal: bind routing events and beforeunload.
|
|
2100
|
+
* Called by Framework.boot().
|
|
2101
|
+
* In hash mode, listens to hashchange + popstate.
|
|
2102
|
+
* In history mode, listens to popstate only.
|
|
2103
|
+
*/
|
|
2104
|
+
_bind() {
|
|
2105
|
+
defaultTitle = document.title;
|
|
2106
|
+
const getLocationKey = () => {
|
|
2107
|
+
if (routeMode === "history") {
|
|
2108
|
+
return window.location.pathname + window.location.search;
|
|
2109
|
+
}
|
|
2110
|
+
return Router.parse().srcHash;
|
|
2111
|
+
};
|
|
2112
|
+
let lastKey = getLocationKey();
|
|
2113
|
+
let suspend;
|
|
2114
|
+
const watchChange = () => {
|
|
2115
|
+
if (suspend) {
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
hrefCache.clear();
|
|
2119
|
+
const loc = Router.parse();
|
|
2120
|
+
const newKey = routeMode === "history" ? loc.srcQuery : loc.srcHash;
|
|
2121
|
+
if (newKey !== lastKey) {
|
|
2122
|
+
const changeEvent = {
|
|
2123
|
+
p: 0,
|
|
2124
|
+
reject: () => {
|
|
2125
|
+
changeEvent.p = 1;
|
|
2126
|
+
suspend = "";
|
|
2127
|
+
updateBrowserUrl(lastKey);
|
|
2128
|
+
},
|
|
2129
|
+
resolve: () => {
|
|
2130
|
+
changeEvent.p = 1;
|
|
2131
|
+
lastKey = newKey;
|
|
2132
|
+
suspend = "";
|
|
2133
|
+
updateBrowserUrl(newKey);
|
|
2134
|
+
Router.diff();
|
|
2135
|
+
},
|
|
2136
|
+
prevent: () => {
|
|
2137
|
+
suspend = 1;
|
|
2138
|
+
}
|
|
2139
|
+
};
|
|
2140
|
+
Router.fire(RouterEvents.CHANGE, changeEvent);
|
|
2141
|
+
if (suspend || changeEvent.p) {
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
if (beforeEachGuards.length === 0) {
|
|
2145
|
+
changeEvent.resolve();
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
const from = lastLocation;
|
|
2149
|
+
const to = loc;
|
|
2150
|
+
const guards = beforeEachGuards.slice();
|
|
2151
|
+
let chain = Promise.resolve(true);
|
|
2152
|
+
for (const guard of guards) {
|
|
2153
|
+
chain = chain.then((prev) => {
|
|
2154
|
+
if (prev === false) return false;
|
|
2155
|
+
return guard(to, from);
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
chain.then(
|
|
2159
|
+
(result) => {
|
|
2160
|
+
if (changeEvent.p) return;
|
|
2161
|
+
if (result === false) {
|
|
2162
|
+
changeEvent.reject();
|
|
2163
|
+
} else {
|
|
2164
|
+
changeEvent.resolve();
|
|
2165
|
+
}
|
|
2166
|
+
},
|
|
2167
|
+
() => {
|
|
2168
|
+
if (!changeEvent.p) changeEvent.reject();
|
|
2169
|
+
}
|
|
2170
|
+
);
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
Router.notify = watchChange;
|
|
2174
|
+
if (routeMode === "history") {
|
|
2175
|
+
window.addEventListener("popstate", watchChange);
|
|
2176
|
+
} else {
|
|
2177
|
+
window.addEventListener("hashchange", watchChange);
|
|
2178
|
+
window.addEventListener("popstate", watchChange);
|
|
2179
|
+
}
|
|
2180
|
+
window.addEventListener("beforeunload", (domEvent) => {
|
|
2181
|
+
const data = {};
|
|
2182
|
+
Router.fire(RouterEvents.PAGE_UNLOAD, data);
|
|
2183
|
+
const msg = data["msg"];
|
|
2184
|
+
if (msg) {
|
|
2185
|
+
domEvent.returnValue = msg;
|
|
2186
|
+
}
|
|
2187
|
+
});
|
|
2188
|
+
Router.diff();
|
|
2189
|
+
},
|
|
2190
|
+
/**
|
|
2191
|
+
* Internal: set framework config for route resolution.
|
|
2192
|
+
*/
|
|
2193
|
+
_setConfig(cfg) {
|
|
2194
|
+
frameworkConfig = cfg;
|
|
2195
|
+
routeMode = cfg.routeMode || "history";
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
// src/view.ts
|
|
2200
|
+
var VIEW_GLOBALS = {};
|
|
2201
|
+
if (typeof window !== "undefined") {
|
|
2202
|
+
VIEW_GLOBALS["window"] = window;
|
|
2203
|
+
}
|
|
2204
|
+
if (typeof document !== "undefined") {
|
|
2205
|
+
VIEW_GLOBALS["document"] = document;
|
|
2206
|
+
}
|
|
2207
|
+
var View = class _View {
|
|
2208
|
+
/** View ID (same as owner frame ID) */
|
|
2209
|
+
id = "";
|
|
2210
|
+
/** Owner frame */
|
|
2211
|
+
owner = 0;
|
|
2212
|
+
/** Updater instance */
|
|
2213
|
+
updater;
|
|
2214
|
+
/** Signature: > 0 means active, incremented on render, 0 = destroyed */
|
|
2215
|
+
signature = 0;
|
|
2216
|
+
/** Whether rendered at least once */
|
|
2217
|
+
rendered;
|
|
2218
|
+
/** Whether view has template */
|
|
2219
|
+
template;
|
|
2220
|
+
/** Location observation config */
|
|
2221
|
+
locationObserved = {
|
|
2222
|
+
flag: 0,
|
|
2223
|
+
keys: [],
|
|
2224
|
+
observePath: false
|
|
2225
|
+
};
|
|
2226
|
+
/** Observed state keys */
|
|
2227
|
+
observedStateKeys;
|
|
2228
|
+
/** Resource map */
|
|
2229
|
+
resources = {};
|
|
2230
|
+
/** Whether endUpdate pending */
|
|
2231
|
+
endUpdatePending;
|
|
2232
|
+
/** Internal event storage */
|
|
2233
|
+
_events = new EventEmitter();
|
|
2234
|
+
// ============================================================
|
|
2235
|
+
// Getters for prototype-stored event maps
|
|
2236
|
+
// ============================================================
|
|
2237
|
+
/** Prototype-stored event maps shape (set by View.prepare). */
|
|
2238
|
+
get protoEventState() {
|
|
2239
|
+
return Object.getPrototypeOf(this);
|
|
2240
|
+
}
|
|
2241
|
+
/**
|
|
2242
|
+
* Event bitmask map: eventType -> bitmask (1=root, 2=selector).
|
|
2243
|
+
* Read from prototype ($evtObjMap) set by View.prepare.
|
|
2244
|
+
* Using a getter avoids ES6 class field shadowing the prototype value.
|
|
2245
|
+
*/
|
|
2246
|
+
get eventObjectMap() {
|
|
2247
|
+
return this.protoEventState.$evtObjMap ?? {};
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Selector event map: eventType -> selector list.
|
|
2251
|
+
* Read from prototype ($selMap) set by View.prepare.
|
|
2252
|
+
*/
|
|
2253
|
+
get eventSelectorMap() {
|
|
2254
|
+
return this.protoEventState.$selMap ?? {};
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* Global event list: [{handler, element, eventName, modifiers}].
|
|
2258
|
+
* Read from prototype ($globalEvtList) set by View.prepare.
|
|
2259
|
+
*/
|
|
2260
|
+
get globalEventList() {
|
|
2261
|
+
return this.protoEventState.$globalEvtList ?? [];
|
|
2262
|
+
}
|
|
2263
|
+
// ============================================================
|
|
2264
|
+
// Instance lifecycle methods
|
|
2265
|
+
// ============================================================
|
|
2266
|
+
/**
|
|
2267
|
+
* Initialize view (called by Frame when mounting).
|
|
2268
|
+
*/
|
|
2269
|
+
init() {
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Render view template (called by Frame after init).
|
|
2273
|
+
* Wrapped by View.wrapMethod to manage signature + resources.
|
|
2274
|
+
*/
|
|
2275
|
+
render() {
|
|
2276
|
+
this.updater.digest();
|
|
2277
|
+
}
|
|
2278
|
+
// ============================================================
|
|
2279
|
+
// Event methods (delegate to internal EventEmitter)
|
|
2280
|
+
// ============================================================
|
|
2281
|
+
on(event, handler) {
|
|
2282
|
+
this._events.on(event, handler);
|
|
2283
|
+
return this;
|
|
2284
|
+
}
|
|
2285
|
+
off(event, handler) {
|
|
2286
|
+
this._events.off(event, handler);
|
|
2287
|
+
return this;
|
|
2288
|
+
}
|
|
2289
|
+
fire(event, data, remove, lastToFirst) {
|
|
2290
|
+
this._events.fire(event, data, remove, lastToFirst);
|
|
2291
|
+
return this;
|
|
2292
|
+
}
|
|
2293
|
+
// ============================================================
|
|
2294
|
+
// Update methods
|
|
2295
|
+
// ============================================================
|
|
2296
|
+
/** Get the owning frame, asserting it has been bound. */
|
|
2297
|
+
get ownerFrame() {
|
|
2298
|
+
return this.owner;
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* Notify view that HTML update is about to begin.
|
|
2302
|
+
* Unmounts child frames in the update zone.
|
|
2303
|
+
*/
|
|
2304
|
+
beginUpdate(id) {
|
|
2305
|
+
if (this.signature > 0 && this.endUpdatePending !== void 0) {
|
|
2306
|
+
this.ownerFrame.unmountZone(id);
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Notify view that HTML update has ended.
|
|
2311
|
+
* Mounts child frames in the update zone and runs deferred invokes.
|
|
2312
|
+
*/
|
|
2313
|
+
endUpdate(id, inner) {
|
|
2314
|
+
if (this.signature > 0) {
|
|
2315
|
+
const updateId = id || this.id;
|
|
2316
|
+
let flag;
|
|
2317
|
+
if (inner) {
|
|
2318
|
+
flag = inner;
|
|
2319
|
+
} else {
|
|
2320
|
+
flag = this.endUpdatePending;
|
|
2321
|
+
this.endUpdatePending = 1;
|
|
2322
|
+
this.rendered = true;
|
|
2323
|
+
}
|
|
2324
|
+
const ownerFrame = this.ownerFrame;
|
|
2325
|
+
ownerFrame.mountZone(updateId);
|
|
2326
|
+
if (!flag) {
|
|
2327
|
+
setTimeout(
|
|
2328
|
+
this.wrapAsync(() => {
|
|
2329
|
+
_View.runInvokes(ownerFrame);
|
|
2330
|
+
}),
|
|
2331
|
+
0
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
// ============================================================
|
|
2337
|
+
// Async wrapper
|
|
2338
|
+
// ============================================================
|
|
2339
|
+
/**
|
|
2340
|
+
* Wrap an async callback to check view signature before executing.
|
|
2341
|
+
* If the view has been re-rendered or destroyed, the callback is skipped.
|
|
2342
|
+
*/
|
|
2343
|
+
wrapAsync(fn, context) {
|
|
2344
|
+
const currentSignature = this.signature;
|
|
2345
|
+
return (...args) => {
|
|
2346
|
+
if (currentSignature > 0 && currentSignature === this.signature) {
|
|
2347
|
+
return fn.apply(context || this, args);
|
|
2348
|
+
}
|
|
2349
|
+
return void 0;
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
// ============================================================
|
|
2353
|
+
// Location observation
|
|
2354
|
+
// ============================================================
|
|
2355
|
+
/**
|
|
2356
|
+
* Observe location parameters or path changes.
|
|
2357
|
+
* When observed keys change, render() is called automatically.
|
|
2358
|
+
*/
|
|
2359
|
+
observeLocation(params, observePath = false) {
|
|
2360
|
+
const loc = this.locationObserved;
|
|
2361
|
+
loc.flag = 1;
|
|
2362
|
+
if (typeof params === "object" && !Array.isArray(params)) {
|
|
2363
|
+
const opts = params;
|
|
2364
|
+
if (opts["path"]) {
|
|
2365
|
+
observePath = true;
|
|
2366
|
+
}
|
|
2367
|
+
const paramKeys = opts["params"];
|
|
2368
|
+
if (typeof paramKeys === "string" || Array.isArray(paramKeys)) {
|
|
2369
|
+
params = paramKeys;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
loc.observePath = observePath;
|
|
2373
|
+
if (params) {
|
|
2374
|
+
if (typeof params === "string") {
|
|
2375
|
+
loc.keys = params.split(",");
|
|
2376
|
+
} else if (Array.isArray(params)) {
|
|
2377
|
+
loc.keys = params;
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
// ============================================================
|
|
2382
|
+
// State observation
|
|
2383
|
+
// ============================================================
|
|
2384
|
+
/**
|
|
2385
|
+
* Observe State data keys for changes.
|
|
2386
|
+
* When observed keys change via State.digest(), render() is called.
|
|
2387
|
+
*/
|
|
2388
|
+
observeState(observedKeys) {
|
|
2389
|
+
if (typeof observedKeys === "string") {
|
|
2390
|
+
this.observedStateKeys = observedKeys.split(",");
|
|
2391
|
+
} else {
|
|
2392
|
+
this.observedStateKeys = observedKeys;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
// ============================================================
|
|
2396
|
+
// Resource management
|
|
2397
|
+
// ============================================================
|
|
2398
|
+
/**
|
|
2399
|
+
* Capture (register) a resource under a key.
|
|
2400
|
+
* If a resource already exists at that key, it's destroyed first.
|
|
2401
|
+
* When destroyOnRender=true, the resource is destroyed on next render call.
|
|
2402
|
+
*/
|
|
2403
|
+
capture(key, resource, destroyOnRender = false) {
|
|
2404
|
+
const cache = this.resources;
|
|
2405
|
+
if (resource) {
|
|
2406
|
+
_View.destroyResource(cache, key, true, resource);
|
|
2407
|
+
cache[key] = {
|
|
2408
|
+
entity: resource,
|
|
2409
|
+
destroyOnRender
|
|
2410
|
+
};
|
|
2411
|
+
} else {
|
|
2412
|
+
const entry = cache[key];
|
|
2413
|
+
return entry ? entry.entity : void 0;
|
|
2414
|
+
}
|
|
2415
|
+
return resource;
|
|
2416
|
+
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Release a captured resource.
|
|
2419
|
+
* If destroy=true, calls the resource's destroy() method.
|
|
2420
|
+
*/
|
|
2421
|
+
release(key, destroy = true) {
|
|
2422
|
+
return _View.destroyResource(this.resources, key, destroy);
|
|
2423
|
+
}
|
|
2424
|
+
// ============================================================
|
|
2425
|
+
// Leave tip
|
|
2426
|
+
// ============================================================
|
|
2427
|
+
/**
|
|
2428
|
+
* Set up a leave confirmation for route changes and page unload.
|
|
2429
|
+
*/
|
|
2430
|
+
leaveTip(message, condition) {
|
|
2431
|
+
const changeListener = function(e) {
|
|
2432
|
+
const isRouterChange = e.type === RouterEvents.CHANGE;
|
|
2433
|
+
const aKey = isRouterChange ? "a" : "b";
|
|
2434
|
+
const bKey = isRouterChange ? "b" : "a";
|
|
2435
|
+
if (changeListener[aKey]) {
|
|
2436
|
+
e.prevent?.();
|
|
2437
|
+
e.reject?.();
|
|
2438
|
+
} else if (condition()) {
|
|
2439
|
+
e.prevent?.();
|
|
2440
|
+
changeListener[bKey] = 1;
|
|
2441
|
+
e.resolve?.();
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
const unloadListener = (e) => {
|
|
2445
|
+
if (condition()) {
|
|
2446
|
+
e["msg"] = message;
|
|
2447
|
+
}
|
|
2448
|
+
};
|
|
2449
|
+
Router.on(RouterEvents.CHANGE, changeListener);
|
|
2450
|
+
Router.on(RouterEvents.PAGE_UNLOAD, unloadListener);
|
|
2451
|
+
this.on("unload", changeListener);
|
|
2452
|
+
this.on("destroy", () => {
|
|
2453
|
+
Router.off(RouterEvents.CHANGE, changeListener);
|
|
2454
|
+
Router.off(RouterEvents.PAGE_UNLOAD, unloadListener);
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
// ============================================================
|
|
2458
|
+
// Static public methods
|
|
2459
|
+
// ============================================================
|
|
2460
|
+
/** Collected makes from mixins */
|
|
2461
|
+
static makes;
|
|
2462
|
+
/**
|
|
2463
|
+
* Prepare a View subclass by scanning its prototype for event method patterns.
|
|
2464
|
+
* Pattern: `$?name<eventType1,eventType2>(&modifiers)`
|
|
2465
|
+
*
|
|
2466
|
+
* Only runs once per View subclass (guarded by makes marker).
|
|
2467
|
+
* Called from Frame.mountView before creating the view instance.
|
|
2468
|
+
*/
|
|
2469
|
+
static prepare(oView) {
|
|
2470
|
+
if (oView.makes) {
|
|
2471
|
+
return oView.makes;
|
|
2472
|
+
}
|
|
2473
|
+
const makes = [];
|
|
2474
|
+
oView.makes = makes;
|
|
2475
|
+
const eventsObject = {};
|
|
2476
|
+
const eventsList = [];
|
|
2477
|
+
const selectorObject = {};
|
|
2478
|
+
const mixins = Reflect.get(oView.prototype, "mixins");
|
|
2479
|
+
if (mixins && Array.isArray(mixins)) {
|
|
2480
|
+
_View.mergeMixins(mixins, oView, makes);
|
|
2481
|
+
}
|
|
2482
|
+
for (const p in oView.prototype) {
|
|
2483
|
+
if (!hasOwnProperty(oView.prototype, p)) continue;
|
|
2484
|
+
const currentFn = Reflect.get(oView.prototype, p);
|
|
2485
|
+
if (typeof currentFn !== "function") continue;
|
|
2486
|
+
const matches = p.match(VIEW_EVENT_METHOD_REGEXP);
|
|
2487
|
+
if (!matches) continue;
|
|
2488
|
+
const isSelector = matches[1];
|
|
2489
|
+
const selectorOrCallback = matches[2];
|
|
2490
|
+
const events = matches[3];
|
|
2491
|
+
const modifiers = matches[4];
|
|
2492
|
+
const mod = {};
|
|
2493
|
+
if (modifiers) {
|
|
2494
|
+
for (const item of modifiers.split(",")) {
|
|
2495
|
+
mod[item] = true;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
const eventTypes = events.split(",");
|
|
2499
|
+
for (const item of eventTypes) {
|
|
2500
|
+
const globalNode = VIEW_GLOBALS[selectorOrCallback];
|
|
2501
|
+
let mask = 1;
|
|
2502
|
+
if (isSelector) {
|
|
2503
|
+
if (globalNode) {
|
|
2504
|
+
eventsList.push({
|
|
2505
|
+
handler: currentFn,
|
|
2506
|
+
element: globalNode,
|
|
2507
|
+
eventName: item,
|
|
2508
|
+
modifiers: mod
|
|
2509
|
+
});
|
|
2510
|
+
continue;
|
|
2511
|
+
}
|
|
2512
|
+
mask = 2;
|
|
2513
|
+
let selectorEntry = selectorObject[item];
|
|
2514
|
+
if (!selectorEntry) {
|
|
2515
|
+
selectorEntry = selectorObject[item] = {
|
|
2516
|
+
selectors: []
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
if (!selectorEntry[selectorOrCallback]) {
|
|
2520
|
+
selectorEntry[selectorOrCallback] = 1;
|
|
2521
|
+
selectorEntry.selectors.push(selectorOrCallback);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
eventsObject[item] = (eventsObject[item] || 0) | mask;
|
|
2525
|
+
const combinedKey = selectorOrCallback + SPLITTER + item;
|
|
2526
|
+
const existingFn = Reflect.get(oView.prototype, combinedKey);
|
|
2527
|
+
if (!existingFn) {
|
|
2528
|
+
Reflect.set(oView.prototype, combinedKey, currentFn);
|
|
2529
|
+
} else if (typeof existingFn === "function") {
|
|
2530
|
+
const mixinFn = currentFn;
|
|
2531
|
+
const existingMixin = existingFn;
|
|
2532
|
+
if (existingMixin.marker) {
|
|
2533
|
+
if (mixinFn.marker) {
|
|
2534
|
+
Reflect.set(
|
|
2535
|
+
oView.prototype,
|
|
2536
|
+
combinedKey,
|
|
2537
|
+
_View.processMixinsSameEvent(mixinFn, existingMixin)
|
|
2538
|
+
);
|
|
2539
|
+
} else if (hasOwnProperty(oView.prototype, p)) {
|
|
2540
|
+
Reflect.set(oView.prototype, combinedKey, currentFn);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
_View.wrapMethod(asRecord(oView.prototype), "render", "$renderWrap");
|
|
2547
|
+
Reflect.set(oView.prototype, "$evtObjMap", eventsObject);
|
|
2548
|
+
Reflect.set(oView.prototype, "$globalEvtList", eventsList);
|
|
2549
|
+
Reflect.set(oView.prototype, "$selMap", selectorObject);
|
|
2550
|
+
return makes;
|
|
2551
|
+
}
|
|
2552
|
+
/**
|
|
2553
|
+
* Bind or unbind event delegation for a view instance.
|
|
2554
|
+
* Called from Frame during mount/unmount.
|
|
2555
|
+
*/
|
|
2556
|
+
static delegateEvents(view, destroy = false) {
|
|
2557
|
+
const eventsObject = view.eventObjectMap;
|
|
2558
|
+
const selectorObject = view.eventSelectorMap;
|
|
2559
|
+
const eventsList = view.globalEventList;
|
|
2560
|
+
for (const e in eventsObject) {
|
|
2561
|
+
if (hasOwnProperty(eventsObject, e)) {
|
|
2562
|
+
if (destroy) {
|
|
2563
|
+
EventDelegator.unbind(e, !!selectorObject[e]);
|
|
2564
|
+
} else {
|
|
2565
|
+
EventDelegator.bind(e, !!selectorObject[e]);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
for (const entry of eventsList) {
|
|
2570
|
+
if (destroy) {
|
|
2571
|
+
entry.element.removeEventListener(
|
|
2572
|
+
entry.eventName,
|
|
2573
|
+
entry.boundHandler
|
|
2574
|
+
);
|
|
2575
|
+
} else {
|
|
2576
|
+
const handler = entry.handler;
|
|
2577
|
+
const element = entry.element;
|
|
2578
|
+
const modifiers = entry.modifiers;
|
|
2579
|
+
entry.boundHandler = function(domEvent) {
|
|
2580
|
+
const extendedEvent = domEvent;
|
|
2581
|
+
extendedEvent.eventTarget = element;
|
|
2582
|
+
if (modifiers) {
|
|
2583
|
+
const kbEvent = domEvent;
|
|
2584
|
+
if (modifiers["ctrl"] && !kbEvent.ctrlKey || modifiers["shift"] && !kbEvent.shiftKey || modifiers["alt"] && !kbEvent.altKey || modifiers["meta"] && !kbEvent.metaKey) {
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
funcWithTry(handler, [domEvent], view, noop);
|
|
2589
|
+
};
|
|
2590
|
+
entry.element.addEventListener(
|
|
2591
|
+
entry.eventName,
|
|
2592
|
+
entry.boundHandler
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* Destroy all resources managed by a view.
|
|
2599
|
+
* If lastly=true, destroy ALL resources; otherwise only destroyOnRender ones.
|
|
2600
|
+
*/
|
|
2601
|
+
static destroyAllResources(view, lastly) {
|
|
2602
|
+
const cache = view.resources;
|
|
2603
|
+
for (const p in cache) {
|
|
2604
|
+
if (hasOwnProperty(cache, p)) {
|
|
2605
|
+
const entry = cache[p];
|
|
2606
|
+
if (lastly || entry.destroyOnRender) {
|
|
2607
|
+
_View.destroyResource(cache, p, true);
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2613
|
+
* Process deferred invoke calls on a frame.
|
|
2614
|
+
*/
|
|
2615
|
+
static runInvokes(frame) {
|
|
2616
|
+
const list = frame.invokeList;
|
|
2617
|
+
if (!list) return;
|
|
2618
|
+
while (list.length) {
|
|
2619
|
+
const entry = list.shift();
|
|
2620
|
+
if (entry && !entry.removed) {
|
|
2621
|
+
frame.invoke(entry.name, entry.args);
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
// ============================================================
|
|
2626
|
+
// Static private methods
|
|
2627
|
+
// ============================================================
|
|
2628
|
+
/**
|
|
2629
|
+
* Wrap a method on the prototype to add signature checking and resource cleanup.
|
|
2630
|
+
*/
|
|
2631
|
+
static wrapMethod(proto, fnName, shortKey) {
|
|
2632
|
+
const originalFn = proto[fnName];
|
|
2633
|
+
if (typeof originalFn !== "function") return;
|
|
2634
|
+
const originalAsFn = originalFn;
|
|
2635
|
+
const wrapped = function(...args) {
|
|
2636
|
+
if (this.signature > 0) {
|
|
2637
|
+
this.signature++;
|
|
2638
|
+
this.fire("render");
|
|
2639
|
+
_View.destroyAllResources(this, false);
|
|
2640
|
+
const lookup = asRecord(this);
|
|
2641
|
+
const candidate = lookup[fnName];
|
|
2642
|
+
const instanceFn = typeof candidate === "function" ? candidate : originalAsFn;
|
|
2643
|
+
const fnToCall = instanceFn === wrapped ? originalAsFn : instanceFn;
|
|
2644
|
+
return funcWithTry(fnToCall, args, this, noop);
|
|
2645
|
+
}
|
|
2646
|
+
return void 0;
|
|
2647
|
+
};
|
|
2648
|
+
proto[fnName] = wrapped;
|
|
2649
|
+
proto[shortKey] = wrapped;
|
|
2650
|
+
}
|
|
2651
|
+
/**
|
|
2652
|
+
* When two mixins define the same event method, merge them into
|
|
2653
|
+
* a single function that calls both in sequence.
|
|
2654
|
+
*/
|
|
2655
|
+
static processMixinsSameEvent(additional, exist) {
|
|
2656
|
+
let temp;
|
|
2657
|
+
if (exist.handlerList) {
|
|
2658
|
+
temp = exist;
|
|
2659
|
+
} else {
|
|
2660
|
+
const merged = function(...e) {
|
|
2661
|
+
funcWithTry(merged.handlerList ?? [], e, this, noop);
|
|
2662
|
+
};
|
|
2663
|
+
merged.handlerList = [exist];
|
|
2664
|
+
merged.marker = 1;
|
|
2665
|
+
temp = merged;
|
|
2666
|
+
}
|
|
2667
|
+
temp.handlerList = (temp.handlerList ?? []).concat(
|
|
2668
|
+
additional.handlerList ?? [additional]
|
|
2669
|
+
);
|
|
2670
|
+
return temp;
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* Merge an array of mixin objects into the view prototype.
|
|
2674
|
+
*/
|
|
2675
|
+
static mergeMixins(mixins, viewClass, makes) {
|
|
2676
|
+
const proto = asRecord(viewClass.prototype);
|
|
2677
|
+
const temp = {};
|
|
2678
|
+
for (const node of mixins) {
|
|
2679
|
+
for (const p in node) {
|
|
2680
|
+
if (!hasOwnProperty(node, p)) continue;
|
|
2681
|
+
const fn = node[p];
|
|
2682
|
+
if (typeof fn !== "function") continue;
|
|
2683
|
+
const mixinFn = fn;
|
|
2684
|
+
const exist = temp[p];
|
|
2685
|
+
if (p === "make") {
|
|
2686
|
+
makes.push(mixinFn);
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
if (VIEW_EVENT_METHOD_REGEXP.test(p)) {
|
|
2690
|
+
if (exist) {
|
|
2691
|
+
temp[p] = _View.processMixinsSameEvent(mixinFn, exist);
|
|
2692
|
+
} else {
|
|
2693
|
+
mixinFn.marker = 1;
|
|
2694
|
+
temp[p] = mixinFn;
|
|
2695
|
+
}
|
|
2696
|
+
} else if (!exist) {
|
|
2697
|
+
temp[p] = mixinFn;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
for (const p in temp) {
|
|
2702
|
+
if (!hasOwnProperty(proto, p)) {
|
|
2703
|
+
proto[p] = temp[p];
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Destroy a single resource entry.
|
|
2709
|
+
*/
|
|
2710
|
+
static destroyResource(cache, key, callDestroy, oldEntity) {
|
|
2711
|
+
const entry = cache[key];
|
|
2712
|
+
if (!entry || entry.entity === oldEntity) return void 0;
|
|
2713
|
+
const entity = entry.entity;
|
|
2714
|
+
if (entity && typeof entity === "object") {
|
|
2715
|
+
const destroyFn = entity["destroy"];
|
|
2716
|
+
if (typeof destroyFn === "function" && callDestroy) {
|
|
2717
|
+
funcWithTry(destroyFn, [], entity, noop);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
Reflect.deleteProperty(cache, key);
|
|
2721
|
+
return entity;
|
|
2722
|
+
}
|
|
2723
|
+
// ============================================================
|
|
2724
|
+
// Static: extend and merge
|
|
2725
|
+
// ============================================================
|
|
2726
|
+
/**
|
|
2727
|
+
* Extend View to create a new View subclass.
|
|
2728
|
+
*
|
|
2729
|
+
* Supports:
|
|
2730
|
+
* - props.make: constructor-like init (called with initParams + {node, deep})
|
|
2731
|
+
* - props.mixins: array of mixin objects
|
|
2732
|
+
* - Event method patterns: `'name<click>'` etc.
|
|
2733
|
+
*/
|
|
2734
|
+
static extend(props, statics) {
|
|
2735
|
+
const definedProps = props ?? {};
|
|
2736
|
+
const make = definedProps["make"];
|
|
2737
|
+
const makes = [];
|
|
2738
|
+
if (typeof make === "function") {
|
|
2739
|
+
makes.push(make);
|
|
2740
|
+
}
|
|
2741
|
+
const ParentView = this;
|
|
2742
|
+
const ChildView = class extends ParentView {
|
|
2743
|
+
constructor(nodeId, ownerFrame, initParams, node, mixinCtors) {
|
|
2744
|
+
super(nodeId, ownerFrame, initParams, node, []);
|
|
2745
|
+
const instanceProps = this;
|
|
2746
|
+
for (const key in definedProps) {
|
|
2747
|
+
if (hasOwnProperty(definedProps, key) && key !== "make" && key !== "render") {
|
|
2748
|
+
instanceProps[key] = definedProps[key];
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
this.id = nodeId;
|
|
2752
|
+
this.owner = ownerFrame;
|
|
2753
|
+
this.updater = new Updater(nodeId);
|
|
2754
|
+
const params = [
|
|
2755
|
+
initParams,
|
|
2756
|
+
{
|
|
2757
|
+
node,
|
|
2758
|
+
deep: !this.template
|
|
2759
|
+
}
|
|
2760
|
+
];
|
|
2761
|
+
const concatCtors = makes.concat(mixinCtors || []);
|
|
2762
|
+
if (concatCtors.length) {
|
|
2763
|
+
funcWithTry(concatCtors, params, this, noop);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
for (const key in definedProps) {
|
|
2768
|
+
if (hasOwnProperty(definedProps, key) && key !== "make") {
|
|
2769
|
+
Reflect.set(ChildView.prototype, key, definedProps[key]);
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
if (statics) {
|
|
2773
|
+
for (const key in statics) {
|
|
2774
|
+
if (hasOwnProperty(statics, key)) {
|
|
2775
|
+
Reflect.set(ChildView, key, statics[key]);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
return ChildView;
|
|
2780
|
+
}
|
|
2781
|
+
/**
|
|
2782
|
+
* Merge mixins into View prototype.
|
|
2783
|
+
*/
|
|
2784
|
+
static merge(...mixins) {
|
|
2785
|
+
const existingCtors = this.makes || [];
|
|
2786
|
+
_View.mergeMixins(mixins, this, existingCtors);
|
|
2787
|
+
return this;
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
|
|
2791
|
+
// src/view-registry.ts
|
|
2792
|
+
var viewClassRegistry = {};
|
|
2793
|
+
function getViewClass(path) {
|
|
2794
|
+
return viewClassRegistry[path];
|
|
2795
|
+
}
|
|
2796
|
+
function registerViewClass(viewPath, ViewClass) {
|
|
2797
|
+
const parsed = parseUri(viewPath);
|
|
2798
|
+
const path = parsed.path;
|
|
2799
|
+
if (path) {
|
|
2800
|
+
viewClassRegistry[path] = ViewClass;
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
// src/frame.ts
|
|
2805
|
+
var frameRegistry = /* @__PURE__ */ new Map();
|
|
2806
|
+
var rootFrame;
|
|
2807
|
+
var globalAlter;
|
|
2808
|
+
var MAX_FRAME_POOL = 64;
|
|
2809
|
+
var frameCache = [];
|
|
2810
|
+
var staticEmitter = new EventEmitter();
|
|
2811
|
+
var Frame = class _Frame extends EventEmitter {
|
|
2812
|
+
/** Frame ID (same as owner DOM element ID) */
|
|
2813
|
+
id;
|
|
2814
|
+
/** Parent Frame ID */
|
|
2815
|
+
_parentId = void 0;
|
|
2816
|
+
get parentId() {
|
|
2817
|
+
return this._parentId;
|
|
2818
|
+
}
|
|
2819
|
+
/** Children map: id -> id */
|
|
2820
|
+
childrenMap = {};
|
|
2821
|
+
/** Children count */
|
|
2822
|
+
childrenCount = 0;
|
|
2823
|
+
/** Ready count (children that have fired 'created') */
|
|
2824
|
+
readyCount = 0;
|
|
2825
|
+
/** Set of child frame IDs that have fired 'created' */
|
|
2826
|
+
readyMap = /* @__PURE__ */ new Set();
|
|
2827
|
+
/** View instance */
|
|
2828
|
+
viewInstance;
|
|
2829
|
+
/** Get view instance (read-only) */
|
|
2830
|
+
get view() {
|
|
2831
|
+
return this.viewInstance;
|
|
2832
|
+
}
|
|
2833
|
+
/** Invoke list for deferred method calls */
|
|
2834
|
+
invokeList = [];
|
|
2835
|
+
/** Signature for async operation tracking */
|
|
2836
|
+
signature = 1;
|
|
2837
|
+
/** Whether view has altered */
|
|
2838
|
+
hasAltered = 0;
|
|
2839
|
+
/** Whether view is destroyed */
|
|
2840
|
+
destroyed = 0;
|
|
2841
|
+
/** View path (v-lark attribute value) */
|
|
2842
|
+
viewPath;
|
|
2843
|
+
/** Original template before mount */
|
|
2844
|
+
originalTemplate;
|
|
2845
|
+
/** Hold fire created flag */
|
|
2846
|
+
holdFireCreated = 0;
|
|
2847
|
+
/** Children created flag */
|
|
2848
|
+
childrenCreated = 0;
|
|
2849
|
+
/** Children alter flag */
|
|
2850
|
+
childrenAlter = 0;
|
|
2851
|
+
constructor(id, parentId) {
|
|
2852
|
+
super();
|
|
2853
|
+
this.id = id;
|
|
2854
|
+
if (parentId) {
|
|
2855
|
+
this._parentId = parentId;
|
|
2856
|
+
}
|
|
2857
|
+
frameRegistry.set(id, this);
|
|
2858
|
+
const element = document.getElementById(id);
|
|
2859
|
+
if (element) {
|
|
2860
|
+
element.frame = this;
|
|
2861
|
+
element.frameBound = 1;
|
|
2862
|
+
}
|
|
2863
|
+
_Frame.fire("add", { frame: this });
|
|
2864
|
+
}
|
|
2865
|
+
// ============================================================
|
|
2866
|
+
// Instance methods
|
|
2867
|
+
// ============================================================
|
|
2868
|
+
/**
|
|
2869
|
+
* Mount a view to this frame.
|
|
2870
|
+
*
|
|
2871
|
+
* Complete flow:
|
|
2872
|
+
* 1. Parse viewPath, translate query params from parent
|
|
2873
|
+
* 2. Unmount current view
|
|
2874
|
+
* 3. Load View class (via require or provided ViewClass)
|
|
2875
|
+
* 4. View_Prepare (scan event methods)
|
|
2876
|
+
* 5. Create View instance
|
|
2877
|
+
* 6. View_DelegateEvents (bind DOM events)
|
|
2878
|
+
* 7. Call view.init()
|
|
2879
|
+
* 8. If view has template, call render via Updater
|
|
2880
|
+
* 9. If no template, call endUpdate directly
|
|
2881
|
+
*/
|
|
2882
|
+
mountView(viewPath, viewInitParams) {
|
|
2883
|
+
const node = document.getElementById(this.id);
|
|
2884
|
+
const pId = this.parentId;
|
|
2885
|
+
if (!this.hasAltered && node) {
|
|
2886
|
+
this.hasAltered = 1;
|
|
2887
|
+
this.originalTemplate = node.innerHTML;
|
|
2888
|
+
}
|
|
2889
|
+
this.unmountView();
|
|
2890
|
+
this.destroyed = 0;
|
|
2891
|
+
const parsed = parseUri(viewPath || "");
|
|
2892
|
+
const viewClassName = parsed.path;
|
|
2893
|
+
if (!node || !viewClassName) return;
|
|
2894
|
+
this.viewPath = viewPath;
|
|
2895
|
+
const params = parsed["params"];
|
|
2896
|
+
translateQuery(pId || this.id, viewPath, params);
|
|
2897
|
+
const initParams = { ...params };
|
|
2898
|
+
if (viewInitParams) {
|
|
2899
|
+
assign(initParams, viewInitParams);
|
|
2900
|
+
}
|
|
2901
|
+
const sign = this.signature;
|
|
2902
|
+
const registered = getViewClass(viewClassName);
|
|
2903
|
+
if (registered) {
|
|
2904
|
+
this.doMountView(registered, initParams, node, sign);
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
use(viewClassName, (ViewClass) => {
|
|
2908
|
+
if (sign !== this.signature) return;
|
|
2909
|
+
if (typeof ViewClass === "function") {
|
|
2910
|
+
const ViewClassTyped = ViewClass;
|
|
2911
|
+
registerViewClass(viewClassName, ViewClassTyped);
|
|
2912
|
+
this.doMountView(ViewClassTyped, initParams, node, sign);
|
|
2913
|
+
} else {
|
|
2914
|
+
const error = new Error(`Cannot load view: ${viewClassName}`);
|
|
2915
|
+
const errorHandler = config.error;
|
|
2916
|
+
if (errorHandler) {
|
|
2917
|
+
errorHandler(error);
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
/**
|
|
2923
|
+
* Internal: actually mount the view after class is loaded.
|
|
2924
|
+
*/
|
|
2925
|
+
doMountView(ViewClass, params, node, sign) {
|
|
2926
|
+
if (sign !== this.signature) return;
|
|
2927
|
+
const mixinConstructors = View.prepare(ViewClass);
|
|
2928
|
+
const Constructor = ViewClass;
|
|
2929
|
+
const view = new Constructor(
|
|
2930
|
+
this.id,
|
|
2931
|
+
this,
|
|
2932
|
+
params,
|
|
2933
|
+
node,
|
|
2934
|
+
mixinConstructors
|
|
2935
|
+
);
|
|
2936
|
+
this.viewInstance = view;
|
|
2937
|
+
view.signature = 1;
|
|
2938
|
+
View.delegateEvents(view);
|
|
2939
|
+
const initResult = funcWithTry(
|
|
2940
|
+
view.init,
|
|
2941
|
+
[params, { node, deep: !view.template }],
|
|
2942
|
+
view,
|
|
2943
|
+
noop
|
|
2944
|
+
);
|
|
2945
|
+
const nextSign = ++this.signature;
|
|
2946
|
+
Promise.resolve(initResult).then(() => {
|
|
2947
|
+
if (nextSign !== this.signature) return;
|
|
2948
|
+
if (view.template) {
|
|
2949
|
+
view.render();
|
|
2950
|
+
} else {
|
|
2951
|
+
this.hasAltered = 0;
|
|
2952
|
+
if (!view.endUpdatePendingFlag) {
|
|
2953
|
+
view.endUpdate();
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
});
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Unmount current view.
|
|
2960
|
+
*/
|
|
2961
|
+
unmountView() {
|
|
2962
|
+
const view = this.view;
|
|
2963
|
+
this.invokeList = [];
|
|
2964
|
+
if (!view) return;
|
|
2965
|
+
if (!globalAlter) {
|
|
2966
|
+
globalAlter = { id: this.id };
|
|
2967
|
+
}
|
|
2968
|
+
this.destroyed = 1;
|
|
2969
|
+
this.unmountZone();
|
|
2970
|
+
notifyAlter(this, globalAlter);
|
|
2971
|
+
if (view.signature > 0) {
|
|
2972
|
+
view.fire("destroy", void 0, true, true);
|
|
2973
|
+
}
|
|
2974
|
+
EventDelegator.clearRangeEvents(this.id);
|
|
2975
|
+
delete this["viewInstance"];
|
|
2976
|
+
const node = document.getElementById(this.id);
|
|
2977
|
+
if (node && this.originalTemplate) {
|
|
2978
|
+
node.innerHTML = this.originalTemplate;
|
|
2979
|
+
}
|
|
2980
|
+
globalAlter = void 0;
|
|
2981
|
+
unmark(view);
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Mount a child frame.
|
|
2985
|
+
*/
|
|
2986
|
+
mountFrame(frameId, viewPath, viewInitParams) {
|
|
2987
|
+
notifyAlter(this, { id: frameId });
|
|
2988
|
+
let childFrame = frameRegistry.get(frameId);
|
|
2989
|
+
if (!childFrame) {
|
|
2990
|
+
if (!this.childrenMap[frameId]) {
|
|
2991
|
+
this.childrenCount++;
|
|
2992
|
+
}
|
|
2993
|
+
this.childrenMap[frameId] = frameId;
|
|
2994
|
+
childFrame = frameCache.pop();
|
|
2995
|
+
if (childFrame) {
|
|
2996
|
+
reInitFrame(childFrame, frameId, this.id);
|
|
2997
|
+
} else {
|
|
2998
|
+
childFrame = new _Frame(frameId, this.id);
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
childFrame.mountView(viewPath, viewInitParams);
|
|
3002
|
+
return childFrame;
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* Unmount a child frame.
|
|
3006
|
+
*/
|
|
3007
|
+
unmountFrame(id) {
|
|
3008
|
+
const targetId = id ? this.childrenMap[id] : this.id;
|
|
3009
|
+
const frame = frameRegistry.get(targetId);
|
|
3010
|
+
if (!frame) return;
|
|
3011
|
+
const wasCreated = frame.readyCount > 0;
|
|
3012
|
+
const pId = frame.parentId;
|
|
3013
|
+
frame.unmountView();
|
|
3014
|
+
removeFrame(targetId, wasCreated);
|
|
3015
|
+
reInitFrameForCache(frame);
|
|
3016
|
+
if (frameCache.length < MAX_FRAME_POOL) {
|
|
3017
|
+
frameCache.push(frame);
|
|
3018
|
+
}
|
|
3019
|
+
const parent = frameRegistry.get(pId || "");
|
|
3020
|
+
if (parent && parent.childrenMap[targetId]) {
|
|
3021
|
+
Reflect.deleteProperty(parent.childrenMap, targetId);
|
|
3022
|
+
parent.childrenCount--;
|
|
3023
|
+
notifyCreated(parent);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
/**
|
|
3027
|
+
* Mount all views in a zone.
|
|
3028
|
+
*/
|
|
3029
|
+
mountZone(zoneId) {
|
|
3030
|
+
const targetZone = zoneId || this.id;
|
|
3031
|
+
this.holdFireCreated = 1;
|
|
3032
|
+
const rootEl = document.getElementById(targetZone);
|
|
3033
|
+
if (!rootEl) return;
|
|
3034
|
+
const viewElements = rootEl.querySelectorAll(`[${LARK_VIEW}]`);
|
|
3035
|
+
const frames = [];
|
|
3036
|
+
viewElements.forEach((el) => {
|
|
3037
|
+
if (!(el instanceof HTMLElement)) return;
|
|
3038
|
+
if (htmlElIsBound(el)) return;
|
|
3039
|
+
const elId = ensureElementId(el, "frame_");
|
|
3040
|
+
el.frameBound = 1;
|
|
3041
|
+
const viewPath = getAttribute(el, LARK_VIEW);
|
|
3042
|
+
frames.push([elId, viewPath]);
|
|
3043
|
+
});
|
|
3044
|
+
for (const [frameId, viewPath] of frames) {
|
|
3045
|
+
this.mountFrame(frameId, viewPath);
|
|
3046
|
+
}
|
|
3047
|
+
this.holdFireCreated = 0;
|
|
3048
|
+
notifyCreated(this);
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* Unmount all views in a zone.
|
|
3052
|
+
*/
|
|
3053
|
+
unmountZone(zoneId) {
|
|
3054
|
+
for (const childId in this.childrenMap) {
|
|
3055
|
+
if (hasOwnProperty(this.childrenMap, childId)) {
|
|
3056
|
+
if (!zoneId || childId !== zoneId) {
|
|
3057
|
+
this.unmountFrame(childId);
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
notifyCreated(this);
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Get all child frame IDs.
|
|
3065
|
+
*/
|
|
3066
|
+
children() {
|
|
3067
|
+
const result = [];
|
|
3068
|
+
for (const id in this.childrenMap) {
|
|
3069
|
+
if (hasOwnProperty(this.childrenMap, id)) {
|
|
3070
|
+
result.push(id);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
return result;
|
|
3074
|
+
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Get parent frame at given level.
|
|
3077
|
+
* @param level - How many levels up (default 1)
|
|
3078
|
+
*/
|
|
3079
|
+
parent(level = 1) {
|
|
3080
|
+
let frame = void 0;
|
|
3081
|
+
let currentPid = this.parentId;
|
|
3082
|
+
let n = level >>> 0 || 1;
|
|
3083
|
+
while (currentPid && n--) {
|
|
3084
|
+
frame = frameRegistry.get(currentPid);
|
|
3085
|
+
currentPid = frame?.parentId;
|
|
3086
|
+
}
|
|
3087
|
+
return frame;
|
|
3088
|
+
}
|
|
3089
|
+
/**
|
|
3090
|
+
* Invoke a method on the view.
|
|
3091
|
+
*/
|
|
3092
|
+
invoke(name, args) {
|
|
3093
|
+
let result;
|
|
3094
|
+
const view = this.view;
|
|
3095
|
+
if (view && view.rendered) {
|
|
3096
|
+
const fn = Reflect.get(view, name);
|
|
3097
|
+
if (typeof fn === "function") {
|
|
3098
|
+
result = funcWithTry(fn, args || [], view, noop);
|
|
3099
|
+
}
|
|
3100
|
+
} else {
|
|
3101
|
+
const key = SPLITTER + name;
|
|
3102
|
+
let existingEntry;
|
|
3103
|
+
for (const entry of this.invokeList) {
|
|
3104
|
+
if (entry.key === key) {
|
|
3105
|
+
existingEntry = entry;
|
|
3106
|
+
break;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
if (existingEntry) {
|
|
3110
|
+
existingEntry.removed = args === existingEntry.args;
|
|
3111
|
+
}
|
|
3112
|
+
const newEntry = {
|
|
3113
|
+
name,
|
|
3114
|
+
args: args || [],
|
|
3115
|
+
key
|
|
3116
|
+
};
|
|
3117
|
+
this.invokeList.push(newEntry);
|
|
3118
|
+
}
|
|
3119
|
+
return result;
|
|
3120
|
+
}
|
|
3121
|
+
/**
|
|
3122
|
+
* Type-safe variant of `invoke`.
|
|
3123
|
+
*
|
|
3124
|
+
* `invoke()` accepts any string and any args, which silently hides
|
|
3125
|
+
* mismatched call sites when a method gets renamed. `invokeTyped` carries
|
|
3126
|
+
* the view's method signature through TypeScript so the compiler catches
|
|
3127
|
+
* those mistakes:
|
|
3128
|
+
*
|
|
3129
|
+
* ```ts
|
|
3130
|
+
* type Home = View & { loadData(id: string): Promise<void> };
|
|
3131
|
+
* frame.invokeTyped<Home, "loadData">("loadData", ["user-1"]);
|
|
3132
|
+
* ```
|
|
3133
|
+
*
|
|
3134
|
+
* Behavior is identical to `invoke` at runtime — same defer / direct-call
|
|
3135
|
+
* paths — so it's a drop-in safer overload.
|
|
3136
|
+
*/
|
|
3137
|
+
invokeTyped(name, args) {
|
|
3138
|
+
return this.invoke(name, args);
|
|
3139
|
+
}
|
|
3140
|
+
// ============================================================
|
|
3141
|
+
// Static methods
|
|
3142
|
+
// ============================================================
|
|
3143
|
+
/** Get frame by ID */
|
|
3144
|
+
static get(id) {
|
|
3145
|
+
return frameRegistry.get(id);
|
|
3146
|
+
}
|
|
3147
|
+
/** Get all frames */
|
|
3148
|
+
static getAll() {
|
|
3149
|
+
return frameRegistry;
|
|
3150
|
+
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Returns the existing root frame, or `undefined` if none has been created.
|
|
3153
|
+
* Pure getter — never creates a Frame, never touches the DOM.
|
|
3154
|
+
*
|
|
3155
|
+
* Use `Frame.createRoot(id)` to create the root explicitly during framework
|
|
3156
|
+
* boot. For Micro-Frontend hosts that own multiple independent containers,
|
|
3157
|
+
* use `new Frame(containerId)` directly so each MF mount has its own root.
|
|
3158
|
+
*/
|
|
3159
|
+
static getRoot() {
|
|
3160
|
+
return rootFrame;
|
|
3161
|
+
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Create (or return) the singleton root frame for this app.
|
|
3164
|
+
*
|
|
3165
|
+
* Idempotent: subsequent calls always return the original root regardless
|
|
3166
|
+
* of `rootId` — so passing a different id later is silently ignored.
|
|
3167
|
+
* `Framework.boot()` is the canonical caller; user code rarely needs this.
|
|
3168
|
+
*/
|
|
3169
|
+
static createRoot(rootId) {
|
|
3170
|
+
if (!rootFrame) {
|
|
3171
|
+
rootId = rootId || "root";
|
|
3172
|
+
let rootElement = document.getElementById(rootId);
|
|
3173
|
+
if (!rootElement) {
|
|
3174
|
+
rootElement = document.body;
|
|
3175
|
+
rootElement.id = rootId;
|
|
3176
|
+
}
|
|
3177
|
+
rootFrame = new _Frame(rootId);
|
|
3178
|
+
}
|
|
3179
|
+
return rootFrame;
|
|
3180
|
+
}
|
|
3181
|
+
/** Bind event listener (static) */
|
|
3182
|
+
static on(event, handler) {
|
|
3183
|
+
staticEmitter.on(event, handler);
|
|
3184
|
+
return _Frame;
|
|
3185
|
+
}
|
|
3186
|
+
/** Unbind event listener (static) */
|
|
3187
|
+
static off(event, handler) {
|
|
3188
|
+
staticEmitter.off(event, handler);
|
|
3189
|
+
return _Frame;
|
|
3190
|
+
}
|
|
3191
|
+
/** Fire event (static) */
|
|
3192
|
+
static fire(event, data) {
|
|
3193
|
+
staticEmitter.fire(event, data);
|
|
3194
|
+
}
|
|
3195
|
+
};
|
|
3196
|
+
function htmlElIsBound(element) {
|
|
3197
|
+
return !!element.frameBound;
|
|
3198
|
+
}
|
|
3199
|
+
function removeFrame(id, wasCreated) {
|
|
3200
|
+
const frameInstance = frameRegistry.get(id);
|
|
3201
|
+
if (!frameInstance) return;
|
|
3202
|
+
frameRegistry.delete(id);
|
|
3203
|
+
Frame.fire("remove", { frame: frameInstance, fcc: wasCreated });
|
|
3204
|
+
const element = document.getElementById(id);
|
|
3205
|
+
if (element) {
|
|
3206
|
+
element.frameBound = 0;
|
|
3207
|
+
Reflect.deleteProperty(element, "frame");
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
function notifyCreated(frameInstance) {
|
|
3211
|
+
if (!frameInstance["childrenCreated"] && !frameInstance["holdFireCreated"] && frameInstance["childrenCount"] === frameInstance["readyCount"]) {
|
|
3212
|
+
if (!frameInstance["childrenCreated"]) {
|
|
3213
|
+
frameInstance["childrenCreated"] = 1;
|
|
3214
|
+
frameInstance["childrenAlter"] = 0;
|
|
3215
|
+
frameInstance.fire("created");
|
|
3216
|
+
}
|
|
3217
|
+
const pId = frameInstance.parentId;
|
|
3218
|
+
if (pId) {
|
|
3219
|
+
const parent = frameRegistry.get(pId);
|
|
3220
|
+
if (parent && !parent.readyMap.has(frameInstance.id)) {
|
|
3221
|
+
parent.readyMap.add(frameInstance.id);
|
|
3222
|
+
parent.readyCount++;
|
|
3223
|
+
notifyCreated(parent);
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
function notifyAlter(frameInstance, data) {
|
|
3229
|
+
if (!frameInstance["childrenAlter"] && frameInstance["childrenCreated"]) {
|
|
3230
|
+
frameInstance["childrenCreated"] = 0;
|
|
3231
|
+
frameInstance["childrenAlter"] = 1;
|
|
3232
|
+
frameInstance.fire("alter", data);
|
|
3233
|
+
const pId = frameInstance.parentId;
|
|
3234
|
+
if (pId) {
|
|
3235
|
+
const parent = frameRegistry.get(pId);
|
|
3236
|
+
if (parent && parent.readyMap.has(frameInstance.id)) {
|
|
3237
|
+
parent.readyCount--;
|
|
3238
|
+
parent.readyMap.delete(frameInstance.id);
|
|
3239
|
+
notifyAlter(parent, data);
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
function reInitFrame(frame, id, parentId) {
|
|
3245
|
+
Reflect.set(frame, "id", id);
|
|
3246
|
+
frame["_parentId"] = parentId;
|
|
3247
|
+
frame["childrenMap"] = {};
|
|
3248
|
+
frame["childrenCount"] = 0;
|
|
3249
|
+
frame["readyCount"] = 0;
|
|
3250
|
+
frame["signature"] = 1;
|
|
3251
|
+
frame["readyMap"] = /* @__PURE__ */ new Set();
|
|
3252
|
+
frame["invokeList"] = [];
|
|
3253
|
+
frameRegistry.set(id, frame);
|
|
3254
|
+
}
|
|
3255
|
+
function reInitFrameForCache(frame) {
|
|
3256
|
+
Reflect.set(frame, "id", "");
|
|
3257
|
+
frame["_parentId"] = void 0;
|
|
3258
|
+
frame["childrenMap"] = {};
|
|
3259
|
+
frame["readyMap"] = /* @__PURE__ */ new Set();
|
|
3260
|
+
}
|
|
3261
|
+
function translateQuery(pId, src, params) {
|
|
3262
|
+
const parentFrame = frameRegistry.get(pId);
|
|
3263
|
+
const parentView = parentFrame?.view;
|
|
3264
|
+
if (!parentView) return;
|
|
3265
|
+
const parentRefData = parentView.updater.refData;
|
|
3266
|
+
if (!parentRefData) return;
|
|
3267
|
+
if (src.indexOf(SPLITTER) > 0) {
|
|
3268
|
+
translateData(parentRefData, params);
|
|
3269
|
+
const paramsRec = params;
|
|
3270
|
+
const splitterValue = paramsRec[SPLITTER];
|
|
3271
|
+
if (splitterValue && typeof splitterValue === "object") {
|
|
3272
|
+
assign(params, splitterValue);
|
|
3273
|
+
Reflect.deleteProperty(params, SPLITTER);
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
// src/devtool.ts
|
|
3279
|
+
var FrameDevtoolBridge = {
|
|
3280
|
+
MSG_PING: "LARK_DEVTOOL_PING",
|
|
3281
|
+
MSG_PONG: "LARK_DEVTOOL_PONG",
|
|
3282
|
+
MSG_REQUEST_TREE: "LARK_DEVTOOL_REQUEST_TREE",
|
|
3283
|
+
MSG_TREE: "LARK_DEVTOOL_TREE",
|
|
3284
|
+
MSG_TREE_DELTA: "LARK_DEVTOOL_TREE_DELTA"
|
|
3285
|
+
};
|
|
3286
|
+
function serializeView(view) {
|
|
3287
|
+
const evtMap = view.eventObjectMap;
|
|
3288
|
+
const eventMethodKeys = evtMap ? Object.keys(evtMap) : [];
|
|
3289
|
+
const resourceKeys = view.resources ? Object.keys(view.resources) : [];
|
|
3290
|
+
const hasAssign = typeof view["assign"] === "function";
|
|
3291
|
+
let updaterData = null;
|
|
3292
|
+
try {
|
|
3293
|
+
const ref = view.updater?.refData;
|
|
3294
|
+
if (ref && typeof ref === "object") {
|
|
3295
|
+
updaterData = {};
|
|
3296
|
+
for (const k of Object.keys(ref)) {
|
|
3297
|
+
const v = ref[k];
|
|
3298
|
+
updaterData[k] = v === null || typeof v !== "object" ? v : `[${typeof v}]`;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
} catch {
|
|
3302
|
+
}
|
|
3303
|
+
return {
|
|
3304
|
+
id: view.id,
|
|
3305
|
+
rendered: !!view.rendered,
|
|
3306
|
+
signature: view.signature,
|
|
3307
|
+
observedStateKeys: view.observedStateKeys ?? null,
|
|
3308
|
+
locationObserved: {
|
|
3309
|
+
flag: view.locationObserved.flag,
|
|
3310
|
+
keys: view.locationObserved.keys,
|
|
3311
|
+
observePath: view.locationObserved.observePath
|
|
3312
|
+
},
|
|
3313
|
+
hasTemplate: !!view.template,
|
|
3314
|
+
eventMethodKeys,
|
|
3315
|
+
resourceKeys,
|
|
3316
|
+
hasAssign,
|
|
3317
|
+
updaterData
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
function serializeFrame(frameId) {
|
|
3321
|
+
const frame = Frame.get(frameId);
|
|
3322
|
+
if (!frame) return null;
|
|
3323
|
+
const view = frame.view;
|
|
3324
|
+
const children = [];
|
|
3325
|
+
for (const childId of frame.children()) {
|
|
3326
|
+
const childNode = serializeFrame(childId);
|
|
3327
|
+
if (childNode) {
|
|
3328
|
+
children.push(childNode);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
return {
|
|
3332
|
+
id: frame.id,
|
|
3333
|
+
parentId: frame.parentId ?? null,
|
|
3334
|
+
viewPath: frame.viewPath ?? null,
|
|
3335
|
+
childrenCount: frame.childrenCount,
|
|
3336
|
+
readyCount: frame.readyCount,
|
|
3337
|
+
childrenCreated: frame.childrenCreated,
|
|
3338
|
+
childrenAlter: frame.childrenAlter,
|
|
3339
|
+
destroyed: frame.destroyed,
|
|
3340
|
+
view: view ? serializeView(view) : null,
|
|
3341
|
+
children
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
function serializeFrameTree() {
|
|
3345
|
+
const root = Frame.getRoot();
|
|
3346
|
+
if (!root) {
|
|
3347
|
+
return { root: null, totalFrames: 0, timestamp: Date.now(), rootId: "" };
|
|
3348
|
+
}
|
|
3349
|
+
const rootNode = serializeFrame(root.id);
|
|
3350
|
+
let totalFrames = 0;
|
|
3351
|
+
const countFrames = (node) => {
|
|
3352
|
+
if (!node) return;
|
|
3353
|
+
totalFrames++;
|
|
3354
|
+
for (const child of node.children) {
|
|
3355
|
+
countFrames(child);
|
|
3356
|
+
}
|
|
3357
|
+
};
|
|
3358
|
+
countFrames(rootNode);
|
|
3359
|
+
return {
|
|
3360
|
+
root: rootNode,
|
|
3361
|
+
totalFrames,
|
|
3362
|
+
timestamp: Date.now(),
|
|
3363
|
+
rootId: root.id
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
var bridgeInstalled = false;
|
|
3367
|
+
var lastTreeJson = "";
|
|
3368
|
+
function installFrameDevtoolBridge() {
|
|
3369
|
+
if (bridgeInstalled) return;
|
|
3370
|
+
if (typeof window === "undefined") return;
|
|
3371
|
+
bridgeInstalled = true;
|
|
3372
|
+
window.addEventListener("message", (event) => {
|
|
3373
|
+
const data = event.data;
|
|
3374
|
+
if (!data || typeof data !== "object") return;
|
|
3375
|
+
const type = data.type;
|
|
3376
|
+
if (type === FrameDevtoolBridge.MSG_PING) {
|
|
3377
|
+
const source = event.source;
|
|
3378
|
+
if (source) {
|
|
3379
|
+
source.postMessage(
|
|
3380
|
+
{ type: FrameDevtoolBridge.MSG_PONG },
|
|
3381
|
+
{ targetOrigin: "*" }
|
|
3382
|
+
);
|
|
3383
|
+
}
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
if (type === FrameDevtoolBridge.MSG_REQUEST_TREE) {
|
|
3387
|
+
const tree = serializeFrameTree();
|
|
3388
|
+
const source = event.source;
|
|
3389
|
+
if (source) {
|
|
3390
|
+
source.postMessage(
|
|
3391
|
+
{ type: FrameDevtoolBridge.MSG_TREE, data: tree },
|
|
3392
|
+
{ targetOrigin: "*" }
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
Frame.on("add", () => {
|
|
3398
|
+
pushTreeUpdate();
|
|
3399
|
+
});
|
|
3400
|
+
Frame.on("remove", () => {
|
|
3401
|
+
pushTreeUpdate();
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
3404
|
+
function pushTreeUpdate() {
|
|
3405
|
+
if (window === window.parent) return;
|
|
3406
|
+
const tree = serializeFrameTree();
|
|
3407
|
+
const treeJson = JSON.stringify(tree);
|
|
3408
|
+
if (treeJson !== lastTreeJson) {
|
|
3409
|
+
lastTreeJson = treeJson;
|
|
3410
|
+
window.parent.postMessage(
|
|
3411
|
+
{ type: FrameDevtoolBridge.MSG_TREE_DELTA, data: tree },
|
|
3412
|
+
"*"
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3417
|
+
0 && (module.exports = {
|
|
3418
|
+
FrameDevtoolBridge,
|
|
3419
|
+
installFrameDevtoolBridge,
|
|
3420
|
+
serializeFrameTree
|
|
3421
|
+
});
|