PageSourceSearch

https://tsf.gmbh/wp-content/plugins/dethemekit-for-elementor/inclu…/js/intersectionobserver.js?ver=7.1.3

js tsf.gmbh collected 2026-10-06 19:43:32 UTC 25,372 bytes, 741 lines download raw bytes

vendor: 5,445 bytes, lines 1-158
1/**
2 * Copyright 2016 Google Inc. All Rights Reserved.
3 *
4 * Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE.
5 *
6 *  https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
7 *
8 */
9(function() {
10    'use strict';
11    
12    // Exit early if we're not running in a browser.
13    if (typeof window !== 'object') {
14      return;
15    }
16    
17    // Exit early if all IntersectionObserver and IntersectionObserverEntry
18    // features are natively supported.
19    if ('IntersectionObserver' in window &&
20        'IntersectionObserverEntry' in window &&
21        'intersectionRatio' in window.IntersectionObserverEntry.prototype) {
22    
23      // Minimal polyfill for Edge 15's lack of `isIntersecting`
24      // See: https://github.com/w3c/IntersectionObserver/issues/211
25      if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
26        Object.defineProperty(window.IntersectionObserverEntry.prototype,
27          'isIntersecting', {
28          get: function () {
29            return this.intersectionRatio > 0;
30          }
31        });
32      }
33      return;
34    }
35    
36    
37    /**
38     * A local reference to the document.
39     */
40    var document = window.document;
41    
42    
43    /**
44     * An IntersectionObserver registry. This registry exists to hold a strong
45     * reference to IntersectionObserver instances currently observing a target
46     * element. Without this registry, instances without another reference may be
47     * garbage collected.
48     */
49    var registry = [];
50    
51    
52    /**
53     * Creates the global IntersectionObserverEntry constructor.
54     * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry
55     * @param {Object} entry A dictionary of instance properties.
56     * @constructor
57     */
58    function IntersectionObserverEntry(entry) {
59      this.time = entry.time;
60      this.target = entry.target;
61      this.rootBounds = entry.rootBounds;
62      this.boundingClientRect = entry.boundingClientRect;
63      this.intersectionRect = entry.intersectionRect || getEmptyRect();
64      this.isIntersecting = !!entry.intersectionRect;
65    
66      // Calculates the intersection ratio.
67      var targetRect = this.boundingClientRect;
68      var targetArea = targetRect.width * targetRect.height;
69      var intersectionRect = this.intersectionRect;
70      var intersectionArea = intersectionRect.width * intersectionRect.height;
71    
72      // Sets intersection ratio.
73      if (targetArea) {
74        // Round the intersection ratio to avoid floating point math issues:
75        // https://github.com/w3c/IntersectionObserver/issues/324
76        this.intersectionRatio = Number((intersectionArea / targetArea).toFixed(4));
77      } else {
78        // If area is zero and is intersecting, sets to 1, otherwise to 0
79        this.intersectionRatio = this.isIntersecting ? 1 : 0;
80      }
81    }
82    
83    
84    /**
85     * Creates the global IntersectionObserver constructor.
86     * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface
87     * @param {Function} callback The function to be invoked after intersection
88     *     changes have queued. The function is not invoked if the queue has
89     *     been emptied by calling the `takeRecords` method.
90     * @param {Object=} opt_options Optional configuration options.
91     * @constructor
92     */
93    function IntersectionObserver(callback, opt_options) {
94    
95      var options = opt_options || {};
96    
97      if (typeof callback != 'function') {
98        throw new Error('callback must be a function');
99      }
100    
101      if (options.root && options.root.nodeType != 1) {
102        throw new Error('root must be an Element');
103      }
104    
105      // Binds and throttles `this._checkForIntersections`.
106      this._checkForIntersections = throttle(
107          this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT);
108    
109      // Private properties.
110      this._callback = callback;
111      this._observationTargets = [];
112      this._queuedEntries = [];
113      this._rootMarginValues = this._parseRootMargin(options.rootMargin);
114    
115      // Public properties.
116      this.thresholds = this._initThresholds(options.threshold);
117      this.root = options.root || null;
118      this.rootMargin = this._rootMarginValues.map(function(margin) {
119        return margin.value + margin.unit;
120      }).join(' ');
121    }
122    
123    
124    /**
125     * The minimum interval within which the document will be checked for
126     * intersection changes.
127     */
128    IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100;
129    
130    
131    /**
132     * The frequency in which the polyfill polls for intersection changes.
133     * this can be updated on a per instance basis and must be set prior to
134     * calling `observe` on the first target.
135     */
136    IntersectionObserver.prototype.POLL_INTERVAL = null;
137    
138    /**
139     * Use a mutation observer on the root element
140     * to detect intersection changes.
141     */
142    IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true;
143    
144    
145    /**
146     * Starts observing a target element for intersection changes based on
147     * the thresholds values.
148     * @param {Element} target The DOM element to observe.
149     */
150    IntersectionObserver.prototype.observe = function(target) {
151      var isTargetAlreadyObserved = this._observationTargets.some(function(item) {
152        return item.element == target;
153      });
154    
155      if (isTargetAlreadyObserved) {
156        return;
157      }
158    
159      if (!(target && target.nodeType == 1)) {
160        throw new Error('target must be an Element');
161      }
162    
163      this._registerInstance();
164      this._observationTargets.push({element: target, entry: null});
165      this._monitorIntersections();
166      this._checkForIntersections();
167    };
168    
169    
170    /**
171     * Stops observing a target element for intersection changes.
172     * @param {Element} target The DOM element to observe.
173     */
174    IntersectionObserver.prototype.unobserve = function(target) {
175      this._observationTargets =
176          this._observationTargets.filter(function(item) {
177    
178        return item.element != target;
179      });
180      if (!this._observationTargets.length) {
181        this._unmonitorIntersections();
182        this._unregisterInstance();
183      }
184    };
185    
186    
187    /**
188     * Stops observing all target elements for intersection changes.
189     */
190    IntersectionObserver.prototype.disconnect = function() {
191      this._observationTargets = [];
192      this._unmonitorIntersections();
193      this._unregisterInstance();
194    };
195    
196    
197    /**
198     * Returns any queue entries that have not yet been reported to the
199     * callback and clears the queue. This can be used in conjunction with the
200     * callback to obtain the absolute most up-to-date intersection information.
201     * @return {Array} The currently queued entries.
202     */
203    IntersectionObserver.prototype.takeRecords = function() {
204      var records = this._queuedEntries.slice();
205      this._queuedEntries = [];
206      return records;
207    };
208    
209    
210    /**
211     * Accepts the threshold value from the user configuration object and
212     * returns a sorted array of unique threshold values. If a value is not
213     * between 0 and 1 and error is thrown.
214     * @private
215     * @param {Array|number=} opt_threshold An optional threshold value or
216     *     a list of threshold values, defaulting to [0].
217     * @return {Array} A sorted list of unique and valid threshold values.
218     */
219    IntersectionObserver.prototype._initThresholds = function(opt_threshold) {
220      var threshold = opt_threshold || [0];
221      if (!Array.isArray(threshold)) threshold = [threshold];
222    
223      return threshold.sort().filter(function(t, i, a) {
224        if (typeof t != 'number' || isNaN(t) || t < 0 || t > 1) {
225          throw new Error('threshold must be a number between 0 and 1 inclusively');
226        }
227        return t !== a[i - 1];
228      });
229    };
230    
231    
232    /**
233     * Accepts the rootMargin value from the user configuration object
234     * and returns an array of the four margin values as an object c
234ontaining
235     * the value and unit properties. If any of the values are not properly
236     * formatted or use a unit other than px or %, and error is thrown.
237     * @private
238     * @param {string=} opt_rootMargin An optional rootMargin value,
239     *     defaulting to '0px'.
240     * @return {Array<Object>} An array of margin objects with the keys
241     *     value and unit.
242     */
243    IntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) {
244      var marginString = opt_rootMargin || '0px';
245      var margins = marginString.split(/\s+/).map(function(margin) {
246        var parts = /^(-?\d*\.?\d+)(px|%)$/.exec(margin);
247        if (!parts) {
248          throw new Error('rootMargin must be specified in pixels or percent');
249        }
250        return {value: parseFloat(parts[1]), unit: parts[2]};
251      });
252    
253      // Handles shorthand.
254      margins[1] = margins[1] || margins[0];
255      margins[2] = margins[2] || margins[0];
256      margins[3] = margins[3] || margins[1];
257    
258      return margins;
259    };
260    
261    
262    /**
263     * Starts polling for intersection changes if the polling is not already
264     * happening, and if the page's visibility state is visible.
265     * @private
266     */
267    IntersectionObserver.prototype._monitorIntersections = function() {
268      if (!this._monitoringIntersections) {
269        this._monitoringIntersections = true;
270    
271        // If a poll interval is set, use polling instead of listening to
272        // resize and scroll events or DOM mutations.
273        if (this.POLL_INTERVAL) {
274          this._monitoringInterval = setInterval(
275              this._checkForIntersections, this.POLL_INTERVAL);
276        }
277        else {
278          addEvent(window, 'resize', this._checkForIntersections, true);
279          addEvent(document, 'scroll', this._checkForIntersections, true);
280    
281          if (this.USE_MUTATION_OBSERVER && 'MutationObserver' in window) {
282            this._domObserver = new MutationObserver(this._checkForIntersections);
283            this._domObserver.observe(document, {
284              attributes: true,
285              childList: true,
286              characterData: true,
287              subtree: true
288            });
289          }
290        }
291      }
292    };
293    
294    
295    /**
296     * Stops polling for intersection changes.
297     * @private
298     */
299    IntersectionObserver.prototype._unmonitorIntersections = function() {
300      if (this._monitoringIntersections) {
301        this._monitoringIntersections = false;
302    
303        clearInterval(this._monitoringInterval);
304        this._monitoringInterval = null;
305    
306        removeEvent(window, 'resize', this._checkForIntersections, true);
307        removeEvent(document, 'scroll', this._checkForIntersections, true);
308    
309        if (this._domObserver) {
310          this._domObserver.disconnect();
311          this._domObserver = null;
312        }
313      }
314    };
315    
316    
317    /**
318     * Scans each observation target for intersection changes and adds them
319     * to the internal entries queue. If new entries are found, it
320     * schedules the callback to be invoked.
321     * @private
322     */
323    IntersectionObserver.prototype._checkForIntersections = function() {
324      var rootIsInDom = this._rootIsInDom();
325      var rootRect = rootIsInDom ? this._getRootRect() : getEmptyRect();
326    
327      this._observationTargets.forEach(function(item) {
328        var target = item.element;
329        var targetRect = getBoundingClientRect(target);
330        var rootContainsTarget = this._rootContainsTarget(target);
331        var oldEntry = item.entry;
332        var intersectionRect = rootIsInDom && rootContainsTarget &&
333            this._computeTargetAndRootIntersection(target, rootRect);
334    
335        var newEntry = item.entry = new IntersectionObserverEntry({
336          time: now(),
337          target: target,
338          boundingClientRect: targetRect,
339          rootBounds: rootRect,
340          intersectionRect: intersectionRect
341        });
342    
343        if (!oldEntry) {
344          this._queuedEntries.push(newEntry);
345        } else if (rootIsInDom && rootContainsTarget) {
346          // If the new entry intersection ratio has crossed any of the
347          // thresholds, add a new entry.
348          if (this._hasCrossedThreshold(oldEntry, newEntry)) {
349            this._queuedEntries.push(newEntry);
350          }
351        } else {
352          // If the root is not in the DOM or target is not contained within
353          // root but the previous entry for this target had an intersection,
354          // add a new record indicating removal.
355          if (oldEntry && oldEntry.isIntersecting) {
356            this._queuedEntries.push(newEntry);
357          }
358        }
359      }, this);
360    
361      if (this._queuedEntries.length) {
362        this._callback(this.takeRecords(), this);
363      }
364    };
365    
366    
367    /**
368     * Accepts a target and root rect computes the intersection between then
369     * following the algorithm in the spec.
370     * TODO(philipwalton): at this time clip-path is not considered.
371     * https://w3c.github.io/IntersectionObserver/#calculate-intersection-rect-algo
372     * @param {Element} target The target DOM element
373     * @param {Object} rootRect The bounding rect of the root after being
374     *     expanded by the rootMargin value.
375     * @return {?Object} The final intersection rect object or undefined if no
376     *     intersection is found.
377     * @private
378     */
379    IntersectionObserver.prototype._computeTargetAndRootIntersection =
380        function(target, rootRect) {
381    
382      // If the element isn't displayed, an intersection can't happen.
383      if (window.getComputedStyle(target).display == 'none') return;
384    
385      var targetRect = getBoundingClientRect(target);
386      var intersectionRect = targetRect;
387      var parent = getParentNode(target);
388      var atRoot = false;
vendor: 9,222 bytes, lines 389-655
389    
390      while (!atRoot) {
391        var parentRect = null;
392        var parentComputedStyle = parent.nodeType == 1 ?
393            window.getComputedStyle(parent) : {};
394    
395        // If the parent isn't displayed, an intersection can't happen.
396        if (parentComputedStyle.display == 'none') return;
397    
398        if (parent == this.root || parent == document) {
399          atRoot = true;
400          parentRect = rootRect;
401        } else {
402          // If the element has a non-visible overflow, and it's not the <body>
403          // or <html> element, update the intersection rect.
404          // Note: <body> and <html> cannot be clipped to a rect that's not also
405          // the document rect, so no need to compute a new intersection.
406          if (parent != document.body &&
407              parent != document.documentElement &&
408              parentComputedStyle.overflow != 'visible') {
409            parentRect = getBoundingClientRect(parent);
410          }
411        }
412    
413        // If either of the above conditionals set a new parentRect,
414        // calculate new intersection data.
415        if (parentRect) {
416          intersectionRect = computeRectIntersection(parentRect, intersectionRect);
417    
418          if (!intersectionRect) break;
419        }
420        parent = getParentNode(parent);
421      }
422      return intersectionRect;
423    };
424    
425    
426    /**
427     * Returns the root rect after being expanded by the rootMargin value.
428     * @return {Object} The expanded root rect.
429     * @private
430     */
431    IntersectionObserver.prototype._getRootRect = function() {
432      var rootRect;
433      if (this.root) {
434        rootRect = getBoundingClientRect(this.root);
435      } else {
436        // Use <html>/<body> instead of window since scroll bars affect size.
437        var html = document.documentElement;
438        var body = document.body;
439        rootRect = {
440          top: 0,
441          left: 0,
442          right: html.clientWidth || body.clientWidth,
443          width: html.clientWidth || body.clientWidth,
444          bottom: html.clientHeight || body.clientHeight,
445          height: html.clientHeight || body.clientHeight
446        };
447      }
448      return this._expandRectByRootMargin(rootRect);
449    };
450    
451    
452    /**
453     * Accepts a rect and expands it by the rootMargin value.
454     * @param {Object} rect The rect object to expand.
455     * @return {Object} The expanded rect.
456     * @private
457     */
458    IntersectionObserver.prototype._expandRectByRootMargin = function(rect) {
459      var margins = this._rootMarginValues.map(function(margin, i) {
460        return margin.unit == 'px' ? margin.value :
461            margin.value * (i % 2 ? rect.width : rect.height) / 100;
462      });
463      var newRect = {
464        top: rect.top - margins[0],
465        right: rect.right + margins[1],
466        bottom: rect.bottom + margins[2],
467        left: rect.left - margins[3]
468      };
469      newRect.width = newRect.right - newRect.left;
470      newRect.height = newRect.bottom - newRect.top;
471    
472      return newRect;
473    };
474    
475    
476    /**
477     * Accepts an old and new entry and returns true if at least one of the
478     * threshold values has been crossed.
479     * @param {?IntersectionObserverEntry} oldEntry The previous entry for a
480     *    particular target element or null if no previous entry exists.
481     * @param {IntersectionObserverEntry} newEntry The current entry for a
482     *    particular target element.
483     * @return {boolean} Returns true if a any threshold has been crossed.
484     * @private
485     */
486    IntersectionObserver.prototype._hasCrossedThreshold =
487        function(oldEntry, newEntry) {
488    
489      // To make comparing easier, an entry that has a ratio of 0
490      // but does not actually intersect is given a value of -1
491      var oldRatio = oldEntry && oldEntry.isIntersecting ?
492          oldEntry.intersectionRatio || 0 : -1;
493      var newRatio = newEntry.isIntersecting ?
494          newEntry.intersectionRatio || 0 : -1;
495    
496      // Ignore unchanged ratios
497      if (oldRatio === newRatio) return;
498    
499      for (var i = 0; i < this.thresholds.length; i++) {
500        var threshold = this.thresholds[i];
501    
502        // Return true if an entry matches a threshold or if the new ratio
503        // and the old ratio are on the opposite sides of a threshold.
504        if (threshold == oldRatio || threshold == newRatio ||
505            threshold < oldRatio !== threshold < newRatio) {
506          return true;
507        }
508      }
509    };
510    
511    
512    /**
513     * Returns whether or not the root element is an element and is in the DOM.
514     * @return {boolean} True if the root element is an element and is in the DOM.
515     * @private
516     */
517    IntersectionObserver.prototype._rootIsInDom = function() {
518      return !this.root || containsDeep(document, this.root);
519    };
520    
521    
522    /**
523     * Returns whether or not the target element is a child of root.
524     * @param {Element} target The target element to check.
525     * @return {boolean} True if the target element is a child of root.
526     * @private
527     */
528    IntersectionObserver.prototype._rootContainsTarget = function(target) {
529      return containsDeep(this.root || document, target);
530    };
531    
532    
533    /**
534     * Adds the instance to the global IntersectionObserver registry if it isn't
535     * already present.
536     * @private
537     */
538    IntersectionObserver.prototype._registerInstance = function() {
539      if (registry.indexOf(this) < 0) {
540        registry.push(this);
541      }
542    };
543    
544    
545    /**
546     * Removes the instance from the global IntersectionObserver registry.
547     * @private
548     */
549    IntersectionObserver.prototype._unregisterInstance = function() {
550      var index = registry.indexOf(this);
551      if (index != -1) registry.splice(index, 1);
552    };
553    
554    
555    /**
556     * Returns the result of the performance.now() method or null in browsers
557     * that don't support the API.
558     * @return {number} The elapsed time since the page was requested.
559     */
560    function now() {
561      return window.performance && performance.now && performance.now();
562    }
563    
564    
565    /**
566     * Throttles a function and delays its execution, so it's only called at most
567     * once within a given time period.
568     * @param {Function} fn The function to throttle.
569     * @param {number} timeout The amount of time that must pass before the
570     *     function can be called again.
571     * @return {Function} The throttled function.
572     */
573    function throttle(fn, timeout) {
574      var timer = null;
575      return function () {
576        if (!timer) {
577          timer = setTimeout(function() {
578            fn();
579            timer = null;
580          }, timeout);
581        }
582      };
583    }
584    
585    
586    /**
587     * Adds an event handler to a DOM node ensuring cross-browser compatibility.
588     * @param {Node} node The DOM node to add the event handler to.
589     * @param {string} event The event name.
590     * @param {Function} fn The event handler to add.
591     * @param {boolean} opt_useCapture Optionally adds the even to the capture
592     *     phase. Note: this only works in modern browsers.
593     */
594    function addEvent(node, event, fn, opt_useCapture) {
595      if (typeof node.addEventListener == 'function') {
596        node.addEventListener(event, fn, opt_useCapture || false);
597      }
598      else if (typeof node.attachEvent == 'function') {
599        node.attachEvent('on' + event, fn);
600      }
601    }
602    
603    
604    /**
605     * Removes a previously added event handler from a DOM node.
606     * @param {Node} node The DOM node to remove the event handler from.
607     * @param {string} event The event name.
608     * @param {Function} fn The event handler to remove.
609     * @param {boolean} opt_useCapture If the event handler was added with this
610     *     flag set to true, it should be set to true here in order to remove it.
611     */
612    function removeEvent(node, event, fn, opt_useCapture) {
613      if (typeof node.removeEventListener == 'function') {
614        node.removeEventListener(event, fn, opt_useCapture || false);
615      }
616      else if (typeof node.detatchEvent == 'function') {
617        node.detatchEvent('on' + event, fn);
618      }
619    }
620    
621    
622    /**
623     * Returns the intersection between two rect objects.
624     * @param {Object} rect1 The first rect.
625     * @param {Object} rect2 The second rect.
626     * @return {?Object} The intersection rect or undefined if no intersection
627     *     is found.
628     */
629    function computeRectIntersection(rect1, rect2) {
630      var top = Math.max(rect1.top, rect2.top);
631      var bottom = Math.min(rect1.bottom, rect2.bottom);
632      var left = Math.max(rect1.left, rect2.left);
633      var right = Math.min(rect1.right, rect2.right);
634      var width = right - left;
635      var height = bottom - top;
636    
637      return (width >= 0 && height >= 0) && {
638        top: top,
639        bottom: bottom,
640        left: left,
641        right: right,
642        width: width,
643        height: height
644      };
645    }
646    
647    
648    /**
649     * Shims the native getBoundingClientRect for compatibility with older IE.
650     * @param {Element} el The element whose bounding rect to get.
651     * @return {Object} The (possibly shimmed) rect of the element.
652     */
653    function getBoundingClientRect(el) {
654      var rect;
655    
656      try {
657        rect = el.getBoundingClientRect();
658      } catch (err) {
659        // Ignore Windows 7 IE11 "Unspecified error"
660        // https://github.com/w3c/IntersectionObserver/pull/205
661      }
662    
663      if (!rect) return getEmptyRect();
664    
665      // Older IE
666      if (!(rect.width && rect.height)) {
667        rect = {
668          top: rect.top,
669          right: rect.right,
670          bottom: rect.bottom,
671          left: rect.left,
672          width: rect.right - rect.left,
673          height: rect.bottom - rect.top
674        };
675      }
676      return rect;
677    }
678    
679    
680    /**
681     * Returns an empty rect object. An empty rect is returned when an element
682     * is not in the DOM.
683     * @return {Object} The empty rect.
684     */
685    function getEmptyRect() {
686      return {
687        top: 0,
688        bottom: 0,
689        left: 0,
690        right: 0,
691        width: 0,
692        height: 0
693      };
694    }
695    
696    /**
697     * Checks to see if a parent element contains a child element (including inside
698     * shadow DOM).
699     * @param {Node} parent The parent element.
700     * @param {Node} child The child element.
701     * @return {boolean} True if the parent node contains the child node.
702     */
703    function containsDeep(parent, child) {
704      var node = child;
705      while (node) {
706        if (node == parent) return true;
707    
708        node = getParentNode(node);
709      }
710      return false;
711    }
712    
713    
714    /**
715     * Gets the parent node of an element or its host element if the parent node
716     * is a shadow root.
717     * @param {Node} node The node whose parent to get.
718     * @return {Node|null} The parent node or null if no parent exists.
719     */
720    function getParentNode(node) {
721      var parent = node.parentNode;
722    
723      if (parent && parent.nodeType == 11 && parent.host) {
724        // If the parent is a shadow root, return the host element.
725        return parent.host;
726      }
727    
728      if (parent && parent.assignedSlot) {
729        // If the parent is distributed in a <slot>, return the parent of a slot.
730        return parent.assignedSlot.parentNode;
731      }
732    
733      return parent;
734    }
735    
736    
737    // Exposes the constructors globally.
738    window.IntersectionObserver = IntersectionObserver;
739    window.IntersectionObserverEntry = IntersectionObserverEntry;
740    
741    }());

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.