Socket
Socket
Sign inDemoInstall

intersection-observer-admin

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

intersection-observer-admin - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

.rpt2_cache/rpt2_1d672005ca5ba2a28974f5e8eda630de9cc288a9/types/cache/4bee298fa984846190f67909289645f71ebadb8d

279

dist/es/index.js

@@ -1,4 +0,22 @@

var IntersectionObserverAdmin = /** @class */ (function () {
var __extends = (this && this.__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 (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import Notifications, { CallbackType } from './notification';
import Registry from './registry';
var IntersectionObserverAdmin = /** @class */ (function (_super) {
__extends(IntersectionObserverAdmin, _super);
function IntersectionObserverAdmin() {
this.DOMRef = new WeakMap();
var _this = _super.call(this) || this;
_this.elementRegistry = new Registry();
return _this;
}

@@ -11,46 +29,12 @@ /**

* @param {HTMLElement | Window} element
* @param {Function} enterCallback
* @param {Function} exitCallback
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
IntersectionObserverAdmin.prototype.observe = function (element, enterCallback, exitCallback, observerOptions, scrollableArea) {
var _a;
if (!element || !observerOptions) {
IntersectionObserverAdmin.prototype.observe = function (element, options) {
if (options === void 0) { options = {}; }
if (!element) {
return;
}
var _b = observerOptions.root, root = _b === void 0 ? window : _b;
// first find shared root element (window or scrollable area)
var potentialRootMatch = this._findRoot(root);
// second if there is a matching root, find an entry with the same observerOptions
var matchingEntryForRoot;
if (potentialRootMatch) {
matchingEntryForRoot = this._determineMatchingElements(observerOptions, potentialRootMatch);
}
if (matchingEntryForRoot) {
var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver;
elements.push({ element: element, enterCallback: enterCallback, exitCallback: exitCallback });
intersectionObserver.observe(element);
return;
}
// No matching entry for root in static admin, thus create new IntersectionObserver instance
var newIO = new IntersectionObserver(this._setupOnIntersection(observerOptions, scrollableArea).bind(this), observerOptions);
newIO.observe(element);
var observerEntry = {
elements: [{ element: element, enterCallback: enterCallback, exitCallback: exitCallback }],
intersectionObserver: newIO,
observerOptions: observerOptions
};
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea);
if (potentialRootMatch) {
// if share same root and need to add new entry to root match
potentialRootMatch[stringifiedOptions] = observerEntry;
}
else {
// no root exists, so add to WeakMap
if (this.DOMRef) {
this.DOMRef.set(root, (_a = {}, _a[stringifiedOptions] = observerEntry, _a));
}
}
this.elementRegistry.addElement(element, options);
this.setupObserver(element, options);
};

@@ -62,21 +46,49 @@ /**

* @param {HTMLElement|Window} target
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
IntersectionObserverAdmin.prototype.unobserve = function (target, observerOptions, scrollableArea) {
var matchingRootEntry = this._findMatchingRootEntry(observerOptions, scrollableArea);
IntersectionObserverAdmin.prototype.unobserve = function (target, options) {
var matchingRootEntry = this._findMatchingRootEntry(options);
if (matchingRootEntry) {
var intersectionObserver = matchingRootEntry.intersectionObserver, elements = matchingRootEntry.elements;
var intersectionObserver = matchingRootEntry.intersectionObserver;
intersectionObserver.unobserve(target);
// important to do this in reverse order
for (var i = elements.length - 1; i >= 0; i--) {
if (elements[i] && elements[i].element === target) {
elements.splice(i, 1);
break;
}
}
}
};
/**
* 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) {
this.dispatchCallback(CallbackType.enter, element);
};
/**
* retrieve registered callback and call with data on exit
*
* @method dispatchExitCallback
* @public
*/
IntersectionObserverAdmin.prototype.dispatchExitCallback = function (element) {
this.dispatchCallback(CallbackType.exit, element);
};
/**
* cleanup data structures and unobserve elements

@@ -88,27 +100,77 @@ *

IntersectionObserverAdmin.prototype.destroy = function () {
this.DOMRef = null;
this.elementRegistry.destroyRegistry();
};
/**
* use function composition to curry observerOptions
* use function composition to curry options
*
* @method _setupOnIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method setupOnIntersection
* @param {Object} options
*/
IntersectionObserverAdmin.prototype._setupOnIntersection = function (observerOptions, scrollableArea) {
IntersectionObserverAdmin.prototype.setupOnIntersection = function (options) {
var _this = this;
return function (entries) {
return _this._onIntersection(observerOptions, scrollableArea, entries);
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;
// find shared root element (window or scrollable area)
// this root is responsible for coordinating it's set of elements
var potentialRootMatch = this._findRoot(root);
// third 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
*
* @method _onIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method onIntersection
* @param {Object} options
* @param {Array} ioEntries
* @private
*/
IntersectionObserverAdmin.prototype._onIntersection = function (observerOptions, scrollableArea, ioEntries) {
IntersectionObserverAdmin.prototype.onIntersection = function (options, ioEntries) {
var _this = this;

@@ -120,10 +182,7 @@ ioEntries.forEach(function (entry) {

// then find entry's callback in static administration
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea);
var matchingRootEntry = _this._findMatchingRootEntry(options);
if (matchingRootEntry) {
matchingRootEntry.elements.some(function (obj) {
if (obj.element === entry.target) {
// call entry's enter callback
if (obj.enterCallback) {
obj.enterCallback();
}
matchingRootEntry.elements.some(function (element) {
if (element && element === entry.target) {
_this.dispatchEnterCallback(element);
return true;

@@ -137,10 +196,7 @@ }

// then find entry's callback in static administration
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea);
var matchingRootEntry = _this._findMatchingRootEntry(options);
if (matchingRootEntry) {
matchingRootEntry.elements.some(function (obj) {
if (obj.element === entry.target) {
// call entry's enter callback
if (obj.exitCallback) {
obj.exitCallback();
}
matchingRootEntry.elements.some(function (element) {
if (element && element === entry.target) {
_this.dispatchExitCallback(element);
return true;

@@ -155,3 +211,3 @@ }

/**
* { root: { stringifiedOptions: { elements: []...] } }
* { root: { stringifiedOptions: { observer, elements: []...] } }
* @method _findRoot

@@ -163,22 +219,19 @@ * @param {HTMLElement|Window} root

IntersectionObserverAdmin.prototype._findRoot = function (root) {
if (this.DOMRef) {
return this.DOMRef.get(root);
if (this.elementRegistry) {
return this.elementRegistry.getElement(root);
}
};
/**
* Used for onIntersection callbacks and unobserving the IntersectionObserver
* We don't care about observerOptions key order because we already added
* to the static administrator or found an existing IntersectionObserver with the same
* root && observerOptions to reuse
* We don't care about options key order because we already added
* to the static administrator
*
* @method _findMatchingRootEntry
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @return {Object} entry with elements and other options
*/
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (observerOptions, scrollableArea) {
var _a = observerOptions.root, root = _a === void 0 ? window : _a;
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (options) {
var _a = options.root, root = _a === void 0 ? window : _a;
var matchingRoot = this._findRoot(root);
if (matchingRoot) {
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea);
var stringifiedOptions = this._stringifyOptions(options);
return matchingRoot[stringifiedOptions];

@@ -188,7 +241,7 @@ }

/**
* Determine if existing elements for a given root based on passed in observerOptions
* Determine if existing elements for a given root based on passed in options
* regardless of sort order of keys
*
* @method _determineMatchingElements
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}

@@ -198,10 +251,7 @@ * @private

*/
IntersectionObserverAdmin.prototype._determineMatchingElements = function (observerOptions, potentialRootMatch) {
IntersectionObserverAdmin.prototype._determineMatchingElements = function (options, potentialRootMatch) {
var _this = this;
if (!potentialRootMatch) {
return;
}
var matchingKey = Object.keys(potentialRootMatch).filter(function (key) {
var comparableOptions = potentialRootMatch[key].observerOptions;
return _this._areOptionsSame(observerOptions, comparableOptions);
var comparableOptions = potentialRootMatch[key].options;
return _this._areOptionsSame(options, comparableOptions);
})[0];

@@ -215,3 +265,3 @@ return potentialRootMatch[matchingKey];

* @method _areOptionsSame
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} comparableOptions

@@ -221,5 +271,5 @@ * @private

*/
IntersectionObserverAdmin.prototype._areOptionsSame = function (observerOptions, comparableOptions) {
IntersectionObserverAdmin.prototype._areOptionsSame = function (options, comparableOptions) {
// simple comparison of string, number or even null/undefined
var type1 = Object.prototype.toString.call(observerOptions);
var type1 = Object.prototype.toString.call(options);
var type2 = Object.prototype.toString.call(comparableOptions);

@@ -230,10 +280,9 @@ if (type1 !== type2) {

else if (type1 !== '[object Object]' && type2 !== '[object Object]') {
return observerOptions === comparableOptions;
return options === comparableOptions;
}
// complex comparison for only type of [object Object]
for (var key in observerOptions) {
if (observerOptions.hasOwnProperty(key)) {
for (var key in options) {
if (options.hasOwnProperty(key)) {
// recursion to check nested
if (this._areOptionsSame(observerOptions[key], comparableOptions[key]) ===
false) {
if (this._areOptionsSame(options[key], comparableOptions[key]) === false) {
return false;

@@ -246,11 +295,11 @@ }

/**
* Stringify observerOptions for use as a key.
* Excludes observerOptions.root so that the resulting key is stable
* Stringify options for use as a key.
* Excludes options.root so that the resulting key is stable
*
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @private
* @return {String}
*/
IntersectionObserverAdmin.prototype._stringifyObserverOptions = function (observerOptions, scrollableArea) {
IntersectionObserverAdmin.prototype._stringifyOptions = function (options) {
var scrollableArea = options.scrollableArea;
var replacer = function (key, value) {

@@ -262,7 +311,7 @@ if (key === 'root' && scrollableArea) {

};
return JSON.stringify(observerOptions, replacer);
return JSON.stringify(options, replacer);
};
return IntersectionObserverAdmin;
}());
}(Notifications));
export default IntersectionObserverAdmin;
//# sourceMappingURL=index.js.map

@@ -1,22 +0,11 @@

declare type IndividualEntry = {
element?: HTMLElement | Window;
enterCallback?: Function;
exitCallback?: Function;
};
export interface IObserverOption {
import Notifications from './notification';
export interface IOptions {
root?: HTMLElement;
rootMargin?: string;
threshold?: number;
scrollableArea?: string;
[key: string]: any;
}
declare type RootEntry = {
elements: [IndividualEntry];
observerOptions: IObserverOption;
intersectionObserver: any;
};
declare type PotentialRootEntry = {
[stringifiedOptions: string]: RootEntry;
};
export default class IntersectionObserverAdmin {
private DOMRef;
export default class IntersectionObserverAdmin extends Notifications {
private elementRegistry;
constructor();

@@ -29,9 +18,6 @@ /**

* @param {HTMLElement | Window} element
* @param {Function} enterCallback
* @param {Function} exitCallback
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
observe(element: HTMLElement, enterCallback: Function, exitCallback: Function, observerOptions?: IObserverOption, scrollableArea?: string): void;
observe(element: HTMLElement, options?: IOptions): void;
/**

@@ -42,8 +28,35 @@ * Unobserve target element and remove element from static admin

* @param {HTMLElement|Window} target
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
unobserve(target: HTMLElement, observerOptions: IObserverOption, scrollableArea: string): void;
unobserve(target: HTMLElement, options: IOptions): void;
/**
* register event to handle when intersection observer detects enter
*
* @method addEnterCallback
* @public
*/
addEnterCallback(element: HTMLElement | Window, callback: (data?: any) => void): void;
/**
* register event to handle when intersection observer detects exit
*
* @method addExitCallback
* @public
*/
addExitCallback(element: HTMLElement | Window, callback: (data?: any) => void): void;
/**
* retrieve registered callback and call with data
*
* @method dispatchEnterCallback
* @public
*/
dispatchEnterCallback(element: HTMLElement | Window): void;
/**
* retrieve registered callback and call with data on exit
*
* @method dispatchExitCallback
* @public
*/
dispatchExitCallback(element: HTMLElement | Window): void;
/**
* cleanup data structures and unobserve elements

@@ -56,21 +69,21 @@ *

/**
* use function composition to curry observerOptions
* use function composition to curry options
*
* @method _setupOnIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method setupOnIntersection
* @param {Object} options
*/
protected _setupOnIntersection(observerOptions: IObserverOption, scrollableArea: string | undefined): Function;
protected setupOnIntersection(options: IOptions): Function;
protected setupObserver(element: HTMLElement, options: IOptions): void;
private newObserver;
/**
* IntersectionObserver callback when element is intersecting viewport
*
* @method _onIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method onIntersection
* @param {Object} options
* @param {Array} ioEntries
* @private
*/
protected _onIntersection(observerOptions: IObserverOption, scrollableArea: string | undefined, ioEntries: Array<any>): void;
private onIntersection;
/**
* { root: { stringifiedOptions: { elements: []...] } }
* { root: { stringifiedOptions: { observer, elements: []...] } }
* @method _findRoot

@@ -81,21 +94,18 @@ * @param {HTMLElement|Window} root

*/
protected _findRoot(root: HTMLElement | Window): PotentialRootEntry | null | undefined;
private _findRoot;
/**
* Used for onIntersection callbacks and unobserving the IntersectionObserver
* We don't care about observerOptions key order because we already added
* to the static administrator or found an existing IntersectionObserver with the same
* root && observerOptions to reuse
* We don't care about options key order because we already added
* to the static administrator
*
* @method _findMatchingRootEntry
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @return {Object} entry with elements and other options
*/
protected _findMatchingRootEntry(observerOptions: IObserverOption, scrollableArea: string | undefined): RootEntry | undefined;
private _findMatchingRootEntry;
/**
* Determine if existing elements for a given root based on passed in observerOptions
* Determine if existing elements for a given root based on passed in options
* regardless of sort order of keys
*
* @method _determineMatchingElements
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}

@@ -105,3 +115,3 @@ * @private

*/
protected _determineMatchingElements(observerOptions: IObserverOption, potentialRootMatch?: PotentialRootEntry): RootEntry | undefined;
private _determineMatchingElements;
/**

@@ -112,3 +122,3 @@ * recursive method to test primitive string, number, null, etc and complex

* @method _areOptionsSame
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} comparableOptions

@@ -118,14 +128,12 @@ * @private

*/
protected _areOptionsSame(observerOptions: IObserverOption, comparableOptions: IObserverOption): boolean;
private _areOptionsSame;
/**
* Stringify observerOptions for use as a key.
* Excludes observerOptions.root so that the resulting key is stable
* Stringify options for use as a key.
* Excludes options.root so that the resulting key is stable
*
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @private
* @return {String}
*/
protected _stringifyObserverOptions(observerOptions: IObserverOption, scrollableArea: string | undefined): string;
private _stringifyOptions;
}
export {};

@@ -1,57 +0,132 @@

var IntersectionObserverAdmin = /** @class */ (function () {
function IntersectionObserverAdmin() {
this.DOMRef = new WeakMap();
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);
};
/**
* Adds element to observe via IntersectionObserver and stores element + relevant callbacks and observer options in static
* administrator for lookup in the future
*
* @method add
* @param {HTMLElement | Window} element
* @param {Function} enterCallback
* @param {Function} exitCallback
* @param {Object} observerOptions
* @param {String} scrollableArea
* @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
*/
IntersectionObserverAdmin.prototype.observe = function (element, enterCallback, exitCallback, observerOptions, scrollableArea) {
var _a;
if (!element || !observerOptions) {
Registry.prototype.addElement = function (element, options) {
if (!element) {
return;
}
var _b = observerOptions.root, root = _b === void 0 ? window : _b;
// first find shared root element (window or scrollable area)
var potentialRootMatch = this._findRoot(root);
// second if there is a matching root, find an entry with the same observerOptions
var matchingEntryForRoot;
if (potentialRootMatch) {
matchingEntryForRoot = this._determineMatchingElements(observerOptions, potentialRootMatch);
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);
}
if (matchingEntryForRoot) {
var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver;
elements.push({ element: element, enterCallback: enterCallback, exitCallback: exitCallback });
intersectionObserver.observe(element);
return;
else {
entry = (_b = {}, _b[CallbackType.exit] = callback, _b);
}
// No matching entry for root in static admin, thus create new IntersectionObserver instance
var newIO = new IntersectionObserver(this._setupOnIntersection(observerOptions, scrollableArea).bind(this), observerOptions);
newIO.observe(element);
var observerEntry = {
elements: [{ element: element, enterCallback: enterCallback, exitCallback: exitCallback }],
intersectionObserver: newIO,
observerOptions: observerOptions
};
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea);
if (potentialRootMatch) {
// if share same root and need to add new entry to root match
potentialRootMatch[stringifiedOptions] = observerEntry;
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 root exists, so add to WeakMap
if (this.DOMRef) {
this.DOMRef.set(root, (_a = {}, _a[stringifiedOptions] = observerEntry, _a));
}
var _b = this.registry.getElement(element).exit, exit = _b === void 0 ? noop : _b;
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 (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
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 add
* @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, options);
this.setupObserver(element, options);
};
/**
* Unobserve target element and remove element from static admin

@@ -61,21 +136,49 @@ *

* @param {HTMLElement|Window} target
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
IntersectionObserverAdmin.prototype.unobserve = function (target, observerOptions, scrollableArea) {
var matchingRootEntry = this._findMatchingRootEntry(observerOptions, scrollableArea);
IntersectionObserverAdmin.prototype.unobserve = function (target, options) {
var matchingRootEntry = this._findMatchingRootEntry(options);
if (matchingRootEntry) {
var intersectionObserver = matchingRootEntry.intersectionObserver, elements = matchingRootEntry.elements;
var intersectionObserver = matchingRootEntry.intersectionObserver;
intersectionObserver.unobserve(target);
// important to do this in reverse order
for (var i = elements.length - 1; i >= 0; i--) {
if (elements[i] && elements[i].element === target) {
elements.splice(i, 1);
break;
}
}
}
};
/**
* 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) {
this.dispatchCallback(CallbackType.enter, element);
};
/**
* retrieve registered callback and call with data on exit
*
* @method dispatchExitCallback
* @public
*/
IntersectionObserverAdmin.prototype.dispatchExitCallback = function (element) {
this.dispatchCallback(CallbackType.exit, element);
};
/**
* cleanup data structures and unobserve elements

@@ -87,27 +190,77 @@ *

IntersectionObserverAdmin.prototype.destroy = function () {
this.DOMRef = null;
this.elementRegistry.destroyRegistry();
};
/**
* use function composition to curry observerOptions
* use function composition to curry options
*
* @method _setupOnIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method setupOnIntersection
* @param {Object} options
*/
IntersectionObserverAdmin.prototype._setupOnIntersection = function (observerOptions, scrollableArea) {
IntersectionObserverAdmin.prototype.setupOnIntersection = function (options) {
var _this = this;
return function (entries) {
return _this._onIntersection(observerOptions, scrollableArea, entries);
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;
// find shared root element (window or scrollable area)
// this root is responsible for coordinating it's set of elements
var potentialRootMatch = this._findRoot(root);
// third 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
*
* @method _onIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method onIntersection
* @param {Object} options
* @param {Array} ioEntries
* @private
*/
IntersectionObserverAdmin.prototype._onIntersection = function (observerOptions, scrollableArea, ioEntries) {
IntersectionObserverAdmin.prototype.onIntersection = function (options, ioEntries) {
var _this = this;

@@ -119,10 +272,7 @@ ioEntries.forEach(function (entry) {

// then find entry's callback in static administration
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea);
var matchingRootEntry = _this._findMatchingRootEntry(options);
if (matchingRootEntry) {
matchingRootEntry.elements.some(function (obj) {
if (obj.element === entry.target) {
// call entry's enter callback
if (obj.enterCallback) {
obj.enterCallback();
}
matchingRootEntry.elements.some(function (element) {
if (element && element === entry.target) {
_this.dispatchEnterCallback(element);
return true;

@@ -136,10 +286,7 @@ }

// then find entry's callback in static administration
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea);
var matchingRootEntry = _this._findMatchingRootEntry(options);
if (matchingRootEntry) {
matchingRootEntry.elements.some(function (obj) {
if (obj.element === entry.target) {
// call entry's enter callback
if (obj.exitCallback) {
obj.exitCallback();
}
matchingRootEntry.elements.some(function (element) {
if (element && element === entry.target) {
_this.dispatchExitCallback(element);
return true;

@@ -154,3 +301,3 @@ }

/**
* { root: { stringifiedOptions: { elements: []...] } }
* { root: { stringifiedOptions: { observer, elements: []...] } }
* @method _findRoot

@@ -162,22 +309,19 @@ * @param {HTMLElement|Window} root

IntersectionObserverAdmin.prototype._findRoot = function (root) {
if (this.DOMRef) {
return this.DOMRef.get(root);
if (this.elementRegistry) {
return this.elementRegistry.getElement(root);
}
};
/**
* Used for onIntersection callbacks and unobserving the IntersectionObserver
* We don't care about observerOptions key order because we already added
* to the static administrator or found an existing IntersectionObserver with the same
* root && observerOptions to reuse
* We don't care about options key order because we already added
* to the static administrator
*
* @method _findMatchingRootEntry
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @return {Object} entry with elements and other options
*/
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (observerOptions, scrollableArea) {
var _a = observerOptions.root, root = _a === void 0 ? window : _a;
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (options) {
var _a = options.root, root = _a === void 0 ? window : _a;
var matchingRoot = this._findRoot(root);
if (matchingRoot) {
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea);
var stringifiedOptions = this._stringifyOptions(options);
return matchingRoot[stringifiedOptions];

@@ -187,7 +331,7 @@ }

/**
* Determine if existing elements for a given root based on passed in observerOptions
* Determine if existing elements for a given root based on passed in options
* regardless of sort order of keys
*
* @method _determineMatchingElements
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}

@@ -197,10 +341,7 @@ * @private

*/
IntersectionObserverAdmin.prototype._determineMatchingElements = function (observerOptions, potentialRootMatch) {
IntersectionObserverAdmin.prototype._determineMatchingElements = function (options, potentialRootMatch) {
var _this = this;
if (!potentialRootMatch) {
return;
}
var matchingKey = Object.keys(potentialRootMatch).filter(function (key) {
var comparableOptions = potentialRootMatch[key].observerOptions;
return _this._areOptionsSame(observerOptions, comparableOptions);
var comparableOptions = potentialRootMatch[key].options;
return _this._areOptionsSame(options, comparableOptions);
})[0];

@@ -214,3 +355,3 @@ return potentialRootMatch[matchingKey];

* @method _areOptionsSame
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} comparableOptions

@@ -220,5 +361,5 @@ * @private

*/
IntersectionObserverAdmin.prototype._areOptionsSame = function (observerOptions, comparableOptions) {
IntersectionObserverAdmin.prototype._areOptionsSame = function (options, comparableOptions) {
// simple comparison of string, number or even null/undefined
var type1 = Object.prototype.toString.call(observerOptions);
var type1 = Object.prototype.toString.call(options);
var type2 = Object.prototype.toString.call(comparableOptions);

@@ -229,10 +370,9 @@ if (type1 !== type2) {

else if (type1 !== '[object Object]' && type2 !== '[object Object]') {
return observerOptions === comparableOptions;
return options === comparableOptions;
}
// complex comparison for only type of [object Object]
for (var key in observerOptions) {
if (observerOptions.hasOwnProperty(key)) {
for (var key in options) {
if (options.hasOwnProperty(key)) {
// recursion to check nested
if (this._areOptionsSame(observerOptions[key], comparableOptions[key]) ===
false) {
if (this._areOptionsSame(options[key], comparableOptions[key]) === false) {
return false;

@@ -245,11 +385,11 @@ }

/**
* Stringify observerOptions for use as a key.
* Excludes observerOptions.root so that the resulting key is stable
* Stringify options for use as a key.
* Excludes options.root so that the resulting key is stable
*
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @private
* @return {String}
*/
IntersectionObserverAdmin.prototype._stringifyObserverOptions = function (observerOptions, scrollableArea) {
IntersectionObserverAdmin.prototype._stringifyOptions = function (options) {
var scrollableArea = options.scrollableArea;
var replacer = function (key, value) {

@@ -261,7 +401,7 @@ if (key === 'root' && scrollableArea) {

};
return JSON.stringify(observerOptions, replacer);
return JSON.stringify(options, replacer);
};
return IntersectionObserverAdmin;
}());
}(Notifications));
export default IntersectionObserverAdmin;

@@ -7,58 +7,133 @@ (function (global, factory) {

var IntersectionObserverAdmin = /** @class */ (function () {
function IntersectionObserverAdmin() {
this.DOMRef = new WeakMap();
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);
};
/**
* Adds element to observe via IntersectionObserver and stores element + relevant callbacks and observer options in static
* administrator for lookup in the future
*
* @method add
* @param {HTMLElement | Window} element
* @param {Function} enterCallback
* @param {Function} exitCallback
* @param {Object} observerOptions
* @param {String} scrollableArea
* @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
*/
IntersectionObserverAdmin.prototype.observe = function (element, enterCallback, exitCallback, observerOptions, scrollableArea) {
var _a;
if (!element || !observerOptions) {
Registry.prototype.addElement = function (element, options) {
if (!element) {
return;
}
var _b = observerOptions.root, root = _b === void 0 ? window : _b;
// first find shared root element (window or scrollable area)
var potentialRootMatch = this._findRoot(root);
// second if there is a matching root, find an entry with the same observerOptions
var matchingEntryForRoot;
if (potentialRootMatch) {
matchingEntryForRoot = this._determineMatchingElements(observerOptions, potentialRootMatch);
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);
}
if (matchingEntryForRoot) {
var elements = matchingEntryForRoot.elements, intersectionObserver = matchingEntryForRoot.intersectionObserver;
elements.push({ element: element, enterCallback: enterCallback, exitCallback: exitCallback });
intersectionObserver.observe(element);
return;
else {
entry = (_b = {}, _b[CallbackType.exit] = callback, _b);
}
// No matching entry for root in static admin, thus create new IntersectionObserver instance
var newIO = new IntersectionObserver(this._setupOnIntersection(observerOptions, scrollableArea).bind(this), observerOptions);
newIO.observe(element);
var observerEntry = {
elements: [{ element: element, enterCallback: enterCallback, exitCallback: exitCallback }],
intersectionObserver: newIO,
observerOptions: observerOptions
};
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea);
if (potentialRootMatch) {
// if share same root and need to add new entry to root match
potentialRootMatch[stringifiedOptions] = observerEntry;
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 root exists, so add to WeakMap
if (this.DOMRef) {
this.DOMRef.set(root, (_a = {}, _a[stringifiedOptions] = observerEntry, _a));
}
var _b = this.registry.getElement(element).exit, exit = _b === void 0 ? noop : _b;
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 (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
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 add
* @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, options);
this.setupObserver(element, options);
};
/**
* Unobserve target element and remove element from static admin

@@ -68,21 +143,49 @@ *

* @param {HTMLElement|Window} target
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
IntersectionObserverAdmin.prototype.unobserve = function (target, observerOptions, scrollableArea) {
var matchingRootEntry = this._findMatchingRootEntry(observerOptions, scrollableArea);
IntersectionObserverAdmin.prototype.unobserve = function (target, options) {
var matchingRootEntry = this._findMatchingRootEntry(options);
if (matchingRootEntry) {
var intersectionObserver = matchingRootEntry.intersectionObserver, elements = matchingRootEntry.elements;
var intersectionObserver = matchingRootEntry.intersectionObserver;
intersectionObserver.unobserve(target);
// important to do this in reverse order
for (var i = elements.length - 1; i >= 0; i--) {
if (elements[i] && elements[i].element === target) {
elements.splice(i, 1);
break;
}
}
}
};
/**
* 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) {
this.dispatchCallback(CallbackType.enter, element);
};
/**
* retrieve registered callback and call with data on exit
*
* @method dispatchExitCallback
* @public
*/
IntersectionObserverAdmin.prototype.dispatchExitCallback = function (element) {
this.dispatchCallback(CallbackType.exit, element);
};
/**
* cleanup data structures and unobserve elements

@@ -94,27 +197,77 @@ *

IntersectionObserverAdmin.prototype.destroy = function () {
this.DOMRef = null;
this.elementRegistry.destroyRegistry();
};
/**
* use function composition to curry observerOptions
* use function composition to curry options
*
* @method _setupOnIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method setupOnIntersection
* @param {Object} options
*/
IntersectionObserverAdmin.prototype._setupOnIntersection = function (observerOptions, scrollableArea) {
IntersectionObserverAdmin.prototype.setupOnIntersection = function (options) {
var _this = this;
return function (entries) {
return _this._onIntersection(observerOptions, scrollableArea, entries);
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;
// find shared root element (window or scrollable area)
// this root is responsible for coordinating it's set of elements
var potentialRootMatch = this._findRoot(root);
// third 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
*
* @method _onIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method onIntersection
* @param {Object} options
* @param {Array} ioEntries
* @private
*/
IntersectionObserverAdmin.prototype._onIntersection = function (observerOptions, scrollableArea, ioEntries) {
IntersectionObserverAdmin.prototype.onIntersection = function (options, ioEntries) {
var _this = this;

@@ -126,10 +279,7 @@ ioEntries.forEach(function (entry) {

// then find entry's callback in static administration
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea);
var matchingRootEntry = _this._findMatchingRootEntry(options);
if (matchingRootEntry) {
matchingRootEntry.elements.some(function (obj) {
if (obj.element === entry.target) {
// call entry's enter callback
if (obj.enterCallback) {
obj.enterCallback();
}
matchingRootEntry.elements.some(function (element) {
if (element && element === entry.target) {
_this.dispatchEnterCallback(element);
return true;

@@ -143,10 +293,7 @@ }

// then find entry's callback in static administration
var matchingRootEntry = _this._findMatchingRootEntry(observerOptions, scrollableArea);
var matchingRootEntry = _this._findMatchingRootEntry(options);
if (matchingRootEntry) {
matchingRootEntry.elements.some(function (obj) {
if (obj.element === entry.target) {
// call entry's enter callback
if (obj.exitCallback) {
obj.exitCallback();
}
matchingRootEntry.elements.some(function (element) {
if (element && element === entry.target) {
_this.dispatchExitCallback(element);
return true;

@@ -161,3 +308,3 @@ }

/**
* { root: { stringifiedOptions: { elements: []...] } }
* { root: { stringifiedOptions: { observer, elements: []...] } }
* @method _findRoot

@@ -169,22 +316,19 @@ * @param {HTMLElement|Window} root

IntersectionObserverAdmin.prototype._findRoot = function (root) {
if (this.DOMRef) {
return this.DOMRef.get(root);
if (this.elementRegistry) {
return this.elementRegistry.getElement(root);
}
};
/**
* Used for onIntersection callbacks and unobserving the IntersectionObserver
* We don't care about observerOptions key order because we already added
* to the static administrator or found an existing IntersectionObserver with the same
* root && observerOptions to reuse
* We don't care about options key order because we already added
* to the static administrator
*
* @method _findMatchingRootEntry
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @return {Object} entry with elements and other options
*/
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (observerOptions, scrollableArea) {
var _a = observerOptions.root, root = _a === void 0 ? window : _a;
IntersectionObserverAdmin.prototype._findMatchingRootEntry = function (options) {
var _a = options.root, root = _a === void 0 ? window : _a;
var matchingRoot = this._findRoot(root);
if (matchingRoot) {
var stringifiedOptions = this._stringifyObserverOptions(observerOptions, scrollableArea);
var stringifiedOptions = this._stringifyOptions(options);
return matchingRoot[stringifiedOptions];

@@ -194,7 +338,7 @@ }

/**
* Determine if existing elements for a given root based on passed in observerOptions
* Determine if existing elements for a given root based on passed in options
* regardless of sort order of keys
*
* @method _determineMatchingElements
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}

@@ -204,10 +348,7 @@ * @private

*/
IntersectionObserverAdmin.prototype._determineMatchingElements = function (observerOptions, potentialRootMatch) {
IntersectionObserverAdmin.prototype._determineMatchingElements = function (options, potentialRootMatch) {
var _this = this;
if (!potentialRootMatch) {
return;
}
var matchingKey = Object.keys(potentialRootMatch).filter(function (key) {
var comparableOptions = potentialRootMatch[key].observerOptions;
return _this._areOptionsSame(observerOptions, comparableOptions);
var comparableOptions = potentialRootMatch[key].options;
return _this._areOptionsSame(options, comparableOptions);
})[0];

@@ -221,3 +362,3 @@ return potentialRootMatch[matchingKey];

* @method _areOptionsSame
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} comparableOptions

@@ -227,5 +368,5 @@ * @private

*/
IntersectionObserverAdmin.prototype._areOptionsSame = function (observerOptions, comparableOptions) {
IntersectionObserverAdmin.prototype._areOptionsSame = function (options, comparableOptions) {
// simple comparison of string, number or even null/undefined
var type1 = Object.prototype.toString.call(observerOptions);
var type1 = Object.prototype.toString.call(options);
var type2 = Object.prototype.toString.call(comparableOptions);

@@ -236,10 +377,9 @@ if (type1 !== type2) {

else if (type1 !== '[object Object]' && type2 !== '[object Object]') {
return observerOptions === comparableOptions;
return options === comparableOptions;
}
// complex comparison for only type of [object Object]
for (var key in observerOptions) {
if (observerOptions.hasOwnProperty(key)) {
for (var key in options) {
if (options.hasOwnProperty(key)) {
// recursion to check nested
if (this._areOptionsSame(observerOptions[key], comparableOptions[key]) ===
false) {
if (this._areOptionsSame(options[key], comparableOptions[key]) === false) {
return false;

@@ -252,11 +392,11 @@ }

/**
* Stringify observerOptions for use as a key.
* Excludes observerOptions.root so that the resulting key is stable
* Stringify options for use as a key.
* Excludes options.root so that the resulting key is stable
*
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @private
* @return {String}
*/
IntersectionObserverAdmin.prototype._stringifyObserverOptions = function (observerOptions, scrollableArea) {
IntersectionObserverAdmin.prototype._stringifyOptions = function (options) {
var scrollableArea = options.scrollableArea;
var replacer = function (key, value) {

@@ -268,6 +408,6 @@ if (key === 'root' && scrollableArea) {

};
return JSON.stringify(observerOptions, replacer);
return JSON.stringify(options, replacer);
};
return IntersectionObserverAdmin;
}());
}(Notifications));

@@ -274,0 +414,0 @@ return IntersectionObserverAdmin;

@@ -1,22 +0,11 @@

declare type IndividualEntry = {
element?: HTMLElement | Window;
enterCallback?: Function;
exitCallback?: Function;
};
export interface IObserverOption {
import Notifications from './notification';
export interface IOptions {
root?: HTMLElement;
rootMargin?: string;
threshold?: number;
scrollableArea?: string;
[key: string]: any;
}
declare type RootEntry = {
elements: [IndividualEntry];
observerOptions: IObserverOption;
intersectionObserver: any;
};
declare type PotentialRootEntry = {
[stringifiedOptions: string]: RootEntry;
};
export default class IntersectionObserverAdmin {
private DOMRef;
export default class IntersectionObserverAdmin extends Notifications {
private elementRegistry;
constructor();

@@ -29,9 +18,6 @@ /**

* @param {HTMLElement | Window} element
* @param {Function} enterCallback
* @param {Function} exitCallback
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
observe(element: HTMLElement, enterCallback: Function, exitCallback: Function, observerOptions?: IObserverOption, scrollableArea?: string): void;
observe(element: HTMLElement, options?: IOptions): void;
/**

@@ -42,8 +28,35 @@ * Unobserve target element and remove element from static admin

* @param {HTMLElement|Window} target
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @public
*/
unobserve(target: HTMLElement, observerOptions: IObserverOption, scrollableArea: string): void;
unobserve(target: HTMLElement, options: IOptions): void;
/**
* register event to handle when intersection observer detects enter
*
* @method addEnterCallback
* @public
*/
addEnterCallback(element: HTMLElement | Window, callback: (data?: any) => void): void;
/**
* register event to handle when intersection observer detects exit
*
* @method addExitCallback
* @public
*/
addExitCallback(element: HTMLElement | Window, callback: (data?: any) => void): void;
/**
* retrieve registered callback and call with data
*
* @method dispatchEnterCallback
* @public
*/
dispatchEnterCallback(element: HTMLElement | Window): void;
/**
* retrieve registered callback and call with data on exit
*
* @method dispatchExitCallback
* @public
*/
dispatchExitCallback(element: HTMLElement | Window): void;
/**
* cleanup data structures and unobserve elements

@@ -56,21 +69,21 @@ *

/**
* use function composition to curry observerOptions
* use function composition to curry options
*
* @method _setupOnIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method setupOnIntersection
* @param {Object} options
*/
protected _setupOnIntersection(observerOptions: IObserverOption, scrollableArea: string | undefined): Function;
protected setupOnIntersection(options: IOptions): Function;
protected setupObserver(element: HTMLElement, options: IOptions): void;
private newObserver;
/**
* IntersectionObserver callback when element is intersecting viewport
*
* @method _onIntersection
* @param {Object} observerOptions
* @param {String} scrollableArea
* @method onIntersection
* @param {Object} options
* @param {Array} ioEntries
* @private
*/
protected _onIntersection(observerOptions: IObserverOption, scrollableArea: string | undefined, ioEntries: Array<any>): void;
private onIntersection;
/**
* { root: { stringifiedOptions: { elements: []...] } }
* { root: { stringifiedOptions: { observer, elements: []...] } }
* @method _findRoot

@@ -81,21 +94,18 @@ * @param {HTMLElement|Window} root

*/
protected _findRoot(root: HTMLElement | Window): PotentialRootEntry | null | undefined;
private _findRoot;
/**
* Used for onIntersection callbacks and unobserving the IntersectionObserver
* We don't care about observerOptions key order because we already added
* to the static administrator or found an existing IntersectionObserver with the same
* root && observerOptions to reuse
* We don't care about options key order because we already added
* to the static administrator
*
* @method _findMatchingRootEntry
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @return {Object} entry with elements and other options
*/
protected _findMatchingRootEntry(observerOptions: IObserverOption, scrollableArea: string | undefined): RootEntry | undefined;
private _findMatchingRootEntry;
/**
* Determine if existing elements for a given root based on passed in observerOptions
* Determine if existing elements for a given root based on passed in options
* regardless of sort order of keys
*
* @method _determineMatchingElements
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} potentialRootMatch e.g. { stringifiedOptions: { elements: [], ... }, stringifiedOptions: { elements: [], ... }}

@@ -105,3 +115,3 @@ * @private

*/
protected _determineMatchingElements(observerOptions: IObserverOption, potentialRootMatch?: PotentialRootEntry): RootEntry | undefined;
private _determineMatchingElements;
/**

@@ -112,3 +122,3 @@ * recursive method to test primitive string, number, null, etc and complex

* @method _areOptionsSame
* @param {Object} observerOptions
* @param {Object} options
* @param {Object} comparableOptions

@@ -118,14 +128,12 @@ * @private

*/
protected _areOptionsSame(observerOptions: IObserverOption, comparableOptions: IObserverOption): boolean;
private _areOptionsSame;
/**
* Stringify observerOptions for use as a key.
* Excludes observerOptions.root so that the resulting key is stable
* Stringify options for use as a key.
* Excludes options.root so that the resulting key is stable
*
* @param {Object} observerOptions
* @param {String} scrollableArea
* @param {Object} options
* @private
* @return {String}
*/
protected _stringifyObserverOptions(observerOptions: IObserverOption, scrollableArea: string | undefined): string;
private _stringifyOptions;
}
export {};
{
"name": "intersection-observer-admin",
"version": "0.1.0",
"version": "0.2.0",
"description": "Intersection Observer Admin for better performance",

@@ -62,3 +62,4 @@ "main": "dist/intersection-observer-admin.umd.js",

"typescript": "^3.2.0"
}
},
"dependencies": {}
}

@@ -31,6 +31,6 @@ intersection-observer-admin

- callback function to perform when element is leaving the viewport
4. observerOptions: Function
4. observerOptions: Object
- list of options to pass to Intersection Observer constructor (https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver)
4. scrollableArea: String
- used for determining if element should use existing or new IntersectionObserver
- scrollableArea: String
- used for determining if element should use existing or new IntersectionObserver

@@ -40,12 +40,16 @@ ```js

const intersectionObserverAdmin = new IntersectionObserver();
const intersectionObserverAdmin = new IntersectionObserverAdmin();
// Add callbacks that will be called when observer detects entering and leaving viewport
intersectionObserverAdmin.addEnterCallback(element, enterCallback);
intersectionObserverAdmin.addExitCallback(element, exitCallback);
// add an element to static administrator
intersectionObserverAdmin.observe(element, enterCallback, exitCallback, { root, rootMargin: '0px 0px 100px 0px', threshold: 0 });
intersectionObserverAdmin.observe(element, { root, rootMargin: '0px 0px 100px 0px', threshold: 0 });
// add an element in a scrolling container
intersectionObserverAdmin.add(element, enterCallback, exitCallback, { root, rootMargin: '0px 0px 100px 0px', threshold: 0 }, '.my-list');
intersectionObserverAdmin.add(element, { root, rootMargin: '0px 0px 100px 0px', threshold: 0, scrollableArea: '.my-list' });
// Use in cleanup lifecycle hooks (if applicable) from the element being observed
intersectionObserverAdmin.unobserve(element, observerOptions, scrollableArea);
intersectionObserverAdmin.unobserve(element, observerOptions);

@@ -52,0 +56,0 @@ // Use in cleanup lifecycle hooks of your application as a whole

@@ -1,5 +0,19 @@

describe('format number', () => {
it('ok', () => {
expect(true).toBe(true);
import IntersectionObserverAdmin from '../src/index';
describe('add entry', () => {
it('observe exists', () => {
const el = document.createElement('div');
const ioAdmin = new IntersectionObserverAdmin();
expect(ioAdmin.observe).toBeDefined();
expect(ioAdmin.unobserve).toBeDefined();
});
it('callbacks', () => {
const el = document.createElement('div');
const ioAdmin = new IntersectionObserverAdmin();
expect(ioAdmin.addEnterCallback).toBeDefined();
expect(ioAdmin.addExitCallback).toBeDefined();
expect(ioAdmin.dispatchEnterCallback).toBeDefined();
expect(ioAdmin.dispatchExitCallback).toBeDefined();
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc