mount-observer 0.0.112 → 0.1.1
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 +62 -26
- package/Events.ts +52 -30
- package/MountObserver.js +285 -514
- package/MountObserver.ts +362 -538
- package/README.md +149 -56
- package/SharedMutationObserver.js +70 -0
- package/SharedMutationObserver.ts +96 -0
- package/attrCoordinates.js +93 -0
- package/attrCoordinates.ts +122 -0
- package/index.js +3 -0
- package/index.ts +22 -0
- package/loadImports.js +47 -0
- package/loadImports.ts +56 -0
- package/mediaQuery.js +86 -0
- package/mediaQuery.ts +113 -0
- package/package.json +11 -119
- package/playwright.config.ts +0 -1
- package/types.d.ts +104 -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 -24
- 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 -198
- 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,439 @@
|
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
IMountObserver,
|
|
5
|
+
MountContext,
|
|
6
|
+
AttrChange
|
|
7
|
+
} from './types.js';
|
|
8
|
+
import {
|
|
9
|
+
MountEvent,
|
|
10
|
+
DismountEvent,
|
|
11
|
+
DisconnectEvent,
|
|
12
|
+
LoadEvent,
|
|
13
|
+
AttrChangeEvent,
|
|
14
|
+
MediaMatchEvent,
|
|
15
|
+
MediaUnmatchEvent
|
|
16
|
+
} from './Events.js';
|
|
17
|
+
import {
|
|
18
|
+
registerSharedObserver,
|
|
19
|
+
unregisterSharedObserver,
|
|
20
|
+
type MutationCallback
|
|
21
|
+
} from './SharedMutationObserver.js';
|
|
22
|
+
|
|
23
|
+
export class MountObserver extends EventTarget implements IMountObserver {
|
|
24
|
+
#init: MountInit;
|
|
25
|
+
#options: MountObserverOptions;
|
|
24
26
|
#abortController: AbortController;
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
#modules: any[] = [];
|
|
28
|
+
#mountedElements = new WeakSet<Element>();
|
|
29
|
+
#processedElements = new WeakSet<Element>();
|
|
30
|
+
#mutationCallback: MutationCallback | undefined;
|
|
31
|
+
#rootNode: WeakRef<Node> | undefined;
|
|
32
|
+
#importsLoaded = false;
|
|
33
|
+
#elementAttrStates = new WeakMap<Element, Map<string, string | null>>();
|
|
34
|
+
#elementOnceAttrs = new WeakMap<Element, Set<string>>();
|
|
35
|
+
#matchesWhereAttrFn: ((element: Element, whereAttr: any) => boolean) | null = null;
|
|
36
|
+
#buildAttrCoordinateMapFn: ((whereAttr: any, isCustomElement: boolean) => any) | null = null;
|
|
37
|
+
#mediaQueryCleanup?: () => void;
|
|
38
|
+
#mediaMatches: boolean = true;
|
|
39
|
+
|
|
40
|
+
constructor(init: MountInit, options: MountObserverOptions = {}) {
|
|
33
41
|
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;
|
|
42
|
+
this.#init = init;
|
|
43
|
+
this.#options = options;
|
|
44
44
|
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
45
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
76
|
-
//This method is called publicly from outside mount-observer -- keep it public
|
|
77
|
-
async composeFragment(fragment: DocumentFragment, level: number){
|
|
78
|
-
const bis = fragment.querySelectorAll(`${inclTemplQry}`) as NodeListOf<HTMLTemplateElement>;
|
|
79
|
-
for(const bi of bis){
|
|
80
|
-
if(bi.getAttribute('rel') === 'preload'){
|
|
81
|
-
(await import('./preloadContent.js')).preloadContent(bi);
|
|
82
|
-
}else{
|
|
83
|
-
await this.#compose(bi, level);
|
|
84
|
-
}
|
|
85
|
-
|
|
46
|
+
if (options.disconnectedSignal) {
|
|
47
|
+
options.disconnectedSignal.addEventListener('abort', () => {
|
|
48
|
+
this.disconnect();
|
|
49
|
+
});
|
|
86
50
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
51
|
|
|
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
|
-
|
|
52
|
+
// Preload whereAttr utilities if needed
|
|
53
|
+
if (init.whereAttr) {
|
|
54
|
+
this.#preloadWhereAttrUtilities();
|
|
55
|
+
}
|
|
99
56
|
|
|
57
|
+
// Start loading imports if eager
|
|
58
|
+
if (init.loadingEagerness === 'eager' && init.import) {
|
|
59
|
+
this.#loadImports();
|
|
60
|
+
}
|
|
100
61
|
}
|
|
101
|
-
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
);
|
|
111
|
-
//console.log({xpathResult})
|
|
112
|
-
try {
|
|
113
|
-
let thisNode = iterator.iterateNext();
|
|
114
|
-
return thisNode;
|
|
115
|
-
}catch(e){
|
|
116
|
-
return null;
|
|
62
|
+
|
|
63
|
+
async #preloadWhereAttrUtilities(): Promise<void> {
|
|
64
|
+
if (!this.#matchesWhereAttrFn) {
|
|
65
|
+
const { matchesWhereAttr } = await import('./whereAttr.js');
|
|
66
|
+
this.#matchesWhereAttrFn = matchesWhereAttr;
|
|
67
|
+
}
|
|
68
|
+
if (!this.#buildAttrCoordinateMapFn) {
|
|
69
|
+
const { buildAttrCoordinateMap } = await import('./attrCoordinates.js');
|
|
70
|
+
this.#buildAttrCoordinateMapFn = buildAttrCoordinateMap;
|
|
117
71
|
}
|
|
118
72
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if(this.#templLookUp.has(refName)) return this.#templLookUp.get(refName)!;
|
|
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
|
-
}
|
|
73
|
+
|
|
74
|
+
async #setupMediaQuery(): Promise<void> {
|
|
75
|
+
if (!this.#rootNode) {
|
|
76
|
+
throw new Error('Cannot setup media query before observe() is called');
|
|
133
77
|
}
|
|
78
|
+
|
|
79
|
+
const { setupMediaQuery } = await import('./mediaQuery.js');
|
|
80
|
+
const result = setupMediaQuery(
|
|
81
|
+
this.#init,
|
|
82
|
+
this.#rootNode,
|
|
83
|
+
this.#mountedElements,
|
|
84
|
+
this.#modules,
|
|
85
|
+
this,
|
|
86
|
+
(node) => this.#processNode(node)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
this.#mediaMatches = result.mediaMatches;
|
|
90
|
+
this.#mediaQueryCleanup = result.cleanup;
|
|
91
|
+
}
|
|
134
92
|
|
|
93
|
+
get disconnectedSignal(): AbortSignal {
|
|
94
|
+
return this.#abortController.signal;
|
|
95
|
+
}
|
|
135
96
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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);
|
|
97
|
+
async observe(rootNode: Node): Promise<void> {
|
|
98
|
+
if (this.#rootNode) {
|
|
99
|
+
throw new Error('Already observing');
|
|
172
100
|
}
|
|
173
|
-
return templ as HTMLTemplateElement;
|
|
174
|
-
}
|
|
175
101
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if(
|
|
180
|
-
|
|
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
|
-
}
|
|
102
|
+
this.#rootNode = new WeakRef(rootNode);
|
|
103
|
+
|
|
104
|
+
// Set up media query if specified (needs rootNode to be set first)
|
|
105
|
+
if (this.#init.whereMediaMatches) {
|
|
106
|
+
await this.#setupMediaQuery();
|
|
196
107
|
}
|
|
197
|
-
this.dispatchEvent(new Event('disconnectedCallback'));
|
|
198
|
-
|
|
199
|
-
}
|
|
200
108
|
|
|
201
|
-
|
|
202
|
-
this.#
|
|
203
|
-
|
|
204
|
-
const {whereMediaMatches} = init;
|
|
205
|
-
if(whereMediaMatches === undefined){
|
|
206
|
-
await this.#observe2(within);
|
|
207
|
-
return;
|
|
109
|
+
// Wait for whereAttr utilities to load if needed
|
|
110
|
+
if (this.#init.whereAttr && !this.#matchesWhereAttrFn) {
|
|
111
|
+
await this.#preloadWhereAttrUtilities();
|
|
208
112
|
}
|
|
209
|
-
|
|
210
|
-
if
|
|
211
|
-
|
|
113
|
+
|
|
114
|
+
// Process existing elements only if media matches
|
|
115
|
+
if (this.#mediaMatches) {
|
|
116
|
+
this.#processNode(rootNode);
|
|
212
117
|
}
|
|
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
118
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if(!mutationObserverLookup.has(nodeToMonitor)){
|
|
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
|
-
}
|
|
242
|
-
}
|
|
243
|
-
const rootMutObs = mutationObserverLookup.get(within)!;
|
|
244
|
-
const fullListOfAttrs = this.#fullListOfEnhancementAttrs;
|
|
245
|
-
(rootMutObs as any as AddMutationEventListener).addEventListener('mutation-event', async (e: MutationEvent) => {
|
|
246
|
-
//TODO: disconnected
|
|
247
|
-
if(this.#isComplex){
|
|
248
|
-
this.#inspectWithin(within, false);
|
|
119
|
+
// Create mutation callback
|
|
120
|
+
this.#mutationCallback = (mutations) => {
|
|
121
|
+
// Skip processing if media doesn't match
|
|
122
|
+
if (!this.#mediaMatches) {
|
|
249
123
|
return;
|
|
250
124
|
}
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
286
|
-
|
|
125
|
+
|
|
126
|
+
const attrChanges: AttrChange[] = [];
|
|
127
|
+
|
|
128
|
+
for (const mutation of mutations) {
|
|
129
|
+
if (mutation.type === 'childList') {
|
|
130
|
+
for (const node of mutation.addedNodes) {
|
|
131
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
132
|
+
this.#processNode(node);
|
|
287
133
|
}
|
|
288
|
-
|
|
289
134
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
135
|
+
mutation.removedNodes.forEach(node => {
|
|
136
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
137
|
+
this.#handleRemoval(node as Element);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
} else if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
|
|
141
|
+
// Handle attribute changes for mounted elements
|
|
142
|
+
const element = mutation.target as Element;
|
|
143
|
+
if (this.#mountedElements.has(element) && this.#init.whereAttr) {
|
|
144
|
+
const changes = this.#checkAttrChanges(element);
|
|
145
|
+
attrChanges.push(...changes);
|
|
299
146
|
}
|
|
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
147
|
}
|
|
309
148
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
149
|
+
|
|
150
|
+
// Batch and dispatch attribute changes
|
|
151
|
+
if (attrChanges.length > 0) {
|
|
152
|
+
this.dispatchEvent(new AttrChangeEvent(attrChanges, this.#init));
|
|
313
153
|
}
|
|
314
|
-
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const observerConfig: MutationObserverInit = {
|
|
157
|
+
childList: true,
|
|
158
|
+
subtree: true
|
|
159
|
+
};
|
|
315
160
|
|
|
316
|
-
|
|
161
|
+
// Add attribute observation if whereAttr is configured
|
|
162
|
+
if (this.#init.whereAttr) {
|
|
163
|
+
observerConfig.attributes = true;
|
|
164
|
+
observerConfig.attributeOldValue = true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Register with shared mutation observer
|
|
168
|
+
registerSharedObserver(rootNode, this.#mutationCallback, observerConfig);
|
|
317
169
|
}
|
|
318
170
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
instance = new customElement();
|
|
327
|
-
if(within === document){
|
|
328
|
-
within.head.appendChild(instance);
|
|
329
|
-
}else{
|
|
330
|
-
within.appendChild(instance);
|
|
331
|
-
}
|
|
171
|
+
disconnect(): void {
|
|
172
|
+
const rootNode = this.#rootNode?.deref();
|
|
173
|
+
|
|
174
|
+
// Unregister from shared mutation observer
|
|
175
|
+
if (rootNode && this.#mutationCallback) {
|
|
176
|
+
unregisterSharedObserver(rootNode, this.#mutationCallback);
|
|
177
|
+
this.#mutationCallback = undefined;
|
|
332
178
|
}
|
|
333
|
-
|
|
179
|
+
|
|
180
|
+
// Remove media query listener
|
|
181
|
+
if (this.#mediaQueryCleanup) {
|
|
182
|
+
this.#mediaQueryCleanup();
|
|
183
|
+
this.#mediaQueryCleanup = undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.#abortController.abort();
|
|
187
|
+
this.#rootNode = undefined;
|
|
334
188
|
}
|
|
335
189
|
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
190
|
+
async #loadImports(): Promise<void> {
|
|
191
|
+
if (this.#importsLoaded || !this.#init.import) {
|
|
192
|
+
return;
|
|
339
193
|
}
|
|
340
|
-
|
|
194
|
+
|
|
195
|
+
// Dynamically load the import utilities only when needed
|
|
196
|
+
const { loadImports } = await import('./loadImports.js');
|
|
197
|
+
this.#modules = await loadImports(this.#init.import);
|
|
198
|
+
this.#importsLoaded = true;
|
|
199
|
+
|
|
200
|
+
this.dispatchEvent(new LoadEvent(this.#modules, this.#init));
|
|
341
201
|
}
|
|
342
202
|
|
|
343
|
-
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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);
|
|
203
|
+
#processNode(node: Node): void {
|
|
204
|
+
// If it's an element node, check if it matches
|
|
205
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
206
|
+
const element = node as Element;
|
|
207
|
+
|
|
208
|
+
if (this.#matchesSelector(element)) {
|
|
209
|
+
this.#handleMatch(element);
|
|
385
210
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Process children
|
|
214
|
+
if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE) {
|
|
215
|
+
const root = node as Element | Document;
|
|
390
216
|
|
|
391
|
-
|
|
217
|
+
// Get all elements matching the CSS selector first
|
|
218
|
+
root.querySelectorAll(this.#init.whereElementMatches).forEach(child => {
|
|
219
|
+
if (this.#matchesSelector(child)) {
|
|
220
|
+
this.#handleMatch(child);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
392
223
|
}
|
|
393
224
|
}
|
|
394
225
|
|
|
395
|
-
|
|
396
|
-
//TODO:
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
226
|
+
#matchesSelector(element: Element): boolean {
|
|
227
|
+
//TODO: reduce redundncy with this.#init?
|
|
228
|
+
// Check whereElementMatches condition
|
|
229
|
+
const matchesElement = element.matches(this.#init.whereElementMatches);
|
|
230
|
+
if (!matchesElement) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check whereAttr condition if specified
|
|
235
|
+
if (this.#init.whereAttr) {
|
|
236
|
+
// Use cached function (should be loaded by now from constructor)
|
|
237
|
+
if (!this.#matchesWhereAttrFn) {
|
|
238
|
+
console.warn('whereAttr utilities not loaded yet');
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
402
241
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const {branchIdx} = parts;
|
|
406
|
-
if(branchIndexes !== undefined){
|
|
407
|
-
if(!branchIndexes.has(branchIdx)) continue;
|
|
408
|
-
}
|
|
409
|
-
const name = fullListOfAttrs[idx];
|
|
410
|
-
|
|
411
|
-
const newValue = match.getAttribute(name);
|
|
412
|
-
|
|
413
|
-
attrChangeInfos.push({
|
|
414
|
-
idx,
|
|
415
|
-
isSOfTAttr: false,
|
|
416
|
-
newValue,
|
|
417
|
-
oldValue,
|
|
418
|
-
name,
|
|
419
|
-
parts
|
|
420
|
-
});
|
|
242
|
+
if (!this.#matchesWhereAttrFn(element, this.#init.whereAttr)) {
|
|
243
|
+
return false;
|
|
421
244
|
}
|
|
422
|
-
|
|
423
245
|
}
|
|
424
|
-
|
|
425
|
-
if
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
attrChangeInfos.push({
|
|
438
|
-
isSOfTAttr: true,
|
|
439
|
-
newValue,
|
|
440
|
-
oldValue,
|
|
441
|
-
name,
|
|
442
|
-
mapsTo
|
|
443
|
-
});
|
|
246
|
+
|
|
247
|
+
// Check whereInstanceOf condition if specified
|
|
248
|
+
if (this.#init.whereInstanceOf) {
|
|
249
|
+
const constructors = Array.isArray(this.#init.whereInstanceOf)
|
|
250
|
+
? this.#init.whereInstanceOf
|
|
251
|
+
: [this.#init.whereInstanceOf];
|
|
252
|
+
|
|
253
|
+
// Element must be an instance of at least one constructor (OR logic for array)
|
|
254
|
+
const matchesInstanceOf = constructors.some(constructor => element instanceof constructor);
|
|
255
|
+
|
|
256
|
+
if (!matchesInstanceOf) {
|
|
257
|
+
return false;
|
|
444
258
|
}
|
|
445
259
|
}
|
|
446
|
-
|
|
447
|
-
|
|
260
|
+
|
|
261
|
+
// All conditions passed
|
|
262
|
+
return true;
|
|
448
263
|
}
|
|
449
264
|
|
|
450
|
-
async #
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
for(const unmatch of unmatching){
|
|
454
|
-
if(onDismount !== undefined){
|
|
455
|
-
onDismount(unmatch, this, {});
|
|
456
|
-
}
|
|
457
|
-
this.dispatchEvent(new DismountEvent(unmatch));
|
|
265
|
+
async #handleMatch(element: Element): Promise<void> {
|
|
266
|
+
if (this.#processedElements.has(element)) {
|
|
267
|
+
return;
|
|
458
268
|
}
|
|
459
|
-
}
|
|
460
269
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
270
|
+
// Load imports if not already loaded
|
|
271
|
+
if (!this.#importsLoaded && this.#init.import) {
|
|
272
|
+
await this.#loadImports();
|
|
273
|
+
}
|
|
466
274
|
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
}
|
|
275
|
+
this.#processedElements.add(element);
|
|
276
|
+
this.#mountedElements.add(element);
|
|
474
277
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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);
|
|
278
|
+
const rootNode = this.#rootNode?.deref();
|
|
279
|
+
if (!rootNode) {
|
|
280
|
+
// Root node was garbage collected
|
|
281
|
+
return;
|
|
492
282
|
}
|
|
493
|
-
this.#mountedList = Array.from(returnSet).map(x => new WeakRef(x));
|
|
494
|
-
return returnSet;
|
|
495
|
-
}
|
|
496
283
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
284
|
+
const context: MountContext = {
|
|
285
|
+
modules: this.#modules,
|
|
286
|
+
observer: this,
|
|
287
|
+
observeInfo: {
|
|
288
|
+
rootNode
|
|
289
|
+
}
|
|
290
|
+
};
|
|
504
291
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if(!x.matches(match)) return false;
|
|
292
|
+
// Apply assignGingerly if specified
|
|
293
|
+
if (this.#init.assignGingerly) {
|
|
294
|
+
const { assignGingerly } = await import('assign-gingerly/index.js');
|
|
295
|
+
assignGingerly(element, this.#init.assignGingerly);
|
|
296
|
+
}
|
|
511
297
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if(
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if(!whereSatisfies(x, this, {stage: 'Inspecting', initializing})) return false;
|
|
519
|
-
}
|
|
520
|
-
if(whereInstanceOf !== undefined){
|
|
521
|
-
if(!this.#confirmInstanceOf(x, whereInstanceOf)) return false;
|
|
298
|
+
// Call do callback
|
|
299
|
+
if (this.#init.do) {
|
|
300
|
+
if (typeof this.#init.do === 'function') {
|
|
301
|
+
this.#init.do(element, context);
|
|
302
|
+
} else if (this.#init.do.mount) {
|
|
303
|
+
this.#init.do.mount(element, context);
|
|
522
304
|
}
|
|
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
|
-
|
|
535
305
|
}
|
|
536
|
-
await bindishIt(els, target, {assigner});
|
|
537
|
-
if(elsToMount.length === 0) return;
|
|
538
|
-
this.#mount(elsToMount, initializing);
|
|
539
|
-
}
|
|
540
306
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
for(const el of idGenerators){
|
|
550
|
-
genIds(el);
|
|
551
|
-
el.removeAttribute('-id');
|
|
307
|
+
// Dispatch mount event
|
|
308
|
+
this.dispatchEvent(new MountEvent(element, this.#modules, this.#init));
|
|
309
|
+
|
|
310
|
+
// Check for initial attribute changes if whereAttr is configured
|
|
311
|
+
if (this.#init.whereAttr) {
|
|
312
|
+
const changes = this.#checkAttrChanges(element);
|
|
313
|
+
if (changes.length > 0) {
|
|
314
|
+
this.dispatchEvent(new AttrChangeEvent(changes, this.#init));
|
|
552
315
|
}
|
|
553
316
|
}
|
|
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);
|
|
559
|
-
|
|
560
317
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
export function waitForIdleNodes(nodes: Array<Node>, idleTimeout?: number): Promise<void>{
|
|
566
|
-
const mountInit: MountInit = {
|
|
567
|
-
idleTimeout
|
|
568
|
-
};
|
|
569
|
-
return new Promise((resolve) => {
|
|
570
|
-
const mutObservers: Array<RootMutObs> = [];
|
|
571
|
-
for(const node of nodes){
|
|
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);
|
|
581
|
-
}
|
|
318
|
+
|
|
319
|
+
#checkAttrChanges(element: Element): AttrChange[] {
|
|
320
|
+
if (!this.#init.whereAttr || !this.#buildAttrCoordinateMapFn) {
|
|
321
|
+
return [];
|
|
582
322
|
}
|
|
583
|
-
|
|
584
|
-
|
|
323
|
+
|
|
324
|
+
const isCustomElement = element.tagName.toLowerCase().includes('-');
|
|
325
|
+
const attrCoordMap = this.#buildAttrCoordinateMapFn(this.#init.whereAttr, isCustomElement);
|
|
326
|
+
|
|
327
|
+
// Get or create the attribute state for this element
|
|
328
|
+
let attrState = this.#elementAttrStates.get(element);
|
|
329
|
+
if (!attrState) {
|
|
330
|
+
attrState = new Map<string, string | null>();
|
|
331
|
+
this.#elementAttrStates.set(element, attrState);
|
|
585
332
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
333
|
+
|
|
334
|
+
const changes: AttrChange[] = [];
|
|
335
|
+
const currentAttrs = new Set<string>();
|
|
336
|
+
|
|
337
|
+
// Check all possible attributes from the coordinate map
|
|
338
|
+
for (const attrName of Object.keys(attrCoordMap)) {
|
|
339
|
+
const coordinate = attrCoordMap[attrName];
|
|
340
|
+
const currentValue = element.getAttribute(attrName);
|
|
341
|
+
const previousValue = attrState.get(attrName);
|
|
342
|
+
|
|
343
|
+
if (currentValue !== null) {
|
|
344
|
+
currentAttrs.add(attrName);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Check if this attribute has "once: true" in its map entry
|
|
348
|
+
const mapEntry = this.#init.map?.[coordinate] || null;
|
|
349
|
+
const isOnce = mapEntry?.once === true;
|
|
350
|
+
|
|
351
|
+
// If "once" is true, check if we've already seen this attribute
|
|
352
|
+
if (isOnce) {
|
|
353
|
+
let onceAttrs = this.#elementOnceAttrs.get(element);
|
|
354
|
+
if (!onceAttrs) {
|
|
355
|
+
onceAttrs = new Set<string>();
|
|
356
|
+
this.#elementOnceAttrs.set(element, onceAttrs);
|
|
590
357
|
}
|
|
591
|
-
|
|
358
|
+
|
|
359
|
+
// If we've already seen this attribute, skip it
|
|
360
|
+
if (onceAttrs.has(attrName)) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Mark this attribute as seen if it currently has a value
|
|
365
|
+
if (currentValue !== null) {
|
|
366
|
+
onceAttrs.add(attrName);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Include if: currently has value OR previously had value but now removed
|
|
371
|
+
if (currentValue !== null || (previousValue !== undefined && currentValue === null)) {
|
|
372
|
+
// Check if value changed
|
|
373
|
+
if (currentValue !== previousValue) {
|
|
374
|
+
const attrNode = currentValue !== null ? element.getAttributeNode(attrName) : null;
|
|
375
|
+
|
|
376
|
+
changes.push({
|
|
377
|
+
value: currentValue,
|
|
378
|
+
attrNode,
|
|
379
|
+
mapEntry,
|
|
380
|
+
attrName,
|
|
381
|
+
coordinate,
|
|
382
|
+
element
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Update state
|
|
386
|
+
if (currentValue !== null) {
|
|
387
|
+
attrState.set(attrName, currentValue);
|
|
388
|
+
} else {
|
|
389
|
+
attrState.delete(attrName);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
592
393
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
//make external
|
|
597
|
-
function areAllIdle(mutObs: Array<RootMutObs>){
|
|
598
|
-
for(const mo of mutObs){
|
|
599
|
-
if(!mo.isIdle) return false;
|
|
394
|
+
|
|
395
|
+
return changes;
|
|
600
396
|
}
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
397
|
|
|
398
|
+
#handleRemoval(element: Element): void {
|
|
399
|
+
if (!this.#mountedElements.has(element)) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
604
402
|
|
|
605
|
-
|
|
606
|
-
export const inclTemplQry = 'template[src^="#"]:not([hidden]),template[src^="!"]:not([hidden])';
|
|
607
|
-
|
|
608
|
-
export interface MountObserver extends IMountObserver{}
|
|
403
|
+
this.#mountedElements.delete(element);
|
|
609
404
|
|
|
405
|
+
const rootNode = this.#rootNode?.deref();
|
|
406
|
+
if (!rootNode) {
|
|
407
|
+
// Root node was garbage collected
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
610
410
|
|
|
411
|
+
const context: MountContext = {
|
|
412
|
+
modules: this.#modules,
|
|
413
|
+
observer: this,
|
|
414
|
+
observeInfo: {
|
|
415
|
+
rootNode
|
|
416
|
+
}
|
|
417
|
+
};
|
|
611
418
|
|
|
419
|
+
// Call dismount callback
|
|
420
|
+
if (this.#init.do && typeof this.#init.do !== 'function' && this.#init.do.dismount) {
|
|
421
|
+
this.#init.do.dismount(element, context);
|
|
422
|
+
}
|
|
612
423
|
|
|
424
|
+
// Dispatch dismount event
|
|
425
|
+
this.dispatchEvent(new DismountEvent(element, 'where-element-matches-failed', this.#init));
|
|
613
426
|
|
|
614
|
-
//
|
|
427
|
+
// Check if element is being moved within the same root
|
|
428
|
+
// If it's truly disconnected, dispatch disconnect event
|
|
429
|
+
setTimeout(() => {
|
|
430
|
+
if (!rootNode.contains(element)) {
|
|
431
|
+
if (this.#init.do && typeof this.#init.do !== 'function' && this.#init.do.disconnect) {
|
|
432
|
+
this.#init.do.disconnect(element, context);
|
|
433
|
+
}
|
|
615
434
|
|
|
435
|
+
this.dispatchEvent(new DisconnectEvent(element, this.#init));
|
|
436
|
+
}
|
|
437
|
+
}, 0);
|
|
438
|
+
}
|
|
439
|
+
}
|