mount-observer 0.1.19 → 0.1.20

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.
@@ -333,57 +333,58 @@ export class HTMLIncludeHandler extends EvtRt {
333
333
  if (sourceRootNode === templateRootNode) {
334
334
  return;
335
335
  }
336
- // Find all MOSE scripts in the source element
337
- const sourceScripts = sourceElement.querySelectorAll('script[type="mountobserver"]');
338
- if (sourceScripts.length === 0) {
339
- return;
340
- }
341
- // Find all MOSE scripts in the clone
342
- let cloneScripts;
343
- if (clone instanceof Element) {
344
- cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
345
- }
346
- else if (clone instanceof DocumentFragment) {
347
- cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
348
- }
349
- else {
350
- return;
351
- }
352
- // Copy exports from source scripts to cloned scripts (matching by ID)
353
- for (let i = 0; i < sourceScripts.length; i++) {
354
- const sourceScript = sourceScripts[i];
355
- const sourceId = sourceScript.getAttribute('id');
356
- if (!sourceId)
336
+ const types = ['mountobserver', 'emc'];
337
+ for (const t of types) {
338
+ const qry = `script[type="${t}"]`;
339
+ // Find all MOSE scripts in the source element
340
+ const sourceScripts = sourceElement.querySelectorAll(qry);
341
+ if (sourceScripts.length === 0) {
357
342
  continue;
358
- // Find matching clone script by ID
359
- const cloneScript = Array.from(cloneScripts).find(s => s.getAttribute('id') === sourceId);
360
- if (!cloneScript)
343
+ }
344
+ // Find all MOSE scripts in the clone
345
+ let cloneScripts;
346
+ if (clone instanceof Element || clone instanceof DocumentFragment) {
347
+ cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
348
+ }
349
+ else {
361
350
  continue;
362
- // Check if source script has export
363
- let sourceExport = sourceScript.export;
364
- if (!sourceExport) {
365
- // Wait for the source script to resolve
366
- try {
367
- // Create a promise that waits for the resolved event
368
- const event = await new Promise((resolve, reject) => {
369
- const timeout = setTimeout(() => {
370
- reject(new Error('Timeout'));
371
- }, 5000);
372
- sourceScript.addEventListener('resolved', (e) => {
373
- clearTimeout(timeout);
374
- resolve(e);
375
- }, { once: true });
376
- });
377
- sourceExport = event.export;
378
- }
379
- catch (error) {
380
- console.warn(`HTMLInclude: Timeout waiting for MOSE script #${sourceId} to resolve`);
351
+ }
352
+ // Copy exports from source scripts to cloned scripts (matching by ID)
353
+ for (let i = 0; i < sourceScripts.length; i++) {
354
+ const sourceScript = sourceScripts[i];
355
+ const sourceId = sourceScript.getAttribute('id');
356
+ if (!sourceId)
381
357
  continue;
358
+ // Find matching clone script by ID
359
+ const cloneScript = Array.from(cloneScripts).find(s => s.getAttribute('id') === sourceId);
360
+ if (!cloneScript)
361
+ continue;
362
+ // Check if source script has export
363
+ let sourceExport = sourceScript.export;
364
+ if (!sourceExport) {
365
+ // Wait for the source script to resolve
366
+ try {
367
+ // Create a promise that waits for the resolved event
368
+ const event = await new Promise((resolve, reject) => {
369
+ const timeout = setTimeout(() => {
370
+ reject(new Error('Timeout'));
371
+ }, 5000);
372
+ sourceScript.addEventListener('resolved', (e) => {
373
+ clearTimeout(timeout);
374
+ resolve(e);
375
+ }, { once: true });
376
+ });
377
+ sourceExport = event.export;
378
+ }
379
+ catch (error) {
380
+ console.warn(`HTMLInclude: Timeout waiting for MOSE script #${sourceId} to resolve`);
381
+ continue;
382
+ }
383
+ }
384
+ // Copy export to cloned script
385
+ if (sourceExport) {
386
+ cloneScript.export = sourceExport;
382
387
  }
383
- }
384
- // Copy export to cloned script
385
- if (sourceExport) {
386
- cloneScript.export = sourceExport;
387
388
  }
388
389
  }
389
390
  }
@@ -31,19 +31,19 @@ function toQuery(el: Element): string {
31
31
  // Get the list of attributes to exclude from the selector
32
32
  const insertAttrs = el.getAttribute('-i');
33
33
  const excludeAttrs = new Set(['-i']); // Always exclude -i itself
34
-
34
+
35
35
  if (insertAttrs !== null) {
36
36
  const attrs = splitRefs(insertAttrs);
37
37
  attrs.forEach(attr => excludeAttrs.add(attr));
38
38
  }
39
-
39
+
40
40
  const classes = Array.from(el.classList).map(c => `.${c}`).join('');
41
41
  const parts = Array.from(el.part).map(p => `[part~="${p}"]`).join('');
42
42
  const attributes = Array.from(el.attributes)
43
43
  .filter(attr => !excludeAttrs.has(attr.name))
44
44
  .map(attr => `[${attr.name}="${attr.value}"]`)
45
45
  .join('');
46
- const {localName} = el;
46
+ const { localName } = el;
47
47
  return `${localName}${classes}${parts}${attributes}`;
48
48
  }
49
49
 
@@ -51,19 +51,19 @@ function toQuery(el: Element): string {
51
51
  * Prepares an element for insertion by extracting its children and insertion attributes.
52
52
  * Returns a DocumentFragment with the children and a map of attributes to insert.
53
53
  */
54
- function prepareForInsertion(el: Element): { fragment: DocumentFragment, attributeMap: {[key: string]: string} | null } {
54
+ function prepareForInsertion(el: Element): { fragment: DocumentFragment, attributeMap: { [key: string]: string } | null } {
55
55
  const fragment = new DocumentFragment();
56
56
  const clone = el.cloneNode(true) as Element;
57
-
57
+
58
58
  // Move all children to the fragment
59
59
  while (clone.firstChild) {
60
60
  fragment.appendChild(clone.firstChild);
61
61
  }
62
-
62
+
63
63
  // Check for -i attribute which specifies which attributes to insert
64
64
  const insertAttrs = el.getAttribute('-i');
65
- let attributeMap: {[key: string]: string} | null = null;
66
-
65
+ let attributeMap: { [key: string]: string } | null = null;
66
+
67
67
  if (insertAttrs !== null) {
68
68
  const attrs = splitRefs(insertAttrs);
69
69
  attributeMap = {};
@@ -74,7 +74,7 @@ function prepareForInsertion(el: Element): { fragment: DocumentFragment, attribu
74
74
  }
75
75
  }
76
76
  }
77
-
77
+
78
78
  return { fragment, attributeMap };
79
79
  }
80
80
 
@@ -84,14 +84,14 @@ function prepareForInsertion(el: Element): { fragment: DocumentFragment, attribu
84
84
  function applyInsertion(
85
85
  targetElement: Element,
86
86
  sourceFragment: DocumentFragment,
87
- attributeMap: {[key: string]: string} | null
87
+ attributeMap: { [key: string]: string } | null
88
88
  ): void {
89
89
  // Clone the fragment so it can be reused
90
90
  const fragmentClone = sourceFragment.cloneNode(true) as DocumentFragment;
91
-
91
+
92
92
  // Replace all children of the target element
93
93
  targetElement.replaceChildren(fragmentClone);
94
-
94
+
95
95
  // Update attributes if specified
96
96
  if (attributeMap !== null) {
97
97
  for (const key in attributeMap) {
@@ -151,7 +151,7 @@ function applyInsertion(
151
151
  export class HTMLIncludeHandler extends EvtRt {
152
152
  static matching = 'template[src^="#"]';
153
153
  static whereInstanceOf = HTMLTemplateElement;
154
-
154
+
155
155
  async mount(mountedElement: Element): Promise<void> {
156
156
  try {
157
157
  const template = mountedElement as HTMLTemplateElement;
@@ -210,7 +210,7 @@ export class HTMLIncludeHandler extends EvtRt {
210
210
  console.warn(`HTMLInclude: ${error}`);
211
211
  return;
212
212
  }
213
-
213
+
214
214
  // Optimization 4: Copy MOSE exports if cloning live element from different root
215
215
  if (isLiveElement) {
216
216
  await this.copyMoseExports(sourceElement, clone, rootNode);
@@ -278,27 +278,27 @@ export class HTMLIncludeHandler extends EvtRt {
278
278
  }
279
279
  }
280
280
 
281
-
281
+
282
282
  /**
283
283
  * Gets a cached element reference if available and still valid.
284
284
  */
285
285
  getCachedElement(rootNode: Node, id: string): Element | null {
286
286
  const rootCache = idCache.get(rootNode);
287
287
  if (!rootCache) return null;
288
-
288
+
289
289
  const weakRef = rootCache.get(id);
290
290
  if (!weakRef) return null;
291
-
291
+
292
292
  const element = weakRef.deref();
293
293
  if (!element) {
294
294
  // Element was garbage collected, remove from cache
295
295
  rootCache.delete(id);
296
296
  return null;
297
297
  }
298
-
298
+
299
299
  return element;
300
300
  }
301
-
301
+
302
302
  /**
303
303
  * Caches an element reference for future lookups.
304
304
  */
@@ -310,7 +310,7 @@ export class HTMLIncludeHandler extends EvtRt {
310
310
  }
311
311
  rootCache.set(id, new WeakRef(element));
312
312
  }
313
-
313
+
314
314
  /**
315
315
  * Processes matching insertions by finding elements in the cloned content that match
316
316
  * the selectors from template children and applying insertions to them.
@@ -320,13 +320,13 @@ export class HTMLIncludeHandler extends EvtRt {
320
320
  for (const templateChild of templateChildren) {
321
321
  // Generate a selector from the template child
322
322
  const selector = toQuery(templateChild);
323
-
323
+
324
324
  // Prepare the insertion content and attribute map
325
325
  const { fragment, attributeMap } = prepareForInsertion(templateChild);
326
-
326
+
327
327
  // Find all matching elements in the cloned content
328
328
  let matchingElements: Element[] = [];
329
-
329
+
330
330
  if (clonedContent instanceof Element) {
331
331
  // Check if the cloned element itself matches
332
332
  if (clonedContent.matches(selector)) {
@@ -339,14 +339,14 @@ export class HTMLIncludeHandler extends EvtRt {
339
339
  // Search within the fragment
340
340
  matchingElements = Array.from(clonedContent.querySelectorAll(selector));
341
341
  }
342
-
342
+
343
343
  // Apply insertion to each matching element
344
344
  for (const matchingElement of matchingElements) {
345
345
  applyInsertion(matchingElement, fragment, attributeMap);
346
346
  }
347
347
  }
348
348
  }
349
-
349
+
350
350
  /**
351
351
  * Clones content from the source element.
352
352
  * Priority: remoteContent (hoisted templates) > content (templates) > element itself
@@ -362,88 +362,93 @@ export class HTMLIncludeHandler extends EvtRt {
362
362
  console.warn('HTMLInclude: Failed to access remoteContent', e);
363
363
  }
364
364
  }
365
-
365
+
366
366
  // Check for content property (regular templates)
367
367
  if (sourceElement instanceof HTMLTemplateElement && sourceElement.content) {
368
368
  return { clone: sourceElement.content.cloneNode(true), isLiveElement: false };
369
369
  }
370
-
370
+
371
371
  // Clone the element itself (live DOM element)
372
372
  return { clone: sourceElement.cloneNode(true), isLiveElement: true };
373
373
  }
374
-
374
+
375
375
  /**
376
376
  * Copies MOSE script exports from source to cloned scripts.
377
377
  * This optimization avoids re-parsing JSON when cloning MOSE scripts across shadow boundaries.
378
378
  */
379
379
  async copyMoseExports(sourceElement: Element, clone: Node, templateRootNode: Node): Promise<void> {
380
380
  const sourceRootNode = sourceElement.getRootNode();
381
-
381
+
382
382
  // Only process if source and template are in different root nodes
383
383
  if (sourceRootNode === templateRootNode) {
384
384
  return;
385
385
  }
386
-
387
- // Find all MOSE scripts in the source element
388
- const sourceScripts = sourceElement.querySelectorAll('script[type="mountobserver"]');
389
-
390
- if (sourceScripts.length === 0) {
391
- return;
392
- }
393
-
394
- // Find all MOSE scripts in the clone
395
- let cloneScripts: NodeListOf<Element>;
396
- if (clone instanceof Element) {
397
- cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
398
- } else if (clone instanceof DocumentFragment) {
399
- cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
400
- } else {
401
- return;
402
- }
403
-
404
- // Copy exports from source scripts to cloned scripts (matching by ID)
405
- for (let i = 0; i < sourceScripts.length; i++) {
406
- const sourceScript = sourceScripts[i] as HTMLScriptElement;
407
- const sourceId = sourceScript.getAttribute('id');
408
-
409
- if (!sourceId) continue;
410
-
411
- // Find matching clone script by ID
412
- const cloneScript = Array.from(cloneScripts).find(
413
- s => s.getAttribute('id') === sourceId
414
- ) as HTMLScriptElement | undefined;
415
-
416
- if (!cloneScript) continue;
417
-
418
- // Check if source script has export
419
- let sourceExport = (sourceScript as any).export;
420
-
421
- if (!sourceExport) {
422
- // Wait for the source script to resolve
423
- try {
424
- // Create a promise that waits for the resolved event
425
- const event = await new Promise<Event>((resolve, reject) => {
426
- const timeout = setTimeout(() => {
427
- reject(new Error('Timeout'));
428
- }, 5000);
429
-
430
- sourceScript.addEventListener('resolved', (e) => {
431
- clearTimeout(timeout);
432
- resolve(e);
433
- }, { once: true });
434
- });
435
- sourceExport = (event as any).export;
436
- } catch (error) {
437
- console.warn(`HTMLInclude: Timeout waiting for MOSE script #${sourceId} to resolve`);
438
- continue;
439
- }
386
+
387
+ const types = ['mountobserver', 'emc'];
388
+
389
+ for (const t of types) {
390
+ const qry = `script[type="${t}"]`;
391
+ // Find all MOSE scripts in the source element
392
+ const sourceScripts = sourceElement.querySelectorAll(qry);
393
+
394
+ if (sourceScripts.length === 0) {
395
+ continue;
440
396
  }
441
-
442
- // Copy export to cloned script
443
- if (sourceExport) {
444
- (cloneScript as any).export = sourceExport;
397
+
398
+ // Find all MOSE scripts in the clone
399
+ let cloneScripts: NodeListOf<Element>;
400
+ if (clone instanceof Element || clone instanceof DocumentFragment) {
401
+ cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
402
+ } else {
403
+ continue;
404
+ }
405
+
406
+ // Copy exports from source scripts to cloned scripts (matching by ID)
407
+ for (let i = 0; i < sourceScripts.length; i++) {
408
+ const sourceScript = sourceScripts[i] as HTMLScriptElement;
409
+ const sourceId = sourceScript.getAttribute('id');
410
+
411
+ if (!sourceId) continue;
412
+
413
+ // Find matching clone script by ID
414
+ const cloneScript = Array.from(cloneScripts).find(
415
+ s => s.getAttribute('id') === sourceId
416
+ ) as HTMLScriptElement | undefined;
417
+
418
+ if (!cloneScript) continue;
419
+
420
+ // Check if source script has export
421
+ let sourceExport = (sourceScript as any).export;
422
+
423
+ if (!sourceExport) {
424
+ // Wait for the source script to resolve
425
+ try {
426
+ // Create a promise that waits for the resolved event
427
+ const event = await new Promise<Event>((resolve, reject) => {
428
+ const timeout = setTimeout(() => {
429
+ reject(new Error('Timeout'));
430
+ }, 5000);
431
+
432
+ sourceScript.addEventListener('resolved', (e) => {
433
+ clearTimeout(timeout);
434
+ resolve(e);
435
+ }, { once: true });
436
+ });
437
+ sourceExport = (event as any).export;
438
+ } catch (error) {
439
+ console.warn(`HTMLInclude: Timeout waiting for MOSE script #${sourceId} to resolve`);
440
+ continue;
441
+ }
442
+ }
443
+
444
+ // Copy export to cloned script
445
+ if (sourceExport) {
446
+ (cloneScript as any).export = sourceExport;
447
+ }
445
448
  }
446
449
  }
450
+
451
+
447
452
  }
448
453
  }
449
454
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",