/ shared / components / node_modules / intersection-observer-admin / dist / intersection-observer-admin.es5.js
intersection-observer-admin.es5.js
  1  var Registry = /** @class */ (function () {
  2      function Registry() {
  3          this.registry = new WeakMap();
  4      }
  5      Registry.prototype.elementExists = function (elem) {
  6          return this.registry.has(elem);
  7      };
  8      Registry.prototype.getElement = function (elem) {
  9          return this.registry.get(elem);
 10      };
 11      /**
 12       * administrator for lookup in the future
 13       *
 14       * @method add
 15       * @param {HTMLElement | Window} element - the item to add to root element registry
 16       * @param {IOption} options
 17       * @param {IOption.root} [root] - contains optional root e.g. window, container div, etc
 18       * @param {IOption.watcher} [observer] - optional
 19       * @public
 20       */
 21      Registry.prototype.addElement = function (element, options) {
 22          if (!element) {
 23              return;
 24          }
 25          this.registry.set(element, options || {});
 26      };
 27      /**
 28       * @method remove
 29       * @param {HTMLElement|Window} target
 30       * @public
 31       */
 32      Registry.prototype.removeElement = function (target) {
 33          this.registry.delete(target);
 34      };
 35      /**
 36       * reset weak map
 37       *
 38       * @method destroy
 39       * @public
 40       */
 41      Registry.prototype.destroyRegistry = function () {
 42          this.registry = new WeakMap();
 43      };
 44      return Registry;
 45  }());
 46  
 47  var noop = function () { };
 48  var CallbackType;
 49  (function (CallbackType) {
 50      CallbackType["enter"] = "enter";
 51      CallbackType["exit"] = "exit";
 52  })(CallbackType || (CallbackType = {}));
 53  var Notifications = /** @class */ (function () {
 54      function Notifications() {
 55          this.registry = new Registry();
 56      }
 57      /**
 58       * Adds an EventListener as a callback for an event key.
 59       * @param type 'enter' or 'exit'
 60       * @param key The key of the event
 61       * @param callback The callback function to invoke when the event occurs
 62       */
 63      Notifications.prototype.addCallback = function (type, element, callback) {
 64          var _a, _b;
 65          var entry;
 66          if (type === CallbackType.enter) {
 67              entry = (_a = {}, _a[CallbackType.enter] = callback, _a);
 68          }
 69          else {
 70              entry = (_b = {}, _b[CallbackType.exit] = callback, _b);
 71          }
 72          this.registry.addElement(element, Object.assign({}, this.registry.getElement(element), entry));
 73      };
 74      /**
 75       * @hidden
 76       * Executes registered callbacks for key.
 77       * @param type
 78       * @param element
 79       * @param data
 80       */
 81      Notifications.prototype.dispatchCallback = function (type, element, data) {
 82          if (type === CallbackType.enter) {
 83              var _a = this.registry.getElement(element).enter, enter = _a === void 0 ? noop : _a;
 84              enter(data);
 85          }
 86          else {
 87              // no element in WeakMap possible because element may be removed from DOM by the time we get here
 88              var found = this.registry.getElement(element);
 89              if (found && found.exit) {
 90                  found.exit(data);
 91              }
 92          }
 93      };
 94      return Notifications;
 95  }());
 96  
 97  var __extends = (undefined && undefined.__extends) || (function () {
 98      var extendStatics = function (d, b) {
 99          extendStatics = Object.setPrototypeOf ||
100              ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
101              function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
102          return extendStatics(d, b);
103      };
104      return function (d, b) {
105          if (typeof b !== "function" && b !== null)
106              throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
107          extendStatics(d, b);
108          function __() { this.constructor = d; }
109          d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
110      };
111  })();
112  var __assign = (undefined && undefined.__assign) || function () {
113      __assign = Object.assign || function(t) {
114          for (var s, i = 1, n = arguments.length; i < n; i++) {
115              s = arguments[i];
116              for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
117                  t[p] = s[p];
118          }
119          return t;
120      };
121      return __assign.apply(this, arguments);
122  };
123  var IntersectionObserverAdmin = /** @class */ (function (_super) {
124      __extends(IntersectionObserverAdmin, _super);
125      function IntersectionObserverAdmin() {
126          var _this = _super.call(this) || this;
127          _this.elementRegistry = new Registry();
128          return _this;
129      }
130      /**
131       * Adds element to observe via IntersectionObserver and stores element + relevant callbacks and observer options in static
132       * administrator for lookup in the future
133       *
134       * @method observe
135       * @param {HTMLElement | Window} element
136       * @param {Object} options
137       * @public
138       */
139      IntersectionObserverAdmin.prototype.observe = function (element, options) {
140          if (options === void 0) { options = {}; }
141          if (!element) {
142              return;
143          }
144          this.elementRegistry.addElement(element, __assign({}, options));
145          this.setupObserver(element, __assign({}, options));
146      };
147      /**
148       * Unobserve target element and remove element from static admin
149       *
150       * @method unobserve
151       * @param {HTMLElement|Window} target
152       * @param {Object} options
153       * @public
154       */
155      IntersectionObserverAdmin.prototype.unobserve = function (target, options) {
156          var matchingRootEntry = this.findMatchingRootEntry(options);
157          if (matchingRootEntry) {
158              var intersectionObserver = matchingRootEntry.intersectionObserver;
159              intersectionObserver.unobserve(target);
160          }
161      };
162      /**
163       * register event to handle when intersection observer detects enter
164       *
165       * @method addEnterCallback
166       * @public
167       */
168      IntersectionObserverAdmin.prototype.addEnterCallback = function (element, callback) {
169          this.addCallback(CallbackType.enter, element, callback);
170      };
171      /**
172       * register event to handle when intersection observer detects exit
173       *
174       * @method addExitCallback
175       * @public
176       */
177      IntersectionObserverAdmin.prototype.addExitCallback = function (element, callback) {
178          this.addCallback(CallbackType.exit, element, callback);
179      };
180      /**
181       * retrieve registered callback and call with data
182       *
183       * @method dispatchEnterCallback
184       * @public
185       */
186      IntersectionObserverAdmin.prototype.dispatchEnterCallback = function (element, entry) {
187          this.dispatchCallback(CallbackType.enter, element, entry);
188      };
189      /**
190       * retrieve registered callback and call with data on exit
191       *
192       * @method dispatchExitCallback
193       * @public
194       */
195      IntersectionObserverAdmin.prototype.dispatchExitCallback = function (element, entry) {
196          this.dispatchCallback(CallbackType.exit, element, entry);
197      };
198      /**
199       * cleanup data structures and unobserve elements
200       *
201       * @method destroy
202       * @public
203       */
204      IntersectionObserverAdmin.prototype.destroy = function () {
205          this.elementRegistry.destroyRegistry();
206      };
207      /**
208       * use function composition to curry options
209       *
210       * @method setupOnIntersection
211       * @param {Object} options
212       */
213      IntersectionObserverAdmin.prototype.setupOnIntersection = function (options) {
214          var _this = this;
215          return function (ioEntries) {
216              return _this.onIntersection(options, ioEntries);
217          };
218      };
219      IntersectionObserverAdmin.prototype.setupObserver = function (element, options) {
220          var _a;
221          var _b = options.root, root = _b === void 0 ? window : _b;
222          // First - find shared root element (window or target HTMLElement)
223          // this root is responsible for coordinating it's set of elements
224          var potentialRootMatch = this.findRootFromRegistry(root);
225          // Second - if there is a matching root, see if an existing entry with the same options
226          // regardless of sort order. This is a bit of work
227          var matchingEntryForRoot;
228          if (potentialRootMatch) {
229              matchingEntryForRoot = this.determineMatchingElements(options, potentialRootMatch);
230          }
231          // next add found entry to elements and call observer if applicable
232          if (matchingEntryForRoot) {
233              var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver;
234              elements.push(element);
235              if (intersectionObserver) {
236                  intersectionObserver.observe(element);
237              }
238          }
239          else {
240              // otherwise start observing this element if applicable
241              // watcher is an instance that has an observe method
242              var intersectionObserver = this.newObserver(element, options);
243              var observerEntry = {
244                  elements: [element],
245                  intersectionObserver: intersectionObserver,
246                  options: options
247              };
248              // and add entry to WeakMap under a root element
249              // with watcher so we can use it later on
250              var stringifiedOptions = this.stringifyOptions(options);
251              if (potentialRootMatch) {
252                  // if share same root and need to add new entry to root match
253                  // not functional but :shrug
254                  potentialRootMatch[stringifiedOptions] = observerEntry;
255              }
256              else {
257                  // no root exists, so add to WeakMap
258                  this.elementRegistry.addElement(root, (_a = {},
259                      _a[stringifiedOptions] = observerEntry,
260                      _a));
261              }
262          }
263      };
264      IntersectionObserverAdmin.prototype.newObserver = function (element, options) {
265          // No matching entry for root in static admin, thus create new IntersectionObserver instance
266          var root = options.root, rootMargin = options.rootMargin, threshold = options.threshold;
267          var newIO = new IntersectionObserver(this.setupOnIntersection(options).bind(this), { root: root, rootMargin: rootMargin, threshold: threshold });
268          newIO.observe(element);
269          return newIO;
270      };
271      /**
272       * IntersectionObserver callback when element is intersecting viewport
273       * either when `isIntersecting` changes or `intersectionRadio` crosses on of the
274       * configured `threshold`s.
275       * Exit callback occurs eagerly (when element is initially out of scope)
276       * See https://stackoverflow.com/questions/53214116/intersectionobserver-callback-firing-immediately-on-page-load/53385264#53385264
277       *
278       * @method onIntersection
279       * @param {Object} options
280       * @param {Array} ioEntries
281       * @private
282       */
283      IntersectionObserverAdmin.prototype.onIntersection = function (options, ioEntries) {
284          var _this = this;
285          ioEntries.forEach(function (entry) {
286              var isIntersecting = entry.isIntersecting, intersectionRatio = entry.intersectionRatio;
287              var threshold = options.threshold || 0;
288              if (Array.isArray(threshold)) {
289                  threshold = threshold[threshold.length - 1];
290              }
291              // then find entry's callback in static administration
292              var matchingRootEntry = _this.findMatchingRootEntry(options);
293              // first determine if entry intersecting
294              if (isIntersecting || intersectionRatio > threshold) {
295                  if (matchingRootEntry) {
296                      matchingRootEntry.elements.some(function (element) {
297                          if (element && element === entry.target) {
298                              _this.dispatchEnterCallback(element, entry);
299                              return true;
300                          }
301                          return false;
302                      });
303                  }
304              }
305              else {
306                  if (matchingRootEntry) {
307                      matchingRootEntry.elements.some(function (element) {
308                          if (element && element === entry.target) {
309                              _this.dispatchExitCallback(element, entry);
310                              return true;
311                          }
312                          return false;
313                      });
314                  }
315              }
316          });
317      };
318      /**
319       * { root: { stringifiedOptions: { observer, elements: []...] } }
320       * @method findRootFromRegistry
321       * @param {HTMLElement|Window} root
322       * @private
323       * @return {Object} of elements that share same root
324       */
325      IntersectionObserverAdmin.prototype.findRootFromRegistry = function (root) {
326          if (this.elementRegistry) {
327              return this.elementRegistry.getElement(root);
328          }
329      };
330      /**
331       * We don't care about options key order because we already added
332       * to the static administrator
333       *
334       * @method findMatchingRootEntry
335       * @param {Object} options
336       * @return {Object} entry with elements and other options
337       */
338      IntersectionObserverAdmin.prototype.findMatchingRootEntry = function (options) {
339          var _a = options.root, root = _a === void 0 ? window : _a;
340          var matchingRoot = this.findRootFromRegistry(root);
341          if (matchingRoot) {
342              var stringifiedOptions = this.stringifyOptions(options);
343              return matchingRoot[stringifiedOptions];
344          }
345      };
346      /**
347       * Determine if existing elements for a given root based on passed in options
348       * regardless of sort order of keys
349       *
350       * @method determineMatchingElements
351       * @param {Object} options
352       * @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}
353       * @private
354       * @return {Object} containing array of elements and other meta
355       */
356      IntersectionObserverAdmin.prototype.determineMatchingElements = function (options, potentialRootMatch) {
357          var _this = this;
358          var matchingStringifiedOptions = Object.keys(potentialRootMatch).filter(function (key) {
359              var comparableOptions = potentialRootMatch[key].options;
360              return _this.areOptionsSame(options, comparableOptions);
361          })[0];
362          return potentialRootMatch[matchingStringifiedOptions];
363      };
364      /**
365       * recursive method to test primitive string, number, null, etc and complex
366       * object equality.
367       *
368       * @method areOptionsSame
369       * @param {any} a
370       * @param {any} b
371       * @private
372       * @return {boolean}
373       */
374      IntersectionObserverAdmin.prototype.areOptionsSame = function (a, b) {
375          if (a === b) {
376              return true;
377          }
378          // simple comparison
379          var type1 = Object.prototype.toString.call(a);
380          var type2 = Object.prototype.toString.call(b);
381          if (type1 !== type2) {
382              return false;
383          }
384          else if (type1 !== '[object Object]' && type2 !== '[object Object]') {
385              return a === b;
386          }
387          if (a && b && typeof a === 'object' && typeof b === 'object') {
388              // complex comparison for only type of [object Object]
389              for (var key in a) {
390                  if (Object.prototype.hasOwnProperty.call(a, key)) {
391                      // recursion to check nested
392                      if (this.areOptionsSame(a[key], b[key]) === false) {
393                          return false;
394                      }
395                  }
396              }
397          }
398          // if nothing failed
399          return true;
400      };
401      /**
402       * Stringify options for use as a key.
403       * Excludes options.root so that the resulting key is stable
404       *
405       * @param {Object} options
406       * @private
407       * @return {String}
408       */
409      IntersectionObserverAdmin.prototype.stringifyOptions = function (options) {
410          var root = options.root;
411          var replacer = function (key, value) {
412              if (key === 'root' && root) {
413                  var classList = Array.prototype.slice.call(root.classList);
414                  var classToken = classList.reduce(function (acc, item) {
415                      return (acc += item);
416                  }, '');
417                  var id = root.id;
418                  return "".concat(id, "-").concat(classToken);
419              }
420              return value;
421          };
422          return JSON.stringify(options, replacer);
423      };
424      return IntersectionObserverAdmin;
425  }(Notifications));
426  
427  export default IntersectionObserverAdmin;
428  //# sourceMappingURL=intersection-observer-admin.es5.js.map