init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

View File

@@ -0,0 +1,428 @@
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