mount-observer 0.0.68 → 0.0.70

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/MountObserver.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { RootMutObs } from './RootMutObs.js';
2
2
  import { bindish, bindishIt } from './bindish.js';
3
3
  export const guid = '5Pv6bHOVH0ae07opRZ8N/g';
4
+ export const wasItemReffed = Symbol.for('8aA6xB8+PkScmivaslBk5Q');
4
5
  export const mutationObserverLookup = new WeakMap();
5
6
  const refCount = new WeakMap();
6
7
  export class MountObserver extends EventTarget {
@@ -72,10 +73,10 @@ export class MountObserver extends EventTarget {
72
73
  }
73
74
  }
74
75
  #templLookUp = new Map();
75
- findByID(id, fragment) {
76
+ async findByID(id, fragment) {
76
77
  if (this.#templLookUp.has(id))
77
78
  return this.#templLookUp.get(id);
78
- let templ = fragment.getElementById(id);
79
+ let templ = fragment.querySelector(`#${id}`);
79
80
  if (templ === null) {
80
81
  let rootToSearchOutwardFrom = ((fragment.isConnected ? fragment.getRootNode() : this.#mountInit.withTargetShadowRoot) || document);
81
82
  templ = rootToSearchOutwardFrom.getElementById(id);
@@ -84,8 +85,34 @@ export class MountObserver extends EventTarget {
84
85
  templ = rootToSearchOutwardFrom.getElementById(id);
85
86
  }
86
87
  }
87
- if (templ !== null)
88
+ if (templ !== null) {
89
+ if (!(templ instanceof HTMLTemplateElement)) {
90
+ const newTempl = document.createElement('template');
91
+ const { getAdjRefs } = await import('./refid/getAdjRefs.js');
92
+ const adjRefs = getAdjRefs(templ);
93
+ // if(adjRefs.length > 1){
94
+ // (<any>newTempl)[wasItemReffed] = true;
95
+ // adjRefs[0].setAttribute('itemref', '<autogen>');
96
+ // }
97
+ const fragment = document.createDocumentFragment();
98
+ let first = true;
99
+ for (const adjRef of adjRefs) {
100
+ const clone = adjRef.cloneNode(true);
101
+ if (first && adjRefs.length > 1) {
102
+ clone.setAttribute('itemref', '<autogen>');
103
+ newTempl[wasItemReffed] = true;
104
+ first = false;
105
+ }
106
+ clone.removeAttribute('id');
107
+ fragment.appendChild(clone);
108
+ }
109
+ const { doCleanup } = await import('./doCleanup.js');
110
+ doCleanup(templ, fragment);
111
+ newTempl.content.appendChild(fragment);
112
+ templ = newTempl;
113
+ }
88
114
  this.#templLookUp.set(id, templ);
115
+ }
89
116
  return templ;
90
117
  }
91
118
  disconnect(within) {
package/MountObserver.ts CHANGED
@@ -10,6 +10,7 @@ import {RootMutObs} from './RootMutObs.js';
10
10
  import {bindish, bindishIt} from './bindish.js';
11
11
  export {MOSE} from './ts-refs/mount-observer/types';
12
12
  export const guid = '5Pv6bHOVH0ae07opRZ8N/g';
13
+ export const wasItemReffed = Symbol.for('8aA6xB8+PkScmivaslBk5Q');
13
14
 
14
15
  export const mutationObserverLookup = new WeakMap<Node, RootMutObs>();
15
16
  const refCount = new WeakMap<Node, number>();
@@ -87,9 +88,9 @@ export class MountObserver extends EventTarget implements IMountObserver{
87
88
 
88
89
  }
89
90
  #templLookUp: Map<string, HTMLElement> = new Map();
90
- findByID(id: string, fragment: DocumentFragment): HTMLElement | null{
91
+ async findByID(id: string, fragment: DocumentFragment): Promise<HTMLElement | null>{
91
92
  if(this.#templLookUp.has(id)) return this.#templLookUp.get(id)!;
92
- let templ = fragment.getElementById(id);
93
+ let templ = fragment.querySelector(`#${id}`);
93
94
  if(templ === null){
94
95
  let rootToSearchOutwardFrom = ((fragment.isConnected ? fragment.getRootNode() : this.#mountInit.withTargetShadowRoot) || document) as any;
95
96
  templ = rootToSearchOutwardFrom.getElementById(id);
@@ -98,8 +99,35 @@ export class MountObserver extends EventTarget implements IMountObserver{
98
99
  templ = rootToSearchOutwardFrom.getElementById(id);
99
100
  }
100
101
  }
101
- if(templ !== null) this.#templLookUp.set(id, templ);
102
- return templ;
102
+ if(templ !== null) {
103
+ if(!(templ instanceof HTMLTemplateElement)){
104
+ const newTempl = document.createElement('template');
105
+ const {getAdjRefs} = await import('./refid/getAdjRefs.js');
106
+ const adjRefs = getAdjRefs(templ);
107
+ // if(adjRefs.length > 1){
108
+ // (<any>newTempl)[wasItemReffed] = true;
109
+ // adjRefs[0].setAttribute('itemref', '<autogen>');
110
+ // }
111
+ const fragment = document.createDocumentFragment();
112
+ let first = true;
113
+ for(const adjRef of adjRefs){
114
+ const clone = adjRef.cloneNode(true) as HTMLElement;
115
+ if(first && adjRefs.length > 1){
116
+ clone.setAttribute('itemref', '<autogen>');
117
+ (<any>newTempl)[wasItemReffed] = true;
118
+ first = false;
119
+ }
120
+ clone.removeAttribute('id');
121
+ fragment.appendChild(clone);
122
+ }
123
+ const {doCleanup} = await import('./doCleanup.js');
124
+ doCleanup(templ as HTMLElement, fragment);
125
+ newTempl.content.appendChild(fragment);
126
+ templ = newTempl;
127
+ }
128
+ this.#templLookUp.set(id, templ as HTMLTemplateElement);
129
+ }
130
+ return templ as HTMLTemplateElement;
103
131
  }
104
132
 
105
133
  disconnect(within: Node){
package/Newish.js CHANGED
@@ -45,9 +45,9 @@ export class Newish {
45
45
  if (initPropVals !== undefined)
46
46
  this.queue.push(initPropVals);
47
47
  }
48
- if ('tbd' in ce && typeof ce['tbd'] === 'function') {
49
- await ce['tbd'](ce, enhancedElement, this.#options);
50
- }
48
+ // if('tbd' in ce && typeof ce['tbd'] === 'function'){
49
+ // await ce['tbd'](ce, enhancedElement, this.#options);
50
+ // }
51
51
  this.#ce = ce;
52
52
  const self = this;
53
53
  Object.defineProperty(enhancedElement, 'ish', {
@@ -121,12 +121,15 @@ export class Newish {
121
121
  actions.add('ishListAssigned');
122
122
  }
123
123
  else {
124
- const { assigner } = this.#options;
124
+ let { assigner } = this.#options;
125
+ if (assigner === undefined) {
126
+ assigner = Object.assign;
127
+ }
125
128
  await assigner(ce, fi);
126
129
  actions.add('ishAssigned');
127
130
  }
128
131
  }
129
- if (fromDo && !foundArray) {
132
+ if (fromDo && !foundArray && hasArrFilter) {
130
133
  const filtered = await (ce['arr=>'])(ce, undefined, ref, this.#options);
131
134
  if (filtered !== undefined) {
132
135
  ce[arr] = filtered;
package/Newish.ts CHANGED
@@ -51,9 +51,9 @@ export class Newish implements EventListenerObject {
51
51
  ce = new (ctr as any)();
52
52
  if(initPropVals !== undefined) this.queue.push(initPropVals);
53
53
  }
54
- if('tbd' in ce && typeof ce['tbd'] === 'function'){
55
- await ce['tbd'](ce, enhancedElement, this.#options);
56
- }
54
+ // if('tbd' in ce && typeof ce['tbd'] === 'function'){
55
+ // await ce['tbd'](ce, enhancedElement, this.#options);
56
+ // }
57
57
 
58
58
  this.#ce = ce;
59
59
  const self = this;
@@ -130,13 +130,16 @@ export class Newish implements EventListenerObject {
130
130
  (<any>ce)[arr] = filtered;
131
131
  actions.add('ishListAssigned');
132
132
  }else{
133
- const {assigner} = this.#options;
133
+ let {assigner} = this.#options;
134
+ if(assigner === undefined){
135
+ assigner = Object.assign;
136
+ }
134
137
  await assigner!(ce, fi);
135
138
  actions.add('ishAssigned');
136
139
  }
137
140
 
138
141
  }
139
- if(fromDo && !foundArray){
142
+ if(fromDo && !foundArray && hasArrFilter){
140
143
  const filtered = await (ce['arr=>']!)(ce, undefined, ref! as HasIsh & Element, this.#options);
141
144
  if(filtered !== undefined){
142
145
  (<any>ce)[arr] = filtered;
package/README.md CHANGED
@@ -295,6 +295,7 @@ const observer = new MountObserver({
295
295
  on: 'div > p + p ~ span[class$="name"]',
296
296
  whereMediaMatches: '(max-width: 1250px)',
297
297
  whereSizeOfContainerMatches: '(min-width: 700px)',
298
+ whereContainerHas: '[itemprop=isActive][value="true"]',
298
299
  whereInstanceOf: [HTMLMarqueeElement], //or ['HTMLMarqueeElement']
299
300
  whereLangIn: ['en-GB'],
300
301
  whereConnection:{
@@ -850,10 +851,53 @@ This proposal (and polyfill) also supports the option to utilize ShadowDOM / slo
850
851
  <compose src="#productCard"></compose>
851
852
  ```
852
853
 
853
- The discussion there leads to an open question whether a processing instruction would be better. I think the compose tag would make much more sense, vs a processing instruction, as it could then support slotted children (behaving similar to the Beatles' example above). Or maybe another tag should be introduced that is the equivalent of the slot, to avoid confusion. But I strongly suspect that could significantly reduce the payload size of some documents, if we can reuse blocks of HTML, inserting sections of customized content for each instance.
854
+ The discussion there leads to an open question whether a processing instruction would be better. I think the compose tag would make much more sense, vs a processing instruction, as it could then support slotted children (behaving similar to the Beatles' example above). Or maybe another tag should be introduced that is the equivalent of the slot, to avoid confusion. But I strongly suspect supporting intra document HTML imports could significantly reduce the payload size of some documents, if we can reuse blocks of HTML, inserting sections of customized content for each instance.
854
855
 
855
856
  The [add src attribute to template to load a template from file](https://github.com/whatwg/html/issues/10571) and an interesting proposal that is [coming from](https://github.com/htmlcomponents/declarative-shadow-imports/blob/main/examples/02-explainer-proposal/02-html.html) the Edge team [seem quite compatible](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ShadowDOM/explainer.md#proposal-inline-declarative-css-module-scripts) with this idea.
856
857
 
858
+ ## Lazy Loading / Conditionally loading intra document imports [WIP specification]
859
+
860
+ Just as it is useful to be able lazy load external imports when needed, it would also be useful to do the same for intra document HTML imports. The most straightforward way this could be done seems to be as follows, either introducing some attribute like "type=conditional", or defining a new element that inherits from the HTMLTemplateElement, for example:
861
+
862
+ ```html
863
+ <template id=source-template type=conditional>
864
+
865
+ <template mount='{
866
+ "on": ":not([defer-loading])",
867
+ "loadingEagerness": "eager",
868
+ "whereMediaMatches": "(min-width: 700px)",
869
+ "whereLangIn": ["en-GB"],
870
+ }'>
871
+ <div>I don't know why you say <slot name=slot2></slot> I say <slot name=slot1></slot></div>
872
+ </template>
873
+
874
+ <template mount='{
875
+ "on": ":not([defer-loading])",
876
+ "loadingEagerness": "lazy",
877
+ "whereMediaMatches": "(max-width: 700px)",
878
+ "whereLangIn": ["fr"],
879
+ }'>
880
+ <div>Je ne sais pas pourquoi tu dis <slot name=slot2></slot> je dis <slot name=slot1></slot></div>
881
+ </template>
882
+
883
+ </template>
884
+
885
+ ...
886
+ <template src=#source-template>
887
+ <span slot=slot1>hello</span>
888
+ <span slot=slot2>goodbye<span>
889
+ </template>
890
+
891
+ <!-- or, alternatively: -->
892
+
893
+ <compose src=#source-template>
894
+ <span slot=slot1>hello</span>
895
+ <span slot=slot2>goodbye<span>
896
+ </compose>
897
+ ```
898
+
899
+
900
+
857
901
  ## Creating "frameworks" that revolve around MOSEs.
858
902
 
859
903
  Often, we will want to define a large number of "mount observer script elements (MOSEs)" programmatically, and we need it to be done in a generic way, that can be published and easily referenced.
package/compose.js CHANGED
@@ -1,20 +1,53 @@
1
- import { inclTemplQry } from './MountObserver.js';
1
+ import { inclTemplQry, wasItemReffed } from './MountObserver.js';
2
2
  export const childRefsKey = Symbol.for('Wr0WPVh84k+O93miuENdMA');
3
3
  export const cloneKey = Symbol.for('LD97VKZYc02CQv23DT/6fQ');
4
+ const autogenKey = Symbol.for('YpP5EP0i1UKcBBBH9tsm0w');
4
5
  export async function compose(self, el, level) {
5
- if (!el.hasAttribute('src')) {
6
- return;
7
- }
8
6
  const src = el.getAttribute('src');
7
+ if (src === null)
8
+ return;
9
9
  el.removeAttribute('src');
10
10
  const templID = src.substring(1);
11
11
  const fragment = self.objNde?.deref();
12
12
  if (fragment === undefined)
13
13
  return;
14
- const templ = self.findByID(templID, fragment);
14
+ const templ = await self.findByID(templID, fragment);
15
15
  if (!(templ instanceof HTMLTemplateElement))
16
16
  throw 404;
17
17
  const clone = templ.content.cloneNode(true);
18
+ const dataLd = el.dataset.ld;
19
+ const wasReffed = templ[wasItemReffed];
20
+ if (wasReffed || dataLd) {
21
+ const firstElement = clone.firstElementChild;
22
+ if (wasReffed) {
23
+ let ns = firstElement.nextElementSibling;
24
+ const ids = [];
25
+ let count = window[autogenKey];
26
+ if (count === undefined) {
27
+ count = 0;
28
+ }
29
+ else {
30
+ count++;
31
+ }
32
+ window[autogenKey] = count;
33
+ while (ns !== null) {
34
+ const id = ns.id = `mount-observer-${count}`;
35
+ ids.push(id);
36
+ ns = ns.nextElementSibling;
37
+ }
38
+ firstElement.setAttribute('itemref', ids.join(' '));
39
+ }
40
+ if (dataLd) {
41
+ const parsed = JSON.parse(dataLd);
42
+ let type = parsed['@type'];
43
+ const itemscopeAttr = firstElement.getAttribute('itemscope');
44
+ if (type && !itemscopeAttr) {
45
+ firstElement.setAttribute('itemscope', type);
46
+ }
47
+ firstElement['ish'] = parsed;
48
+ delete el.dataset.ld;
49
+ }
50
+ }
18
51
  const slots = el.content.querySelectorAll(`[slot]`);
19
52
  for (const slot of slots) {
20
53
  const name = slot.getAttribute('slot');
package/compose.ts CHANGED
@@ -1,25 +1,56 @@
1
1
  import { ILoadEvent, loadEventName } from './ts-refs/mount-observer/types';
2
- import { MountObserver, inclTemplQry } from './MountObserver.js';
2
+ import { MountObserver, inclTemplQry, wasItemReffed } from './MountObserver.js';
3
3
 
4
4
  export const childRefsKey = Symbol.for('Wr0WPVh84k+O93miuENdMA');
5
5
  export const cloneKey = Symbol.for('LD97VKZYc02CQv23DT/6fQ');
6
-
6
+ const autogenKey = Symbol.for('YpP5EP0i1UKcBBBH9tsm0w');
7
7
  export async function compose(
8
8
  self: MountObserver,
9
9
  el: HTMLTemplateElement,
10
10
  level: number
11
11
  ){
12
- if(!el.hasAttribute('src')){
13
- return;
14
- }
15
- const src = el.getAttribute('src');
12
+ const src = el.getAttribute('src'); if(src === null) return;
16
13
  el.removeAttribute('src');
17
14
  const templID = src!.substring(1);
18
15
  const fragment = self.objNde?.deref() as DocumentFragment;
19
16
  if(fragment === undefined) return;
20
- const templ = self.findByID(templID, fragment);
17
+ const templ = await self.findByID(templID, fragment);
21
18
  if(!(templ instanceof HTMLTemplateElement)) throw 404;
22
19
  const clone = templ.content.cloneNode(true) as DocumentFragment;
20
+ const dataLd = el.dataset.ld;
21
+ const wasReffed = (<any>templ)[wasItemReffed];
22
+ if(wasReffed || dataLd){
23
+ const firstElement = clone.firstElementChild!;
24
+ if(wasReffed){
25
+ let ns = firstElement.nextElementSibling;
26
+ const ids = [];
27
+ let count = (<any>window)[autogenKey];
28
+ if(count === undefined){
29
+ count = 0;
30
+ }else{
31
+ count++;
32
+ }
33
+ (<any>window)[autogenKey] = count;
34
+ while(ns !== null){
35
+ const id = ns.id = `mount-observer-${count}`;
36
+ ids.push(id);
37
+ ns = ns.nextElementSibling;
38
+ }
39
+ firstElement.setAttribute('itemref', ids.join(' '));
40
+ }
41
+
42
+ if(dataLd){
43
+ const parsed = JSON.parse(dataLd);
44
+ let type = parsed['@type'];
45
+ const itemscopeAttr = firstElement.getAttribute('itemscope');
46
+ if(type && !itemscopeAttr){
47
+ firstElement.setAttribute('itemscope', type);
48
+ }
49
+ (<any>firstElement)['ish'] = parsed;
50
+ delete el.dataset.ld;
51
+ }
52
+
53
+ }
23
54
  const slots = el.content.querySelectorAll(`[slot]`);
24
55
 
25
56
  for(const slot of slots){
package/doCleanup.js ADDED
@@ -0,0 +1,31 @@
1
+ export function doCleanup(htmlSrc, clone, options = {
2
+ removeInner: '[itemprop]:not([itemscope])',
3
+ removeOuter: '[itemprop][itemscope]'
4
+ }) {
5
+ const removeInner = htmlSrc.getAttribute('remove-inner') || options.removeInner;
6
+ if (removeInner) {
7
+ const removeInnerEls = clone.querySelectorAll(removeInner);
8
+ for (const removeInnerEl of removeInnerEls) {
9
+ if ('href' in removeInnerEl) {
10
+ removeInnerEl.href = '';
11
+ }
12
+ else if ('value' in removeInnerEl) {
13
+ removeInnerEl.value = '';
14
+ }
15
+ else if ('datetime' in removeInnerEl) {
16
+ removeInnerEl.datetime = '';
17
+ }
18
+ else {
19
+ //any other exceptions?
20
+ removeInnerEl.textContent = '';
21
+ }
22
+ }
23
+ }
24
+ const removeOuter = htmlSrc.getAttribute('remove-outer') || options.removeOuter;
25
+ if (removeOuter) {
26
+ const removeOuterEls = clone.querySelectorAll(removeOuter);
27
+ for (const removeOuterEl of removeOuterEls) {
28
+ removeOuterEl.remove();
29
+ }
30
+ }
31
+ }
package/doCleanup.ts ADDED
@@ -0,0 +1,34 @@
1
+ interface doCleanUpOptions {
2
+ removeInner?: string;
3
+ removeOuter?: string;
4
+ }
5
+
6
+ export function doCleanup(htmlSrc: HTMLElement, clone: DocumentFragment, options: doCleanUpOptions = {
7
+ removeInner: '[itemprop]:not([itemscope])',
8
+ removeOuter: '[itemprop][itemscope]'
9
+ }){
10
+ const removeInner = htmlSrc.getAttribute('remove-inner') || options.removeInner;
11
+ if(removeInner){
12
+ const removeInnerEls = clone.querySelectorAll(removeInner);
13
+ for(const removeInnerEl of removeInnerEls){
14
+ if('href' in removeInnerEl){
15
+ removeInnerEl.href = '';
16
+ } else if ('value' in removeInnerEl){
17
+ removeInnerEl.value = '';
18
+ }else if('datetime' in removeInnerEl){
19
+ removeInnerEl.datetime = '';
20
+ }else{
21
+ //any other exceptions?
22
+ removeInnerEl.textContent = '';
23
+ }
24
+ }
25
+ }
26
+ const removeOuter = htmlSrc.getAttribute('remove-outer') || options.removeOuter;
27
+ if(removeOuter){
28
+ const removeOuterEls = clone.querySelectorAll(removeOuter);
29
+ for(const removeOuterEl of removeOuterEls){
30
+ removeOuterEl.remove();
31
+ }
32
+ }
33
+
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.0.68",
3
+ "version": "0.0.70",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
@@ -37,6 +37,10 @@
37
37
  "default": "./compose.js",
38
38
  "types": "./compose.ts"
39
39
  },
40
+ "./doCleanup.js": {
41
+ "default": "./doCleanup.js",
42
+ "types": "./doCleanup.ts"
43
+ },
40
44
  "./waitForEvent.js": {
41
45
  "default": "./waitForEvent.js",
42
46
  "types": "./waitForEvent.ts"
@@ -45,6 +49,10 @@
45
49
  "default": "./waitForIsh.js",
46
50
  "types": "./waitForIsh.ts"
47
51
  },
52
+ "./refid/getAdjRefs.js": {
53
+ "default": "./refid/getAdjRefs.js",
54
+ "types": "./refid/getAdjRefs.ts"
55
+ },
48
56
  "./refid/regIsh.js": {
49
57
  "default": "./refid/regIsh.js",
50
58
  "types": "./refid/regIsh.js"
@@ -52,13 +60,18 @@
52
60
  "./refid/splitRefs.js": {
53
61
  "default": "./refid/splitRefs.js",
54
62
  "types": "./refid/splitRefs.ts"
63
+ },
64
+ "./slotkin/beKindred.js": {
65
+ "default": "./slotkin/beKindred.js",
66
+ "types": "./slotkin/beKindred.ts"
55
67
  }
56
68
  },
57
69
  "files": [
58
70
  "*.js",
59
71
  "*.ts",
60
72
  "./ts-refs/*",
61
- "./refid/*"
73
+ "./refid/*",
74
+ "./slotkin/*"
62
75
  ],
63
76
  "types": "./ts-refs/mount-observer/types.d.ts",
64
77
  "scripts": {
@@ -0,0 +1,16 @@
1
+ import { splitRefs } from './splitRefs.js';
2
+ export function getAdjRefs(el) {
3
+ const returnArr = [el];
4
+ const itemref = el.getAttribute('itemref');
5
+ if (itemref === null)
6
+ return returnArr;
7
+ const itemrefList = splitRefs(itemref); // itemref.split(' ').map((id) => id.trim()).filter((id) => id.length > 0);
8
+ if (itemrefList.length === 0)
9
+ return returnArr;
10
+ let ns = el.nextElementSibling;
11
+ while (ns !== null && itemrefList.includes(ns.id)) {
12
+ returnArr.push(ns);
13
+ ns = ns.nextElementSibling;
14
+ }
15
+ return returnArr;
16
+ }
@@ -0,0 +1,16 @@
1
+ import { splitRefs } from './splitRefs.js';
2
+ export function getAdjRefs(el: Element): Array<Element>{
3
+ const returnArr = [el];
4
+ const itemref = el.getAttribute('itemref');
5
+ if(itemref === null) return returnArr;
6
+ const itemrefList = splitRefs(itemref);// itemref.split(' ').map((id) => id.trim()).filter((id) => id.length > 0);
7
+ if(itemrefList.length === 0) return returnArr;
8
+ let ns = el.nextElementSibling;
9
+ while(ns !== null && itemrefList.includes(ns.id)){
10
+ returnArr.push(ns);
11
+ ns = ns.nextElementSibling;
12
+ }
13
+ return returnArr;
14
+ }
15
+
16
+
@@ -0,0 +1,30 @@
1
+ import { toQuery } from './toQuery.js';
2
+ import { splitRefs } from '../refid/splitRefs.js';
3
+ export function beKindred(fragment, el) {
4
+ const qry = toQuery(el);
5
+ const matches = Array.from(fragment.querySelectorAll(qry));
6
+ const elFragment = new DocumentFragment();
7
+ const clone = el.cloneNode(true);
8
+ for (const child of clone.childNodes) {
9
+ elFragment.appendChild(child);
10
+ }
11
+ const insertAttrs = el.getAttribute('-i');
12
+ let map = null;
13
+ if (insertAttrs !== null) {
14
+ const attrs = splitRefs(insertAttrs);
15
+ map = {};
16
+ for (const attr of attrs) {
17
+ map[attr] = el.getAttribute(attr);
18
+ }
19
+ }
20
+ for (const match of matches) {
21
+ const fragmentClone = elFragment.cloneNode(true);
22
+ match.replaceChildren(fragmentClone);
23
+ if (map !== null) {
24
+ for (const key in map) {
25
+ const value = map[key];
26
+ match.setAttribute(key, value);
27
+ }
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,31 @@
1
+ import {toQuery} from './toQuery.js';
2
+ import {splitRefs} from '../refid/splitRefs.js';
3
+
4
+ export function beKindred(fragment: DocumentFragment | Element, el: Element){
5
+ const qry = toQuery(el);
6
+ const matches = Array.from(fragment.querySelectorAll(qry));
7
+ const elFragment = new DocumentFragment();
8
+ const clone = el.cloneNode(true);
9
+ for(const child of clone.childNodes){
10
+ elFragment.appendChild(child);
11
+ }
12
+ const insertAttrs = el.getAttribute('-i');
13
+ let map: {[key: string]: string} | null = null;
14
+ if(insertAttrs !== null){
15
+ const attrs = splitRefs(insertAttrs);
16
+ map = {};
17
+ for(const attr of attrs){
18
+ map[attr] = el.getAttribute(attr)!;
19
+ }
20
+ }
21
+ for(const match of matches){
22
+ const fragmentClone = elFragment.cloneNode(true) as DocumentFragment;
23
+ match.replaceChildren(fragmentClone);
24
+ if(map !== null){
25
+ for(const key in map){
26
+ const value = map[key]!;
27
+ match.setAttribute(key, value);
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,12 @@
1
+ export function toQuery(el) {
2
+ //from the element el, create a selector that matches all the classes and parts of the element el, as
3
+ //well as the values of all the attributes of el.
4
+ const classes = Array.from(el.classList).map(c => `.${c}`).join('');
5
+ const parts = Array.from(el.part).map(p => `[part~="${p}"`).join('');
6
+ const attributes = Array.from(el.attributes)
7
+ .filter(attr => attr.name !== '-i')
8
+ .map(attr => `[${attr.name}="${attr.value}"]`)
9
+ .join('');
10
+ const { localName } = el;
11
+ return `${localName}${classes}${parts}${attributes}`;
12
+ }
@@ -0,0 +1,13 @@
1
+
2
+ export function toQuery(el: Element): string {
3
+ //from the element el, create a selector that matches all the classes and parts of the element el, as
4
+ //well as the values of all the attributes of el.
5
+ const classes = Array.from(el.classList).map(c => `.${c}`).join('');
6
+ const parts = Array.from(el.part).map(p => `[part~="${p}"`).join('');
7
+ const attributes = Array.from(el.attributes)
8
+ .filter(attr => attr.name !== '-i')
9
+ .map(attr => `[${attr.name}="${attr.value}"]`)
10
+ .join('');
11
+ const {localName} = el;
12
+ return `${localName}${classes}${parts}${attributes}`;
13
+ }
@@ -0,0 +1,30 @@
1
+ import {IEnhancement, BEAllProps} from '../trans-render/be/types';
2
+ import {Specifier} from '../trans-render/dss/types';
3
+
4
+ export interface EndUserProps extends IEnhancement<HTMLTemplateElement>{
5
+
6
+ }
7
+
8
+ export interface AllProps extends EndUserProps{
9
+ nodesToImbue: Element[],
10
+ imbueRules: Array<ImbueRule>,
11
+ }
12
+
13
+ export type AP = AllProps;
14
+
15
+ export type PAP = Partial<AP>;
16
+
17
+ export type ProPAP = Promise<PAP>;
18
+
19
+ export type BAP = AP & BEAllProps;
20
+
21
+ export interface Actions{
22
+ hydrate(self: BAP): Promise<PAP>;
23
+ imbue(self: BAP): void;
24
+ }
25
+
26
+ export interface ImbueRule {
27
+ remoteSpecifierString?: string,
28
+ remoteSpecifier: Specifier,
29
+ }
30
+
@@ -0,0 +1,20 @@
1
+ import {IEnhancement, BEAllProps} from '../trans-render/be/types';
2
+
3
+ export interface EndUserProps extends IEnhancement<HTMLTemplateElement>{
4
+ in: string,
5
+ }
6
+
7
+ export interface AllProps extends EndUserProps{
8
+ }
9
+
10
+ export type AP = AllProps;
11
+
12
+ export type PAP = Partial<AP>;
13
+
14
+ export type ProPAP = Promise<PAP>;
15
+
16
+ export type BAP = AP & BEAllProps;
17
+
18
+ export interface Actions{
19
+ hydrate(self: BAP): void;
20
+ }