mount-observer 0.0.111 → 0.1.0
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/Events.js +28 -26
- package/Events.ts +34 -30
- package/MountObserver.js +235 -520
- package/MountObserver.ts +281 -542
- package/README.md +149 -56
- package/attrCoordinates.js +93 -0
- package/attrCoordinates.ts +122 -0
- package/constants.js +6 -0
- package/constants.ts +7 -0
- package/index.js +3 -0
- package/index.ts +19 -0
- package/loadImports.js +47 -0
- package/loadImports.ts +56 -0
- package/package.json +8 -115
- package/playwright.config.ts +0 -1
- package/types.d.ts +86 -0
- package/whereAttr.js +174 -0
- package/whereAttr.ts +221 -0
- package/LICENSE +0 -21
- package/Newish.js +0 -145
- package/Newish.ts +0 -169
- package/ObsAttr.js +0 -18
- package/ObsAttr.ts +0 -18
- package/RootMutObs.js +0 -49
- package/RootMutObs.ts +0 -58
- package/Synthesizer.js +0 -125
- package/Synthesizer.ts +0 -130
- package/bindish.js +0 -15
- package/bindish.ts +0 -22
- package/compose.js +0 -148
- package/compose.ts +0 -164
- package/doCleanup.js +0 -31
- package/doCleanup.ts +0 -34
- package/getWhereAttrSelector.js +0 -83
- package/getWhereAttrSelector.ts +0 -92
- package/preloadContent.js +0 -44
- package/preloadContent.ts +0 -47
- package/readAttrs.ts +0 -60
- package/refid/README.md +0 -259
- package/refid/arr.js +0 -4
- package/refid/arr.ts +0 -4
- package/refid/camelToKebab.js +0 -4
- package/refid/camelToKebab.ts +0 -4
- package/refid/genIds.js +0 -190
- package/refid/genIds.ts +0 -177
- package/refid/getAdjRefs.js +0 -38
- package/refid/getAdjRefs.ts +0 -38
- package/refid/getContext.js +0 -13
- package/refid/getContext.ts +0 -14
- package/refid/getCount.js +0 -8
- package/refid/getCount.ts +0 -8
- package/refid/getIsh.js +0 -35
- package/refid/getIsh.ts +0 -37
- package/refid/hostish.js +0 -18
- package/refid/hostish.ts +0 -20
- package/refid/ism.js +0 -78
- package/refid/ism.ts +0 -81
- package/refid/itemprops.js +0 -60
- package/refid/itemprops.ts +0 -67
- package/refid/joinMatching.js +0 -56
- package/refid/joinMatching.ts +0 -54
- package/refid/nudge.js +0 -23
- package/refid/nudge.ts +0 -23
- package/refid/regIsh.js +0 -27
- package/refid/regIsh.ts +0 -31
- package/refid/secretKeys.js +0 -5
- package/refid/secretKeys.ts +0 -5
- package/refid/splitRefs.js +0 -6
- package/refid/splitRefs.ts +0 -6
- package/refid/stdVal.js +0 -15
- package/refid/stdVal.ts +0 -15
- package/refid/via.js +0 -114
- package/refid/via.ts +0 -113
- package/slotkin/affine.js +0 -39
- package/slotkin/affine.ts +0 -46
- package/slotkin/beKindred.js +0 -45
- package/slotkin/beKindred.ts +0 -55
- package/slotkin/getBreadth.js +0 -19
- package/slotkin/getBreadth.ts +0 -21
- package/slotkin/getFrag.js +0 -22
- package/slotkin/getFrag.ts +0 -21
- package/slotkin/toQuery.js +0 -12
- package/slotkin/toQuery.ts +0 -13
- package/slotkin/wrap.js +0 -13
- package/slotkin/wrap.ts +0 -18
- package/ts-refs/LICENSE +0 -21
- package/ts-refs/README.md +0 -18
- package/ts-refs/be-a-beacon/types.d.ts +0 -22
- package/ts-refs/be-alit/types.d.ts +0 -1
- package/ts-refs/be-based/types.d.ts +0 -32
- package/ts-refs/be-bound/types.d.ts +0 -65
- package/ts-refs/be-buttoned-up/types.d.ts +0 -21
- package/ts-refs/be-calculating/types.d.ts +0 -57
- package/ts-refs/be-clonable/types.d.ts +0 -28
- package/ts-refs/be-committed/types.d.ts +0 -26
- package/ts-refs/be-consoling/types.d.ts +0 -25
- package/ts-refs/be-counted/types.d.ts +0 -88
- package/ts-refs/be-delible/types.d.ts +0 -26
- package/ts-refs/be-directive/types.d.ts +0 -43
- package/ts-refs/be-dispatching/types.d.ts +0 -41
- package/ts-refs/be-elevating/types.d.ts +0 -55
- package/ts-refs/be-enhanced/types.d.ts +0 -32
- package/ts-refs/be-enhancing/types.d.ts +0 -31
- package/ts-refs/be-evanescent/types.d.ts +0 -20
- package/ts-refs/be-eventing/types.d.ts +0 -27
- package/ts-refs/be-exportable/types.d.ts +0 -26
- package/ts-refs/be-fetching/types.d.ts +0 -73
- package/ts-refs/be-flashy/types.d.ts +0 -27
- package/ts-refs/be-formalizing/types.d.ts +0 -29
- package/ts-refs/be-formidable/types.d.ts +0 -64
- package/ts-refs/be-giddy/types.d.ts +0 -26
- package/ts-refs/be-gingerly/types.d.ts +0 -19
- package/ts-refs/be-gone/types.d.ts +0 -24
- package/ts-refs/be-hashing-out/types.d.ts +0 -22
- package/ts-refs/be-hive/types.d.ts +0 -18
- package/ts-refs/be-imbued/types.d.ts +0 -30
- package/ts-refs/be-included/types.d.ts +0 -20
- package/ts-refs/be-inclusive/types.d.ts +0 -30
- package/ts-refs/be-intersectional/types.d.ts +0 -37
- package/ts-refs/be-intl/types.d.ts +0 -28
- package/ts-refs/be-invoking/types.d.ts +0 -28
- package/ts-refs/be-joining/types.d.ts +0 -26
- package/ts-refs/be-kvetching/types.d.ts +0 -24
- package/ts-refs/be-lazy/types.d.ts +0 -29
- package/ts-refs/be-literate/types.d.ts +0 -29
- package/ts-refs/be-mediating/types.d.ts +0 -34
- package/ts-refs/be-methodical/types.d.ts +0 -20
- package/ts-refs/be-modding/types.d.ts +0 -18
- package/ts-refs/be-observant/types.d.ts +0 -27
- package/ts-refs/be-observing/types.d.ts +0 -84
- package/ts-refs/be-parsed/types.d.ts +0 -19
- package/ts-refs/be-parsing/types.d.ts +0 -37
- package/ts-refs/be-persistent/types.d.ts +0 -66
- package/ts-refs/be-propagating/types.d.ts +0 -26
- package/ts-refs/be-reformable/types.d.ts +0 -48
- package/ts-refs/be-render-neutral/types.d.ts +0 -31
- package/ts-refs/be-scoped/types.d.ts +0 -24
- package/ts-refs/be-sharing/types.d.ts +0 -17
- package/ts-refs/be-switched/types.d.ts +0 -155
- package/ts-refs/be-typed/types.d.ts +0 -36
- package/ts-refs/be-value-added/types.d.ts +0 -34
- package/ts-refs/be-valued/types.d.ts +0 -22
- package/ts-refs/be-written/types.d.ts +0 -59
- package/ts-refs/css-charts/types.d.ts +0 -38
- package/ts-refs/css-echarts/types.d.ts +0 -13
- package/ts-refs/data-props/types.d.ts +0 -27
- package/ts-refs/do-inc/types.d.ts +0 -28
- package/ts-refs/do-invoke/types.d.ts +0 -28
- package/ts-refs/do-toggle/types.d.ts +0 -27
- package/ts-refs/em-bower/types.d.ts +0 -18
- package/ts-refs/fetch-for/types.d.ts +0 -37
- package/ts-refs/folder-picker/types.d.ts +0 -43
- package/ts-refs/for-fetch/doc.d.ts +0 -98
- package/ts-refs/for-fetch/types.d.ts +0 -83
- package/ts-refs/mount-observer/types.d.ts +0 -248
- package/ts-refs/mt-si/types.d.ts +0 -21
- package/ts-refs/per-each/types.d.ts +0 -51
- package/ts-refs/soak-up/types.d.ts +0 -36
- package/ts-refs/trans-render/XV/types.d.ts +0 -69
- package/ts-refs/trans-render/asmr/types.d.ts +0 -138
- package/ts-refs/trans-render/be/types.d.ts +0 -190
- package/ts-refs/trans-render/dss/types.d.ts +0 -57
- package/ts-refs/trans-render/froop/types.d.ts +0 -416
- package/ts-refs/trans-render/funions/types.d.ts +0 -12
- package/ts-refs/trans-render/lib/mixins/types.d.ts +0 -42
- package/ts-refs/trans-render/lib/prs/types.d.ts +0 -40
- package/ts-refs/trans-render/lib/types.d.ts +0 -489
- package/ts-refs/trans-render/types.d.ts +0 -583
- package/ts-refs/wc-info/SimpleWCInfo.d.ts +0 -15
- package/ts-refs/when-resolved/types.d.ts +0 -30
- package/ts-refs/xp-as/types.d.ts +0 -20
- package/ts-refs/xtal-element/types.d.ts +0 -43
- package/ts-refs/xtal-frappe-chart/types.d.ts +0 -193
- package/upShadowSearch.js +0 -25
- package/upShadowSearch.ts +0 -23
- package/waitForEvent.js +0 -12
- package/waitForEvent.ts +0 -13
- package/waitForIsh.js +0 -21
- package/waitForIsh.ts +0 -20
package/MountObserver.ts
CHANGED
|
@@ -1,615 +1,354 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
disconnectedEventName, IDisconnectEvent, IAttrChangeEvent, attrChangeEventName, AttrChangeInfo, loadEventName, ILoadEvent,
|
|
4
|
-
AttrParts,
|
|
5
|
-
MOSE, WeakDual,
|
|
1
|
+
import {
|
|
2
|
+
MountInit,
|
|
6
3
|
MountObserverOptions,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export class MountObserver extends EventTarget implements IMountObserver{
|
|
20
|
-
|
|
21
|
-
#
|
|
22
|
-
#options: MountObserverOptions | undefined;
|
|
23
|
-
//#rootMutObs: RootMutObs | undefined;
|
|
4
|
+
IMountObserver,
|
|
5
|
+
MountContext,
|
|
6
|
+
AttrChange
|
|
7
|
+
} from './types.js';
|
|
8
|
+
import {
|
|
9
|
+
MountEvent,
|
|
10
|
+
DismountEvent,
|
|
11
|
+
DisconnectEvent,
|
|
12
|
+
LoadEvent,
|
|
13
|
+
AttrChangeEvent
|
|
14
|
+
} from './Events.js';
|
|
15
|
+
|
|
16
|
+
export class MountObserver extends EventTarget implements IMountObserver {
|
|
17
|
+
#init: MountInit;
|
|
18
|
+
#options: MountObserverOptions;
|
|
24
19
|
#abortController: AbortController;
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
#modules: any[] = [];
|
|
21
|
+
#mountedElements = new WeakSet<Element>();
|
|
22
|
+
#processedElements = new WeakSet<Element>();
|
|
23
|
+
#mutationObserver: MutationObserver | undefined;
|
|
24
|
+
#rootNode: WeakRef<Node> | undefined;
|
|
25
|
+
#importsLoaded = false;
|
|
26
|
+
#elementAttrStates = new WeakMap<Element, Map<string, string | null>>();
|
|
27
|
+
#matchesWhereAttrFn: ((element: Element, whereAttr: any) => boolean) | null = null;
|
|
28
|
+
#buildAttrCoordinateMapFn: ((whereAttr: any, isCustomElement: boolean) => any) | null = null;
|
|
29
|
+
|
|
30
|
+
constructor(init: MountInit, options: MountObserverOptions = {}) {
|
|
33
31
|
super();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//TODO: study this problem further. Starting to think this is basically not polyfillable
|
|
37
|
-
if(on !== undefined){
|
|
38
|
-
const reducedMatch = on.replaceAll(':not(', '');
|
|
39
|
-
isComplex = reducedMatch.includes(' ') || (reducedMatch.includes(':') && reducedMatch.includes('('));
|
|
40
|
-
}
|
|
41
|
-
this.#isComplex = isComplex;
|
|
42
|
-
if(whereElementIntersectsWith) throw 'NI'; //not implemented
|
|
43
|
-
this.#mountInit = init;
|
|
32
|
+
this.#init = init;
|
|
33
|
+
this.#options = options;
|
|
44
34
|
this.#abortController = new AbortController();
|
|
45
|
-
this.mountedElements = {
|
|
46
|
-
weakSet: new WeakSet(),
|
|
47
|
-
setWeak: new Set(),
|
|
48
|
-
};
|
|
49
|
-
this.#disconnected = new WeakSet();
|
|
50
|
-
//this.#unmounted = new WeakSet();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
#calculatedSelector: string | undefined;
|
|
54
|
-
#attrParts: Array<AttrParts> | undefined;
|
|
55
|
-
|
|
56
|
-
#fullListOfEnhancementAttrs: Array<string> | undefined;
|
|
57
|
-
async observedAttrs(){
|
|
58
|
-
await this.#selector();
|
|
59
|
-
return this.#fullListOfEnhancementAttrs;
|
|
60
|
-
}
|
|
61
|
-
//get #attrVals
|
|
62
|
-
async #selector() : Promise<string>{
|
|
63
|
-
if(this.#calculatedSelector !== undefined) return this.#calculatedSelector;
|
|
64
|
-
const {on, whereAttr} = this.#mountInit;
|
|
65
|
-
const withoutAttrs = on || '*';
|
|
66
|
-
if(whereAttr === undefined) return withoutAttrs;
|
|
67
|
-
const {getWhereAttrSelector} = await import('./getWhereAttrSelector.js');
|
|
68
|
-
const info = await getWhereAttrSelector(whereAttr, withoutAttrs);
|
|
69
|
-
const {fullListOfAttrs, calculatedSelector, partitionedAttrs} = info;
|
|
70
|
-
this.#fullListOfEnhancementAttrs = fullListOfAttrs;
|
|
71
|
-
this.#attrParts = partitionedAttrs;
|
|
72
|
-
this.#calculatedSelector = calculatedSelector
|
|
73
|
-
return this.#calculatedSelector;
|
|
74
|
-
}
|
|
75
35
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if(bi.getAttribute('rel') === 'preload'){
|
|
81
|
-
(await import('./preloadContent.js')).preloadContent(bi);
|
|
82
|
-
}else{
|
|
83
|
-
await this.#compose(bi, level);
|
|
84
|
-
}
|
|
85
|
-
|
|
36
|
+
if (options.disconnectedSignal) {
|
|
37
|
+
options.disconnectedSignal.addEventListener('abort', () => {
|
|
38
|
+
this.disconnect();
|
|
39
|
+
});
|
|
86
40
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
41
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const refType = src[0] as RefType;
|
|
95
|
-
if(!['!', '#'].includes(refType)) return;
|
|
96
|
-
const {compose} = await import('./compose.js');
|
|
97
|
-
await compose(this, el, level, src.substring(1), refType);
|
|
98
|
-
|
|
42
|
+
// Preload whereAttr utilities if needed
|
|
43
|
+
if (init.whereAttr) {
|
|
44
|
+
this.#preloadWhereAttrUtilities();
|
|
45
|
+
}
|
|
99
46
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//get rid of
|
|
104
|
-
const iterator = document.evaluate(
|
|
105
|
-
`//comment()[.="${refName}"]`,
|
|
106
|
-
fragment,
|
|
107
|
-
null,
|
|
108
|
-
XPathResult.ANY_TYPE,
|
|
109
|
-
null
|
|
110
|
-
);
|
|
111
|
-
//console.log({xpathResult})
|
|
112
|
-
try {
|
|
113
|
-
let thisNode = iterator.iterateNext();
|
|
114
|
-
return thisNode;
|
|
115
|
-
}catch(e){
|
|
116
|
-
return null;
|
|
47
|
+
// Start loading imports if eager
|
|
48
|
+
if (init.loadingEagerness === 'eager' && init.import) {
|
|
49
|
+
this.#loadImports();
|
|
117
50
|
}
|
|
118
51
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
let templ: Node | null = null;
|
|
125
|
-
templ = refType === '#' ? fragment.querySelector(`#${refName}`) : this.#searchForComment(refName, fragment);
|
|
126
|
-
if(templ === null){
|
|
127
|
-
let rootToSearchOutwardFrom = ((fragment.isConnected ? fragment.getRootNode() : this.#mountInit.withTargetShadowRoot) || document) as any;
|
|
128
|
-
templ = refType === '#' ? rootToSearchOutwardFrom.getElementById(refName) : this.#searchForComment(refName, rootToSearchOutwardFrom);
|
|
129
|
-
while(templ === null && rootToSearchOutwardFrom !== (document as any as DocumentFragment) ){
|
|
130
|
-
rootToSearchOutwardFrom = (rootToSearchOutwardFrom.host || rootToSearchOutwardFrom).getRootNode() as DocumentFragment;
|
|
131
|
-
templ = refType === '#' ? rootToSearchOutwardFrom.getElementById(refName) : this.#searchForComment(refName, rootToSearchOutwardFrom);
|
|
132
|
-
}
|
|
52
|
+
|
|
53
|
+
async #preloadWhereAttrUtilities(): Promise<void> {
|
|
54
|
+
if (!this.#matchesWhereAttrFn) {
|
|
55
|
+
const { matchesWhereAttr } = await import('./whereAttr.js');
|
|
56
|
+
this.#matchesWhereAttrFn = matchesWhereAttr;
|
|
133
57
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if(!(templ instanceof HTMLTemplateElement)){
|
|
138
|
-
const newTempl = document.createElement('template');
|
|
139
|
-
const {getAdjRefs} = await import('./refid/getAdjRefs.js');
|
|
140
|
-
const adjRefs = getAdjRefs(templ);
|
|
141
|
-
// if(adjRefs.length > 1){
|
|
142
|
-
// (<any>newTempl)[wasItemReffed] = true;
|
|
143
|
-
// adjRefs[0].setAttribute('itemref', '<autogen>');
|
|
144
|
-
// }
|
|
145
|
-
const fragment = document.createDocumentFragment();
|
|
146
|
-
let first = true;
|
|
147
|
-
for(const adjRef of adjRefs){
|
|
148
|
-
const clone = adjRef.cloneNode(true) as HTMLElement;
|
|
149
|
-
if(refType === '#' && clone instanceof Element){
|
|
150
|
-
if(first && adjRefs.length > 1){
|
|
151
|
-
clone.setAttribute('itemref', '<autogen>');
|
|
152
|
-
(<any>newTempl)[wasItemReffed] = true;
|
|
153
|
-
first = false;
|
|
154
|
-
}
|
|
155
|
-
clone.removeAttribute('id');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
fragment.appendChild(clone);
|
|
159
|
-
}
|
|
160
|
-
if(templ instanceof Element){
|
|
161
|
-
const {doCleanup} = await import('./doCleanup.js');
|
|
162
|
-
doCleanup(templ as HTMLElement, fragment);
|
|
163
|
-
|
|
164
|
-
}else{
|
|
165
|
-
//TODO: cleanup
|
|
166
|
-
}
|
|
167
|
-
newTempl.content.appendChild(fragment);
|
|
168
|
-
templ = newTempl;
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
this.#templLookUp.set(refName, templ as HTMLTemplateElement);
|
|
58
|
+
if (!this.#buildAttrCoordinateMapFn) {
|
|
59
|
+
const { buildAttrCoordinateMap } = await import('./attrCoordinates.js');
|
|
60
|
+
this.#buildAttrCoordinateMapFn = buildAttrCoordinateMap;
|
|
172
61
|
}
|
|
173
|
-
return templ as HTMLTemplateElement;
|
|
174
62
|
}
|
|
175
63
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const currentCount = refCount.get(nodeToMonitor);
|
|
179
|
-
if(currentCount !== undefined){
|
|
180
|
-
if(currentCount <= 1){
|
|
181
|
-
const observer = mutationObserverLookup.get(nodeToMonitor);
|
|
182
|
-
if(observer === undefined){
|
|
183
|
-
console.warn(refCountErr);
|
|
184
|
-
}else{
|
|
185
|
-
observer.disconnect();
|
|
186
|
-
mutationObserverLookup.delete(nodeToMonitor);
|
|
187
|
-
refCount.delete(nodeToMonitor);
|
|
188
|
-
}
|
|
189
|
-
}else{
|
|
190
|
-
refCount.set(nodeToMonitor, currentCount + 1);
|
|
191
|
-
}
|
|
192
|
-
}else{
|
|
193
|
-
if(mutationObserverLookup.has(nodeToMonitor)){
|
|
194
|
-
console.warn(refCountErr);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
this.dispatchEvent(new Event('disconnectedCallback'));
|
|
198
|
-
|
|
64
|
+
get disconnectedSignal(): AbortSignal {
|
|
65
|
+
return this.#abortController.signal;
|
|
199
66
|
}
|
|
200
67
|
|
|
201
|
-
async observe(
|
|
202
|
-
this.#
|
|
203
|
-
|
|
204
|
-
const {whereMediaMatches} = init;
|
|
205
|
-
if(whereMediaMatches === undefined){
|
|
206
|
-
await this.#observe2(within);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const mql = window.matchMedia(whereMediaMatches);
|
|
210
|
-
if(mql.matches){
|
|
211
|
-
await this.#observe2(within);
|
|
68
|
+
async observe(rootNode: Node): Promise<void> {
|
|
69
|
+
if (this.#rootNode) {
|
|
70
|
+
throw new Error('Already observing');
|
|
212
71
|
}
|
|
213
|
-
mql.addEventListener('change', async (e) => {
|
|
214
|
-
if(e.matches){
|
|
215
|
-
if(this.objNde === undefined){
|
|
216
|
-
await this.#observe2(within);
|
|
217
|
-
}else{
|
|
218
|
-
await this.#mountAll();
|
|
219
|
-
}
|
|
220
|
-
}else{
|
|
221
|
-
if(this.objNde !== undefined){
|
|
222
|
-
await this.#dismountAll();
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
72
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
mutationObserverLookup.set(nodeToMonitor, new RootMutObs(nodeToMonitor, this.#mountInit));
|
|
234
|
-
refCount.set(nodeToMonitor, 1);
|
|
235
|
-
}else{
|
|
236
|
-
const currentCount = refCount.get(nodeToMonitor);
|
|
237
|
-
if(currentCount === undefined){
|
|
238
|
-
console.warn(refCountErr);
|
|
239
|
-
}else{
|
|
240
|
-
refCount.set(nodeToMonitor, currentCount + 1);
|
|
241
|
-
}
|
|
73
|
+
this.#rootNode = new WeakRef(rootNode);
|
|
74
|
+
|
|
75
|
+
// Wait for whereAttr utilities to load if needed
|
|
76
|
+
if (this.#init.whereAttr && !this.#matchesWhereAttrFn) {
|
|
77
|
+
await this.#preloadWhereAttrUtilities();
|
|
242
78
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
(
|
|
246
|
-
//TODO: disconnected
|
|
247
|
-
if(this.#isComplex){
|
|
248
|
-
this.#inspectWithin(within, false);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
const {mutationRecords} = e;
|
|
252
|
-
const elsToInspect: Array<Element> = [];
|
|
253
|
-
//const elsToDisconnect: Array<Element> = [];
|
|
254
|
-
const doDisconnect = this.#mountInit.do?.disconnect;
|
|
255
|
-
let attrChangeInfosMap: Map<Element, Array<AttrChangeInfo>> | undefined;
|
|
256
|
-
for(const mutationRecord of mutationRecords){
|
|
257
|
-
const {addedNodes, type, removedNodes} = mutationRecord;
|
|
258
|
-
const addedElements = Array.from(addedNodes).filter(x => x instanceof Element) as Array<Element>;
|
|
259
|
-
addedElements.forEach(x => elsToInspect.push(x));
|
|
260
|
-
if(type === 'attributes'){
|
|
261
|
-
const {target, attributeName, oldValue} = mutationRecord;
|
|
262
|
-
if(target instanceof Element && attributeName !== null /*&& this.#mounted.has(target)*/){
|
|
263
|
-
|
|
264
|
-
if(fullListOfAttrs !== undefined){
|
|
265
|
-
const idx = fullListOfAttrs.indexOf(attributeName);
|
|
266
|
-
if(idx !== -1){
|
|
267
|
-
if(attrChangeInfosMap === undefined) attrChangeInfosMap = new Map();
|
|
268
|
-
let attrChangeInfos = attrChangeInfosMap.get(target);
|
|
269
|
-
if(attrChangeInfos === undefined){
|
|
270
|
-
attrChangeInfos = [];
|
|
271
|
-
attrChangeInfosMap.set(target, attrChangeInfos);
|
|
272
|
-
}
|
|
273
|
-
const newValue = target.getAttribute(attributeName);
|
|
274
|
-
const parts = this.#attrParts![idx];
|
|
275
|
-
const attrChangeInfo: AttrChangeInfo = {
|
|
276
|
-
isSOfTAttr: false,
|
|
277
|
-
oldValue,
|
|
278
|
-
name: attributeName,
|
|
279
|
-
newValue,
|
|
280
|
-
idx,
|
|
281
|
-
parts
|
|
282
|
-
}
|
|
283
|
-
attrChangeInfos.push(attrChangeInfo)
|
|
284
|
-
}
|
|
285
|
-
|
|
79
|
+
|
|
80
|
+
// Process existing elements
|
|
81
|
+
this.#processNode(rootNode);
|
|
286
82
|
|
|
83
|
+
// Set up mutation observer
|
|
84
|
+
this.#mutationObserver = new MutationObserver((mutations) => {
|
|
85
|
+
const attrChanges: AttrChange[] = [];
|
|
86
|
+
|
|
87
|
+
for (const mutation of mutations) {
|
|
88
|
+
if (mutation.type === 'childList') {
|
|
89
|
+
for (const node of mutation.addedNodes) {
|
|
90
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
91
|
+
this.#processNode(node);
|
|
287
92
|
}
|
|
288
|
-
|
|
289
93
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
94
|
+
mutation.removedNodes.forEach(node => {
|
|
95
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
96
|
+
this.#handleRemoval(node as Element);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
} else if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
|
|
100
|
+
// Handle attribute changes for mounted elements
|
|
101
|
+
const element = mutation.target as Element;
|
|
102
|
+
if (this.#mountedElements.has(element) && this.#init.whereAttr) {
|
|
103
|
+
const changes = this.#checkAttrChanges(element);
|
|
104
|
+
attrChanges.push(...changes);
|
|
299
105
|
}
|
|
300
|
-
this.dispatchEvent(new DisconnectEvent(deletedElement));
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
if(attrChangeInfosMap !== undefined){
|
|
305
|
-
const {AttrChangeEvent} = await import('./Events.js');
|
|
306
|
-
for(const [key, value] of attrChangeInfosMap){
|
|
307
|
-
this.dispatchEvent(new AttrChangeEvent(key, value))
|
|
308
106
|
}
|
|
309
107
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
108
|
+
|
|
109
|
+
// Batch and dispatch attribute changes
|
|
110
|
+
if (attrChanges.length > 0) {
|
|
111
|
+
this.dispatchEvent(new AttrChangeEvent(attrChanges));
|
|
313
112
|
}
|
|
314
|
-
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const observerConfig: MutationObserverInit = {
|
|
116
|
+
childList: true,
|
|
117
|
+
subtree: true
|
|
118
|
+
};
|
|
315
119
|
|
|
316
|
-
|
|
120
|
+
// Add attribute observation if whereAttr is configured
|
|
121
|
+
if (this.#init.whereAttr) {
|
|
122
|
+
observerConfig.attributes = true;
|
|
123
|
+
observerConfig.attributeOldValue = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.#mutationObserver.observe(rootNode, observerConfig);
|
|
317
127
|
}
|
|
318
128
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if(name === null) throw 400;
|
|
324
|
-
let instance = within.querySelector(name);
|
|
325
|
-
if(instance === null){
|
|
326
|
-
instance = new customElement();
|
|
327
|
-
if(within === document){
|
|
328
|
-
within.head.appendChild(instance);
|
|
329
|
-
}else{
|
|
330
|
-
within.appendChild(instance);
|
|
331
|
-
}
|
|
129
|
+
disconnect(): void {
|
|
130
|
+
if (this.#mutationObserver) {
|
|
131
|
+
this.#mutationObserver.disconnect();
|
|
132
|
+
this.#mutationObserver = undefined;
|
|
332
133
|
}
|
|
333
|
-
|
|
134
|
+
this.#abortController.abort();
|
|
135
|
+
this.#rootNode = undefined;
|
|
334
136
|
}
|
|
335
137
|
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
138
|
+
async #loadImports(): Promise<void> {
|
|
139
|
+
if (this.#importsLoaded || !this.#init.import) {
|
|
140
|
+
return;
|
|
339
141
|
}
|
|
340
|
-
|
|
142
|
+
|
|
143
|
+
// Dynamically load the import utilities only when needed
|
|
144
|
+
const { loadImports } = await import('./loadImports.js');
|
|
145
|
+
this.#modules = await loadImports(this.#init.import);
|
|
146
|
+
this.#importsLoaded = true;
|
|
147
|
+
|
|
148
|
+
this.dispatchEvent(new LoadEvent(this.#modules));
|
|
341
149
|
}
|
|
342
150
|
|
|
343
|
-
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const {import: imp} = this.#mountInit;
|
|
348
|
-
const me = this.mountedElements;
|
|
349
|
-
const options = this.#options;
|
|
350
|
-
for(const match of matching){
|
|
351
|
-
if(alreadyMounted.has(match)) continue;
|
|
352
|
-
if(!me.weakSet.has(match)){
|
|
353
|
-
me.setWeak.add(new WeakRef(match));
|
|
354
|
-
me.weakSet.add(match);
|
|
355
|
-
}
|
|
356
|
-
if(imp !== undefined){
|
|
357
|
-
switch(typeof imp){
|
|
358
|
-
case 'string':
|
|
359
|
-
this.module = await import(imp);
|
|
360
|
-
break;
|
|
361
|
-
case 'object':
|
|
362
|
-
if(Array.isArray(imp)){
|
|
363
|
-
throw 'NI: Firefox'
|
|
364
|
-
}
|
|
365
|
-
break;
|
|
366
|
-
case 'function':
|
|
367
|
-
this.module = await imp(match, this, {
|
|
368
|
-
stage: 'Import',
|
|
369
|
-
initializing
|
|
370
|
-
});
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
if(mount !== undefined) {
|
|
375
|
-
mount(match, this, {
|
|
376
|
-
stage: 'PostImport',
|
|
377
|
-
initializing
|
|
378
|
-
})
|
|
379
|
-
}
|
|
380
|
-
if(options?.leaveBreadcrumb){
|
|
381
|
-
if((<any>match)[guid] === undefined){
|
|
382
|
-
(<any>match)[guid] = new Set();
|
|
383
|
-
}
|
|
384
|
-
(<any>match)[guid].add(this);
|
|
385
|
-
}
|
|
386
|
-
const {MountEvent} = await import('./Events.js');
|
|
387
|
-
this.dispatchEvent(new MountEvent(match, initializing));
|
|
388
|
-
//should we automatically call readAttrs?
|
|
389
|
-
//the thinking is it might make more sense to call that after mounting
|
|
151
|
+
#processNode(node: Node): void {
|
|
152
|
+
// If it's an element node, check if it matches
|
|
153
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
154
|
+
const element = node as Element;
|
|
390
155
|
|
|
391
|
-
this.#
|
|
156
|
+
if (this.#matchesSelector(element)) {
|
|
157
|
+
this.#handleMatch(element);
|
|
158
|
+
}
|
|
392
159
|
}
|
|
393
|
-
}
|
|
394
160
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const attrChangeInfos: Array<AttrChangeInfo> = [];
|
|
399
|
-
const oldValue = null;
|
|
400
|
-
if(fullListOfAttrs !== undefined){
|
|
401
|
-
const attrParts = this.#attrParts
|
|
161
|
+
// Process children
|
|
162
|
+
if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE) {
|
|
163
|
+
const root = node as Element | Document;
|
|
402
164
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const newValue = match.getAttribute(name);
|
|
412
|
-
|
|
413
|
-
attrChangeInfos.push({
|
|
414
|
-
idx,
|
|
415
|
-
isSOfTAttr: false,
|
|
416
|
-
newValue,
|
|
417
|
-
oldValue,
|
|
418
|
-
name,
|
|
419
|
-
parts
|
|
165
|
+
// If whereAttr is specified, we need to check all elements
|
|
166
|
+
// since we can't use querySelectorAll for complex attribute matching
|
|
167
|
+
if (this.#init.whereAttr) {
|
|
168
|
+
// Get all elements matching the CSS selector first
|
|
169
|
+
root.querySelectorAll(this.#init.whereElementMatches).forEach(child => {
|
|
170
|
+
if (this.#matchesSelector(child)) {
|
|
171
|
+
this.#handleMatch(child);
|
|
172
|
+
}
|
|
420
173
|
});
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if(observedAttrsWhenMounted !== undefined){
|
|
426
|
-
for(const observedAttr of observedAttrsWhenMounted){
|
|
427
|
-
const attrIsString = typeof observedAttr === 'string';
|
|
428
|
-
const name = attrIsString ? observedAttr : observedAttr.name;
|
|
429
|
-
let mapsTo: string | undefined;
|
|
430
|
-
let newValue = match.getAttribute(name);
|
|
431
|
-
if(!attrIsString){
|
|
432
|
-
const {customParser, instanceOf, mapsTo: mt, valIfNull} = observedAttr;
|
|
433
|
-
if(instanceOf || customParser) throw 'NI';
|
|
434
|
-
if(newValue === null) newValue = valIfNull;
|
|
435
|
-
mapsTo = mt;
|
|
436
|
-
}
|
|
437
|
-
attrChangeInfos.push({
|
|
438
|
-
isSOfTAttr: true,
|
|
439
|
-
newValue,
|
|
440
|
-
oldValue,
|
|
441
|
-
name,
|
|
442
|
-
mapsTo
|
|
174
|
+
} else {
|
|
175
|
+
// Optimize: use querySelectorAll directly when no whereAttr
|
|
176
|
+
root.querySelectorAll(this.#init.whereElementMatches).forEach(child => {
|
|
177
|
+
this.#handleMatch(child);
|
|
443
178
|
});
|
|
444
179
|
}
|
|
445
180
|
}
|
|
446
|
-
|
|
447
|
-
return attrChangeInfos;
|
|
448
181
|
}
|
|
449
182
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
this.dispatchEvent(new DismountEvent(unmatch));
|
|
183
|
+
#matchesSelector(element: Element): boolean {
|
|
184
|
+
// Check whereElementMatches condition
|
|
185
|
+
const matchesElement = element.matches(this.#init.whereElementMatches);
|
|
186
|
+
|
|
187
|
+
// If whereAttr is not specified, only check whereElementMatches
|
|
188
|
+
if (!this.#init.whereAttr) {
|
|
189
|
+
return matchesElement;
|
|
458
190
|
}
|
|
191
|
+
|
|
192
|
+
// Use cached function (should be loaded by now from constructor)
|
|
193
|
+
if (!this.#matchesWhereAttrFn) {
|
|
194
|
+
console.warn('whereAttr utilities not loaded yet');
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Both conditions must be true (AND logic)
|
|
199
|
+
return matchesElement && this.#matchesWhereAttrFn(element, this.#init.whereAttr);
|
|
459
200
|
}
|
|
460
201
|
|
|
461
|
-
async #
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
this.#dismount(mounted.map(x => x.deref()).filter(x => x !== undefined) as Array<Element>);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
async #mountAll(){
|
|
468
|
-
//TODO: copilot created, check if needed
|
|
469
|
-
const {whereSatisfies, whereInstanceOf} = this.#mountInit;
|
|
470
|
-
const match = await this.#selector();
|
|
471
|
-
const els = Array.from(document.querySelectorAll(match));
|
|
472
|
-
this.#filterAndMount(els, document.body, false, true);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
async #filterAndDismount(): Promise<Set<Element>>{
|
|
476
|
-
const returnSet = new Set<Element>();
|
|
477
|
-
if(this.#mountedList !== undefined){
|
|
478
|
-
const previouslyMounted = this.#mountedList.map(x => x.deref());
|
|
479
|
-
const {whereSatisfies, whereInstanceOf} = this.#mountInit;
|
|
480
|
-
const match = await this.#selector();
|
|
481
|
-
const elsToUnMount = previouslyMounted.filter(x => {
|
|
482
|
-
if(x === undefined) return false;
|
|
483
|
-
if(!x.matches(match)) return true;
|
|
484
|
-
//TODO: add check for outside
|
|
485
|
-
if(whereSatisfies !== undefined){
|
|
486
|
-
if(!whereSatisfies(x, this, {stage: 'Inspecting', initializing: false})) return true;
|
|
487
|
-
}
|
|
488
|
-
returnSet.add(x);
|
|
489
|
-
return false;
|
|
490
|
-
}) as Array<Element>;
|
|
491
|
-
this.#dismount(elsToUnMount);
|
|
202
|
+
async #handleMatch(element: Element): Promise<void> {
|
|
203
|
+
if (this.#processedElements.has(element)) {
|
|
204
|
+
return;
|
|
492
205
|
}
|
|
493
|
-
this.#mountedList = Array.from(returnSet).map(x => new WeakRef(x));
|
|
494
|
-
return returnSet;
|
|
495
|
-
}
|
|
496
206
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if(elementToExclude === matchCandidate || elementToExclude.contains(matchCandidate)) return false;
|
|
207
|
+
// Load imports if not already loaded
|
|
208
|
+
if (!this.#importsLoaded && this.#init.import) {
|
|
209
|
+
await this.#loadImports();
|
|
501
210
|
}
|
|
502
|
-
return true;
|
|
503
|
-
}
|
|
504
211
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const match = await this.#selector();
|
|
508
|
-
const elsToMount = els.filter(x => {
|
|
509
|
-
if(checkMatch){
|
|
510
|
-
if(!x.matches(match)) return false;
|
|
212
|
+
this.#processedElements.add(element);
|
|
213
|
+
this.#mountedElements.add(element);
|
|
511
214
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
if(whereSatisfies !== undefined){
|
|
518
|
-
if(!whereSatisfies(x, this, {stage: 'Inspecting', initializing})) return false;
|
|
519
|
-
}
|
|
520
|
-
if(whereInstanceOf !== undefined){
|
|
521
|
-
if(!this.#confirmInstanceOf(x, whereInstanceOf)) return false;
|
|
522
|
-
}
|
|
523
|
-
return true;
|
|
524
|
-
});
|
|
525
|
-
for(const elToMount of elsToMount){
|
|
526
|
-
if(elToMount.matches(inclTemplQry)){
|
|
527
|
-
if(elToMount instanceof HTMLTemplateElement && elToMount.getAttribute('rel') === 'preload'){
|
|
528
|
-
(await import('./preloadContent.js')).preloadContent(elToMount/*, this.#mountInit.withTargetShadowRoot*/);
|
|
529
|
-
}else{
|
|
530
|
-
await this.#compose(elToMount as HTMLTemplateElement, 0);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
215
|
+
const rootNode = this.#rootNode?.deref();
|
|
216
|
+
if (!rootNode) {
|
|
217
|
+
// Root node was garbage collected
|
|
218
|
+
return;
|
|
535
219
|
}
|
|
536
|
-
await bindishIt(els, target, {assigner});
|
|
537
|
-
if(elsToMount.length === 0) return;
|
|
538
|
-
this.#mount(elsToMount, initializing);
|
|
539
|
-
}
|
|
540
220
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const idGenerators = Array.from((within as DocumentFragment).querySelectorAll('[-id]'))
|
|
547
|
-
if(idGenerators[0]){
|
|
548
|
-
const {genIds} = await import('./refid/genIds.js');
|
|
549
|
-
for(const el of idGenerators){
|
|
550
|
-
genIds(el);
|
|
551
|
-
el.removeAttribute('-id');
|
|
221
|
+
const context: MountContext = {
|
|
222
|
+
modules: this.#modules,
|
|
223
|
+
observer: this,
|
|
224
|
+
observeInfo: {
|
|
225
|
+
rootNode
|
|
552
226
|
}
|
|
553
|
-
}
|
|
554
|
-
bindish(within as DocumentFragment, within, {assigner: this.#mountInit.assigner});
|
|
555
|
-
await this.composeFragment(within as DocumentFragment, 0);
|
|
556
|
-
const match = await this.#selector();
|
|
557
|
-
const els = Array.from((within as Element).querySelectorAll(match));
|
|
558
|
-
this.#filterAndMount(els, within, false, initializing);
|
|
227
|
+
};
|
|
559
228
|
|
|
560
|
-
|
|
229
|
+
// Apply assignGingerly if specified
|
|
230
|
+
if (this.#init.assignGingerly) {
|
|
231
|
+
const { assignGingerly } = await import('assign-gingerly/index.js');
|
|
232
|
+
assignGingerly(element, this.#init.assignGingerly);
|
|
233
|
+
}
|
|
561
234
|
|
|
562
|
-
|
|
235
|
+
// Call do callback
|
|
236
|
+
if (this.#init.do) {
|
|
237
|
+
if (typeof this.#init.do === 'function') {
|
|
238
|
+
this.#init.do(element, context);
|
|
239
|
+
} else if (this.#init.do.mount) {
|
|
240
|
+
this.#init.do.mount(element, context);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
563
243
|
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
const mutObs = mutationObserverLookup.get(node);
|
|
573
|
-
if(mutObs !== undefined){
|
|
574
|
-
mutObservers.push(mutObs);
|
|
575
|
-
}else{
|
|
576
|
-
const currentCount = refCount.get(node) || 0;
|
|
577
|
-
const newMutObs = new RootMutObs(node, mountInit);
|
|
578
|
-
mutationObserverLookup.set(node, newMutObs);
|
|
579
|
-
refCount.set(node, currentCount + 1);
|
|
580
|
-
mutObservers.push(newMutObs);
|
|
244
|
+
// Dispatch mount event
|
|
245
|
+
this.dispatchEvent(new MountEvent(element, this.#modules));
|
|
246
|
+
|
|
247
|
+
// Check for initial attribute changes if whereAttr is configured
|
|
248
|
+
if (this.#init.whereAttr) {
|
|
249
|
+
const changes = this.#checkAttrChanges(element);
|
|
250
|
+
if (changes.length > 0) {
|
|
251
|
+
this.dispatchEvent(new AttrChangeEvent(changes));
|
|
581
252
|
}
|
|
582
253
|
}
|
|
583
|
-
|
|
584
|
-
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#checkAttrChanges(element: Element): AttrChange[] {
|
|
257
|
+
if (!this.#init.whereAttr || !this.#buildAttrCoordinateMapFn) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const isCustomElement = element.tagName.toLowerCase().includes('-');
|
|
262
|
+
const attrCoordMap = this.#buildAttrCoordinateMapFn(this.#init.whereAttr, isCustomElement);
|
|
263
|
+
|
|
264
|
+
// Get or create the attribute state for this element
|
|
265
|
+
let attrState = this.#elementAttrStates.get(element);
|
|
266
|
+
if (!attrState) {
|
|
267
|
+
attrState = new Map<string, string | null>();
|
|
268
|
+
this.#elementAttrStates.set(element, attrState);
|
|
585
269
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
270
|
+
|
|
271
|
+
const changes: AttrChange[] = [];
|
|
272
|
+
const currentAttrs = new Set<string>();
|
|
273
|
+
|
|
274
|
+
// Check all possible attributes from the coordinate map
|
|
275
|
+
for (const attrName of Object.keys(attrCoordMap)) {
|
|
276
|
+
const coordinate = attrCoordMap[attrName];
|
|
277
|
+
const currentValue = element.getAttribute(attrName);
|
|
278
|
+
const previousValue = attrState.get(attrName);
|
|
279
|
+
|
|
280
|
+
if (currentValue !== null) {
|
|
281
|
+
currentAttrs.add(attrName);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Include if: currently has value OR previously had value but now removed
|
|
285
|
+
if (currentValue !== null || (previousValue !== undefined && currentValue === null)) {
|
|
286
|
+
// Check if value changed
|
|
287
|
+
if (currentValue !== previousValue) {
|
|
288
|
+
const attrNode = currentValue !== null ? element.getAttributeNode(attrName) : null;
|
|
289
|
+
const mapEntry = this.#init.map?.[coordinate] || null;
|
|
290
|
+
|
|
291
|
+
changes.push({
|
|
292
|
+
value: currentValue,
|
|
293
|
+
attrNode,
|
|
294
|
+
mapEntry,
|
|
295
|
+
attrName,
|
|
296
|
+
coordinate,
|
|
297
|
+
element
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Update state
|
|
301
|
+
if (currentValue !== null) {
|
|
302
|
+
attrState.set(attrName, currentValue);
|
|
303
|
+
} else {
|
|
304
|
+
attrState.delete(attrName);
|
|
305
|
+
}
|
|
590
306
|
}
|
|
591
|
-
}
|
|
307
|
+
}
|
|
592
308
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
//make external
|
|
597
|
-
function areAllIdle(mutObs: Array<RootMutObs>){
|
|
598
|
-
for(const mo of mutObs){
|
|
599
|
-
if(!mo.isIdle) return false;
|
|
309
|
+
|
|
310
|
+
return changes;
|
|
600
311
|
}
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
312
|
|
|
313
|
+
#handleRemoval(element: Element): void {
|
|
314
|
+
if (!this.#mountedElements.has(element)) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
604
317
|
|
|
605
|
-
|
|
606
|
-
export const inclTemplQry = 'template[src^="#"]:not([hidden]),template[src^="!"]:not([hidden])';
|
|
607
|
-
|
|
608
|
-
export interface MountObserver extends IMountObserver{}
|
|
318
|
+
this.#mountedElements.delete(element);
|
|
609
319
|
|
|
320
|
+
const rootNode = this.#rootNode?.deref();
|
|
321
|
+
if (!rootNode) {
|
|
322
|
+
// Root node was garbage collected
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
610
325
|
|
|
326
|
+
const context: MountContext = {
|
|
327
|
+
modules: this.#modules,
|
|
328
|
+
observer: this,
|
|
329
|
+
observeInfo: {
|
|
330
|
+
rootNode
|
|
331
|
+
}
|
|
332
|
+
};
|
|
611
333
|
|
|
334
|
+
// Call dismount callback
|
|
335
|
+
if (this.#init.do && typeof this.#init.do !== 'function' && this.#init.do.dismount) {
|
|
336
|
+
this.#init.do.dismount(element, context);
|
|
337
|
+
}
|
|
612
338
|
|
|
339
|
+
// Dispatch dismount event
|
|
340
|
+
this.dispatchEvent(new DismountEvent(element));
|
|
613
341
|
|
|
614
|
-
//
|
|
342
|
+
// Check if element is being moved within the same root
|
|
343
|
+
// If it's truly disconnected, dispatch disconnect event
|
|
344
|
+
setTimeout(() => {
|
|
345
|
+
if (!rootNode.contains(element)) {
|
|
346
|
+
if (this.#init.do && typeof this.#init.do !== 'function' && this.#init.do.disconnect) {
|
|
347
|
+
this.#init.do.disconnect(element, context);
|
|
348
|
+
}
|
|
615
349
|
|
|
350
|
+
this.dispatchEvent(new DisconnectEvent(element));
|
|
351
|
+
}
|
|
352
|
+
}, 0);
|
|
353
|
+
}
|
|
354
|
+
}
|