@adaas/are-html 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.mts +194 -10
- package/dist/browser/index.mjs +696 -245
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/{AreBinding.attribute-doUvtOjc.d.mts → AreBinding.attribute-BWzEIw6H.d.mts} +45 -0
- package/dist/node/{AreBinding.attribute-Bm5LlOyE.d.ts → AreBinding.attribute-GpT-5Qmf.d.ts} +45 -0
- package/dist/node/attributes/AreBinding.attribute.d.mts +1 -1
- package/dist/node/attributes/AreBinding.attribute.d.ts +1 -1
- package/dist/node/attributes/AreDirective.attribute.d.mts +1 -1
- package/dist/node/attributes/AreDirective.attribute.d.ts +1 -1
- package/dist/node/attributes/AreEvent.attribute.d.mts +1 -1
- package/dist/node/attributes/AreEvent.attribute.d.ts +1 -1
- package/dist/node/attributes/AreStatic.attribute.d.mts +1 -1
- package/dist/node/attributes/AreStatic.attribute.d.ts +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.d.mts +18 -1
- package/dist/node/directives/AreDirectiveFor.directive.d.ts +18 -1
- package/dist/node/directives/AreDirectiveFor.directive.js +57 -9
- package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.mjs +57 -9
- package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.mts +18 -2
- package/dist/node/directives/AreDirectiveIf.directive.d.ts +18 -2
- package/dist/node/directives/AreDirectiveIf.directive.js +29 -6
- package/dist/node/directives/AreDirectiveIf.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.mjs +29 -6
- package/dist/node/directives/AreDirectiveIf.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveShow.directive.d.mts +1 -1
- package/dist/node/directives/AreDirectiveShow.directive.d.ts +1 -1
- package/dist/node/engine/AreHTML.compiler.d.mts +4 -2
- package/dist/node/engine/AreHTML.compiler.d.ts +4 -2
- package/dist/node/engine/AreHTML.compiler.js +11 -4
- package/dist/node/engine/AreHTML.compiler.js.map +1 -1
- package/dist/node/engine/AreHTML.compiler.mjs +11 -4
- package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
- package/dist/node/engine/AreHTML.constants.d.mts +33 -1
- package/dist/node/engine/AreHTML.constants.d.ts +33 -1
- package/dist/node/engine/AreHTML.constants.js +166 -0
- package/dist/node/engine/AreHTML.constants.js.map +1 -1
- package/dist/node/engine/AreHTML.constants.mjs +165 -1
- package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
- package/dist/node/engine/AreHTML.context.d.mts +66 -0
- package/dist/node/engine/AreHTML.context.d.ts +66 -0
- package/dist/node/engine/AreHTML.context.js +98 -0
- package/dist/node/engine/AreHTML.context.js.map +1 -1
- package/dist/node/engine/AreHTML.context.mjs +98 -0
- package/dist/node/engine/AreHTML.context.mjs.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
- package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
- package/dist/node/engine/AreHTML.interpreter.js +66 -10
- package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.mjs +66 -10
- package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.d.mts +1 -8
- package/dist/node/engine/AreHTML.lifecycle.d.ts +1 -8
- package/dist/node/engine/AreHTML.lifecycle.js +29 -44
- package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.mjs +29 -44
- package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.d.mts +1 -1
- package/dist/node/engine/AreHTML.tokenizer.d.ts +1 -1
- package/dist/node/engine/AreHTML.tokenizer.js +7 -1
- package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs +7 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
- package/dist/node/engine/AreHTML.transformer.d.mts +1 -1
- package/dist/node/engine/AreHTML.transformer.d.ts +1 -1
- package/dist/node/index.d.mts +4 -3
- package/dist/node/index.d.ts +4 -3
- package/dist/node/index.js +7 -0
- package/dist/node/index.mjs +1 -0
- package/dist/node/instructions/AddStaticHTML.instruction.d.mts +8 -0
- package/dist/node/instructions/AddStaticHTML.instruction.d.ts +8 -0
- package/dist/node/instructions/AddStaticHTML.instruction.js +31 -0
- package/dist/node/instructions/AddStaticHTML.instruction.js.map +1 -0
- package/dist/node/instructions/AddStaticHTML.instruction.mjs +24 -0
- package/dist/node/instructions/AddStaticHTML.instruction.mjs.map +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.js +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
- package/dist/node/instructions/AreHTML.instructions.constants.mjs +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
- package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
- package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
- package/dist/node/lib/AreDirective/AreDirective.component.d.mts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.component.d.ts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.types.d.mts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.types.d.ts +1 -1
- package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.mts +1 -1
- package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.ts +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.mts +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.ts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.mts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.ts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +51 -0
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +51 -0
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
- package/dist/node/nodes/AreComment.d.mts +1 -1
- package/dist/node/nodes/AreComment.d.ts +1 -1
- package/dist/node/nodes/AreComponent.d.mts +1 -1
- package/dist/node/nodes/AreComponent.d.ts +1 -1
- package/dist/node/nodes/AreInterpolation.d.mts +1 -1
- package/dist/node/nodes/AreInterpolation.d.ts +1 -1
- package/dist/node/nodes/AreRoot.d.mts +1 -1
- package/dist/node/nodes/AreRoot.d.ts +1 -1
- package/dist/node/nodes/AreText.d.mts +1 -1
- package/dist/node/nodes/AreText.d.ts +1 -1
- package/examples/dashboard/concept.ts +1 -1
- package/examples/dashboard/dist/index.html +1 -1
- package/examples/dashboard/dist/{mqh9ryml-xat335.js → mqiw5sqa-ypckmj.js} +403 -57
- package/examples/for-perf/dist/index.html +1 -1
- package/examples/for-perf/dist/{mqh9ryfo-6a8d0o.js → mqp8i2py-vltsx0.js} +3030 -2474
- package/examples/lazy-loading/README.md +76 -0
- package/examples/lazy-loading/concept.ts +55 -0
- package/examples/lazy-loading/containers/UI.container.ts +215 -0
- package/examples/lazy-loading/dist/app.js +3803 -0
- package/examples/{for-perf/dist/mqh9ryfq-4pf5cv.js → lazy-loading/dist/chunks/chunk-6K72IBO4.js} +2708 -5476
- package/examples/lazy-loading/dist/index.html +36 -0
- package/examples/lazy-loading/dist/lazy/about-page.js +59 -0
- package/examples/lazy-loading/dist/lazy/reports-page.js +65 -0
- package/examples/lazy-loading/dist/lazy/settings-page.js +54 -0
- package/examples/lazy-loading/public/index.html +36 -0
- package/examples/lazy-loading/src/components/AppShell.component.ts +44 -0
- package/examples/lazy-loading/src/components/HomePage.component.ts +59 -0
- package/examples/lazy-loading/src/components/LazyOutlet.component.ts +108 -0
- package/examples/lazy-loading/src/components/NavBar.component.ts +98 -0
- package/examples/lazy-loading/src/concept.ts +116 -0
- package/examples/lazy-loading/src/lazy/AboutPage.component.ts +54 -0
- package/examples/lazy-loading/src/lazy/ReportsPage.component.ts +56 -0
- package/examples/lazy-loading/src/lazy/SettingsPage.component.ts +45 -0
- package/examples/lazy-loading/src/runtime/ComponentManifest.fragment.ts +61 -0
- package/examples/lazy-loading/src/runtime/LazyComponentResolver.fragment.ts +77 -0
- package/examples/os-desktop/README.md +91 -0
- package/examples/os-desktop/concept.ts +54 -0
- package/examples/os-desktop/containers/OS.container.ts +198 -0
- package/examples/os-desktop/containers/apps/AppBackend.ts +29 -0
- package/examples/os-desktop/containers/apps/GanttApp.backend.ts +56 -0
- package/examples/os-desktop/containers/apps/MarketingApp.backend.ts +68 -0
- package/examples/os-desktop/dist/app.js +4410 -0
- package/examples/os-desktop/dist/apps/gantt/app.js +271 -0
- package/examples/os-desktop/dist/apps/marketing/app.js +346 -0
- package/examples/{for-perf/dist/mqh9ryde-m243t8.js → os-desktop/dist/chunks/chunk-6K72IBO4.js} +2708 -5476
- package/examples/os-desktop/dist/chunks/chunk-EIIGUL6N.js +30 -0
- package/examples/os-desktop/dist/chunks/chunk-WOH7L5UR.js +30 -0
- package/examples/os-desktop/dist/index.html +33 -0
- package/examples/os-desktop/public/index.html +33 -0
- package/examples/os-desktop/src/apps/gantt/GanttApp.component.ts +41 -0
- package/examples/os-desktop/src/apps/gantt/GanttChart.component.ts +126 -0
- package/examples/os-desktop/src/apps/gantt/GanttStore.ts +47 -0
- package/examples/os-desktop/src/apps/gantt/GanttToolbar.component.ts +73 -0
- package/examples/os-desktop/src/apps/gantt/index.ts +13 -0
- package/examples/os-desktop/src/apps/marketing/MarketingApp.component.ts +53 -0
- package/examples/os-desktop/src/apps/marketing/MarketingStore.ts +34 -0
- package/examples/os-desktop/src/apps/marketing/PostEditor.component.ts +153 -0
- package/examples/os-desktop/src/apps/marketing/PostPreview.component.ts +110 -0
- package/examples/os-desktop/src/apps/marketing/index.ts +16 -0
- package/examples/os-desktop/src/concept.ts +126 -0
- package/examples/os-desktop/src/os/AppStage.component.ts +112 -0
- package/examples/os-desktop/src/os/AppWindow.component.ts +102 -0
- package/examples/os-desktop/src/os/Desktop.component.ts +106 -0
- package/examples/os-desktop/src/os/Dock.component.ts +174 -0
- package/examples/os-desktop/src/os/Hud.component.ts +83 -0
- package/examples/os-desktop/src/os/Launchpad.component.ts +191 -0
- package/examples/os-desktop/src/os/MenuBar.component.ts +156 -0
- package/examples/os-desktop/src/runtime/AppComponentResolver.fragment.ts +121 -0
- package/examples/os-desktop/src/runtime/AppRegistry.fragment.ts +104 -0
- package/examples/os-desktop/src/signals/MouseState.signal.ts +34 -0
- package/examples/os-desktop/src/signals/OSRoute.signal.ts +37 -0
- package/examples/os-desktop/src/signals/SelectionState.signal.ts +34 -0
- package/examples/signal-routing/dist/index.html +1 -1
- package/examples/signal-routing/dist/{mqh9ryc9-dkcbkx.js → mqp8hgce-4d6rh0.js} +3196 -2640
- package/package.json +13 -9
- package/src/directives/AreDirectiveFor.directive.ts +99 -16
- package/src/directives/AreDirectiveIf.directive.ts +33 -4
- package/src/engine/AreHTML.compiler.ts +25 -2
- package/src/engine/AreHTML.constants.ts +142 -0
- package/src/engine/AreHTML.context.ts +112 -0
- package/src/engine/AreHTML.interpreter.ts +114 -13
- package/src/engine/AreHTML.lifecycle.ts +81 -74
- package/src/engine/AreHTML.tokenizer.ts +30 -1
- package/src/index.ts +1 -0
- package/src/instructions/AddStaticHTML.instruction.ts +23 -0
- package/src/instructions/AreHTML.instructions.constants.ts +1 -0
- package/src/instructions/AreHTML.instructions.types.ts +9 -0
- package/src/lib/AreHTMLNode/AreHTMLNode.ts +74 -0
- package/src/lib/AreRoot/AreRoot.component.ts +3 -3
- package/tests/PropPropagation.test.ts +181 -0
- package/tests/StaticIsland.test.ts +115 -0
- package/tests/jest.setup.ts +11 -0
package/dist/browser/index.mjs
CHANGED
|
@@ -208,6 +208,7 @@ var AreHTMLInstructions = {
|
|
|
208
208
|
AddListener: "_AreHTML_AddListener",
|
|
209
209
|
AddInterpolation: "_AreHTML_AddInterpolation",
|
|
210
210
|
AddComment: "_AreHTML_AddComment",
|
|
211
|
+
AddStaticHTML: "_AreHTML_AddStaticHTML",
|
|
211
212
|
HideElement: "_AreHTML_HideElement"
|
|
212
213
|
};
|
|
213
214
|
|
|
@@ -272,6 +273,279 @@ var AreSchedulerHelper = class {
|
|
|
272
273
|
};
|
|
273
274
|
/** FIFO queue of callbacks waiting for their posted macrotask to fire. */
|
|
274
275
|
AreSchedulerHelper._queue = [];
|
|
276
|
+
var AreHTMLEngineContext = class extends AreContext {
|
|
277
|
+
constructor(props) {
|
|
278
|
+
super(props.container?.body.innerHTML || props.source || "");
|
|
279
|
+
/**
|
|
280
|
+
* Index structure mapping:
|
|
281
|
+
*
|
|
282
|
+
* Node -> Group ID -> Element
|
|
283
|
+
* -----------------------------------------------------------------------------------
|
|
284
|
+
* | - Attribute | group: string | Node
|
|
285
|
+
* | - Directive (e.g. for) | | Node
|
|
286
|
+
*/
|
|
287
|
+
this.index = {
|
|
288
|
+
/**
|
|
289
|
+
* 1 AreNode = 1 Dom Node
|
|
290
|
+
*
|
|
291
|
+
* uses ASEID
|
|
292
|
+
*/
|
|
293
|
+
nodeToHostElements: /* @__PURE__ */ new Map(),
|
|
294
|
+
/**
|
|
295
|
+
* 1 Group Instruction = MANY Dom Nodes (e.g. for loop)
|
|
296
|
+
*
|
|
297
|
+
* uses ASEID
|
|
298
|
+
*/
|
|
299
|
+
groupToElements: /* @__PURE__ */ new Map(),
|
|
300
|
+
/**
|
|
301
|
+
* 1 Dom Node = 1 Instruction
|
|
302
|
+
*
|
|
303
|
+
* uses ASEID
|
|
304
|
+
*/
|
|
305
|
+
elementToInstruction: /* @__PURE__ */ new WeakMap(),
|
|
306
|
+
/**
|
|
307
|
+
* 1 Instruction = 1 Dom Node (for CreateElement instructions, for example)
|
|
308
|
+
*
|
|
309
|
+
* uses ASEID
|
|
310
|
+
*/
|
|
311
|
+
instructionToElement: /* @__PURE__ */ new Map(),
|
|
312
|
+
/**
|
|
313
|
+
* Event listeners attached to elements, used for proper cleanup when reverting instructions. Maps a DOM element to a map of event names and their corresponding listeners, allowing the engine to track which listeners are attached to which elements and remove them when necessary (e.g., when an instruction is reverted).
|
|
314
|
+
*/
|
|
315
|
+
elementListeners: /* @__PURE__ */ new WeakMap()
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* Parsed-fragment cache for static islands (see AddStaticHTMLInstruction).
|
|
319
|
+
*
|
|
320
|
+
* Keyed by `hostTag\u0000markup`, each entry holds a `DocumentFragment` whose
|
|
321
|
+
* children were parsed by the browser exactly once — in the *correct element
|
|
322
|
+
* context* (the host tag), so table fragments (`<tr>`, `<td>`, …) and other
|
|
323
|
+
* context-sensitive content parse correctly. Repeated static islands with
|
|
324
|
+
* identical markup (e.g. list rows, reused components) clone the pre-parsed
|
|
325
|
+
* fragment instead of re-parsing the HTML string on every mount — turning an
|
|
326
|
+
* O(parse) operation into an O(clone) one.
|
|
327
|
+
*/
|
|
328
|
+
this._staticFragmentCache = /* @__PURE__ */ new Map();
|
|
329
|
+
/**
|
|
330
|
+
* Live-DOM attachments deferred while a mount pass is batching.
|
|
331
|
+
*
|
|
332
|
+
* A freshly-mounted subtree is built inside a *detached* root element, so
|
|
333
|
+
* every descendant `appendChild`/`insertBefore` happens off-document and
|
|
334
|
+
* triggers zero layout/paint invalidation. The single mutation that actually
|
|
335
|
+
* connects the built subtree to the live document is deferred and collected
|
|
336
|
+
* here, then flushed once when the batch closes — collapsing O(nodes) reflows
|
|
337
|
+
* into O(1) per mount root.
|
|
338
|
+
*/
|
|
339
|
+
this._pendingAttachments = [];
|
|
340
|
+
/**
|
|
341
|
+
* Depth of the currently open batching scopes. Re-entrant so that nested
|
|
342
|
+
* `beginBatch`/`endBatch` pairs flush exactly once, when the outermost scope
|
|
343
|
+
* closes.
|
|
344
|
+
*/
|
|
345
|
+
this._batchDepth = 0;
|
|
346
|
+
this._container = props.container;
|
|
347
|
+
}
|
|
348
|
+
get container() {
|
|
349
|
+
return this._container;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* `true` while a synchronous mount pass is batching live-DOM attachments.
|
|
353
|
+
* Interpreter handlers consult this to decide whether to attach an element
|
|
354
|
+
* immediately or hand the attachment to {@link deferAttach}.
|
|
355
|
+
*/
|
|
356
|
+
get isBatching() {
|
|
357
|
+
return this._batchDepth > 0;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Opens a batching scope. Re-entrant: only the outermost matching
|
|
361
|
+
* {@link endBatch} flushes the deferred attachments, so a single mount pass
|
|
362
|
+
* connects its built subtree to the live DOM exactly once.
|
|
363
|
+
*/
|
|
364
|
+
beginBatch() {
|
|
365
|
+
this._batchDepth++;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Registers a live-DOM attachment to run when the current batch flushes. If
|
|
369
|
+
* no batch is active the attachment runs immediately, preserving the original
|
|
370
|
+
* synchronous behaviour for updates that mount outside a batch.
|
|
371
|
+
*
|
|
372
|
+
* @param attach the DOM mutation that connects a built subtree to the document
|
|
373
|
+
*/
|
|
374
|
+
deferAttach(attach) {
|
|
375
|
+
if (this._batchDepth > 0) {
|
|
376
|
+
this._pendingAttachments.push(attach);
|
|
377
|
+
} else {
|
|
378
|
+
attach();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Closes a batching scope. When the outermost scope closes, every deferred
|
|
383
|
+
* attachment runs in registration (document) order, connecting the built
|
|
384
|
+
* subtrees to the live DOM in a single pass.
|
|
385
|
+
*/
|
|
386
|
+
endBatch() {
|
|
387
|
+
if (this._batchDepth === 0) return;
|
|
388
|
+
this._batchDepth--;
|
|
389
|
+
if (this._batchDepth > 0) return;
|
|
390
|
+
const pending = this._pendingAttachments;
|
|
391
|
+
this._pendingAttachments = [];
|
|
392
|
+
for (let i = 0; i < pending.length; i++) {
|
|
393
|
+
pending[i]();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Returns a `DocumentFragment` containing the parsed form of `html`, parsed
|
|
398
|
+
* once in the context of `hostTag` (so context-sensitive content such as
|
|
399
|
+
* table rows/cells parses correctly) and cached thereafter. Callers should
|
|
400
|
+
* `cloneNode(true)` the returned fragment rather than mutating it, so the
|
|
401
|
+
* cache stays reusable.
|
|
402
|
+
*
|
|
403
|
+
* @param hostTag the tag name of the element the markup will be injected into
|
|
404
|
+
* @param html verbatim static-island inner markup
|
|
405
|
+
*/
|
|
406
|
+
getStaticFragment(hostTag, html) {
|
|
407
|
+
const key = `${hostTag}\0${html}`;
|
|
408
|
+
let fragment = this._staticFragmentCache.get(key);
|
|
409
|
+
if (!fragment) {
|
|
410
|
+
const container = this._container.createElement(hostTag);
|
|
411
|
+
container.innerHTML = html;
|
|
412
|
+
fragment = this._container.createDocumentFragment();
|
|
413
|
+
while (container.firstChild) {
|
|
414
|
+
fragment.appendChild(container.firstChild);
|
|
415
|
+
}
|
|
416
|
+
this._staticFragmentCache.set(key, fragment);
|
|
417
|
+
}
|
|
418
|
+
return fragment;
|
|
419
|
+
}
|
|
420
|
+
getNodeElement(node) {
|
|
421
|
+
if (typeof node === "string") {
|
|
422
|
+
return this.index.nodeToHostElements.get(node);
|
|
423
|
+
} else {
|
|
424
|
+
return this.index.nodeToHostElements.get(node.aseid.toString());
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Associates a DOM element with a given instruction and its owner node. This method updates the context's index to map the instruction's ASEID to the provided DOM element, and also maps the element back to the instruction's ASEID for reverse lookup. If the instruction has an owner node, it also maps the node's ASEID to the element. Additionally, if the instruction belongs to a group, it adds the element to the set of elements associated with that group. This indexing allows the engine to efficiently manage and update DOM elements based on instructions and their corresponding nodes, enabling dynamic rendering and interaction in response to application state changes.
|
|
429
|
+
*
|
|
430
|
+
* @param instruction
|
|
431
|
+
* @param element
|
|
432
|
+
*/
|
|
433
|
+
setInstructionElement(instruction, element) {
|
|
434
|
+
const node = instruction.owner;
|
|
435
|
+
this.index.instructionToElement.set(instruction.aseid.toString(), element);
|
|
436
|
+
this.index.elementToInstruction.set(element, instruction.aseid.toString());
|
|
437
|
+
if (node && instruction instanceof AreDeclaration) {
|
|
438
|
+
this.index.nodeToHostElements.set(node.aseid.toString(), element);
|
|
439
|
+
}
|
|
440
|
+
if (instruction.group) {
|
|
441
|
+
const groupId = instruction.group;
|
|
442
|
+
if (!this.index.groupToElements.has(groupId)) {
|
|
443
|
+
this.index.groupToElements.set(groupId, /* @__PURE__ */ new Set());
|
|
444
|
+
}
|
|
445
|
+
this.index.groupToElements.get(groupId).add(element);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
getElementByInstruction(instruction) {
|
|
449
|
+
if (typeof instruction === "string") {
|
|
450
|
+
return this.index.instructionToElement.get(instruction);
|
|
451
|
+
} else {
|
|
452
|
+
return this.index.instructionToElement.get(instruction.aseid.toString());
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Removes the association between a given instruction and its corresponding DOM element. This method looks up the instruction's ASEID to find the associated DOM element, and if found, it deletes the mapping from both instructionToElement and elementToInstruction. If the instruction has an owner node, it also removes the mapping from nodeToHostElements. Additionally, if the instruction belongs to a group, it removes the element from the set of elements associated with that group, and if the group has no more elements, it deletes the group from the index. This cleanup is essential for maintaining an accurate and efficient mapping of instructions to DOM elements, especially when instructions are reverted or when nodes are removed from the DOM.
|
|
457
|
+
*
|
|
458
|
+
* @param instruction
|
|
459
|
+
*/
|
|
460
|
+
removeInstructionElement(instruction) {
|
|
461
|
+
const element = this.index.instructionToElement.get(instruction.aseid.toString());
|
|
462
|
+
if (element) {
|
|
463
|
+
this.index.instructionToElement.delete(instruction.aseid.toString());
|
|
464
|
+
this.index.elementToInstruction.delete(element);
|
|
465
|
+
const node = instruction.owner;
|
|
466
|
+
if (node && instruction instanceof AreDeclaration) {
|
|
467
|
+
this.index.nodeToHostElements.delete(node.aseid.toString());
|
|
468
|
+
}
|
|
469
|
+
if (instruction.group) {
|
|
470
|
+
const groupId = instruction.group;
|
|
471
|
+
const groupElements = this.index.groupToElements.get(groupId);
|
|
472
|
+
if (groupElements) {
|
|
473
|
+
groupElements.delete(element);
|
|
474
|
+
if (groupElements.size === 0) {
|
|
475
|
+
this.index.groupToElements.delete(groupId);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
getElementsByGroup(instruction) {
|
|
482
|
+
if (typeof instruction === "string") {
|
|
483
|
+
return this.index.groupToElements.get(instruction);
|
|
484
|
+
} else {
|
|
485
|
+
return this.index.groupToElements.get(instruction.aseid.toString());
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Adds an event listener to a specific DOM element and keeps track of it in the context's index for proper cleanup later. This method takes a DOM element, an event name, and a listener function or object, and stores this information in the elementListeners map. This allows the engine to efficiently manage event listeners attached to dynamically created elements, ensuring that they can be removed when the associated instructions are reverted or when nodes are removed from the DOM, preventing memory leaks and unintended behavior.
|
|
490
|
+
*
|
|
491
|
+
* @param element
|
|
492
|
+
* @param eventName
|
|
493
|
+
* @param listener
|
|
494
|
+
*/
|
|
495
|
+
addListener(element, eventName, listener) {
|
|
496
|
+
if (!this.index.elementListeners.has(element)) {
|
|
497
|
+
this.index.elementListeners.set(element, /* @__PURE__ */ new Map());
|
|
498
|
+
}
|
|
499
|
+
const byEvent = this.index.elementListeners.get(element);
|
|
500
|
+
if (!byEvent.has(eventName)) {
|
|
501
|
+
byEvent.set(eventName, /* @__PURE__ */ new Set());
|
|
502
|
+
}
|
|
503
|
+
byEvent.get(eventName).add(listener);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Retrieves the event listener associated with a specific DOM element and event name from the context's index. This method looks up the element in the elementListeners map and then retrieves the listener for the specified event name. If no listener is found for the given element and event, it returns undefined. This allows the engine to efficiently access and manage event listeners that have been attached to dynamically created elements, enabling proper cleanup when instructions are reverted or when nodes are removed from the DOM.
|
|
507
|
+
*
|
|
508
|
+
* @param element
|
|
509
|
+
* @param eventName
|
|
510
|
+
* @returns
|
|
511
|
+
*/
|
|
512
|
+
getListener(element, eventName) {
|
|
513
|
+
const set = this.index.elementListeners.get(element)?.get(eventName);
|
|
514
|
+
if (!set || set.size === 0) return void 0;
|
|
515
|
+
return set.values().next().value;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Returns all listeners registered for a given element + event name.
|
|
519
|
+
*/
|
|
520
|
+
getListeners(element, eventName) {
|
|
521
|
+
return this.index.elementListeners.get(element)?.get(eventName);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Removes an event listener from a specific DOM element and updates the context's index accordingly. This method looks up the element in the elementListeners map and deletes the listener for the specified event name. This is typically called when an instruction is reverted or when a node is removed from the DOM, ensuring that any attached event listeners are properly cleaned up to prevent memory leaks and unintended behavior.
|
|
525
|
+
*
|
|
526
|
+
* @param element
|
|
527
|
+
* @param eventName
|
|
528
|
+
*/
|
|
529
|
+
removeListener(element, eventName, listener) {
|
|
530
|
+
const byEvent = this.index.elementListeners.get(element);
|
|
531
|
+
if (!byEvent) return;
|
|
532
|
+
if (listener) {
|
|
533
|
+
const set = byEvent.get(eventName);
|
|
534
|
+
if (set) {
|
|
535
|
+
set.delete(listener);
|
|
536
|
+
if (set.size === 0) byEvent.delete(eventName);
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
byEvent.delete(eventName);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
AreHTMLEngineContext = __decorateClass([
|
|
544
|
+
A_Frame.Define({
|
|
545
|
+
namespace: "a-are-html",
|
|
546
|
+
description: "Runtime index for the HTML rendering engine. Maps each AreNode and instruction ASEID to its corresponding DOM element so that apply and revert handlers on interpreter instructions can look up their DOM node in O(1). Tracks root-element mounts and maintains the group-level index used by structural directives."
|
|
547
|
+
})
|
|
548
|
+
], AreHTMLEngineContext);
|
|
275
549
|
|
|
276
550
|
// src/directives/AreDirectiveFor.directive.ts
|
|
277
551
|
var AreDirectiveFor = class extends AreDirective {
|
|
@@ -287,7 +561,8 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
287
561
|
node.init();
|
|
288
562
|
attribute.template = forTemplate;
|
|
289
563
|
const { key, index, arrayExpr } = this.parseExpression(attribute.content);
|
|
290
|
-
const
|
|
564
|
+
const contextScope = attribute.owner.scope.resolve(AreDirectiveContext)?.scope || {};
|
|
565
|
+
const array = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
|
|
291
566
|
attribute.value = array;
|
|
292
567
|
for (let i = 0; i < array.length; i++) {
|
|
293
568
|
this.spawnItemNode(attribute.template, attribute.owner, key, index, array[i], i);
|
|
@@ -321,8 +596,9 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
321
596
|
*/
|
|
322
597
|
performUpdate(attribute, store, scene, state) {
|
|
323
598
|
const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);
|
|
324
|
-
const newArray = this.resolveArray(store, arrayExpr, attribute.content);
|
|
325
599
|
const owner = attribute.owner;
|
|
600
|
+
const contextScope = owner.scope.resolve(AreDirectiveContext)?.scope || {};
|
|
601
|
+
const newArray = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
|
|
326
602
|
const currentChildren = [...owner.children];
|
|
327
603
|
attribute.value = newArray;
|
|
328
604
|
const attached = this.isAttached(owner);
|
|
@@ -337,12 +613,16 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
337
613
|
remaining.add(child);
|
|
338
614
|
}
|
|
339
615
|
const toCreate = [];
|
|
616
|
+
const finalByKey = /* @__PURE__ */ new Map();
|
|
617
|
+
const orderedKeys = new Array(newArray.length);
|
|
340
618
|
for (let i = 0; i < newArray.length; i++) {
|
|
341
619
|
const item = newArray[i];
|
|
342
620
|
const k = computeKey(item, i);
|
|
621
|
+
orderedKeys[i] = k;
|
|
343
622
|
const existing = childByKey.get(k);
|
|
344
623
|
if (existing) {
|
|
345
624
|
remaining.delete(existing);
|
|
625
|
+
finalByKey.set(k, existing);
|
|
346
626
|
let directiveContext = existing.scope.resolveFlat(AreDirectiveContext);
|
|
347
627
|
if (!directiveContext) {
|
|
348
628
|
directiveContext = new AreDirectiveContext(existing.aseid);
|
|
@@ -354,7 +634,7 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
354
634
|
[index || "index"]: i
|
|
355
635
|
};
|
|
356
636
|
} else {
|
|
357
|
-
toCreate.push({ item, idx: i });
|
|
637
|
+
toCreate.push({ item, idx: i, key: k });
|
|
358
638
|
}
|
|
359
639
|
}
|
|
360
640
|
for (const child of remaining) {
|
|
@@ -363,12 +643,14 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
363
643
|
}
|
|
364
644
|
const createItem = (desc) => {
|
|
365
645
|
const child = this.spawnItemNode(attribute.template, owner, key, index, desc.item, desc.idx);
|
|
646
|
+
finalByKey.set(desc.key, child);
|
|
366
647
|
child.transform();
|
|
367
648
|
child.compile();
|
|
368
649
|
if (attached) child.mount();
|
|
369
650
|
};
|
|
370
651
|
if (toCreate.length <= AreDirectiveFor.SYNC_THRESHOLD) {
|
|
371
652
|
for (const desc of toCreate) createItem(desc);
|
|
653
|
+
if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
|
|
372
654
|
return this.finishUpdate(attribute, store, scene, state);
|
|
373
655
|
}
|
|
374
656
|
state.running = true;
|
|
@@ -391,10 +673,39 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
391
673
|
AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));
|
|
392
674
|
});
|
|
393
675
|
}
|
|
676
|
+
if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
|
|
394
677
|
return this.finishUpdate(attribute, store, scene, state);
|
|
395
678
|
};
|
|
396
679
|
return processChunk();
|
|
397
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* Repositions the item nodes' DOM elements so the rendered order matches the
|
|
683
|
+
* source array order. The keyed diff (steps 1–4) reuses existing nodes in
|
|
684
|
+
* place and mounts new ones at the end; without this pass a `prepend` or
|
|
685
|
+
* `shuffle` would leave reused rows where they were and pile new rows at the
|
|
686
|
+
* bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
|
|
687
|
+
* the element each item must precede (starting at the `$for` anchor comment),
|
|
688
|
+
* and only call `insertBefore` when an element is not already in position —
|
|
689
|
+
* so a plain `append` (already-correct order) performs ZERO DOM moves.
|
|
690
|
+
*/
|
|
691
|
+
reconcileOrder(owner, orderedKeys, finalByKey) {
|
|
692
|
+
const context = owner.scope.resolve(AreHTMLEngineContext);
|
|
693
|
+
if (!context) return;
|
|
694
|
+
const anchor = context.getNodeElement(owner);
|
|
695
|
+
if (!anchor || !anchor.parentNode) return;
|
|
696
|
+
const parent = anchor.parentNode;
|
|
697
|
+
let ref = anchor;
|
|
698
|
+
for (let i = orderedKeys.length - 1; i >= 0; i--) {
|
|
699
|
+
const node = finalByKey.get(orderedKeys[i]);
|
|
700
|
+
if (!node) continue;
|
|
701
|
+
const element = context.getNodeElement(node);
|
|
702
|
+
if (!element || element.parentNode !== parent) continue;
|
|
703
|
+
if (element.nextSibling !== ref) {
|
|
704
|
+
parent.insertBefore(element, ref);
|
|
705
|
+
}
|
|
706
|
+
ref = element;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
398
709
|
/**
|
|
399
710
|
* Completes an update pass. If another update() arrived while a chunked
|
|
400
711
|
* render was streaming, run exactly one more pass now from the latest store
|
|
@@ -485,13 +796,23 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
485
796
|
* Supports both plain key lookups and function-call expressions:
|
|
486
797
|
* items → store.get('items')
|
|
487
798
|
* filter(items) → store.get('filter')(store.get('items'))
|
|
799
|
+
*
|
|
800
|
+
* `contextScope` carries item-scoped variables introduced by an enclosing
|
|
801
|
+
* directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
|
|
802
|
+
* store so a nested `$for="cell in row.cells"` resolves `row` from the
|
|
803
|
+
* parent iteration instead of looking for a (non-existent) top-level store
|
|
804
|
+
* key. Leading identifiers not present in the context fall back to the store.
|
|
488
805
|
*/
|
|
489
|
-
resolveArray(store, arrayExpr, fullContent) {
|
|
806
|
+
resolveArray(store, arrayExpr, fullContent, contextScope = {}) {
|
|
807
|
+
const getRoot = (rawKey) => {
|
|
808
|
+
const k = rawKey.replace(/\?$/, "");
|
|
809
|
+
return k in contextScope ? contextScope[k] : store.get(k);
|
|
810
|
+
};
|
|
490
811
|
let result;
|
|
491
812
|
const callMatch = arrayExpr.match(/^([^(]+)\((.+)\)$/);
|
|
492
813
|
if (callMatch) {
|
|
493
814
|
const fnName = callMatch[1].trim();
|
|
494
|
-
const fn =
|
|
815
|
+
const fn = getRoot(fnName);
|
|
495
816
|
if (typeof fn !== "function")
|
|
496
817
|
throw new AreCompilerError({
|
|
497
818
|
title: 'Invalid "for" Directive Function',
|
|
@@ -505,25 +826,25 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
505
826
|
const stripped = arg.replace(/\?$/, "");
|
|
506
827
|
if (stripped.includes(".")) {
|
|
507
828
|
const parts = stripped.split(".").map((p) => p.replace(/\?$/, ""));
|
|
508
|
-
let val =
|
|
829
|
+
let val = getRoot(parts[0]);
|
|
509
830
|
for (let j = 1; j < parts.length; j++) {
|
|
510
831
|
if (val == null) return void 0;
|
|
511
832
|
val = val[parts[j]];
|
|
512
833
|
}
|
|
513
834
|
return val ?? void 0;
|
|
514
835
|
}
|
|
515
|
-
return
|
|
836
|
+
return getRoot(stripped);
|
|
516
837
|
});
|
|
517
838
|
result = fn(...resolvedArgs);
|
|
518
839
|
} else if (arrayExpr.includes(".")) {
|
|
519
840
|
const parts = arrayExpr.split(".").map((p) => p.replace(/\?$/, ""));
|
|
520
|
-
result =
|
|
841
|
+
result = getRoot(parts[0]);
|
|
521
842
|
for (let i = 1; i < parts.length; i++) {
|
|
522
843
|
if (result == null) break;
|
|
523
844
|
result = result[parts[i]];
|
|
524
845
|
}
|
|
525
846
|
} else {
|
|
526
|
-
result =
|
|
847
|
+
result = getRoot(arrayExpr);
|
|
527
848
|
}
|
|
528
849
|
if (result == null) return [];
|
|
529
850
|
if (!Array.isArray(result))
|
|
@@ -631,9 +952,7 @@ var AreDirectiveIf = class extends AreDirective {
|
|
|
631
952
|
attribute.template = ifTemplate;
|
|
632
953
|
}
|
|
633
954
|
compile(attribute, store, scene, syntax, directiveContext, ...args) {
|
|
634
|
-
attribute.value =
|
|
635
|
-
...directiveContext?.scope || {}
|
|
636
|
-
});
|
|
955
|
+
attribute.value = this.evaluateCondition(syntax, attribute, store, directiveContext);
|
|
637
956
|
const hostInstruction = scene.host;
|
|
638
957
|
const commentIdentifier = ` --- if: ${attribute.template.id} --- `;
|
|
639
958
|
const declaration = new AddCommentInstruction({ content: commentIdentifier });
|
|
@@ -645,9 +964,9 @@ var AreDirectiveIf = class extends AreDirective {
|
|
|
645
964
|
else
|
|
646
965
|
attribute.template.scene.deactivate();
|
|
647
966
|
}
|
|
648
|
-
update(attribute, store, scope, syntax, scene, ...args) {
|
|
967
|
+
update(attribute, store, scope, syntax, scene, directiveContext, ...args) {
|
|
649
968
|
const previous = !!attribute.value;
|
|
650
|
-
const next =
|
|
969
|
+
const next = this.evaluateCondition(syntax, attribute, store, directiveContext);
|
|
651
970
|
attribute.value = next;
|
|
652
971
|
if (previous === next) return;
|
|
653
972
|
if (next) {
|
|
@@ -658,6 +977,30 @@ var AreDirectiveIf = class extends AreDirective {
|
|
|
658
977
|
attribute.template.scene.deactivate();
|
|
659
978
|
}
|
|
660
979
|
}
|
|
980
|
+
/**
|
|
981
|
+
* Evaluates the `$if` condition defensively.
|
|
982
|
+
*
|
|
983
|
+
* A condition can reference data that is momentarily unavailable — most
|
|
984
|
+
* commonly a nested `$if` (e.g. `$if="selected.fields.length"`) living
|
|
985
|
+
* inside a parent `$if="selected"` whose object has just become `null`.
|
|
986
|
+
* Because the nested directive is still subscribed to the store, its
|
|
987
|
+
* update fires on that same change and the raw expression would throw
|
|
988
|
+
* `Cannot read properties of null`, crashing the whole update pipeline.
|
|
989
|
+
*
|
|
990
|
+
* Treating an evaluation error as `false` is the correct contract for a
|
|
991
|
+
* conditional: if the condition cannot be resolved, the subtree simply
|
|
992
|
+
* stays hidden until the referenced data is present again (at which point
|
|
993
|
+
* the parent `$if` re-activates and re-evaluates this one).
|
|
994
|
+
*/
|
|
995
|
+
evaluateCondition(syntax, attribute, store, directiveContext) {
|
|
996
|
+
try {
|
|
997
|
+
return !!syntax.evaluate(attribute.content, store, {
|
|
998
|
+
...directiveContext?.scope || {}
|
|
999
|
+
});
|
|
1000
|
+
} catch {
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
661
1004
|
};
|
|
662
1005
|
__decorateClass([
|
|
663
1006
|
AreDirective.Transform,
|
|
@@ -681,7 +1024,8 @@ __decorateClass([
|
|
|
681
1024
|
__decorateParam(1, A_Inject(AreStore)),
|
|
682
1025
|
__decorateParam(2, A_Inject(A_Scope)),
|
|
683
1026
|
__decorateParam(3, A_Inject(AreSyntax)),
|
|
684
|
-
__decorateParam(4, A_Inject(AreScene))
|
|
1027
|
+
__decorateParam(4, A_Inject(AreScene)),
|
|
1028
|
+
__decorateParam(5, A_Inject(AreDirectiveContext))
|
|
685
1029
|
], AreDirectiveIf.prototype, "update", 1);
|
|
686
1030
|
AreDirectiveIf = __decorateClass([
|
|
687
1031
|
A_Frame.Define({
|
|
@@ -823,6 +1167,21 @@ AddListenerInstruction = __decorateClass([
|
|
|
823
1167
|
description: "Attaches a DOM event listener to an element. Apply calls addEventListener; revert calls removeEventListener."
|
|
824
1168
|
})
|
|
825
1169
|
], AddListenerInstruction);
|
|
1170
|
+
var AddStaticHTMLInstruction = class extends AreMutation {
|
|
1171
|
+
constructor(parent, props) {
|
|
1172
|
+
if ("aseid" in props) {
|
|
1173
|
+
super(props);
|
|
1174
|
+
} else {
|
|
1175
|
+
super(AreHTMLInstructions.AddStaticHTML, parent, props);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
AddStaticHTMLInstruction = __decorateClass([
|
|
1180
|
+
A_Frame.Define({
|
|
1181
|
+
namespace: "a-are-html",
|
|
1182
|
+
description: 'Materialises a fully static subtree (a "static island") onto its parent element in a single pass via browser-parsed innerHTML / a cached <template> clone. Apply injects the markup; revert clears it. Decodes HTML entities (e.g. ) for free.'
|
|
1183
|
+
})
|
|
1184
|
+
], AddStaticHTMLInstruction);
|
|
826
1185
|
var AddStyleInstruction = class extends AreMutation {
|
|
827
1186
|
constructor(parent, props) {
|
|
828
1187
|
if ("aseid" in props) {
|
|
@@ -877,6 +1236,57 @@ var AreHTMLNode = class extends AreNode {
|
|
|
877
1236
|
get tag() {
|
|
878
1237
|
return this.aseid.entity;
|
|
879
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* The verbatim inner markup captured when this node was identified as a
|
|
1241
|
+
* static island, or `undefined` for ordinary (per-node) nodes.
|
|
1242
|
+
*/
|
|
1243
|
+
get staticInnerHTML() {
|
|
1244
|
+
return this._staticInnerHTML;
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Whether this node is a static-island root (see `_staticInnerHTML`).
|
|
1248
|
+
*/
|
|
1249
|
+
get isStaticIsland() {
|
|
1250
|
+
return this._staticInnerHTML !== void 0;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Marks this node as a static-island root, capturing the verbatim inner
|
|
1254
|
+
* markup to be materialised in one shot by the interpreter. Called by the
|
|
1255
|
+
* tokenizer when the node's inner content is detected to be fully static.
|
|
1256
|
+
*/
|
|
1257
|
+
markStatic(innerHTML) {
|
|
1258
|
+
this._staticInnerHTML = innerHTML;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Deep-clone the node. Overridden to carry over the static-island marker
|
|
1262
|
+
* (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
|
|
1263
|
+
* copied by the base AreNode.clone(). Without this, cloning a directive
|
|
1264
|
+
* template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
|
|
1265
|
+
* would drop the captured inner markup and render an empty element. The
|
|
1266
|
+
* base clone() recurses via each child's polymorphic clone(), so nested
|
|
1267
|
+
* island children are preserved automatically through this override.
|
|
1268
|
+
*/
|
|
1269
|
+
clone() {
|
|
1270
|
+
const cloned = super.clone();
|
|
1271
|
+
const self = this;
|
|
1272
|
+
if (self._staticInnerHTML !== void 0)
|
|
1273
|
+
cloned.markStatic(self._staticInnerHTML);
|
|
1274
|
+
return cloned;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Clone the node while transferring its existing scope to the clone (used by
|
|
1278
|
+
* the $if/$for directives to turn the original node into a lightweight group
|
|
1279
|
+
* container). Overridden for the same reason as `clone()`: the static-island
|
|
1280
|
+
* marker must survive so a directive applied to an island root keeps its
|
|
1281
|
+
* inner markup.
|
|
1282
|
+
*/
|
|
1283
|
+
cloneWithScope() {
|
|
1284
|
+
const cloned = super.cloneWithScope();
|
|
1285
|
+
const self = this;
|
|
1286
|
+
if (self._staticInnerHTML !== void 0)
|
|
1287
|
+
cloned.markStatic(self._staticInnerHTML);
|
|
1288
|
+
return cloned;
|
|
1289
|
+
}
|
|
880
1290
|
/**
|
|
881
1291
|
* The static attributes defined for the node, which are typically used to represent static properties or characteristics of the node that do not change based on the context or state. These attributes are usually defined in the template and are not reactive.
|
|
882
1292
|
*
|
|
@@ -1175,184 +1585,176 @@ function toDOMString(value) {
|
|
|
1175
1585
|
return "";
|
|
1176
1586
|
}
|
|
1177
1587
|
}
|
|
1178
|
-
var
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1588
|
+
var STANDARD_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
1589
|
+
// root / sections
|
|
1590
|
+
"html",
|
|
1591
|
+
"body",
|
|
1592
|
+
"header",
|
|
1593
|
+
"footer",
|
|
1594
|
+
"main",
|
|
1595
|
+
"nav",
|
|
1596
|
+
"section",
|
|
1597
|
+
"article",
|
|
1598
|
+
"aside",
|
|
1599
|
+
"address",
|
|
1600
|
+
"hgroup",
|
|
1601
|
+
// headings
|
|
1602
|
+
"h1",
|
|
1603
|
+
"h2",
|
|
1604
|
+
"h3",
|
|
1605
|
+
"h4",
|
|
1606
|
+
"h5",
|
|
1607
|
+
"h6",
|
|
1608
|
+
// grouping
|
|
1609
|
+
"div",
|
|
1610
|
+
"p",
|
|
1611
|
+
"span",
|
|
1612
|
+
"pre",
|
|
1613
|
+
"blockquote",
|
|
1614
|
+
"figure",
|
|
1615
|
+
"figcaption",
|
|
1616
|
+
"hr",
|
|
1617
|
+
"br",
|
|
1618
|
+
"wbr",
|
|
1619
|
+
// lists
|
|
1620
|
+
"ul",
|
|
1621
|
+
"ol",
|
|
1622
|
+
"li",
|
|
1623
|
+
"dl",
|
|
1624
|
+
"dt",
|
|
1625
|
+
"dd",
|
|
1626
|
+
"menu",
|
|
1627
|
+
// text-level / phrasing
|
|
1628
|
+
"a",
|
|
1629
|
+
"b",
|
|
1630
|
+
"i",
|
|
1631
|
+
"u",
|
|
1632
|
+
"s",
|
|
1633
|
+
"em",
|
|
1634
|
+
"strong",
|
|
1635
|
+
"small",
|
|
1636
|
+
"mark",
|
|
1637
|
+
"abbr",
|
|
1638
|
+
"cite",
|
|
1639
|
+
"q",
|
|
1640
|
+
"code",
|
|
1641
|
+
"kbd",
|
|
1642
|
+
"samp",
|
|
1643
|
+
"var",
|
|
1644
|
+
"sub",
|
|
1645
|
+
"sup",
|
|
1646
|
+
"time",
|
|
1647
|
+
"data",
|
|
1648
|
+
"dfn",
|
|
1649
|
+
"bdi",
|
|
1650
|
+
"bdo",
|
|
1651
|
+
"ruby",
|
|
1652
|
+
"rt",
|
|
1653
|
+
"rp",
|
|
1654
|
+
"del",
|
|
1655
|
+
"ins",
|
|
1656
|
+
// media / embedded (no special namespace handling needed)
|
|
1657
|
+
"img",
|
|
1658
|
+
"picture",
|
|
1659
|
+
"source",
|
|
1660
|
+
"figure",
|
|
1661
|
+
"audio",
|
|
1662
|
+
"video",
|
|
1663
|
+
"track",
|
|
1664
|
+
// tables
|
|
1665
|
+
"table",
|
|
1666
|
+
"caption",
|
|
1667
|
+
"colgroup",
|
|
1668
|
+
"col",
|
|
1669
|
+
"thead",
|
|
1670
|
+
"tbody",
|
|
1671
|
+
"tfoot",
|
|
1672
|
+
"tr",
|
|
1673
|
+
"th",
|
|
1674
|
+
"td",
|
|
1675
|
+
// forms (display only — these still render fine from innerHTML)
|
|
1676
|
+
"label",
|
|
1677
|
+
"fieldset",
|
|
1678
|
+
"legend",
|
|
1679
|
+
"datalist",
|
|
1680
|
+
"option",
|
|
1681
|
+
"optgroup",
|
|
1682
|
+
"output",
|
|
1683
|
+
"progress",
|
|
1684
|
+
"meter",
|
|
1685
|
+
// interactive
|
|
1686
|
+
"details",
|
|
1687
|
+
"summary",
|
|
1688
|
+
"dialog"
|
|
1689
|
+
]);
|
|
1690
|
+
function isStaticMarkup(inner) {
|
|
1691
|
+
if (!inner) return false;
|
|
1692
|
+
if (inner.indexOf("{{") !== -1) return false;
|
|
1693
|
+
const n = inner.length;
|
|
1694
|
+
let i = 0;
|
|
1695
|
+
while (i < n) {
|
|
1696
|
+
const lt = inner.indexOf("<", i);
|
|
1697
|
+
if (lt === -1) break;
|
|
1698
|
+
if (inner.startsWith("<!--", lt)) {
|
|
1699
|
+
const end = inner.indexOf("-->", lt + 4);
|
|
1700
|
+
if (end === -1) return false;
|
|
1701
|
+
i = end + 3;
|
|
1702
|
+
continue;
|
|
1703
|
+
}
|
|
1704
|
+
if (inner[lt + 1] === "/" || inner[lt + 1] === "!" || inner[lt + 1] === "?") {
|
|
1705
|
+
const gt = inner.indexOf(">", lt);
|
|
1706
|
+
if (gt === -1) return false;
|
|
1707
|
+
i = gt + 1;
|
|
1708
|
+
continue;
|
|
1709
|
+
}
|
|
1710
|
+
const nameMatch = /^<([a-zA-Z][a-zA-Z0-9-]*)/.exec(inner.slice(lt));
|
|
1711
|
+
if (!nameMatch) {
|
|
1712
|
+
i = lt + 1;
|
|
1713
|
+
continue;
|
|
1714
|
+
}
|
|
1715
|
+
const tag = nameMatch[1].toLowerCase();
|
|
1716
|
+
if (tag.indexOf("-") !== -1 || !STANDARD_HTML_TAGS.has(tag)) return false;
|
|
1717
|
+
let j = lt + nameMatch[0].length;
|
|
1718
|
+
let inSingle = false;
|
|
1719
|
+
let inDouble = false;
|
|
1720
|
+
let atNameBoundary = true;
|
|
1721
|
+
let tagEnd = -1;
|
|
1722
|
+
while (j < n) {
|
|
1723
|
+
const ch = inner[j];
|
|
1724
|
+
if (inDouble) {
|
|
1725
|
+
if (ch === '"') inDouble = false;
|
|
1726
|
+
} else if (inSingle) {
|
|
1727
|
+
if (ch === "'") inSingle = false;
|
|
1728
|
+
} else if (ch === '"') {
|
|
1729
|
+
inDouble = true;
|
|
1730
|
+
atNameBoundary = false;
|
|
1731
|
+
} else if (ch === "'") {
|
|
1732
|
+
inSingle = true;
|
|
1733
|
+
atNameBoundary = false;
|
|
1734
|
+
} else if (ch === ">") {
|
|
1735
|
+
tagEnd = j;
|
|
1736
|
+
break;
|
|
1737
|
+
} else if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "/") {
|
|
1738
|
+
atNameBoundary = true;
|
|
1739
|
+
} else {
|
|
1740
|
+
if (atNameBoundary && (ch === "$" || ch === ":" || ch === "@")) {
|
|
1741
|
+
return false;
|
|
1281
1742
|
}
|
|
1743
|
+
atNameBoundary = false;
|
|
1282
1744
|
}
|
|
1745
|
+
j++;
|
|
1283
1746
|
}
|
|
1747
|
+
if (tagEnd === -1) return false;
|
|
1748
|
+
i = tagEnd + 1;
|
|
1284
1749
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
return this.index.groupToElements.get(instruction);
|
|
1288
|
-
} else {
|
|
1289
|
-
return this.index.groupToElements.get(instruction.aseid.toString());
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
/**
|
|
1293
|
-
* Adds an event listener to a specific DOM element and keeps track of it in the context's index for proper cleanup later. This method takes a DOM element, an event name, and a listener function or object, and stores this information in the elementListeners map. This allows the engine to efficiently manage event listeners attached to dynamically created elements, ensuring that they can be removed when the associated instructions are reverted or when nodes are removed from the DOM, preventing memory leaks and unintended behavior.
|
|
1294
|
-
*
|
|
1295
|
-
* @param element
|
|
1296
|
-
* @param eventName
|
|
1297
|
-
* @param listener
|
|
1298
|
-
*/
|
|
1299
|
-
addListener(element, eventName, listener) {
|
|
1300
|
-
if (!this.index.elementListeners.has(element)) {
|
|
1301
|
-
this.index.elementListeners.set(element, /* @__PURE__ */ new Map());
|
|
1302
|
-
}
|
|
1303
|
-
const byEvent = this.index.elementListeners.get(element);
|
|
1304
|
-
if (!byEvent.has(eventName)) {
|
|
1305
|
-
byEvent.set(eventName, /* @__PURE__ */ new Set());
|
|
1306
|
-
}
|
|
1307
|
-
byEvent.get(eventName).add(listener);
|
|
1308
|
-
}
|
|
1309
|
-
/**
|
|
1310
|
-
* Retrieves the event listener associated with a specific DOM element and event name from the context's index. This method looks up the element in the elementListeners map and then retrieves the listener for the specified event name. If no listener is found for the given element and event, it returns undefined. This allows the engine to efficiently access and manage event listeners that have been attached to dynamically created elements, enabling proper cleanup when instructions are reverted or when nodes are removed from the DOM.
|
|
1311
|
-
*
|
|
1312
|
-
* @param element
|
|
1313
|
-
* @param eventName
|
|
1314
|
-
* @returns
|
|
1315
|
-
*/
|
|
1316
|
-
getListener(element, eventName) {
|
|
1317
|
-
const set = this.index.elementListeners.get(element)?.get(eventName);
|
|
1318
|
-
if (!set || set.size === 0) return void 0;
|
|
1319
|
-
return set.values().next().value;
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* Returns all listeners registered for a given element + event name.
|
|
1323
|
-
*/
|
|
1324
|
-
getListeners(element, eventName) {
|
|
1325
|
-
return this.index.elementListeners.get(element)?.get(eventName);
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* Removes an event listener from a specific DOM element and updates the context's index accordingly. This method looks up the element in the elementListeners map and deletes the listener for the specified event name. This is typically called when an instruction is reverted or when a node is removed from the DOM, ensuring that any attached event listeners are properly cleaned up to prevent memory leaks and unintended behavior.
|
|
1329
|
-
*
|
|
1330
|
-
* @param element
|
|
1331
|
-
* @param eventName
|
|
1332
|
-
*/
|
|
1333
|
-
removeListener(element, eventName, listener) {
|
|
1334
|
-
const byEvent = this.index.elementListeners.get(element);
|
|
1335
|
-
if (!byEvent) return;
|
|
1336
|
-
if (listener) {
|
|
1337
|
-
const set = byEvent.get(eventName);
|
|
1338
|
-
if (set) {
|
|
1339
|
-
set.delete(listener);
|
|
1340
|
-
if (set.size === 0) byEvent.delete(eventName);
|
|
1341
|
-
}
|
|
1342
|
-
} else {
|
|
1343
|
-
byEvent.delete(eventName);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
};
|
|
1347
|
-
AreHTMLEngineContext = __decorateClass([
|
|
1348
|
-
A_Frame.Define({
|
|
1349
|
-
namespace: "a-are-html",
|
|
1350
|
-
description: "Runtime index for the HTML rendering engine. Maps each AreNode and instruction ASEID to its corresponding DOM element so that apply and revert handlers on interpreter instructions can look up their DOM node in O(1). Tracks root-element mounts and maintains the group-level index used by structural directives."
|
|
1351
|
-
})
|
|
1352
|
-
], AreHTMLEngineContext);
|
|
1750
|
+
return true;
|
|
1751
|
+
}
|
|
1353
1752
|
var AreHTMLCompiler = class extends AreCompiler {
|
|
1354
1753
|
compileHTMLNode(node, scene, logger, ...args) {
|
|
1355
1754
|
super.compile(node, scene, logger, ...args);
|
|
1755
|
+
if (node.isStaticIsland && scene.host) {
|
|
1756
|
+
scene.plan(new AddStaticHTMLInstruction(scene.host, { html: node.staticInnerHTML }));
|
|
1757
|
+
}
|
|
1356
1758
|
if (node.styles?.styles) {
|
|
1357
1759
|
const host = scene.host;
|
|
1358
1760
|
if (host) {
|
|
@@ -1410,7 +1812,7 @@ var AreHTMLCompiler = class extends AreCompiler {
|
|
|
1410
1812
|
handler: attribute.content
|
|
1411
1813
|
}));
|
|
1412
1814
|
}
|
|
1413
|
-
compileBindingAttribute(attribute, scene, parentStore, store, syntax, ...args) {
|
|
1815
|
+
compileBindingAttribute(attribute, scene, parentStore, store, syntax, directiveContext, ...args) {
|
|
1414
1816
|
if (!scene.host)
|
|
1415
1817
|
throw new AreCompilerError({
|
|
1416
1818
|
title: "Scene Host Not Found",
|
|
@@ -1446,11 +1848,12 @@ var AreHTMLCompiler = class extends AreCompiler {
|
|
|
1446
1848
|
}
|
|
1447
1849
|
return value;
|
|
1448
1850
|
};
|
|
1851
|
+
const directiveScope = () => directiveContext?.scope ?? {};
|
|
1449
1852
|
const watcher = {
|
|
1450
1853
|
update: () => {
|
|
1451
1854
|
try {
|
|
1452
1855
|
parentStore.watch(watcher);
|
|
1453
|
-
const next = coerce(syntax.evaluate(attribute.content, parentStore));
|
|
1856
|
+
const next = coerce(syntax.evaluate(attribute.content, parentStore, directiveScope()));
|
|
1454
1857
|
parentStore.unwatch(watcher);
|
|
1455
1858
|
store.set(propName, next);
|
|
1456
1859
|
} catch (e) {
|
|
@@ -1459,7 +1862,7 @@ var AreHTMLCompiler = class extends AreCompiler {
|
|
|
1459
1862
|
}
|
|
1460
1863
|
};
|
|
1461
1864
|
parentStore.watch(watcher);
|
|
1462
|
-
const initial = coerce(syntax.evaluate(attribute.content, parentStore));
|
|
1865
|
+
const initial = coerce(syntax.evaluate(attribute.content, parentStore, directiveScope()));
|
|
1463
1866
|
parentStore.unwatch(watcher);
|
|
1464
1867
|
store.set(propName, initial);
|
|
1465
1868
|
return;
|
|
@@ -1515,7 +1918,8 @@ __decorateClass([
|
|
|
1515
1918
|
__decorateParam(2, A_Dependency.Parent()),
|
|
1516
1919
|
__decorateParam(2, A_Inject(AreStore)),
|
|
1517
1920
|
__decorateParam(3, A_Inject(AreStore)),
|
|
1518
|
-
__decorateParam(4, A_Inject(AreSyntax))
|
|
1921
|
+
__decorateParam(4, A_Inject(AreSyntax)),
|
|
1922
|
+
__decorateParam(5, A_Inject(AreDirectiveContext))
|
|
1519
1923
|
], AreHTMLCompiler.prototype, "compileBindingAttribute", 1);
|
|
1520
1924
|
AreHTMLCompiler = __decorateClass([
|
|
1521
1925
|
A_Frame.Define({
|
|
@@ -1547,12 +1951,15 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1547
1951
|
});
|
|
1548
1952
|
}
|
|
1549
1953
|
const element = isSVG ? context.container.createElementNS(SVG_NAMESPACE, tag) : context.container.createElement(tag);
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
} else {
|
|
1954
|
+
context.setInstructionElement(declaration, element);
|
|
1955
|
+
const attach = mountPoint.nodeType === Node.ELEMENT_NODE ? () => mountPoint.appendChild(element) : () => {
|
|
1553
1956
|
mountPoint.parentNode?.insertBefore(element, mountPoint);
|
|
1957
|
+
};
|
|
1958
|
+
if (context.isBatching && mountPoint.isConnected) {
|
|
1959
|
+
context.deferAttach(attach);
|
|
1960
|
+
} else {
|
|
1961
|
+
attach();
|
|
1554
1962
|
}
|
|
1555
|
-
context.setInstructionElement(declaration, element);
|
|
1556
1963
|
} else {
|
|
1557
1964
|
const mountPoint = context.container.getElementById(node.id);
|
|
1558
1965
|
if (!mountPoint) {
|
|
@@ -1562,8 +1969,15 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1562
1969
|
});
|
|
1563
1970
|
}
|
|
1564
1971
|
const element = isSVG ? context.container.createElementNS(SVG_NAMESPACE, tag) : context.container.createElement(tag);
|
|
1565
|
-
mountPoint.parentNode?.replaceChild(element, mountPoint);
|
|
1566
1972
|
context.setInstructionElement(declaration, element);
|
|
1973
|
+
const attach = () => {
|
|
1974
|
+
mountPoint.parentNode?.replaceChild(element, mountPoint);
|
|
1975
|
+
};
|
|
1976
|
+
if (context.isBatching && mountPoint.isConnected) {
|
|
1977
|
+
context.deferAttach(attach);
|
|
1978
|
+
} else {
|
|
1979
|
+
attach();
|
|
1980
|
+
}
|
|
1567
1981
|
}
|
|
1568
1982
|
logger?.debug("green", `Element ${node.aseid.toString()} added to Context:`);
|
|
1569
1983
|
} catch (error) {
|
|
@@ -1573,7 +1987,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1573
1987
|
}
|
|
1574
1988
|
removeElement(declaration, context) {
|
|
1575
1989
|
const element = context.getElementByInstruction(declaration);
|
|
1576
|
-
if (element && element.parentNode) {
|
|
1990
|
+
if (element && element.parentNode && element.isConnected) {
|
|
1577
1991
|
element.parentNode.removeChild(element);
|
|
1578
1992
|
}
|
|
1579
1993
|
context.removeInstructionElement(declaration);
|
|
@@ -1676,7 +2090,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1676
2090
|
const element = context.getElementByInstruction(mutation.parent);
|
|
1677
2091
|
if (!element) return;
|
|
1678
2092
|
const { name } = mutation.payload;
|
|
1679
|
-
if (name && element.nodeType === Node.ELEMENT_NODE) {
|
|
2093
|
+
if (name && element.nodeType === Node.ELEMENT_NODE && element.isConnected) {
|
|
1680
2094
|
const colonIdx = name.indexOf(":");
|
|
1681
2095
|
if (colonIdx > 0) {
|
|
1682
2096
|
const ns = SVG_ATTRIBUTE_NS[name.slice(0, colonIdx)];
|
|
@@ -1703,6 +2117,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1703
2117
|
showElement(mutation, context) {
|
|
1704
2118
|
const element = context.getElementByInstruction(mutation.parent);
|
|
1705
2119
|
if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
|
|
2120
|
+
if (!element.isConnected) return;
|
|
1706
2121
|
const el = element;
|
|
1707
2122
|
el.style.display = mutation.payload?.display ?? mutation.cache ?? "";
|
|
1708
2123
|
}
|
|
@@ -1797,7 +2212,9 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1797
2212
|
const { event: eventName } = parseEventName(name);
|
|
1798
2213
|
const listener = mutation.payload._callback;
|
|
1799
2214
|
if (listener) {
|
|
1800
|
-
element.
|
|
2215
|
+
if (element.isConnected) {
|
|
2216
|
+
element.removeEventListener(eventName, listener);
|
|
2217
|
+
}
|
|
1801
2218
|
context.removeListener(element, name, listener);
|
|
1802
2219
|
mutation.payload._callback = void 0;
|
|
1803
2220
|
}
|
|
@@ -1835,9 +2252,32 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1835
2252
|
removeText(declaration, context) {
|
|
1836
2253
|
const element = context.getElementByInstruction(declaration);
|
|
1837
2254
|
if (!element) return;
|
|
1838
|
-
element.
|
|
2255
|
+
if (element.isConnected) {
|
|
2256
|
+
element.parentNode?.removeChild(element);
|
|
2257
|
+
}
|
|
1839
2258
|
context.removeInstructionElement(declaration);
|
|
1840
2259
|
}
|
|
2260
|
+
addStaticHTML(mutation, context, logger) {
|
|
2261
|
+
const element = context.getElementByInstruction(mutation.parent);
|
|
2262
|
+
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
|
|
2263
|
+
throw new AreInterpreterError({
|
|
2264
|
+
title: "Element Not Found",
|
|
2265
|
+
description: `Could not find a DOM element associated with the instruction ASEID "${mutation.parent}". Ensure the host element is rendered before materialising its static island.`
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
const el = element;
|
|
2269
|
+
const { html } = mutation.payload;
|
|
2270
|
+
el.textContent = "";
|
|
2271
|
+
const fragment = context.getStaticFragment(el.tagName.toLowerCase(), html);
|
|
2272
|
+
el.appendChild(fragment.cloneNode(true));
|
|
2273
|
+
logger?.debug("green", `Static island materialised onto <${(mutation.owner.parent ?? mutation.owner)?.aseid?.toString?.()}>`);
|
|
2274
|
+
}
|
|
2275
|
+
removeStaticHTML(mutation, context) {
|
|
2276
|
+
const element = context.getElementByInstruction(mutation.parent);
|
|
2277
|
+
if (element && element.nodeType === Node.ELEMENT_NODE && element.isConnected) {
|
|
2278
|
+
element.textContent = "";
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
1841
2281
|
addComment(declaration, context, store, syntax, directiveContext, logger) {
|
|
1842
2282
|
const node = declaration.owner.parent;
|
|
1843
2283
|
const { content, evaluate } = declaration.payload;
|
|
@@ -1871,7 +2311,9 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1871
2311
|
removeComment(declaration, context) {
|
|
1872
2312
|
const element = context.getElementByInstruction(declaration);
|
|
1873
2313
|
if (!element) return;
|
|
1874
|
-
element.
|
|
2314
|
+
if (element.isConnected) {
|
|
2315
|
+
element.parentNode?.removeChild(element);
|
|
2316
|
+
}
|
|
1875
2317
|
context.removeInstructionElement(declaration);
|
|
1876
2318
|
}
|
|
1877
2319
|
addStyle(mutation, context, logger) {
|
|
@@ -2015,6 +2457,24 @@ __decorateClass([
|
|
|
2015
2457
|
__decorateParam(0, A_Inject(A_Caller)),
|
|
2016
2458
|
__decorateParam(1, A_Inject(AreHTMLEngineContext))
|
|
2017
2459
|
], AreHTMLInterpreter.prototype, "removeText", 1);
|
|
2460
|
+
__decorateClass([
|
|
2461
|
+
A_Frame.Define({
|
|
2462
|
+
description: "Inject a static island's inner markup onto its host element in one pass via a cached, browser-parsed <template> clone. Decodes HTML entities natively."
|
|
2463
|
+
}),
|
|
2464
|
+
AreInterpreter.Apply(AreHTMLInstructions.AddStaticHTML),
|
|
2465
|
+
AreInterpreter.Update(AreHTMLInstructions.AddStaticHTML),
|
|
2466
|
+
__decorateParam(0, A_Inject(A_Caller)),
|
|
2467
|
+
__decorateParam(1, A_Inject(AreHTMLEngineContext)),
|
|
2468
|
+
__decorateParam(2, A_Inject(A_Logger))
|
|
2469
|
+
], AreHTMLInterpreter.prototype, "addStaticHTML", 1);
|
|
2470
|
+
__decorateClass([
|
|
2471
|
+
A_Frame.Define({
|
|
2472
|
+
description: "Clear a static island's injected markup from its host element on revert."
|
|
2473
|
+
}),
|
|
2474
|
+
AreInterpreter.Revert(AreHTMLInstructions.AddStaticHTML),
|
|
2475
|
+
__decorateParam(0, A_Inject(A_Caller)),
|
|
2476
|
+
__decorateParam(1, A_Inject(AreHTMLEngineContext))
|
|
2477
|
+
], AreHTMLInterpreter.prototype, "removeStaticHTML", 1);
|
|
2018
2478
|
__decorateClass([
|
|
2019
2479
|
A_Frame.Define({
|
|
2020
2480
|
description: "Add a comment node to the DOM based on the provided declaration instruction."
|
|
@@ -2066,7 +2526,12 @@ var AreHTMLTokenizer = class extends AreTokenizer {
|
|
|
2066
2526
|
this.ATTR_PATTERN = /([$:@]?[\w.-]+(?::[\w.-]+)?)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>/"'=]+)))?/g;
|
|
2067
2527
|
}
|
|
2068
2528
|
tokenize(node, context, logger) {
|
|
2069
|
-
|
|
2529
|
+
const isStaticIsland = node instanceof AreComponentNode && !!node.content && isStaticMarkup(node.content);
|
|
2530
|
+
if (isStaticIsland) {
|
|
2531
|
+
node.markStatic(node.content);
|
|
2532
|
+
} else {
|
|
2533
|
+
super.tokenize(node, context, logger);
|
|
2534
|
+
}
|
|
2070
2535
|
context.startPerformance("attributeExtraction");
|
|
2071
2536
|
const attributes = this.extractAttributes(node.markup);
|
|
2072
2537
|
for (const attr of attributes) {
|
|
@@ -2143,46 +2608,39 @@ var AreHTMLLifecycle = class extends AreLifecycle {
|
|
|
2143
2608
|
mount(node, scene, logger, ...args) {
|
|
2144
2609
|
logger?.debug(`[Mount] Component Trigger for <${node.aseid.entity}> with aseid :{${node.aseid.toString()}}`);
|
|
2145
2610
|
if (scene.isInactive) return;
|
|
2146
|
-
node.
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
if (frame.entered) {
|
|
2155
|
-
stack.pop();
|
|
2156
|
-
current.call(AreNodeFeatures.onAfterMount, current.scope);
|
|
2157
|
-
return;
|
|
2158
|
-
}
|
|
2159
|
-
frame.entered = true;
|
|
2160
|
-
current.call(AreNodeFeatures.onBeforeMount, current.scope);
|
|
2161
|
-
if (!current.scene.isInactive) {
|
|
2162
|
-
current.interpret();
|
|
2163
|
-
for (let i = current.children.length - 1; i >= 0; i--) {
|
|
2164
|
-
stack.push({ node: current.children[i], entered: false });
|
|
2165
|
-
}
|
|
2611
|
+
const context = node.scope.resolve(AreHTMLEngineContext);
|
|
2612
|
+
context?.beginBatch();
|
|
2613
|
+
const afterMountQueue = [];
|
|
2614
|
+
try {
|
|
2615
|
+
node.interpret();
|
|
2616
|
+
const stack = [];
|
|
2617
|
+
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
2618
|
+
stack.push({ node: node.children[i], entered: false });
|
|
2166
2619
|
}
|
|
2167
|
-
};
|
|
2168
|
-
const drive = () => {
|
|
2169
|
-
const start = AreSchedulerHelper.now();
|
|
2170
2620
|
while (stack.length > 0) {
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2621
|
+
const frame = stack[stack.length - 1];
|
|
2622
|
+
const current = frame.node;
|
|
2623
|
+
if (frame.entered) {
|
|
2624
|
+
stack.pop();
|
|
2625
|
+
afterMountQueue.push(current);
|
|
2626
|
+
continue;
|
|
2627
|
+
}
|
|
2628
|
+
frame.entered = true;
|
|
2629
|
+
current.call(AreNodeFeatures.onBeforeMount, current.scope);
|
|
2630
|
+
if (!current.scene.isInactive) {
|
|
2631
|
+
current.interpret();
|
|
2632
|
+
for (let i = current.children.length - 1; i >= 0; i--) {
|
|
2633
|
+
stack.push({ node: current.children[i], entered: false });
|
|
2634
|
+
}
|
|
2182
2635
|
}
|
|
2183
2636
|
}
|
|
2184
|
-
}
|
|
2185
|
-
|
|
2637
|
+
} finally {
|
|
2638
|
+
context?.endBatch();
|
|
2639
|
+
}
|
|
2640
|
+
for (let i = 0; i < afterMountQueue.length; i++) {
|
|
2641
|
+
const mounted = afterMountQueue[i];
|
|
2642
|
+
mounted.call(AreNodeFeatures.onAfterMount, mounted.scope);
|
|
2643
|
+
}
|
|
2186
2644
|
}
|
|
2187
2645
|
updateDirectiveAttribute(directive, scope, feature, logger, ...args) {
|
|
2188
2646
|
if (directive.component) {
|
|
@@ -2192,13 +2650,6 @@ var AreHTMLLifecycle = class extends AreLifecycle {
|
|
|
2192
2650
|
}
|
|
2193
2651
|
}
|
|
2194
2652
|
};
|
|
2195
|
-
/**
|
|
2196
|
-
* Per-chunk time budget (ms) for the time-sliced initial mount walk. While
|
|
2197
|
-
* mounting a large subtree we keep applying nodes until this much wall-clock
|
|
2198
|
-
* time has elapsed, then yield to the browser so it can paint and process
|
|
2199
|
-
* input before the next chunk. ~16ms targets a single animation frame.
|
|
2200
|
-
*/
|
|
2201
|
-
AreHTMLLifecycle.MOUNT_BUDGET_MS = 16;
|
|
2202
2653
|
__decorateClass([
|
|
2203
2654
|
AreLifecycle.Init(AreComponentNode),
|
|
2204
2655
|
__decorateParam(0, A_Inject(A_Caller)),
|
|
@@ -2834,6 +3285,6 @@ AreRouteWatcher = __decorateClass([
|
|
|
2834
3285
|
})
|
|
2835
3286
|
], AreRouteWatcher);
|
|
2836
3287
|
|
|
2837
|
-
export { AddAttributeInstruction, AddElementInstruction, AddInterpolationInstruction, AddListenerInstruction, AddStyleInstruction, AddTextInstruction, AreBindingAttribute, AreComment, AreComponentNode, AreDirective, AreDirectiveAttribute, AreDirectiveContext, AreDirectiveFeatures, AreDirectiveFor, AreDirectiveIf, AreDirectiveMeta, AreDirectiveShow, AreEventAttribute, AreHTMLAttribute, AreHTMLCompiler, AreHTMLEngine, AreHTMLEngineContext, AreHTMLInstructions, AreHTMLInterpreter, AreHTMLLifecycle, AreHTMLNode, AreHTMLTokenizer, AreHTMLTransformer, AreInterpolation, AreRoot, AreRootCache, AreRootNode, AreRoute, AreRouteWatcher, AreStaticAttribute, AreStyle, AreText, BOOLEAN_ATTRIBUTES, HideElementInstruction, IDL_FORM_PROPERTIES, LISTENER_OPTION_MODIFIERS, SVG_ATTRIBUTE_NS, SVG_NAMESPACE, VOID_ELEMENTS, isBooleanAttribute, isIDLFormProperty, isVoidElement, normalizeClassValue, normalizeStyleValue, parseEventName, toDOMString };
|
|
3288
|
+
export { AddAttributeInstruction, AddElementInstruction, AddInterpolationInstruction, AddListenerInstruction, AddStaticHTMLInstruction, AddStyleInstruction, AddTextInstruction, AreBindingAttribute, AreComment, AreComponentNode, AreDirective, AreDirectiveAttribute, AreDirectiveContext, AreDirectiveFeatures, AreDirectiveFor, AreDirectiveIf, AreDirectiveMeta, AreDirectiveShow, AreEventAttribute, AreHTMLAttribute, AreHTMLCompiler, AreHTMLEngine, AreHTMLEngineContext, AreHTMLInstructions, AreHTMLInterpreter, AreHTMLLifecycle, AreHTMLNode, AreHTMLTokenizer, AreHTMLTransformer, AreInterpolation, AreRoot, AreRootCache, AreRootNode, AreRoute, AreRouteWatcher, AreStaticAttribute, AreStyle, AreText, BOOLEAN_ATTRIBUTES, HideElementInstruction, IDL_FORM_PROPERTIES, LISTENER_OPTION_MODIFIERS, STANDARD_HTML_TAGS, SVG_ATTRIBUTE_NS, SVG_NAMESPACE, VOID_ELEMENTS, isBooleanAttribute, isIDLFormProperty, isStaticMarkup, isVoidElement, normalizeClassValue, normalizeStyleValue, parseEventName, toDOMString };
|
|
2838
3289
|
//# sourceMappingURL=index.mjs.map
|
|
2839
3290
|
//# sourceMappingURL=index.mjs.map
|