PageSourceSearch

https://ge-iic.com/wp-content/themes/thegem/js/thegem-scrollMonitor.js?ver=5.12.3.1

js ge-iic.com collected 2026-09-24 09:51:16 UTC 11,749 bytes, 402 lines download raw bytes

1(function( factory ) {
2	if (typeof define !== 'undefined' && define.amd) {
3		define([], factory);
4	} else if (typeof module !== 'undefined' && module.exports) {
5		module.exports = factory();
6	} else {
7		window.scrollMonitor = factory();
8	}
9})(function() {
10
11	var scrollTop = function() {
12		return window.pageYOffset ||
13			(document.documentElement && document.documentElement.scrollTop) ||
14			document.body.scrollTop;
15	};
16
17	var exports = {};
18
19	var watchers = [];
20
21	var VISIBILITYCHANGE = 'visibilityChange';
22	var ENTERVIEWPORT = 'enterViewport';
23	var FULLYENTERVIEWPORT = 'fullyEnterViewport';
24	var EXITVIEWPORT = 'exitViewport';
25	var PARTIALLYEXITVIEWPORT = 'partiallyExitViewport';
26	var LOCATIONCHANGE = 'locationChange';
27	var STATECHANGE = 'stateChange';
28
29	var eventTypes = [
30		VISIBILITYCHANGE,
31		ENTERVIEWPORT,
32		FULLYENTERVIEWPORT,
33		EXITVIEWPORT,
34		PARTIALLYEXITVIEWPORT,
35		LOCATIONCHANGE,
36		STATECHANGE
37	];
38
39	var defaultOffsets = {top: 0, bottom: 0};
40
41	var getViewportHeight = function() {
42		return window.innerHeight || document.documentElement.clientHeight;
43	};
44
45	var getDocumentHeight = function() {
46		// jQuery approach
47		// whichever is greatest
48		return Math.max(
49			document.body.scrollHeight, document.documentElement.scrollHeight,
50			document.body.offsetHeight, document.documentElement.offsetHeight,
51			document.documentElement.clientHeight
52		);
53	};
54
55	exports.viewportTop = null;
56	exports.viewportBottom = null;
57	exports.documentHeight = null;
58	exports.viewportHeight = getViewportHeight();
59
60	var previousDocumentHeight;
61	var latestEvent;
62
63	var calculateViewportI;
64	function calculateViewport() {
65		exports.viewportTop = scrollTop();
66		exports.viewportBottom = exports.viewportTop + exports.viewportHeight;
67		exports.documentHeight = getDocumentHeight();
68		if (exports.documentHeight !== previousDocumentHeight) {
69			calculateViewportI = watchers.length;
70			while( calculateViewportI-- ) {
71				watchers[calculateViewportI].recalculateLocation();
72			}
73			previousDocumentHeight = exports.documentHeight;
74		}
75	}
76
77	function recalculateWatchLocationsAndTrigger() {
78		exports.viewportHeight = getViewportHeight();
79		calculateViewport();
80		updateAndTriggerWatchers();
81	}
82
83	var recalculateAndTriggerTimer;
84	function debouncedRecalcuateAndTrigger() {
85		clearTimeout(recalculateAndTriggerTimer);
86		recalculateAndTriggerTimer = setTimeout( recalculateWatchLocationsAndTrigger, 100 );
87	}
88
89	var updateAndTriggerWatchersI;
90	function updateAndTriggerWatchers() {
91		// update all watchers then trigger the events so one can rely on another being up to date.
92		updateAndTriggerWatchersI = watchers.length;
93		var i = 0;
94		while( i < updateAndTriggerWatchersI ) {
95			watchers[i].update();
96			i++;
97		}
98
99		updateAndTriggerWatchersI = watchers.length;
100		var j = 0,
101			newLength;
102		while( j < updateAndTriggerWatchersI ) {
103			watchers[j].triggerCallbacks();
104
105			newLength = watchers.length;
106
107			if (newLength < updateAndTriggerWatchersI) {
108				j -= updateAndTriggerWatchersI - newLength;
109			}
110
111			updateAndTriggerWatchersI = newLength;
112			j++;
113		}
114
115	}
116
117	function ElementWatcher( watchItem, offsets ) {
118		var self = this;
119
120		this.watchItem = watchItem;
121
122		this.uid = Math.random().toString(36).substring(8);
123
124		if (!offsets) {
125			this.offsets = defaultOffsets;
126		} else if (offsets === +offsets) {
127			this.offsets = {top: offsets, bottom: offsets};
128		} else {
129			this.offsets = {
130				top: offsets.top || defaultOffsets.top,
131				bottom: offsets.bottom || defaultOffsets.bottom
132			};
133		}
134
135		this.callbacks = {}; // {callback: function, isOne: true }
136
137		for (var i = 0, j = eventTypes.length; i < j; i++) {
138			self.callbacks[eventTypes[i]] = [];
139		}
140
141		this.locked = false;
142
143		var wasInViewport;
144		var wasFullyInViewport;
145		var wasAboveViewport;
146		var wasBelowViewport;
147
148		var listenerToTriggerListI;
149		var listener;
150		function triggerCallbackArray( listeners ) {
151			if (listeners.length === 0) {
152				return;
153			}
154			listenerToTriggerListI = listeners.length;
155			while( listenerToTriggerListI-- ) {
156				listener = listeners[listenerToTriggerListI];
157				listener.callback.call( self, latestEvent );
158				if (listener.isOne) {
159					listeners.splice(listenerToTriggerListI, 1);
160				}
161			}
162		}
163		this.triggerCallbacks = function triggerCallbacks() {
164
165			if (this.isInViewport && !wasInViewport) {
166				triggerCallbackArray( this.callbacks[ENTERVIEWPORT] );
167			}
168			if (this.isFullyInViewport && !wasFullyInViewport) {
169				triggerCallbackArray( this.callbacks[FULLYENTERVIEWPORT] );
170			}
171
172
173			if (this.isAboveViewport !== wasAboveViewport &&
174				this.isBelowViewport !== wasBelowViewport) {
175
176				triggerCallbackArray( this.callbacks[VISIBILITYCHANGE] );
177
178				// if you skip completely past this element
179				if (!wasFullyInViewport && !this.isFullyInViewport) {
180					triggerCallbackArray( this.callbacks[FULLYENTERVIEWPORT] );
181					triggerCallbackArray( this.callbacks[PARTIALLYEXITVIEWPORT] );
182				}
183				if (!wasInViewport && !this.isInViewport) {
184					triggerCallbackArray( this.callbacks[ENTERVIEWPORT] );
185					triggerCallbackArray( this.callbacks[EXITVIEWPORT] );
186				}
187			}
188
189			if (!this.isFullyInViewport && wasFullyInViewport) {
190				triggerCallbackArray( this.callbacks[PARTIALLYEXITVIEWPORT] );
191			}
192			if (!this.isInViewport && wasInViewport) {
193				triggerCallbackArray( this.callbacks[EXITVIEWPORT] );
194			}
195			if (this.isInViewport !== wasInViewport) {
196				triggerCallbackArray( this.callbacks[VISIBILITYCHANGE] );
197			}
198			switch( true ) {
199				case wasInViewport !== this.isInViewport:
200				case wasFullyInViewport !== this.isFullyInViewport:
201				case wasAboveViewport !== this.isAboveViewport:
202				case wasBelowViewport !== this.isBelowViewport:
203					triggerCallbackArray( this.callbacks[STATECHANGE] );
204			}
205
206			wasInViewport = this.isInViewport;
207			wasFullyInViewport = this.isFullyInViewport;
208			wasAboveViewport = this.isAboveViewport;
209			wasBelowViewport = this.isBelowViewport;
210
211		};
212
213		this.recalculateLocation = function() {
214			if (this.locked) {
215				return;
216			}
217			var previousTop = this.top;
218			var previousBottom = this.bottom;
219			if (this.watchItem.nodeName) { // a dom element
220				var cachedDisplay = this.watchItem.style.display;
221				if (cachedDisplay === 'none') {
222					this.watchItem.style.display = '';
223				}
224
225				var boundingRect = this.watchItem.getBoundingClientRect();
226				this.top = boundingRect.top + exports.viewportTop;
227				this.bottom = boundingRect.bottom + exports.viewportTop;
228
229				if (cachedDisplay === 'none') {
230					this.watchItem.style.display = cachedDisplay;
231				}
232
233			} else if (this.watchItem === +this.watchItem) { // number
234				if (this.watchItem > 0) {
235					this.top = this.bottom = this.watchItem;
236				} else {
237					this.top = this.bottom = exports.documentHeight - this.watchItem;
238				}
239
240			} else { // an object with a top and bottom property
241				this.top = this.watchItem.top;
242				this.bottom = this.watchItem.bottom;
243			}
244
245			this.top -= this.offsets.top;
246			this.bottom += this.offsets.bottom;
247			this.height = this.bottom - this.top;
248
249			if ( (previousTop !== undefined || previousBottom !== undefined) && (this.top !== previousTop || this.bottom !== previousBottom) ) {
250				triggerCallbackArray( this.callbacks[LOCATIONCHANGE] );
251			}
252		};
253
254		this.recalculateLocation();
255		this.update();
256
257		wasInViewport = this.isInViewport;
258		wasFullyInViewport = this.isFullyInViewport;
259		wasAboveViewport = this.isAboveViewport;
260		wasBelowViewport = this.isBelowViewport;
261	}
262
263	ElementWatcher.prototype = {
264		on: function( event, callback, isOne ) {
265
266			// trigger the event if it applies to the element right now.
267			switch( true ) {
268				case event === VISIBILITYCHANGE && !this.isInViewport && this.isAboveViewport:
269				case event === ENTERVIEWPORT && this.isInViewport:
270				case event === FULLYENTERVIEWPORT && this.isFullyInViewport:
271				case event === EXITVIEWPORT && this.isAboveViewport && !this.isInViewport:
272				case event === PARTIALLYEXITVIEWPORT && this.isAboveViewport:
273					callback.call( this, latestEvent );
274					if (isOne) {
275						return;
276					}
277			}
278
279			if (this.callbacks[event]) {
280				this.callbacks[event].push({callback: callback, isOne: isOne||false});
281			} else {
282				throw new Error('Tried to add a scroll monitor listener of type '+event+'. Your options are: '+eventTypes.join(', '));
283			}
284		},
285		off: function( event, callback ) {
286			if (this.callbacks[event]) {
287				for (var i = 0, item; item = this.callbacks[event][i]; i++) {
288					if (item.callback === callback) {
289						this.callbacks[event].splice(i, 1);
290						break;
291					}
292				}
293			} else {
294				throw new Error('Tried to remove a scroll monitor listener of type '+event+'. Your options are: '+eventTypes.join(', '));
295			}
296		},
297		one: function( event, callback ) {
298			this.on( event, callback, true);
299		},
300		recalculateSize: function() {
301			this.height = this.watchItem.offsetHeight + this.offsets.top + this.offsets.bottom;
302			this.bottom = this.top + this.height;
303		},
304		update: function() {
305			this.isAboveViewport = this.top < exports.viewportTop;
306			this.isBelowViewport = this.bottom > exports.viewportBottom;
307
308			this.isInViewport = (this.top <= exports.viewportBottom && this.bottom >= exports.viewportTop);
309			this.isFullyInViewport = (this.top >= exports.viewportTop && this.bottom <= exports.viewportBottom) ||
310								 (this.isAboveViewport && this.isBelowViewport);
311
312		},
313		destroy: function() {
314			var index = -1,
315				self  = this;
316
317			for (var i = 0; i < watchers.length; i++) {
318				if (this.uid == watchers[i].uid) {
319					index = i;
320					break;
321				}
322			}
323
324			if (index == -1) {
325				index = watchers.indexOf(this);
326			}
327
328			watchers.splice(index, 1);
329			for (var i = 0, j = eventTypes.length; i < j; i++) {
330				self.callbacks[eventTypes[i]].length = 0;
331			}
332		},
333		// prevent recalculating the element location
334		lock: function() {
335			this.locked = true;
336		},
337		unlock: function() {
338			this.locked = false;
339		}
340	};
341
342	var eventHandlerFactory = function (type) {
343		return function( callback, isOne ) {
344			this.on.call(this, type, callback, isOne);
345		};
346	};
347
348	for (var i = 0, j = eventTypes.length; i < j; i++) {
349		var type =  eventTypes[i];
350		ElementWatcher.prototype[type] = eventHandlerFactory(type);
351	}
352
353	try {
354		calculateViewport();
355	} catch (e) {
356		try {
357			window.$(calculateViewport);
358		} catch (e) {
359			throw new Error('If you must put scrollMonitor in the <head>, you must use jQuery.');
360		}
361	}
362
363	function scrollMonitorListener(event) {
364		latestEvent = event;
365		calculateViewport();
366		updateAndTriggerWatchers();
367	}
368
369	if (window.addEventListener) {
370		window.addEventListener('scroll', scrollMonitorListener);
371		window.addEventListener('resize', debouncedRecalcuateAndTrigger);
372	} else {
373		// Old IE support
374		window.attachEvent('onscroll', scrollMonitorListener);
375		window.attachEvent('onresize', debouncedRecalcuateAndTrigger);
376	}
377
378	exports.beget = exports.create = function( element, offsets ) {
379		if (typeof element === 'string') {
380			element = document.querySelector(element);
381		} else if (element && element.length > 0) {
382			element = element[0];
383		}
384
385		var watcher = new ElementWatcher( element, offsets );
386		watchers.push(watcher);
387		watcher.update();
388		return watcher;
389	};
390
391	exports.update = function() {
392		latestEvent = null;
393		calculateViewport();
394		updateAndTriggerWatchers();
395	};
396	exports.recalculateLocations = function() {
397		exports.documentHeight = 0;
398		exports.update();
399	};
400
401	return exports;
402});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.