mount-observer 0.1.14 → 0.1.16

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.
Files changed (40) hide show
  1. package/ElementMountExtension.js +5 -2
  2. package/ElementMountExtension.ts +7 -2
  3. package/MountObserver.js +3 -0
  4. package/MountObserver.ts +3 -0
  5. package/RegistryMountCoordinator.js +5 -5
  6. package/RegistryMountCoordinator.ts +8 -6
  7. package/{DefineCustomElementHandler.js → handlers/DefineCustomElement.js} +103 -99
  8. package/handlers/DefineCustomElement.ts +123 -0
  9. package/{EnhanceMountedElementHandler.js → handlers/EnhanceMountedElement.js} +109 -96
  10. package/handlers/EnhanceMountedElement.ts +126 -0
  11. package/handlers/Events.js +110 -0
  12. package/handlers/EvtRt.js +59 -0
  13. package/handlers/GenIds.js +37 -0
  14. package/handlers/GenIds.ts +45 -0
  15. package/handlers/HTMLInclude.js +393 -0
  16. package/handlers/HTMLInclude.ts +453 -0
  17. package/handlers/HoistTemplate.js +77 -0
  18. package/handlers/HoistTemplate.ts +89 -0
  19. package/handlers/MountObserver.js +941 -0
  20. package/handlers/MountObserverScript.js +78 -0
  21. package/handlers/MountObserverScript.ts +89 -0
  22. package/handlers/ScriptExport.js +83 -0
  23. package/handlers/ScriptExport.ts +97 -0
  24. package/handlers/SharedMutationObserver.js +78 -0
  25. package/handlers/arr.js +16 -0
  26. package/handlers/connectionMonitor.js +122 -0
  27. package/handlers/elementIntersection.js +73 -0
  28. package/handlers/emitEvents.js +187 -0
  29. package/handlers/getRegistryRoot.js +52 -0
  30. package/handlers/loadImports.js +129 -0
  31. package/handlers/mediaQuery.js +90 -0
  32. package/handlers/rootSizeObserver.js +131 -0
  33. package/handlers/upShadowSearch.js +70 -0
  34. package/handlers/withScopePerimeter.js +22 -0
  35. package/package.json +12 -2
  36. package/types/assign-gingerly/types.d.ts +244 -0
  37. package/types/be-a-beacon/types.d.ts +3 -0
  38. package/types/global.d.ts +29 -0
  39. package/types/id-generation/types.d.ts +26 -0
  40. package/types/mount-observer/types.d.ts +332 -0
