forked from ampproject/amphtml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathintersection-observer-stub.js
125 lines (114 loc) · 3.12 KB
/
intersection-observer-stub.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import {areEqualOrdered} from '#core/types/array';
/**
* @param {!Object} sandbox
* @param {!Window} window
* @return {!IntersectionObservers}
*/
export function installIntersectionObserverStub(sandbox, win) {
return new IntersectionObservers(sandbox, win);
}
class IntersectionObservers {
/**
* @param {!Object} sandbox
* @param {!Window} win
*/
constructor(sandbox, win) {
const observers = new Set();
this.observers = observers;
sandbox
.stub(win, 'IntersectionObserver')
.value(function (callback, options) {
const observer = new IntersectionObserverStub(callback, options, () => {
observers.delete(observer);
});
observers.add(observer);
return observer;
});
}
/**
* @param {!Element} target
* @param {{
* root: (!Document|!Element|undefined),
* rootMargin: (string|undefined),
* thresholds: (number|!Array<number>|undefined),
* }=} options
* @return {boolean}
*/
isObserved(target, options = {}) {
return Array.from(this.observers).some((observer) => {
if (!observer.elements.has(target)) {
return false;
}
return matchesObserver(observer, options);
});
}
/**
* @param {!IntersectionObserverEntry|!Array<IntersectionObserverEntry>} entryOrEntries
* @param {{
* root: (!Document|!Element|undefined),
* rootMargin: (string|undefined),
* thresholds: (number|!Array<number>|undefined),
* }=} options
*/
notifySync(entryOrEntries, options = {}) {
const entries = Array.isArray(entryOrEntries)
? entryOrEntries
: [entryOrEntries];
this.observers.forEach((observer) => {
if (!matchesObserver(observer, options)) {
return;
}
const subEntries = entries.filter(({target}) =>
observer.elements.has(target)
);
if (subEntries.length > 0) {
observer.callback(subEntries);
}
});
}
}
class IntersectionObserverStub {
constructor(callback, options, onDisconnect) {
this.onDisconnect_ = onDisconnect;
this.callback = callback;
this.elements = new Set();
options = options || {};
this.root = options.root || null;
this.rootMargin = options.rootMargin || '0px';
this.thresholds =
options.threshold != null ? [].concat(options.threshold) : [0];
}
disconnect() {
const onDisconnect = this.onDisconnect_;
onDisconnect();
}
/**
* @param {!Element} element
*/
observe(element) {
this.elements.add(element);
}
/**
* @param {!Element} element
*/
unobserve(element) {
this.elements.delete(element);
}
}
/**
* @param {!IntersectionObserverStub} observer
* @param {{
* root: (!Document|!Element|undefined),
* rootMargin: (string|undefined),
* thresholds: (number|!Array<number>|undefined),
* }} options
*/
function matchesObserver(observer, options) {
const {root, rootMargin, thresholds} = options;
return (
(root === undefined || root == observer.root) &&
(rootMargin === undefined || rootMargin == observer.rootMargin) &&
(thresholds === undefined ||
areEqualOrdered(thresholds, observer.thresholds))
);
}