mirror of
https://github.com/rxliuli/apps.apple.com.git
synced 2025-11-09 22:10:33 +00:00
429 lines
16 KiB
JavaScript
429 lines
16 KiB
JavaScript
var Registry = /** @class */ (function () {
|
|
function Registry() {
|
|
this.registry = new WeakMap();
|
|
}
|
|
Registry.prototype.elementExists = function (elem) {
|
|
return this.registry.has(elem);
|
|
};
|
|
Registry.prototype.getElement = function (elem) {
|
|
return this.registry.get(elem);
|
|
};
|
|
/**
|
|
* administrator for lookup in the future
|
|
*
|
|
* @method add
|
|
* @param {HTMLElement | Window} element - the item to add to root element registry
|
|
* @param {IOption} options
|
|
* @param {IOption.root} [root] - contains optional root e.g. window, container div, etc
|
|
* @param {IOption.watcher} [observer] - optional
|
|
* @public
|
|
*/
|
|
Registry.prototype.addElement = function (element, options) {
|
|
if (!element) {
|
|
return;
|
|
}
|
|
this.registry.set(element, options || {});
|
|
};
|
|
/**
|
|
* @method remove
|
|
* @param {HTMLElement|Window} target
|
|
* @public
|
|
*/
|
|
Registry.prototype.removeElement = function (target) {
|
|
this.registry.delete(target);
|
|
};
|
|
/**
|
|
* reset weak map
|
|
*
|
|
* @method destroy
|
|
* @public
|
|
*/
|
|
Registry.prototype.destroyRegistry = function () {
|
|
this.registry = new WeakMap();
|
|
};
|
|
return Registry;
|
|
}());
|
|
|
|
var noop = function () { };
|
|
var CallbackType;
|
|
(function (CallbackType) {
|
|
CallbackType["enter"] = "enter";
|
|
CallbackType["exit"] = "exit";
|
|
})(CallbackType || (CallbackType = {}));
|
|
var Notifications = /** @class */ (function () {
|
|
function Notifications() {
|
|
this.registry = new Registry();
|
|
}
|
|
/**
|
|
* Adds an EventListener as a callback for an event key.
|
|
* @param type 'enter' or 'exit'
|
|
* @param key The key of the event
|
|
* @param callback The callback function to invoke when the event occurs
|
|
*/
|
|
Notifications.prototype.addCallback = function (type, element, callback) {
|
|
var _a, _b;
|
|
var entry;
|
|
if (type === CallbackType.enter) {
|
|
entry = (_a = {}, _a[CallbackType.enter] = callback, _a);
|
|
}
|
|
else {
|
|
entry = (_b = {}, _b[CallbackType.exit] = callback, _b);
|
|
}
|
|
this.registry.addElement(element, Object.assign({}, this.registry.getElement(element), entry));
|
|
};
|
|
/**
|
|
* @hidden
|
|
* Executes registered callbacks for key.
|
|
* @param type
|
|
* @param element
|
|
* @param data
|
|
*/
|
|
Notifications.prototype.dispatchCallback = function (type, element, data) {
|
|
if (type === CallbackType.enter) {
|
|
var _a = this.registry.getElement(element).enter, enter = _a === void 0 ? noop : _a;
|
|
enter(data);
|
|
}
|
|
else {
|
|
// no element in WeakMap possible because element may be removed from DOM by the time we get here
|
|
var found = this.registry.getElement(element);
|
|
if (found && found.exit) {
|
|
found.exit(data);
|
|
}
|
|
}
|
|
};
|
|
return Notifications;
|
|
}());
|
|
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
var __assign = (undefined && undefined.__assign) || function () {
|
|
__assign = Object.assign || function(t) {
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
s = arguments[i];
|
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
t[p] = s[p];
|
|
}
|
|
return t;
|
|
};
|
|
return __assign.apply(this, arguments);
|
|
};
|
|
var IntersectionObserverAdmin = /** @class */ (function (_super) {
|
|
__extends(IntersectionObserverAdmin, _super);
|
|
function IntersectionObserverAdmin() {
|
|
var _this = _super.call(this) || this;
|
|
_this.elementRegistry = new Registry();
|
|
return _this;
|
|
}
|
|
/**
|
|
* Adds element to observe via IntersectionObserver and stores element + relevant callbacks and observer options in static
|
|
* administrator for lookup in the future
|
|
*
|
|
* @method observe
|
|
* @param {HTMLElement | Window} element
|
|
* @param {Object} options
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.observe = function (element, options) {
|
|
if (options === void 0) { options = {}; }
|
|
if (!element) {
|
|
return;
|
|
}
|
|
this.elementRegistry.addElement(element, __assign({}, options));
|
|
this.setupObserver(element, __assign({}, options));
|
|
};
|
|
/**
|
|
* Unobserve target element and remove element from static admin
|
|
*
|
|
* @method unobserve
|
|
* @param {HTMLElement|Window} target
|
|
* @param {Object} options
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.unobserve = function (target, options) {
|
|
var matchingRootEntry = this.findMatchingRootEntry(options);
|
|
if (matchingRootEntry) {
|
|
var intersectionObserver = matchingRootEntry.intersectionObserver;
|
|
intersectionObserver.unobserve(target);
|
|
}
|
|
};
|
|
/**
|
|
* register event to handle when intersection observer detects enter
|
|
*
|
|
* @method addEnterCallback
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.addEnterCallback = function (element, callback) {
|
|
this.addCallback(CallbackType.enter, element, callback);
|
|
};
|
|
/**
|
|
* register event to handle when intersection observer detects exit
|
|
*
|
|
* @method addExitCallback
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.addExitCallback = function (element, callback) {
|
|
this.addCallback(CallbackType.exit, element, callback);
|
|
};
|
|
/**
|
|
* retrieve registered callback and call with data
|
|
*
|
|
* @method dispatchEnterCallback
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.dispatchEnterCallback = function (element, entry) {
|
|
this.dispatchCallback(CallbackType.enter, element, entry);
|
|
};
|
|
/**
|
|
* retrieve registered callback and call with data on exit
|
|
*
|
|
* @method dispatchExitCallback
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.dispatchExitCallback = function (element, entry) {
|
|
this.dispatchCallback(CallbackType.exit, element, entry);
|
|
};
|
|
/**
|
|
* cleanup data structures and unobserve elements
|
|
*
|
|
* @method destroy
|
|
* @public
|
|
*/
|
|
IntersectionObserverAdmin.prototype.destroy = function () {
|
|
this.elementRegistry.destroyRegistry();
|
|
};
|
|
/**
|
|
* use function composition to curry options
|
|
*
|
|
* @method setupOnIntersection
|
|
* @param {Object} options
|
|
*/
|
|
IntersectionObserverAdmin.prototype.setupOnIntersection = function (options) {
|
|
var _this = this;
|
|
return function (ioEntries) {
|
|
return _this.onIntersection(options, ioEntries);
|
|
};
|
|
};
|
|
IntersectionObserverAdmin.prototype.setupObserver = function (element, options) {
|
|
var _a;
|
|
var _b = options.root, root = _b === void 0 ? window : _b;
|
|
// First - find shared root element (window or target HTMLElement)
|
|
// this root is responsible for coordinating it's set of elements
|
|
var potentialRootMatch = this.findRootFromRegistry(root);
|
|
// Second - if there is a matching root, see if an existing entry with the same options
|
|
// regardless of sort order. This is a bit of work
|
|
var matchingEntryForRoot;
|
|
if (potentialRootMatch) {
|
|
matchingEntryForRoot = this.determineMatchingElements(options, potentialRootMatch);
|
|
}
|
|
// next add found entry to elements and call observer if applicable
|
|
if (matchingEntryForRoot) {
|
|
var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver;
|
|
elements.push(element);
|
|
if (intersectionObserver) {
|
|
intersectionObserver.observe(element);
|
|
}
|
|
}
|
|
else {
|
|
// otherwise start observing this element if applicable
|
|
// watcher is an instance that has an observe method
|
|
var intersectionObserver = this.newObserver(element, options);
|
|
var observerEntry = {
|
|
elements: [element],
|
|
intersectionObserver: intersectionObserver,
|
|
options: options
|
|
};
|
|
// and add entry to WeakMap under a root element
|
|
// with watcher so we can use it later on
|
|
var stringifiedOptions = this.stringifyOptions(options);
|
|
if (potentialRootMatch) {
|
|
// if share same root and need to add new entry to root match
|
|
// not functional but :shrug
|
|
potentialRootMatch[stringifiedOptions] = observerEntry;
|
|
}
|
|
else {
|
|
// no root exists, so add to WeakMap
|
|
this.elementRegistry.addElement(root, (_a = {},
|
|
_a[stringifiedOptions] = observerEntry,
|
|
_a));
|
|
}
|
|
}
|
|
};
|
|
IntersectionObserverAdmin.prototype.newObserver = function (element, options) {
|
|
// No matching entry for root in static admin, thus create new IntersectionObserver instance
|
|
var root = options.root, rootMargin = options.rootMargin, threshold = options.threshold;
|
|
var newIO = new IntersectionObserver(this.setupOnIntersection(options).bind(this), { root: root, rootMargin: rootMargin, threshold: threshold });
|
|
newIO.observe(element);
|
|
return newIO;
|
|
};
|
|
/**
|
|
* IntersectionObserver callback when element is intersecting viewport
|
|
* either when `isIntersecting` changes or `intersectionRadio` crosses on of the
|
|
* configured `threshold`s.
|
|
* Exit callback occurs eagerly (when element is initially out of scope)
|
|
* See https://stackoverflow.com/questions/53214116/intersectionobserver-callback-firing-immediately-on-page-load/53385264#53385264
|
|
*
|
|
* @method onIntersection
|
|
* @param {Object} options
|
|
* @param {Array} ioEntries
|
|
* @private
|
|
*/
|
|
IntersectionObserverAdmin.prototype.onIntersection = function (options, ioEntries) {
|
|
var _this = this;
|
|
ioEntries.forEach(function (entry) {
|
|
var isIntersecting = entry.isIntersecting, intersectionRatio = entry.intersectionRatio;
|
|
var threshold = options.threshold || 0;
|
|
if (Array.isArray(threshold)) {
|
|
threshold = threshold[threshold.length - 1];
|
|
}
|
|
// then find entry's callback in static administration
|
|
var matchingRootEntry = _this.findMatchingRootEntry(options);
|
|
// first determine if entry intersecting
|
|
if (isIntersecting || intersectionRatio > threshold) {
|
|
if (matchingRootEntry) {
|
|
matchingRootEntry.elements.some(function (element) {
|
|
if (element && element === entry.target) {
|
|
_this.dispatchEnterCallback(element, entry);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
}
|
|
else {
|
|
if (matchingRootEntry) {
|
|
matchingRootEntry.elements.some(function (element) {
|
|
if (element && element === entry.target) {
|
|
_this.dispatchExitCallback(element, entry);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
}
|
|
});
|
|
};
|
|
/**
|
|
* { root: { stringifiedOptions: { observer, elements: []...] } }
|
|
* @method findRootFromRegistry
|
|
* @param {HTMLElement|Window} root
|
|
* @private
|
|
* @return {Object} of elements that share same root
|
|
*/
|
|
IntersectionObserverAdmin.prototype.findRootFromRegistry = function (root) {
|
|
if (this.elementRegistry) {
|
|
return this.elementRegistry.getElement(root);
|
|
}
|
|
};
|
|
/**
|
|
* We don't care about options key order because we already added
|
|
* to the static administrator
|
|
*
|
|
* @method findMatchingRootEntry
|
|
* @param {Object} options
|
|
* @return {Object} entry with elements and other options
|
|
*/
|
|
IntersectionObserverAdmin.prototype.findMatchingRootEntry = function (options) {
|
|
var _a = options.root, root = _a === void 0 ? window : _a;
|
|
var matchingRoot = this.findRootFromRegistry(root);
|
|
if (matchingRoot) {
|
|
var stringifiedOptions = this.stringifyOptions(options);
|
|
return matchingRoot[stringifiedOptions];
|
|
}
|
|
};
|
|
/**
|
|
* Determine if existing elements for a given root based on passed in options
|
|
* regardless of sort order of keys
|
|
*
|
|
* @method determineMatchingElements
|
|
* @param {Object} options
|
|
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}
|
|
* @private
|
|
* @return {Object} containing array of elements and other meta
|
|
*/
|
|
IntersectionObserverAdmin.prototype.determineMatchingElements = function (options, potentialRootMatch) {
|
|
var _this = this;
|
|
var matchingStringifiedOptions = Object.keys(potentialRootMatch).filter(function (key) {
|
|
var comparableOptions = potentialRootMatch[key].options;
|
|
return _this.areOptionsSame(options, comparableOptions);
|
|
})[0];
|
|
return potentialRootMatch[matchingStringifiedOptions];
|
|
};
|
|
/**
|
|
* recursive method to test primitive string, number, null, etc and complex
|
|
* object equality.
|
|
*
|
|
* @method areOptionsSame
|
|
* @param {any} a
|
|
* @param {any} b
|
|
* @private
|
|
* @return {boolean}
|
|
*/
|
|
IntersectionObserverAdmin.prototype.areOptionsSame = function (a, b) {
|
|
if (a === b) {
|
|
return true;
|
|
}
|
|
// simple comparison
|
|
var type1 = Object.prototype.toString.call(a);
|
|
var type2 = Object.prototype.toString.call(b);
|
|
if (type1 !== type2) {
|
|
return false;
|
|
}
|
|
else if (type1 !== '[object Object]' && type2 !== '[object Object]') {
|
|
return a === b;
|
|
}
|
|
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
// complex comparison for only type of [object Object]
|
|
for (var key in a) {
|
|
if (Object.prototype.hasOwnProperty.call(a, key)) {
|
|
// recursion to check nested
|
|
if (this.areOptionsSame(a[key], b[key]) === false) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// if nothing failed
|
|
return true;
|
|
};
|
|
/**
|
|
* Stringify options for use as a key.
|
|
* Excludes options.root so that the resulting key is stable
|
|
*
|
|
* @param {Object} options
|
|
* @private
|
|
* @return {String}
|
|
*/
|
|
IntersectionObserverAdmin.prototype.stringifyOptions = function (options) {
|
|
var root = options.root;
|
|
var replacer = function (key, value) {
|
|
if (key === 'root' && root) {
|
|
var classList = Array.prototype.slice.call(root.classList);
|
|
var classToken = classList.reduce(function (acc, item) {
|
|
return (acc += item);
|
|
}, '');
|
|
var id = root.id;
|
|
return "".concat(id, "-").concat(classToken);
|
|
}
|
|
return value;
|
|
};
|
|
return JSON.stringify(options, replacer);
|
|
};
|
|
return IntersectionObserverAdmin;
|
|
}(Notifications));
|
|
|
|
export default IntersectionObserverAdmin;
|
|
//# sourceMappingURL=intersection-observer-admin.es5.js.map
|