@@ -0,0 +1,78 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ import '../ElementMountExtension.js';
3
+ /**
4
+ * Handler for Mount Observer Script Elements (MOSEs).
5
+ * Processes script[type="mountobserver"] elements to declaratively configure mount observers.
6
+ *
7
+ * Supports two modes:
8
+ * 1. External JSON: <script type="mountobserver" src="./config.json"></script>
9
+ * 2. Inline JSON: <script type="mountobserver">{ "matching": "div" }</script>
10
+ *
11
+ * Supports multiple configs in one script element:
12
+ * - Single config: { "do": "builtIns.hoistTemplate" }
13
+ * - Multiple configs: [{ "do": "builtIns.hoistTemplate" }, { "do": "builtIns.HTMLInclude" }]
14
+ */
15
+ export class MountObserverScriptHandler extends EvtRt {
16
+ // Static properties define default MountConfig constraints
17
+ static matching = 'script[type="mountobserver"]';
18
+ static whereInstanceOf = HTMLScriptElement;
19
+ async mount(mountedElement, MountConfig, context) {
20
+ this.abort(); // Clean up event listeners (one-time operation)
21
+ const scriptElement = mountedElement;
22
+ let config = scriptElement.export;
23
+ if (!config) {
24
+ // Check if script has src attribute
25
+ const srcAttr = scriptElement.getAttribute('src');
26
+ if (srcAttr) {
27
+ // External JSON mode: import from src
28
+ //const resolvedUrl = new URL(srcAttr, document.baseURI).href;
29
+ try {
30
+ const module = await import(srcAttr, { with: { type: 'json' } });
31
+ config = module.default;
32
+ }
33
+ catch (error) {
34
+ throw new Error(`Failed to import JSON from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`);
35
+ }
36
+ }
37
+ else {
38
+ // Inline JSON mode: parse textContent
39
+ const jsonText = scriptElement.textContent?.trim();
40
+ if (!jsonText) {
41
+ throw new Error('Script element must have either src attribute or JSON content');
42
+ }
43
+ try {
44
+ config = JSON.parse(jsonText);
45
+ }
46
+ catch (error) {
47
+ throw new Error(`Failed to parse JSON content: ${error instanceof Error ? error.message : String(error)}`);
48
+ }
49
+ }
50
+ // Validate that config is an object or array
51
+ if (typeof config !== 'object' || config === null) {
52
+ throw new Error('Mount observer config must be an object or array');
53
+ }
54
+ // Store the parsed config on the script element's export property
55
+ scriptElement.export = config;
56
+ // Dispatch resolved event
57
+ const { ResolvedEvent } = await import('../Events.js');
58
+ scriptElement.dispatchEvent(new ResolvedEvent(config));
59
+ }
60
+ // Handle array of configs
61
+ if (Array.isArray(config)) {
62
+ // Mount each config in the array
63
+ for (const singleConfig of config) {
64
+ if (typeof singleConfig !== 'object' || singleConfig === null) {
65
+ throw new Error('Each config in array must be an object');
66
+ }
67
+ await scriptElement.mount(singleConfig);
68
+ }
69
+ }
70
+ else {
71
+ // Single config object - mount it
72
+ await scriptElement.mount(config);
73
+ }
74
+ }
75
+ }
76
+ // Register built-in handler
77
+ import { MountObserver } from '../MountObserver.js';
78
+ MountObserver.define('builtIns.mountObserverScript', MountObserverScriptHandler);
@@ -0,0 +1,89 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ import { MountConfig, MountContext } from '../types/mount-observer/types.js';
3
+ import '../ElementMountExtension.js';
4
+
5
+ /**
6
+ * Handler for Mount Observer Script Elements (MOSEs).
7
+ * Processes script[type="mountobserver"] elements to declaratively configure mount observers.
8
+ *
9
+ * Supports two modes:
10
+ * 1. External JSON: <script type="mountobserver" src="./config.json"></script>
11
+ * 2. Inline JSON: <script type="mountobserver">{ "matching": "div" }</script>
12
+ *
13
+ * Supports multiple configs in one script element:
14
+ * - Single config: { "do": "builtIns.hoistTemplate" }
15
+ * - Multiple configs: [{ "do": "builtIns.hoistTemplate" }, { "do": "builtIns.HTMLInclude" }]
16
+ */
17
+ export class MountObserverScriptHandler extends EvtRt {
18
+ // Static properties define default MountConfig constraints
19
+ static matching = 'script[type="mountobserver"]';
20
+ static whereInstanceOf = HTMLScriptElement;
21
+
22
+ async mount(mountedElement: Element, MountConfig: MountConfig, context: MountContext): Promise<void> {
23
+ this.abort(); // Clean up event listeners (one-time operation)
24
+
25
+ const scriptElement = mountedElement as HTMLScriptElement;
26
+
27
+ let config = (scriptElement as any).export;
28
+ if (!config) {
29
+ // Check if script has src attribute
30
+ const srcAttr = scriptElement.getAttribute('src');
31
+
32
+ if (srcAttr) {
33
+ // External JSON mode: import from src
34
+ //const resolvedUrl = new URL(srcAttr, document.baseURI).href;
35
+
36
+ try {
37
+ const module = await import(srcAttr, { with: { type: 'json' } } as any);
38
+ config = module.default;
39
+ } catch (error) {
40
+ throw new Error(`Failed to import JSON from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`);
41
+ }
42
+ } else {
43
+ // Inline JSON mode: parse textContent
44
+ const jsonText = scriptElement.textContent?.trim();
45
+
46
+ if (!jsonText) {
47
+ throw new Error('Script element must have either src attribute or JSON content');
48
+ }
49
+
50
+ try {
51
+ config = JSON.parse(jsonText);
52
+ } catch (error) {
53
+ throw new Error(`Failed to parse JSON content: ${error instanceof Error ? error.message : String(error)}`);
54
+ }
55
+ }
56
+
57
+ // Validate that config is an object or array
58
+ if (typeof config !== 'object' || config === null) {
59
+ throw new Error('Mount observer config must be an object or array');
60
+ }
61
+
62
+ // Store the parsed config on the script element's export property
63
+ (scriptElement as any).export = config;
64
+
65
+ // Dispatch resolved event
66
+ const { ResolvedEvent } = await import('../Events.js');
67
+ scriptElement.dispatchEvent(new ResolvedEvent(config));
68
+ }
69
+
70
+ // Handle array of configs
71
+ if (Array.isArray(config)) {
72
+ // Mount each config in the array
73
+ for (const singleConfig of config) {
74
+ if (typeof singleConfig !== 'object' || singleConfig === null) {
75
+ throw new Error('Each config in array must be an object');
76
+ }
77
+ await scriptElement.mount(singleConfig);
78
+ }
79
+ } else {
80
+ // Single config object - mount it
81
+ await scriptElement.mount(config);
82
+ }
83
+ }
84
+ }
85
+
86
+ // Register built-in handler
87
+ import { MountObserver } from '../MountObserver.js';
88
+
89
+ MountObserver.define('builtIns.mountObserverScript', MountObserverScriptHandler);
@@ -0,0 +1,83 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ /**
3
+ * Handler for exposing module exports from script elements via element.export.
4
+ *
5
+ * Solves two key problems:
6
+ *
7
+ * 1. ES Module Export Access:
8
+ * - Use nomodule attribute to prevent browser from loading the module separately
9
+ * - Handler imports it and exposes exports via element.export
10
+ * - Avoids double-loading the module in memory
11
+ * - Example: <script nomodule src="./config.js" id="cfg"></script>
12
+ * - Access: document.getElementById('cfg').export.myValue
13
+ *
14
+ * 2. JSON/Data Import:
15
+ * - Use type attribute for JSON and other data formats
16
+ * - Browser ignores non-standard script types, so no double-loading issue
17
+ * - Handler imports with appropriate assertion and exposes via element.export
18
+ * - Example: <script src="./data.json" type="json" id="data"></script>
19
+ * - Access: document.getElementById('data').export.default
20
+ *
21
+ * Supported type values:
22
+ * - type="json" - JSON data
23
+ * - type="application/json" - JSON with full MIME type
24
+ * - type="application/ld+json" - JSON-LD linked data
25
+ * - Any type containing "json" triggers JSON import assertion
26
+ */
27
+ export class ScriptExportHandler extends EvtRt {
28
+ // Match script elements with src attribute
29
+ static matching = 'script[src]';
30
+ static whereInstanceOf = HTMLScriptElement;
31
+ async mount(mountedElement, MountConfig, context) {
32
+ this.abort(); // Clean up event listeners (one-time operation)
33
+ const scriptElement = mountedElement;
34
+ // Optimization 3: Skip if already processed (export property exists)
35
+ if (scriptElement.export !== undefined) {
36
+ console.log('ScriptExport: Skipping already processed script element');
37
+ return;
38
+ }
39
+ // Skip if this is a module script (browser handles these)
40
+ const typeAttr = scriptElement.getAttribute('type');
41
+ if (typeAttr === 'module') {
42
+ return;
43
+ }
44
+ // Only process if:
45
+ // 1. Has nomodule attribute (for ES modules), OR
46
+ // 2. Has type attribute containing "json" (for JSON data)
47
+ const hasNoModule = scriptElement.hasAttribute('nomodule');
48
+ const isJsonType = typeAttr && typeAttr.toLowerCase().includes('json');
49
+ if (!hasNoModule && !isJsonType) {
50
+ return;
51
+ }
52
+ // Read src attribute
53
+ const srcAttr = scriptElement.getAttribute('src');
54
+ if (!srcAttr) {
55
+ throw new Error('Script element must have a src attribute');
56
+ }
57
+ // Resolve the src relative to the document's base URL
58
+ const resolvedUrl = new URL(srcAttr, document.baseURI).href;
59
+ // Determine import assertion type
60
+ const importType = isJsonType ? 'json' : null;
61
+ // Perform import
62
+ let module;
63
+ try {
64
+ if (importType) {
65
+ module = await import(resolvedUrl, { with: { type: importType } });
66
+ }
67
+ else {
68
+ module = await import(resolvedUrl);
69
+ }
70
+ }
71
+ catch (error) {
72
+ throw new Error(`Failed to import module from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`);
73
+ }
74
+ // Store result on element
75
+ scriptElement.export = module;
76
+ // Dispatch resolved event
77
+ const { ResolvedEvent } = await import('../Events.js');
78
+ scriptElement.dispatchEvent(new ResolvedEvent(module));
79
+ }
80
+ }
81
+ // Register built-in handler
82
+ import { MountObserver } from '../MountObserver.js';
83
+ MountObserver.define('builtIns.scriptExport', ScriptExportHandler);
@@ -0,0 +1,97 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ import { MountConfig, MountContext } from '../types/mount-observer/types.js';
3
+
4
+ /**
5
+ * Handler for exposing module exports from script elements via element.export.
6
+ *
7
+ * Solves two key problems:
8
+ *
9
+ * 1. ES Module Export Access:
10
+ * - Use nomodule attribute to prevent browser from loading the module separately
11
+ * - Handler imports it and exposes exports via element.export
12
+ * - Avoids double-loading the module in memory
13
+ * - Example: <script nomodule src="./config.js" id="cfg"></script>
14
+ * - Access: document.getElementById('cfg').export.myValue
15
+ *
16
+ * 2. JSON/Data Import:
17
+ * - Use type attribute for JSON and other data formats
18
+ * - Browser ignores non-standard script types, so no double-loading issue
19
+ * - Handler imports with appropriate assertion and exposes via element.export
20
+ * - Example: <script src="./data.json" type="json" id="data"></script>
21
+ * - Access: document.getElementById('data').export.default
22
+ *
23
+ * Supported type values:
24
+ * - type="json" - JSON data
25
+ * - type="application/json" - JSON with full MIME type
26
+ * - type="application/ld+json" - JSON-LD linked data
27
+ * - Any type containing "json" triggers JSON import assertion
28
+ */
29
+ export class ScriptExportHandler extends EvtRt {
30
+ // Match script elements with src attribute
31
+ static matching = 'script[src]';
32
+ static whereInstanceOf = HTMLScriptElement;
33
+
34
+ async mount(mountedElement: Element, MountConfig: MountConfig, context: MountContext): Promise<void> {
35
+ this.abort(); // Clean up event listeners (one-time operation)
36
+
37
+ const scriptElement = mountedElement as HTMLScriptElement;
38
+
39
+ // Optimization 3: Skip if already processed (export property exists)
40
+ if ((scriptElement as any).export !== undefined) {
41
+ console.log('ScriptExport: Skipping already processed script element');
42
+ return;
43
+ }
44
+
45
+ // Skip if this is a module script (browser handles these)
46
+ const typeAttr = scriptElement.getAttribute('type');
47
+ if (typeAttr === 'module') {
48
+ return;
49
+ }
50
+
51
+ // Only process if:
52
+ // 1. Has nomodule attribute (for ES modules), OR
53
+ // 2. Has type attribute containing "json" (for JSON data)
54
+ const hasNoModule = scriptElement.hasAttribute('nomodule');
55
+ const isJsonType = typeAttr && typeAttr.toLowerCase().includes('json');
56
+
57
+ if (!hasNoModule && !isJsonType) {
58
+ return;
59
+ }
60
+
61
+ // Read src attribute
62
+ const srcAttr = scriptElement.getAttribute('src');
63
+ if (!srcAttr) {
64
+ throw new Error('Script element must have a src attribute');
65
+ }
66
+
67
+ // Resolve the src relative to the document's base URL
68
+ const resolvedUrl = new URL(srcAttr, document.baseURI).href;
69
+
70
+ // Determine import assertion type
71
+ const importType = isJsonType ? 'json' : null;
72
+
73
+ // Perform import
74
+ let module;
75
+ try {
76
+ if (importType) {
77
+ module = await import(resolvedUrl, { with: { type: importType } } as any);
78
+ } else {
79
+ module = await import(resolvedUrl);
80
+ }
81
+ } catch (error) {
82
+ throw new Error(`Failed to import module from '${srcAttr}': ${error instanceof Error ? error.message : String(error)}`);
83
+ }
84
+
85
+ // Store result on element
86
+ (scriptElement as any).export = module;
87
+
88
+ // Dispatch resolved event
89
+ const { ResolvedEvent } = await import('../Events.js');
90
+ scriptElement.dispatchEvent(new ResolvedEvent(module));
91
+ }
92
+ }
93
+
94
+ // Register built-in handler
95
+ import { MountObserver } from '../MountObserver.js';
96
+
97
+ MountObserver.define('builtIns.scriptExport', ScriptExportHandler);
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * Manages shared MutationObserver instances for multiple MountObserver instances
4
+ * observing the same root node. This reduces overhead when multiple observers
5
+ * are watching the same DOM fragment.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.registerSharedObserver = registerSharedObserver;
9
+ exports.unregisterSharedObserver = unregisterSharedObserver;
10
+ /**
11
+ * Global registry of shared observers, keyed by root node
12
+ */
13
+ var sharedObservers = new WeakMap();
14
+ /**
15
+ * Registers a callback with the shared MutationObserver for the given root node.
16
+ * Creates a new shared observer if one doesn't exist for this root.
17
+ */
18
+ function registerSharedObserver(rootNode, callback, config) {
19
+ var sharedData = sharedObservers.get(rootNode);
20
+ if (!sharedData) {
21
+ // Create shared data structure first
22
+ sharedData = {
23
+ observer: null, // Will be set immediately below
24
+ callbacks: new Set(),
25
+ config: config
26
+ };
27
+ // Create new shared observer for this root node
28
+ var observer = new MutationObserver(function (mutations) {
29
+ // Distribute mutations to all registered callbacks
30
+ var callbacks = sharedData.callbacks;
31
+ for (var _i = 0, callbacks_1 = callbacks; _i < callbacks_1.length; _i++) {
32
+ var cb = callbacks_1[_i];
33
+ cb(mutations);
34
+ }
35
+ });
36
+ sharedData.observer = observer;
37
+ sharedObservers.set(rootNode, sharedData);
38
+ // Start observing after everything is set up
39
+ observer.observe(rootNode, config);
40
+ }
41
+ else {
42
+ // Verify config matches (for safety)
43
+ // In practice, all MountObservers should use the same config
44
+ if (!configsMatch(sharedData.config, config)) {
45
+ console.warn('MutationObserver config mismatch detected. Using existing config.');
46
+ }
47
+ }
48
+ // Register the callback
49
+ sharedData.callbacks.add(callback);
50
+ }
51
+ /**
52
+ * Unregisters a callback from the shared MutationObserver.
53
+ * If this was the last callback, disconnects and removes the shared observer.
54
+ */
55
+ function unregisterSharedObserver(rootNode, callback) {
56
+ var sharedData = sharedObservers.get(rootNode);
57
+ if (!sharedData) {
58
+ return;
59
+ }
60
+ // Remove the callback
61
+ sharedData.callbacks.delete(callback);
62
+ // If no more callbacks, disconnect and cleanup
63
+ if (sharedData.callbacks.size === 0) {
64
+ sharedData.observer.disconnect();
65
+ sharedObservers.delete(rootNode);
66
+ }
67
+ }
68
+ /**
69
+ * Checks if two MutationObserverInit configs are equivalent
70
+ */
71
+ function configsMatch(a, b) {
72
+ return a.childList === b.childList &&
73
+ a.subtree === b.subtree &&
74
+ a.attributes === b.attributes &&
75
+ a.attributeOldValue === b.attributeOldValue &&
76
+ a.characterData === b.characterData &&
77
+ a.characterDataOldValue === b.characterDataOldValue;
78
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.arr = arr;
4
+ /**
5
+ * Utility function to normalize a value to an array.
6
+ * - If undefined, returns empty array
7
+ * - If already an array, returns as-is
8
+ * - Otherwise, wraps the value in an array
9
+ *
10
+ * @param inp - Value to normalize to array
11
+ * @returns Array containing the value(s)
12
+ */
13
+ function arr(inp) {
14
+ return inp === undefined ? []
15
+ : Array.isArray(inp) ? inp : [inp];
16
+ }
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupConnectionMonitor = setupConnectionMonitor;
4
+ var Events_js_1 = require("./Events.js");
5
+ function setupConnectionMonitor(init, rootNodeRef, mountedElements, modules, observer, processNode) {
6
+ var whereConnectionHas = init.whereConnectionHas;
7
+ if (!whereConnectionHas) {
8
+ throw new Error('whereConnectionHas is required');
9
+ }
10
+ // Get connection object with vendor prefixes
11
+ var nav = navigator;
12
+ var connection = nav.connection || nav.mozConnection || nav.webkitConnection;
13
+ // If Network Information API is not supported, warn and pass the condition
14
+ if (!connection) {
15
+ console.warn('Network Information API is not supported in this browser. whereConnectionHas condition will be ignored.');
16
+ return {
17
+ conditionMatches: true,
18
+ cleanup: function () { }
19
+ };
20
+ }
21
+ // Check initial condition
22
+ var conditionMatches = evaluateConnectionCondition(connection, whereConnectionHas);
23
+ // Set up change listener
24
+ var changeHandler = function () {
25
+ var previousMatches = conditionMatches;
26
+ conditionMatches = evaluateConnectionCondition(connection, whereConnectionHas);
27
+ if (conditionMatches && !previousMatches) {
28
+ // Connection now matches - process elements
29
+ handleConditionMatch();
30
+ }
31
+ else if (!conditionMatches && previousMatches) {
32
+ // Connection no longer matches - dismount all elements
33
+ handleConditionUnmatch();
34
+ }
35
+ };
36
+ function handleConditionMatch() {
37
+ // Process all elements in the observed node
38
+ var rootNode = rootNodeRef.deref();
39
+ if (rootNode) {
40
+ processNode(rootNode);
41
+ }
42
+ }
43
+ function handleConditionUnmatch() {
44
+ // Dismount all currently mounted elements
45
+ var rootNode = rootNodeRef.deref();
46
+ if (!rootNode) {
47
+ return;
48
+ }
49
+ var context = {
50
+ modules: modules,
51
+ observer: observer,
52
+ rootNode: rootNode,
53
+ mountConfig: init
54
+ };
55
+ // Get all mounted elements from the WeakDual setWeak
56
+ var mountedElementsList = [];
57
+ for (var _i = 0, _a = mountedElements.setWeak; _i < _a.length; _i++) {
58
+ var ref = _a[_i];
59
+ var element = ref.deref();
60
+ if (element) {
61
+ mountedElementsList.push(element);
62
+ }
63
+ }
64
+ // Dismount each element
65
+ for (var _b = 0, mountedElementsList_1 = mountedElementsList; _b < mountedElementsList_1.length; _b++) {
66
+ var element = mountedElementsList_1[_b];
67
+ // Remove from both structures
68
+ mountedElements.weakSet.delete(element);
69
+ for (var _c = 0, _d = mountedElements.setWeak; _c < _d.length; _c++) {
70
+ var ref = _d[_c];
71
+ if (ref.deref() === element) {
72
+ mountedElements.setWeak.delete(ref);
73
+ break;
74
+ }
75
+ }
76
+ // Dispatch dismount event with reason
77
+ observer.dispatchEvent(new Events_js_1.DismountEvent(element, 'connection-failed', init));
78
+ }
79
+ }
80
+ // Listen for connection changes
81
+ connection.addEventListener('change', changeHandler);
82
+ return {
83
+ conditionMatches: conditionMatches,
84
+ cleanup: function () {
85
+ connection.removeEventListener('change', changeHandler);
86
+ }
87
+ };
88
+ }
89
+ /**
90
+ * Evaluate if the current connection meets the specified conditions
91
+ */
92
+ function evaluateConnectionCondition(connection, condition) {
93
+ // Check effectiveType (e.g., 'slow-2g', '2g', '3g', '4g')
94
+ if (condition.effectiveTypeIn && condition.effectiveTypeIn.length > 0) {
95
+ var effectiveType = connection.effectiveType;
96
+ if (!effectiveType || !condition.effectiveTypeIn.includes(effectiveType)) {
97
+ return false;
98
+ }
99
+ }
100
+ // Check downlink (bandwidth in Mbps)
101
+ if (condition.downlinkMin !== undefined) {
102
+ var downlink = connection.downlink;
103
+ if (downlink === undefined || downlink < condition.downlinkMin) {
104
+ return false;
105
+ }
106
+ }
107
+ if (condition.downlinkMax !== undefined) {
108
+ var downlink = connection.downlink;
109
+ if (downlink === undefined || downlink > condition.downlinkMax) {
110
+ return false;
111
+ }
112
+ }
113
+ // Check RTT (round-trip time in ms)
114
+ if (condition.rttMax !== undefined) {
115
+ var rtt = connection.rtt;
116
+ if (rtt === undefined || rtt > condition.rttMax) {
117
+ return false;
118
+ }
119
+ }
120
+ // All conditions passed
121
+ return true;
122
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupElementIntersection = setupElementIntersection;
4
+ exports.isElementIntersecting = isElementIntersecting;
5
+ var Events_js_1 = require("./Events.js");
6
+ function setupElementIntersection(init, rootNodeRef, mountedElements, modules, observer, matchesSelector, handleMatch) {
7
+ var whereElementIntersectsWith = init.whereElementIntersectsWith;
8
+ if (!whereElementIntersectsWith) {
9
+ throw new Error('whereElementIntersectsWith is required');
10
+ }
11
+ // Track which elements are currently intersecting
12
+ var intersectingElements = new WeakSet();
13
+ // Create IntersectionObserver with the provided options
14
+ var intersectionObserver = new IntersectionObserver(function (entries) {
15
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
16
+ var entry = entries_1[_i];
17
+ var element = entry.target;
18
+ if (entry.isIntersecting) {
19
+ // Element is now intersecting
20
+ intersectingElements.add(element);
21
+ // Check if element matches all other conditions and mount if so
22
+ if (matchesSelector(element)) {
23
+ handleMatch(element);
24
+ }
25
+ }
26
+ else {
27
+ // Element is no longer intersecting
28
+ intersectingElements.delete(element);
29
+ // Dismount if it was mounted
30
+ if (mountedElements.weakSet.has(element)) {
31
+ dismountElement(element);
32
+ }
33
+ }
34
+ }
35
+ }, whereElementIntersectsWith);
36
+ function dismountElement(element) {
37
+ // Remove from mounted elements
38
+ mountedElements.weakSet.delete(element);
39
+ for (var _i = 0, _a = mountedElements.setWeak; _i < _a.length; _i++) {
40
+ var ref = _a[_i];
41
+ if (ref.deref() === element) {
42
+ mountedElements.setWeak.delete(ref);
43
+ break;
44
+ }
45
+ }
46
+ // Dispatch dismount event
47
+ observer.dispatchEvent(new Events_js_1.DismountEvent(element, 'intersection-failed', init));
48
+ }
49
+ function observeElement(element) {
50
+ intersectionObserver.observe(element);
51
+ }
52
+ return {
53
+ intersectionObserver: intersectionObserver,
54
+ observeElement: observeElement,
55
+ cleanup: function () {
56
+ intersectionObserver.disconnect();
57
+ }
58
+ };
59
+ }
60
+ /**
61
+ * Check if an element is currently intersecting
62
+ * This is called from #matchesSelector to determine if intersection condition is met
63
+ */
64
+ function isElementIntersecting(element, intersectionObserver) {
65
+ // If no intersection observer is set up, consider all elements as intersecting
66
+ if (!intersectionObserver) {
67
+ return true;
68
+ }
69
+ // When intersection observer is active, we can't synchronously determine intersection state
70
+ // The element will be observed and the callback will handle mounting when it intersects
71
+ // Return false here to prevent immediate mounting
72
+ return false;
73
+ }