@adaas/are-html 0.0.21 → 0.0.23
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/.conf/tsconfig.base.json +1 -0
- package/.conf/tsconfig.browser.json +1 -0
- package/.conf/tsconfig.node.json +1 -0
- package/dist/browser/index.d.mts +214 -3
- package/dist/browser/index.mjs +787 -201
- 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 +55 -2
- package/dist/node/directives/AreDirectiveFor.directive.d.ts +55 -2
- package/dist/node/directives/AreDirectiveFor.directive.js +141 -12
- package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.mjs +141 -12
- package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.mts +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.ts +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 +1 -1
- package/dist/node/engine/AreHTML.compiler.d.ts +1 -1
- package/dist/node/engine/AreHTML.compiler.js +4 -0
- package/dist/node/engine/AreHTML.compiler.js.map +1 -1
- package/dist/node/engine/AreHTML.compiler.mjs +4 -0
- 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 +2 -2
- package/dist/node/engine/AreHTML.lifecycle.d.ts +2 -2
- package/dist/node/engine/AreHTML.lifecycle.js +32 -4
- package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.mjs +32 -4
- 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/helpers/AreScheduler.helper.d.mts +39 -0
- package/dist/node/helpers/AreScheduler.helper.d.ts +39 -0
- package/dist/node/helpers/AreScheduler.helper.js +40 -0
- package/dist/node/helpers/AreScheduler.helper.js.map +1 -0
- package/dist/node/helpers/AreScheduler.helper.mjs +40 -0
- package/dist/node/helpers/AreScheduler.helper.mjs.map +1 -0
- 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 +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.mjs +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/{mq19zxz4-mnlgmd.js → mqiw5sqa-ypckmj.js} +2275 -1323
- package/examples/dashboard/src/concept.ts +3 -2
- package/examples/for-perf/concept.ts +45 -0
- package/examples/for-perf/containers/UI.container.ts +161 -0
- package/examples/for-perf/dist/index.html +270 -0
- package/examples/for-perf/dist/mqj1mpf2-z4aokv.js +15664 -0
- package/examples/for-perf/dist/mqj1mpff-4fr7mw.js +15664 -0
- package/examples/for-perf/public/index.html +270 -0
- package/examples/for-perf/src/components/PerfApp.component.ts +37 -0
- package/examples/for-perf/src/components/PerfControls.component.ts +34 -0
- package/examples/for-perf/src/components/PerfGrid.component.ts +225 -0
- package/examples/for-perf/src/components/PerfHeader.component.ts +34 -0
- package/examples/for-perf/src/components/PerfStats.component.ts +43 -0
- package/examples/for-perf/src/concept.ts +94 -0
- package/examples/jumpstart/dist/index.html +1 -1
- package/examples/jumpstart/dist/{mq7hqrxy-4kus50.js → mq7mgf58-vbf07e.js} +269 -91
- package/examples/signal-routing/dist/index.html +1 -1
- package/examples/signal-routing/dist/{mq7k53th-qiwy4x.js → mqiwo23h-bhcolu.js} +2090 -1430
- package/jest.config.ts +1 -0
- package/package.json +10 -9
- package/src/directives/AreDirectiveFor.directive.ts +233 -19
- package/src/engine/AreHTML.compiler.ts +13 -0
- 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 +91 -7
- package/src/engine/AreHTML.tokenizer.ts +30 -1
- package/src/helpers/AreScheduler.helper.ts +61 -0
- 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 +4 -1
- package/tests/StaticIsland.test.ts +115 -0
- package/tsconfig.json +1 -0
package/dist/browser/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AreStore, AreScene, AreSyntax, AreCompiler, AreInterpreter, AreInstructionDefaultNames, AreNodeFeatures, AreContext, AreLifecycle, AreSignalsContext, AreAttributeFeatures, Are, AreNode, AreDeclaration, AreAttribute,
|
|
1
|
+
import { AreStore, AreScene, AreSyntax, AreCompiler, AreInterpreter, AreInstructionDefaultNames, AreNodeFeatures, AreContext, AreLifecycle, AreSignalsContext, AreAttributeFeatures, Are, AreCompilerError, AreNode, AreDeclaration, AreAttribute, AreMutation, AreSignal, AreInterpreterError, AreTokenizer, AreTransformer, AreEngine, AreSignals, AreEvent } from '@adaas/are';
|
|
2
2
|
import { A_Frame } from '@adaas/a-frame/core';
|
|
3
3
|
import { A_Inject, A_Caller, A_Feature, A_Meta, A_Scope, A_Dependency, A_Component, A_Context, A_ComponentMeta, A_FormatterHelper, A_Fragment } from '@adaas/a-concept';
|
|
4
4
|
import { A_Logger } from '@adaas/a-utils/a-logger';
|
|
@@ -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
|
|
|
@@ -236,6 +237,317 @@ var AreDirectiveContext = class extends A_ExecutionContext {
|
|
|
236
237
|
this.scope = {};
|
|
237
238
|
}
|
|
238
239
|
};
|
|
240
|
+
|
|
241
|
+
// src/helpers/AreScheduler.helper.ts
|
|
242
|
+
var AreSchedulerHelper = class {
|
|
243
|
+
/**
|
|
244
|
+
* High-resolution wall-clock time in milliseconds. Uses `performance.now()`
|
|
245
|
+
* when available (monotonic, sub-millisecond), falling back to `Date.now()`.
|
|
246
|
+
*/
|
|
247
|
+
static now() {
|
|
248
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Schedule `fn` to run on the next macrotask.
|
|
252
|
+
*
|
|
253
|
+
* `MessageChannel` yields a true macrotask without the ~4ms clamp that nested
|
|
254
|
+
* `setTimeout(0)` calls incur, so the browser can paint between chunks with
|
|
255
|
+
* minimal scheduling overhead. Falls back to `setTimeout` in non-DOM
|
|
256
|
+
* environments (e.g. tests / SSR).
|
|
257
|
+
*/
|
|
258
|
+
static scheduleMacrotask(fn) {
|
|
259
|
+
if (typeof MessageChannel === "undefined") {
|
|
260
|
+
setTimeout(fn, 0);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (!this._channel) {
|
|
264
|
+
this._channel = new MessageChannel();
|
|
265
|
+
this._channel.port1.onmessage = () => {
|
|
266
|
+
const next = this._queue.shift();
|
|
267
|
+
if (next) next();
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
this._queue.push(fn);
|
|
271
|
+
this._channel.port2.postMessage(null);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
/** FIFO queue of callbacks waiting for their posted macrotask to fire. */
|
|
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);
|
|
549
|
+
|
|
550
|
+
// src/directives/AreDirectiveFor.directive.ts
|
|
239
551
|
var AreDirectiveFor = class extends AreDirective {
|
|
240
552
|
transform(attribute, scope, store, scene, logger, ...args) {
|
|
241
553
|
logger.debug(`[Transform] directive $FOR for <${attribute.owner.aseid.toString()}>`);
|
|
@@ -249,7 +561,8 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
249
561
|
node.init();
|
|
250
562
|
attribute.template = forTemplate;
|
|
251
563
|
const { key, index, arrayExpr } = this.parseExpression(attribute.content);
|
|
252
|
-
const
|
|
564
|
+
const contextScope = attribute.owner.scope.resolve(AreDirectiveContext)?.scope || {};
|
|
565
|
+
const array = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
|
|
253
566
|
attribute.value = array;
|
|
254
567
|
for (let i = 0; i < array.length; i++) {
|
|
255
568
|
this.spawnItemNode(attribute.template, attribute.owner, key, index, array[i], i);
|
|
@@ -264,9 +577,28 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
264
577
|
scene.unPlan(hostInstruction);
|
|
265
578
|
}
|
|
266
579
|
update(attribute, store, scene, ...args) {
|
|
580
|
+
let state = AreDirectiveFor.renderState.get(attribute);
|
|
581
|
+
if (!state) {
|
|
582
|
+
state = { running: false, pending: false };
|
|
583
|
+
AreDirectiveFor.renderState.set(attribute, state);
|
|
584
|
+
}
|
|
585
|
+
if (state.running) {
|
|
586
|
+
state.pending = true;
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
return this.performUpdate(attribute, store, scene, state);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Core of the `$for` update: re-diff the source array against the current
|
|
593
|
+
* children, reconcile reused/removed items, then mount the new ones (small
|
|
594
|
+
* lists synchronously, large lists time-sliced). Never called while another
|
|
595
|
+
* pass for the same `$for` is in flight (see `update`).
|
|
596
|
+
*/
|
|
597
|
+
performUpdate(attribute, store, scene, state) {
|
|
267
598
|
const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);
|
|
268
|
-
const newArray = this.resolveArray(store, arrayExpr, attribute.content);
|
|
269
599
|
const owner = attribute.owner;
|
|
600
|
+
const contextScope = owner.scope.resolve(AreDirectiveContext)?.scope || {};
|
|
601
|
+
const newArray = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
|
|
270
602
|
const currentChildren = [...owner.children];
|
|
271
603
|
attribute.value = newArray;
|
|
272
604
|
const attached = this.isAttached(owner);
|
|
@@ -280,13 +612,17 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
280
612
|
childByKey.set(k, child);
|
|
281
613
|
remaining.add(child);
|
|
282
614
|
}
|
|
283
|
-
const
|
|
615
|
+
const toCreate = [];
|
|
616
|
+
const finalByKey = /* @__PURE__ */ new Map();
|
|
617
|
+
const orderedKeys = new Array(newArray.length);
|
|
284
618
|
for (let i = 0; i < newArray.length; i++) {
|
|
285
619
|
const item = newArray[i];
|
|
286
620
|
const k = computeKey(item, i);
|
|
621
|
+
orderedKeys[i] = k;
|
|
287
622
|
const existing = childByKey.get(k);
|
|
288
623
|
if (existing) {
|
|
289
624
|
remaining.delete(existing);
|
|
625
|
+
finalByKey.set(k, existing);
|
|
290
626
|
let directiveContext = existing.scope.resolveFlat(AreDirectiveContext);
|
|
291
627
|
if (!directiveContext) {
|
|
292
628
|
directiveContext = new AreDirectiveContext(existing.aseid);
|
|
@@ -298,18 +634,88 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
298
634
|
[index || "index"]: i
|
|
299
635
|
};
|
|
300
636
|
} else {
|
|
301
|
-
|
|
302
|
-
newOnes.push(itemNode);
|
|
637
|
+
toCreate.push({ item, idx: i, key: k });
|
|
303
638
|
}
|
|
304
639
|
}
|
|
305
640
|
for (const child of remaining) {
|
|
306
641
|
if (attached) child.unmount();
|
|
307
642
|
owner.removeChild(child);
|
|
308
643
|
}
|
|
309
|
-
|
|
644
|
+
const createItem = (desc) => {
|
|
645
|
+
const child = this.spawnItemNode(attribute.template, owner, key, index, desc.item, desc.idx);
|
|
646
|
+
finalByKey.set(desc.key, child);
|
|
310
647
|
child.transform();
|
|
311
648
|
child.compile();
|
|
312
649
|
if (attached) child.mount();
|
|
650
|
+
};
|
|
651
|
+
if (toCreate.length <= AreDirectiveFor.SYNC_THRESHOLD) {
|
|
652
|
+
for (const desc of toCreate) createItem(desc);
|
|
653
|
+
if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
|
|
654
|
+
return this.finishUpdate(attribute, store, scene, state);
|
|
655
|
+
}
|
|
656
|
+
state.running = true;
|
|
657
|
+
let cursor = 0;
|
|
658
|
+
const processChunk = () => {
|
|
659
|
+
try {
|
|
660
|
+
const start = AreSchedulerHelper.now();
|
|
661
|
+
while (cursor < toCreate.length) {
|
|
662
|
+
createItem(toCreate[cursor]);
|
|
663
|
+
cursor++;
|
|
664
|
+
if (AreSchedulerHelper.now() - start >= AreDirectiveFor.CHUNK_BUDGET_MS) break;
|
|
665
|
+
}
|
|
666
|
+
} catch (error) {
|
|
667
|
+
state.running = false;
|
|
668
|
+
state.pending = false;
|
|
669
|
+
throw error;
|
|
670
|
+
}
|
|
671
|
+
if (cursor < toCreate.length) {
|
|
672
|
+
return new Promise((resolve) => {
|
|
673
|
+
AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
|
|
677
|
+
return this.finishUpdate(attribute, store, scene, state);
|
|
678
|
+
};
|
|
679
|
+
return processChunk();
|
|
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
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Completes an update pass. If another update() arrived while a chunked
|
|
711
|
+
* render was streaming, run exactly one more pass now from the latest store
|
|
712
|
+
* value so the final DOM always reflects the most recent data.
|
|
713
|
+
*/
|
|
714
|
+
finishUpdate(attribute, store, scene, state) {
|
|
715
|
+
state.running = false;
|
|
716
|
+
if (state.pending) {
|
|
717
|
+
state.pending = false;
|
|
718
|
+
return this.performUpdate(attribute, store, scene, state);
|
|
313
719
|
}
|
|
314
720
|
}
|
|
315
721
|
/**
|
|
@@ -390,13 +796,23 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
390
796
|
* Supports both plain key lookups and function-call expressions:
|
|
391
797
|
* items → store.get('items')
|
|
392
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.
|
|
393
805
|
*/
|
|
394
|
-
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
|
+
};
|
|
395
811
|
let result;
|
|
396
812
|
const callMatch = arrayExpr.match(/^([^(]+)\((.+)\)$/);
|
|
397
813
|
if (callMatch) {
|
|
398
814
|
const fnName = callMatch[1].trim();
|
|
399
|
-
const fn =
|
|
815
|
+
const fn = getRoot(fnName);
|
|
400
816
|
if (typeof fn !== "function")
|
|
401
817
|
throw new AreCompilerError({
|
|
402
818
|
title: 'Invalid "for" Directive Function',
|
|
@@ -410,25 +826,25 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
410
826
|
const stripped = arg.replace(/\?$/, "");
|
|
411
827
|
if (stripped.includes(".")) {
|
|
412
828
|
const parts = stripped.split(".").map((p) => p.replace(/\?$/, ""));
|
|
413
|
-
let val =
|
|
829
|
+
let val = getRoot(parts[0]);
|
|
414
830
|
for (let j = 1; j < parts.length; j++) {
|
|
415
831
|
if (val == null) return void 0;
|
|
416
832
|
val = val[parts[j]];
|
|
417
833
|
}
|
|
418
834
|
return val ?? void 0;
|
|
419
835
|
}
|
|
420
|
-
return
|
|
836
|
+
return getRoot(stripped);
|
|
421
837
|
});
|
|
422
838
|
result = fn(...resolvedArgs);
|
|
423
839
|
} else if (arrayExpr.includes(".")) {
|
|
424
840
|
const parts = arrayExpr.split(".").map((p) => p.replace(/\?$/, ""));
|
|
425
|
-
result =
|
|
841
|
+
result = getRoot(parts[0]);
|
|
426
842
|
for (let i = 1; i < parts.length; i++) {
|
|
427
843
|
if (result == null) break;
|
|
428
844
|
result = result[parts[i]];
|
|
429
845
|
}
|
|
430
846
|
} else {
|
|
431
|
-
result =
|
|
847
|
+
result = getRoot(arrayExpr);
|
|
432
848
|
}
|
|
433
849
|
if (result == null) return [];
|
|
434
850
|
if (!Array.isArray(result))
|
|
@@ -470,6 +886,29 @@ var AreDirectiveFor = class extends AreDirective {
|
|
|
470
886
|
return itemNode;
|
|
471
887
|
}
|
|
472
888
|
};
|
|
889
|
+
/**
|
|
890
|
+
* Lists whose number of NEW item nodes is at or below this threshold render
|
|
891
|
+
* fully synchronously — byte-for-byte the previous behavior. Typical UIs
|
|
892
|
+
* (menus, small tables) are therefore completely unaffected; only genuinely
|
|
893
|
+
* large lists pay the (tiny) scheduling cost to keep the main thread responsive.
|
|
894
|
+
*/
|
|
895
|
+
AreDirectiveFor.SYNC_THRESHOLD = 100;
|
|
896
|
+
/**
|
|
897
|
+
* Per-chunk time budget (ms). During a large-list render we mount item nodes
|
|
898
|
+
* until this much time has elapsed, then yield to the browser so it can paint
|
|
899
|
+
* and process input before the next chunk. ~16ms targets one animation frame.
|
|
900
|
+
*/
|
|
901
|
+
AreDirectiveFor.CHUNK_BUDGET_MS = 16;
|
|
902
|
+
/**
|
|
903
|
+
* Per-attribute serialization state. A new update() that arrives while a
|
|
904
|
+
* chunked render of the SAME `$for` is still in flight does NOT start a second
|
|
905
|
+
* concurrent pass (which could interleave mutations on the shared children
|
|
906
|
+
* list); instead it marks `pending` and the in-flight run re-runs once more
|
|
907
|
+
* with the latest data when it finishes. This guarantees the children list is
|
|
908
|
+
* only ever mutated by one pass at a time and the final state always reflects
|
|
909
|
+
* the most recent store value.
|
|
910
|
+
*/
|
|
911
|
+
AreDirectiveFor.renderState = /* @__PURE__ */ new WeakMap();
|
|
473
912
|
__decorateClass([
|
|
474
913
|
AreDirective.Transform,
|
|
475
914
|
__decorateParam(0, A_Inject(A_Caller)),
|
|
@@ -705,6 +1144,21 @@ AddListenerInstruction = __decorateClass([
|
|
|
705
1144
|
description: "Attaches a DOM event listener to an element. Apply calls addEventListener; revert calls removeEventListener."
|
|
706
1145
|
})
|
|
707
1146
|
], AddListenerInstruction);
|
|
1147
|
+
var AddStaticHTMLInstruction = class extends AreMutation {
|
|
1148
|
+
constructor(parent, props) {
|
|
1149
|
+
if ("aseid" in props) {
|
|
1150
|
+
super(props);
|
|
1151
|
+
} else {
|
|
1152
|
+
super(AreHTMLInstructions.AddStaticHTML, parent, props);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
AddStaticHTMLInstruction = __decorateClass([
|
|
1157
|
+
A_Frame.Define({
|
|
1158
|
+
namespace: "a-are-html",
|
|
1159
|
+
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.'
|
|
1160
|
+
})
|
|
1161
|
+
], AddStaticHTMLInstruction);
|
|
708
1162
|
var AddStyleInstruction = class extends AreMutation {
|
|
709
1163
|
constructor(parent, props) {
|
|
710
1164
|
if ("aseid" in props) {
|
|
@@ -759,6 +1213,57 @@ var AreHTMLNode = class extends AreNode {
|
|
|
759
1213
|
get tag() {
|
|
760
1214
|
return this.aseid.entity;
|
|
761
1215
|
}
|
|
1216
|
+
/**
|
|
1217
|
+
* The verbatim inner markup captured when this node was identified as a
|
|
1218
|
+
* static island, or `undefined` for ordinary (per-node) nodes.
|
|
1219
|
+
*/
|
|
1220
|
+
get staticInnerHTML() {
|
|
1221
|
+
return this._staticInnerHTML;
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Whether this node is a static-island root (see `_staticInnerHTML`).
|
|
1225
|
+
*/
|
|
1226
|
+
get isStaticIsland() {
|
|
1227
|
+
return this._staticInnerHTML !== void 0;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Marks this node as a static-island root, capturing the verbatim inner
|
|
1231
|
+
* markup to be materialised in one shot by the interpreter. Called by the
|
|
1232
|
+
* tokenizer when the node's inner content is detected to be fully static.
|
|
1233
|
+
*/
|
|
1234
|
+
markStatic(innerHTML) {
|
|
1235
|
+
this._staticInnerHTML = innerHTML;
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Deep-clone the node. Overridden to carry over the static-island marker
|
|
1239
|
+
* (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
|
|
1240
|
+
* copied by the base AreNode.clone(). Without this, cloning a directive
|
|
1241
|
+
* template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
|
|
1242
|
+
* would drop the captured inner markup and render an empty element. The
|
|
1243
|
+
* base clone() recurses via each child's polymorphic clone(), so nested
|
|
1244
|
+
* island children are preserved automatically through this override.
|
|
1245
|
+
*/
|
|
1246
|
+
clone() {
|
|
1247
|
+
const cloned = super.clone();
|
|
1248
|
+
const self = this;
|
|
1249
|
+
if (self._staticInnerHTML !== void 0)
|
|
1250
|
+
cloned.markStatic(self._staticInnerHTML);
|
|
1251
|
+
return cloned;
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Clone the node while transferring its existing scope to the clone (used by
|
|
1255
|
+
* the $if/$for directives to turn the original node into a lightweight group
|
|
1256
|
+
* container). Overridden for the same reason as `clone()`: the static-island
|
|
1257
|
+
* marker must survive so a directive applied to an island root keeps its
|
|
1258
|
+
* inner markup.
|
|
1259
|
+
*/
|
|
1260
|
+
cloneWithScope() {
|
|
1261
|
+
const cloned = super.cloneWithScope();
|
|
1262
|
+
const self = this;
|
|
1263
|
+
if (self._staticInnerHTML !== void 0)
|
|
1264
|
+
cloned.markStatic(self._staticInnerHTML);
|
|
1265
|
+
return cloned;
|
|
1266
|
+
}
|
|
762
1267
|
/**
|
|
763
1268
|
* 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.
|
|
764
1269
|
*
|
|
@@ -1057,184 +1562,176 @@ function toDOMString(value) {
|
|
|
1057
1562
|
return "";
|
|
1058
1563
|
}
|
|
1059
1564
|
}
|
|
1060
|
-
var
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1565
|
+
var STANDARD_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
1566
|
+
// root / sections
|
|
1567
|
+
"html",
|
|
1568
|
+
"body",
|
|
1569
|
+
"header",
|
|
1570
|
+
"footer",
|
|
1571
|
+
"main",
|
|
1572
|
+
"nav",
|
|
1573
|
+
"section",
|
|
1574
|
+
"article",
|
|
1575
|
+
"aside",
|
|
1576
|
+
"address",
|
|
1577
|
+
"hgroup",
|
|
1578
|
+
// headings
|
|
1579
|
+
"h1",
|
|
1580
|
+
"h2",
|
|
1581
|
+
"h3",
|
|
1582
|
+
"h4",
|
|
1583
|
+
"h5",
|
|
1584
|
+
"h6",
|
|
1585
|
+
// grouping
|
|
1586
|
+
"div",
|
|
1587
|
+
"p",
|
|
1588
|
+
"span",
|
|
1589
|
+
"pre",
|
|
1590
|
+
"blockquote",
|
|
1591
|
+
"figure",
|
|
1592
|
+
"figcaption",
|
|
1593
|
+
"hr",
|
|
1594
|
+
"br",
|
|
1595
|
+
"wbr",
|
|
1596
|
+
// lists
|
|
1597
|
+
"ul",
|
|
1598
|
+
"ol",
|
|
1599
|
+
"li",
|
|
1600
|
+
"dl",
|
|
1601
|
+
"dt",
|
|
1602
|
+
"dd",
|
|
1603
|
+
"menu",
|
|
1604
|
+
// text-level / phrasing
|
|
1605
|
+
"a",
|
|
1606
|
+
"b",
|
|
1607
|
+
"i",
|
|
1608
|
+
"u",
|
|
1609
|
+
"s",
|
|
1610
|
+
"em",
|
|
1611
|
+
"strong",
|
|
1612
|
+
"small",
|
|
1613
|
+
"mark",
|
|
1614
|
+
"abbr",
|
|
1615
|
+
"cite",
|
|
1616
|
+
"q",
|
|
1617
|
+
"code",
|
|
1618
|
+
"kbd",
|
|
1619
|
+
"samp",
|
|
1620
|
+
"var",
|
|
1621
|
+
"sub",
|
|
1622
|
+
"sup",
|
|
1623
|
+
"time",
|
|
1624
|
+
"data",
|
|
1625
|
+
"dfn",
|
|
1626
|
+
"bdi",
|
|
1627
|
+
"bdo",
|
|
1628
|
+
"ruby",
|
|
1629
|
+
"rt",
|
|
1630
|
+
"rp",
|
|
1631
|
+
"del",
|
|
1632
|
+
"ins",
|
|
1633
|
+
// media / embedded (no special namespace handling needed)
|
|
1634
|
+
"img",
|
|
1635
|
+
"picture",
|
|
1636
|
+
"source",
|
|
1637
|
+
"figure",
|
|
1638
|
+
"audio",
|
|
1639
|
+
"video",
|
|
1640
|
+
"track",
|
|
1641
|
+
// tables
|
|
1642
|
+
"table",
|
|
1643
|
+
"caption",
|
|
1644
|
+
"colgroup",
|
|
1645
|
+
"col",
|
|
1646
|
+
"thead",
|
|
1647
|
+
"tbody",
|
|
1648
|
+
"tfoot",
|
|
1649
|
+
"tr",
|
|
1650
|
+
"th",
|
|
1651
|
+
"td",
|
|
1652
|
+
// forms (display only — these still render fine from innerHTML)
|
|
1653
|
+
"label",
|
|
1654
|
+
"fieldset",
|
|
1655
|
+
"legend",
|
|
1656
|
+
"datalist",
|
|
1657
|
+
"option",
|
|
1658
|
+
"optgroup",
|
|
1659
|
+
"output",
|
|
1660
|
+
"progress",
|
|
1661
|
+
"meter",
|
|
1662
|
+
// interactive
|
|
1663
|
+
"details",
|
|
1664
|
+
"summary",
|
|
1665
|
+
"dialog"
|
|
1666
|
+
]);
|
|
1667
|
+
function isStaticMarkup(inner) {
|
|
1668
|
+
if (!inner) return false;
|
|
1669
|
+
if (inner.indexOf("{{") !== -1) return false;
|
|
1670
|
+
const n = inner.length;
|
|
1671
|
+
let i = 0;
|
|
1672
|
+
while (i < n) {
|
|
1673
|
+
const lt = inner.indexOf("<", i);
|
|
1674
|
+
if (lt === -1) break;
|
|
1675
|
+
if (inner.startsWith("<!--", lt)) {
|
|
1676
|
+
const end = inner.indexOf("-->", lt + 4);
|
|
1677
|
+
if (end === -1) return false;
|
|
1678
|
+
i = end + 3;
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
if (inner[lt + 1] === "/" || inner[lt + 1] === "!" || inner[lt + 1] === "?") {
|
|
1682
|
+
const gt = inner.indexOf(">", lt);
|
|
1683
|
+
if (gt === -1) return false;
|
|
1684
|
+
i = gt + 1;
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
const nameMatch = /^<([a-zA-Z][a-zA-Z0-9-]*)/.exec(inner.slice(lt));
|
|
1688
|
+
if (!nameMatch) {
|
|
1689
|
+
i = lt + 1;
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
const tag = nameMatch[1].toLowerCase();
|
|
1693
|
+
if (tag.indexOf("-") !== -1 || !STANDARD_HTML_TAGS.has(tag)) return false;
|
|
1694
|
+
let j = lt + nameMatch[0].length;
|
|
1695
|
+
let inSingle = false;
|
|
1696
|
+
let inDouble = false;
|
|
1697
|
+
let atNameBoundary = true;
|
|
1698
|
+
let tagEnd = -1;
|
|
1699
|
+
while (j < n) {
|
|
1700
|
+
const ch = inner[j];
|
|
1701
|
+
if (inDouble) {
|
|
1702
|
+
if (ch === '"') inDouble = false;
|
|
1703
|
+
} else if (inSingle) {
|
|
1704
|
+
if (ch === "'") inSingle = false;
|
|
1705
|
+
} else if (ch === '"') {
|
|
1706
|
+
inDouble = true;
|
|
1707
|
+
atNameBoundary = false;
|
|
1708
|
+
} else if (ch === "'") {
|
|
1709
|
+
inSingle = true;
|
|
1710
|
+
atNameBoundary = false;
|
|
1711
|
+
} else if (ch === ">") {
|
|
1712
|
+
tagEnd = j;
|
|
1713
|
+
break;
|
|
1714
|
+
} else if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "/") {
|
|
1715
|
+
atNameBoundary = true;
|
|
1716
|
+
} else {
|
|
1717
|
+
if (atNameBoundary && (ch === "$" || ch === ":" || ch === "@")) {
|
|
1718
|
+
return false;
|
|
1163
1719
|
}
|
|
1720
|
+
atNameBoundary = false;
|
|
1164
1721
|
}
|
|
1722
|
+
j++;
|
|
1165
1723
|
}
|
|
1724
|
+
if (tagEnd === -1) return false;
|
|
1725
|
+
i = tagEnd + 1;
|
|
1166
1726
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
return this.index.groupToElements.get(instruction);
|
|
1170
|
-
} else {
|
|
1171
|
-
return this.index.groupToElements.get(instruction.aseid.toString());
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
/**
|
|
1175
|
-
* 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.
|
|
1176
|
-
*
|
|
1177
|
-
* @param element
|
|
1178
|
-
* @param eventName
|
|
1179
|
-
* @param listener
|
|
1180
|
-
*/
|
|
1181
|
-
addListener(element, eventName, listener) {
|
|
1182
|
-
if (!this.index.elementListeners.has(element)) {
|
|
1183
|
-
this.index.elementListeners.set(element, /* @__PURE__ */ new Map());
|
|
1184
|
-
}
|
|
1185
|
-
const byEvent = this.index.elementListeners.get(element);
|
|
1186
|
-
if (!byEvent.has(eventName)) {
|
|
1187
|
-
byEvent.set(eventName, /* @__PURE__ */ new Set());
|
|
1188
|
-
}
|
|
1189
|
-
byEvent.get(eventName).add(listener);
|
|
1190
|
-
}
|
|
1191
|
-
/**
|
|
1192
|
-
* 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.
|
|
1193
|
-
*
|
|
1194
|
-
* @param element
|
|
1195
|
-
* @param eventName
|
|
1196
|
-
* @returns
|
|
1197
|
-
*/
|
|
1198
|
-
getListener(element, eventName) {
|
|
1199
|
-
const set = this.index.elementListeners.get(element)?.get(eventName);
|
|
1200
|
-
if (!set || set.size === 0) return void 0;
|
|
1201
|
-
return set.values().next().value;
|
|
1202
|
-
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Returns all listeners registered for a given element + event name.
|
|
1205
|
-
*/
|
|
1206
|
-
getListeners(element, eventName) {
|
|
1207
|
-
return this.index.elementListeners.get(element)?.get(eventName);
|
|
1208
|
-
}
|
|
1209
|
-
/**
|
|
1210
|
-
* 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.
|
|
1211
|
-
*
|
|
1212
|
-
* @param element
|
|
1213
|
-
* @param eventName
|
|
1214
|
-
*/
|
|
1215
|
-
removeListener(element, eventName, listener) {
|
|
1216
|
-
const byEvent = this.index.elementListeners.get(element);
|
|
1217
|
-
if (!byEvent) return;
|
|
1218
|
-
if (listener) {
|
|
1219
|
-
const set = byEvent.get(eventName);
|
|
1220
|
-
if (set) {
|
|
1221
|
-
set.delete(listener);
|
|
1222
|
-
if (set.size === 0) byEvent.delete(eventName);
|
|
1223
|
-
}
|
|
1224
|
-
} else {
|
|
1225
|
-
byEvent.delete(eventName);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
};
|
|
1229
|
-
AreHTMLEngineContext = __decorateClass([
|
|
1230
|
-
A_Frame.Define({
|
|
1231
|
-
namespace: "a-are-html",
|
|
1232
|
-
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."
|
|
1233
|
-
})
|
|
1234
|
-
], AreHTMLEngineContext);
|
|
1727
|
+
return true;
|
|
1728
|
+
}
|
|
1235
1729
|
var AreHTMLCompiler = class extends AreCompiler {
|
|
1236
1730
|
compileHTMLNode(node, scene, logger, ...args) {
|
|
1237
1731
|
super.compile(node, scene, logger, ...args);
|
|
1732
|
+
if (node.isStaticIsland && scene.host) {
|
|
1733
|
+
scene.plan(new AddStaticHTMLInstruction(scene.host, { html: node.staticInnerHTML }));
|
|
1734
|
+
}
|
|
1238
1735
|
if (node.styles?.styles) {
|
|
1239
1736
|
const host = scene.host;
|
|
1240
1737
|
if (host) {
|
|
@@ -1429,12 +1926,15 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1429
1926
|
});
|
|
1430
1927
|
}
|
|
1431
1928
|
const element = isSVG ? context.container.createElementNS(SVG_NAMESPACE, tag) : context.container.createElement(tag);
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
} else {
|
|
1929
|
+
context.setInstructionElement(declaration, element);
|
|
1930
|
+
const attach = mountPoint.nodeType === Node.ELEMENT_NODE ? () => mountPoint.appendChild(element) : () => {
|
|
1435
1931
|
mountPoint.parentNode?.insertBefore(element, mountPoint);
|
|
1932
|
+
};
|
|
1933
|
+
if (context.isBatching && mountPoint.isConnected) {
|
|
1934
|
+
context.deferAttach(attach);
|
|
1935
|
+
} else {
|
|
1936
|
+
attach();
|
|
1436
1937
|
}
|
|
1437
|
-
context.setInstructionElement(declaration, element);
|
|
1438
1938
|
} else {
|
|
1439
1939
|
const mountPoint = context.container.getElementById(node.id);
|
|
1440
1940
|
if (!mountPoint) {
|
|
@@ -1444,8 +1944,15 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1444
1944
|
});
|
|
1445
1945
|
}
|
|
1446
1946
|
const element = isSVG ? context.container.createElementNS(SVG_NAMESPACE, tag) : context.container.createElement(tag);
|
|
1447
|
-
mountPoint.parentNode?.replaceChild(element, mountPoint);
|
|
1448
1947
|
context.setInstructionElement(declaration, element);
|
|
1948
|
+
const attach = () => {
|
|
1949
|
+
mountPoint.parentNode?.replaceChild(element, mountPoint);
|
|
1950
|
+
};
|
|
1951
|
+
if (context.isBatching && mountPoint.isConnected) {
|
|
1952
|
+
context.deferAttach(attach);
|
|
1953
|
+
} else {
|
|
1954
|
+
attach();
|
|
1955
|
+
}
|
|
1449
1956
|
}
|
|
1450
1957
|
logger?.debug("green", `Element ${node.aseid.toString()} added to Context:`);
|
|
1451
1958
|
} catch (error) {
|
|
@@ -1455,7 +1962,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1455
1962
|
}
|
|
1456
1963
|
removeElement(declaration, context) {
|
|
1457
1964
|
const element = context.getElementByInstruction(declaration);
|
|
1458
|
-
if (element && element.parentNode) {
|
|
1965
|
+
if (element && element.parentNode && element.isConnected) {
|
|
1459
1966
|
element.parentNode.removeChild(element);
|
|
1460
1967
|
}
|
|
1461
1968
|
context.removeInstructionElement(declaration);
|
|
@@ -1558,7 +2065,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1558
2065
|
const element = context.getElementByInstruction(mutation.parent);
|
|
1559
2066
|
if (!element) return;
|
|
1560
2067
|
const { name } = mutation.payload;
|
|
1561
|
-
if (name && element.nodeType === Node.ELEMENT_NODE) {
|
|
2068
|
+
if (name && element.nodeType === Node.ELEMENT_NODE && element.isConnected) {
|
|
1562
2069
|
const colonIdx = name.indexOf(":");
|
|
1563
2070
|
if (colonIdx > 0) {
|
|
1564
2071
|
const ns = SVG_ATTRIBUTE_NS[name.slice(0, colonIdx)];
|
|
@@ -1585,6 +2092,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1585
2092
|
showElement(mutation, context) {
|
|
1586
2093
|
const element = context.getElementByInstruction(mutation.parent);
|
|
1587
2094
|
if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
|
|
2095
|
+
if (!element.isConnected) return;
|
|
1588
2096
|
const el = element;
|
|
1589
2097
|
el.style.display = mutation.payload?.display ?? mutation.cache ?? "";
|
|
1590
2098
|
}
|
|
@@ -1679,7 +2187,9 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1679
2187
|
const { event: eventName } = parseEventName(name);
|
|
1680
2188
|
const listener = mutation.payload._callback;
|
|
1681
2189
|
if (listener) {
|
|
1682
|
-
element.
|
|
2190
|
+
if (element.isConnected) {
|
|
2191
|
+
element.removeEventListener(eventName, listener);
|
|
2192
|
+
}
|
|
1683
2193
|
context.removeListener(element, name, listener);
|
|
1684
2194
|
mutation.payload._callback = void 0;
|
|
1685
2195
|
}
|
|
@@ -1717,9 +2227,32 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1717
2227
|
removeText(declaration, context) {
|
|
1718
2228
|
const element = context.getElementByInstruction(declaration);
|
|
1719
2229
|
if (!element) return;
|
|
1720
|
-
element.
|
|
2230
|
+
if (element.isConnected) {
|
|
2231
|
+
element.parentNode?.removeChild(element);
|
|
2232
|
+
}
|
|
1721
2233
|
context.removeInstructionElement(declaration);
|
|
1722
2234
|
}
|
|
2235
|
+
addStaticHTML(mutation, context, logger) {
|
|
2236
|
+
const element = context.getElementByInstruction(mutation.parent);
|
|
2237
|
+
if (!element || element.nodeType !== Node.ELEMENT_NODE) {
|
|
2238
|
+
throw new AreInterpreterError({
|
|
2239
|
+
title: "Element Not Found",
|
|
2240
|
+
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.`
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
const el = element;
|
|
2244
|
+
const { html } = mutation.payload;
|
|
2245
|
+
el.textContent = "";
|
|
2246
|
+
const fragment = context.getStaticFragment(el.tagName.toLowerCase(), html);
|
|
2247
|
+
el.appendChild(fragment.cloneNode(true));
|
|
2248
|
+
logger?.debug("green", `Static island materialised onto <${(mutation.owner.parent ?? mutation.owner)?.aseid?.toString?.()}>`);
|
|
2249
|
+
}
|
|
2250
|
+
removeStaticHTML(mutation, context) {
|
|
2251
|
+
const element = context.getElementByInstruction(mutation.parent);
|
|
2252
|
+
if (element && element.nodeType === Node.ELEMENT_NODE && element.isConnected) {
|
|
2253
|
+
element.textContent = "";
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
1723
2256
|
addComment(declaration, context, store, syntax, directiveContext, logger) {
|
|
1724
2257
|
const node = declaration.owner.parent;
|
|
1725
2258
|
const { content, evaluate } = declaration.payload;
|
|
@@ -1753,7 +2286,9 @@ var AreHTMLInterpreter = class extends AreInterpreter {
|
|
|
1753
2286
|
removeComment(declaration, context) {
|
|
1754
2287
|
const element = context.getElementByInstruction(declaration);
|
|
1755
2288
|
if (!element) return;
|
|
1756
|
-
element.
|
|
2289
|
+
if (element.isConnected) {
|
|
2290
|
+
element.parentNode?.removeChild(element);
|
|
2291
|
+
}
|
|
1757
2292
|
context.removeInstructionElement(declaration);
|
|
1758
2293
|
}
|
|
1759
2294
|
addStyle(mutation, context, logger) {
|
|
@@ -1897,6 +2432,24 @@ __decorateClass([
|
|
|
1897
2432
|
__decorateParam(0, A_Inject(A_Caller)),
|
|
1898
2433
|
__decorateParam(1, A_Inject(AreHTMLEngineContext))
|
|
1899
2434
|
], AreHTMLInterpreter.prototype, "removeText", 1);
|
|
2435
|
+
__decorateClass([
|
|
2436
|
+
A_Frame.Define({
|
|
2437
|
+
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."
|
|
2438
|
+
}),
|
|
2439
|
+
AreInterpreter.Apply(AreHTMLInstructions.AddStaticHTML),
|
|
2440
|
+
AreInterpreter.Update(AreHTMLInstructions.AddStaticHTML),
|
|
2441
|
+
__decorateParam(0, A_Inject(A_Caller)),
|
|
2442
|
+
__decorateParam(1, A_Inject(AreHTMLEngineContext)),
|
|
2443
|
+
__decorateParam(2, A_Inject(A_Logger))
|
|
2444
|
+
], AreHTMLInterpreter.prototype, "addStaticHTML", 1);
|
|
2445
|
+
__decorateClass([
|
|
2446
|
+
A_Frame.Define({
|
|
2447
|
+
description: "Clear a static island's injected markup from its host element on revert."
|
|
2448
|
+
}),
|
|
2449
|
+
AreInterpreter.Revert(AreHTMLInstructions.AddStaticHTML),
|
|
2450
|
+
__decorateParam(0, A_Inject(A_Caller)),
|
|
2451
|
+
__decorateParam(1, A_Inject(AreHTMLEngineContext))
|
|
2452
|
+
], AreHTMLInterpreter.prototype, "removeStaticHTML", 1);
|
|
1900
2453
|
__decorateClass([
|
|
1901
2454
|
A_Frame.Define({
|
|
1902
2455
|
description: "Add a comment node to the DOM based on the provided declaration instruction."
|
|
@@ -1948,7 +2501,12 @@ var AreHTMLTokenizer = class extends AreTokenizer {
|
|
|
1948
2501
|
this.ATTR_PATTERN = /([$:@]?[\w.-]+(?::[\w.-]+)?)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>/"'=]+)))?/g;
|
|
1949
2502
|
}
|
|
1950
2503
|
tokenize(node, context, logger) {
|
|
1951
|
-
|
|
2504
|
+
const isStaticIsland = node instanceof AreComponentNode && !!node.content && isStaticMarkup(node.content);
|
|
2505
|
+
if (isStaticIsland) {
|
|
2506
|
+
node.markStatic(node.content);
|
|
2507
|
+
} else {
|
|
2508
|
+
super.tokenize(node, context, logger);
|
|
2509
|
+
}
|
|
1952
2510
|
context.startPerformance("attributeExtraction");
|
|
1953
2511
|
const attributes = this.extractAttributes(node.markup);
|
|
1954
2512
|
for (const attr of attributes) {
|
|
@@ -2025,10 +2583,38 @@ var AreHTMLLifecycle = class extends AreLifecycle {
|
|
|
2025
2583
|
mount(node, scene, logger, ...args) {
|
|
2026
2584
|
logger?.debug(`[Mount] Component Trigger for <${node.aseid.entity}> with aseid :{${node.aseid.toString()}}`);
|
|
2027
2585
|
if (scene.isInactive) return;
|
|
2028
|
-
node.
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2586
|
+
const context = node.scope.resolve(AreHTMLEngineContext);
|
|
2587
|
+
context?.beginBatch();
|
|
2588
|
+
const afterMountQueue = [];
|
|
2589
|
+
try {
|
|
2590
|
+
node.interpret();
|
|
2591
|
+
const stack = [];
|
|
2592
|
+
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
2593
|
+
stack.push({ node: node.children[i], entered: false });
|
|
2594
|
+
}
|
|
2595
|
+
while (stack.length > 0) {
|
|
2596
|
+
const frame = stack[stack.length - 1];
|
|
2597
|
+
const current = frame.node;
|
|
2598
|
+
if (frame.entered) {
|
|
2599
|
+
stack.pop();
|
|
2600
|
+
afterMountQueue.push(current);
|
|
2601
|
+
continue;
|
|
2602
|
+
}
|
|
2603
|
+
frame.entered = true;
|
|
2604
|
+
current.call(AreNodeFeatures.onBeforeMount, current.scope);
|
|
2605
|
+
if (!current.scene.isInactive) {
|
|
2606
|
+
current.interpret();
|
|
2607
|
+
for (let i = current.children.length - 1; i >= 0; i--) {
|
|
2608
|
+
stack.push({ node: current.children[i], entered: false });
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
} finally {
|
|
2613
|
+
context?.endBatch();
|
|
2614
|
+
}
|
|
2615
|
+
for (let i = 0; i < afterMountQueue.length; i++) {
|
|
2616
|
+
const mounted = afterMountQueue[i];
|
|
2617
|
+
mounted.call(AreNodeFeatures.onAfterMount, mounted.scope);
|
|
2032
2618
|
}
|
|
2033
2619
|
}
|
|
2034
2620
|
updateDirectiveAttribute(directive, scope, feature, logger, ...args) {
|
|
@@ -2469,7 +3055,7 @@ var AreRoot = class extends Are {
|
|
|
2469
3055
|
}
|
|
2470
3056
|
child.transform();
|
|
2471
3057
|
child.compile();
|
|
2472
|
-
child.mount();
|
|
3058
|
+
await child.mount();
|
|
2473
3059
|
}
|
|
2474
3060
|
}
|
|
2475
3061
|
/**
|
|
@@ -2674,6 +3260,6 @@ AreRouteWatcher = __decorateClass([
|
|
|
2674
3260
|
})
|
|
2675
3261
|
], AreRouteWatcher);
|
|
2676
3262
|
|
|
2677
|
-
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 };
|
|
3263
|
+
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 };
|
|
2678
3264
|
//# sourceMappingURL=index.mjs.map
|
|
2679
3265
|
//# sourceMappingURL=index.mjs.map
|