mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-17 01:30:35 +03:00
code improvements
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { rAF, cAF, isEmptyArray, indexOf, createCache, runEach } from 'support';
|
import { rAF, cAF, isEmptyArray, indexOf, createCache, runEach, push } from 'support';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
|
|
||||||
export interface AutoUpdateLoop {
|
export interface AutoUpdateLoop {
|
||||||
@@ -40,7 +40,7 @@ const createAutoUpdateLoop = (): AutoUpdateLoop => {
|
|||||||
function interval(newInterval: number): () => void;
|
function interval(newInterval: number): () => void;
|
||||||
function interval(newInterval?: number): number | (() => void) {
|
function interval(newInterval?: number): number | (() => void) {
|
||||||
if (newInterval) {
|
if (newInterval) {
|
||||||
intervals.push(newInterval);
|
push(intervals, newInterval);
|
||||||
updateLoopInterval();
|
updateLoopInterval();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -53,7 +53,7 @@ const createAutoUpdateLoop = (): AutoUpdateLoop => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
_add: (fn) => {
|
_add: (fn) => {
|
||||||
loopFunctions.push(fn);
|
push(loopFunctions, fn);
|
||||||
|
|
||||||
if (!loopIsRunning && !isEmptyArray(loopFunctions)) {
|
if (!loopIsRunning && !isEmptyArray(loopFunctions)) {
|
||||||
getEnvironment()._autoUpdateLoop = loopIsRunning = true;
|
getEnvironment()._autoUpdateLoop = loopIsRunning = true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { each, indexOf, isString, MutationObserverConstructor, isEmptyArray, on, off, attr, is, find } from 'support';
|
import { each, indexOf, isString, MutationObserverConstructor, isEmptyArray, on, off, attr, is, find, push } from 'support';
|
||||||
|
|
||||||
type StringNullUndefined = string | null | undefined;
|
type StringNullUndefined = string | null | undefined;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ export const createDOMObserver = (
|
|||||||
const elements = eventName && selector && getElements(selector);
|
const elements = eventName && selector && getElements(selector);
|
||||||
|
|
||||||
if (elements) {
|
if (elements) {
|
||||||
arr.push([eventName!, elements]);
|
push(arr, [eventName!, elements], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
@@ -85,7 +85,7 @@ export const createDOMObserver = (
|
|||||||
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
||||||
|
|
||||||
if (targetAttrChanged) {
|
if (targetAttrChanged) {
|
||||||
targetChangedAttrs.push(attributeName!);
|
push(targetChangedAttrs, attributeName!);
|
||||||
}
|
}
|
||||||
if (_observeContent) {
|
if (_observeContent) {
|
||||||
const notOnlyAttrChanged = !isAttributesType;
|
const notOnlyAttrChanged = !isAttributesType;
|
||||||
@@ -93,9 +93,7 @@ export const createDOMObserver = (
|
|||||||
const contentFinalChanged =
|
const contentFinalChanged =
|
||||||
(notOnlyAttrChanged || contentAttrChanged) && (_ignoreContentChange ? !_ignoreContentChange(mutation, target, options) : _observeContent);
|
(notOnlyAttrChanged || contentAttrChanged) && (_ignoreContentChange ? !_ignoreContentChange(mutation, target, options) : _observeContent);
|
||||||
|
|
||||||
each(addedNodes, (node) => {
|
push(totalAddedNodes, addedNodes);
|
||||||
totalAddedNodes.push(node);
|
|
||||||
});
|
|
||||||
|
|
||||||
contentChanged = contentChanged || contentFinalChanged;
|
contentChanged = contentChanged || contentFinalChanged;
|
||||||
childListChanged = childListChanged || isChildListType;
|
childListChanged = childListChanged || isChildListType;
|
||||||
@@ -103,7 +101,15 @@ export const createDOMObserver = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
||||||
refreshEventContentChange((selector) => totalAddedNodes.filter((node) => is(node as Element, selector)));
|
refreshEventContentChange((selector) =>
|
||||||
|
totalAddedNodes.reduce<Node[]>((arr, node) => {
|
||||||
|
push(arr, find(selector, node));
|
||||||
|
if (is(node, selector)) {
|
||||||
|
push(arr, node);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged || contentChanged) {
|
if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged || contentChanged) {
|
||||||
callback(targetChangedAttrs, targetStyleChanged, contentChanged);
|
callback(targetChangedAttrs, targetStyleChanged, contentChanged);
|
||||||
@@ -123,7 +129,7 @@ export const createDOMObserver = (
|
|||||||
isConnected = true;
|
isConnected = true;
|
||||||
|
|
||||||
if (_observeContent) {
|
if (_observeContent) {
|
||||||
refreshEventContentChange((selector) => find(selector, target) as Node[]);
|
refreshEventContentChange((selector) => find(selector, target));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
addClass,
|
addClass,
|
||||||
isString,
|
isString,
|
||||||
equalWH,
|
equalWH,
|
||||||
|
push,
|
||||||
cAF,
|
cAF,
|
||||||
rAF,
|
rAF,
|
||||||
ResizeObserverConstructor,
|
ResizeObserverConstructor,
|
||||||
@@ -61,7 +62,7 @@ export const createSizeObserver = (
|
|||||||
if (ResizeObserverConstructor) {
|
if (ResizeObserverConstructor) {
|
||||||
const resizeObserverInstance = new ResizeObserverConstructor(onSizeChangedCallbackProxy);
|
const resizeObserverInstance = new ResizeObserverConstructor(onSizeChangedCallbackProxy);
|
||||||
resizeObserverInstance.observe(listenerElement);
|
resizeObserverInstance.observe(listenerElement);
|
||||||
offListeners.push(() => resizeObserverInstance.disconnect());
|
push(offListeners, () => resizeObserverInstance.disconnect());
|
||||||
} else {
|
} else {
|
||||||
const observerElementChildren = createDOM(
|
const observerElementChildren = createDOM(
|
||||||
`<div class="${classNameSizeObserverListenerItem}" dir="ltr"><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}"></div></div><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}" style="width: 200%; height: 200%"></div></div></div>`
|
`<div class="${classNameSizeObserverListenerItem}" dir="ltr"><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}"></div></div><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}" style="width: 200%; height: 200%"></div></div></div>`
|
||||||
@@ -111,8 +112,7 @@ export const createSizeObserver = (
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
offListeners.push(on(expandElement, scrollEventName, onScroll));
|
push(offListeners, [on(expandElement, scrollEventName, onScroll), on(shrinkElement, scrollEventName, onScroll)]);
|
||||||
offListeners.push(on(shrinkElement, scrollEventName, onScroll));
|
|
||||||
|
|
||||||
// lets assume that the divs will never be that large and a constant value is enough
|
// lets assume that the divs will never be that large and a constant value is enough
|
||||||
style(expandElementChild, {
|
style(expandElementChild, {
|
||||||
@@ -125,7 +125,8 @@ export const createSizeObserver = (
|
|||||||
|
|
||||||
if (direction) {
|
if (direction) {
|
||||||
const updateDirectionCache = createCache(() => getDirection(sizeObserver));
|
const updateDirectionCache = createCache(() => getDirection(sizeObserver));
|
||||||
offListeners.push(
|
push(
|
||||||
|
offListeners,
|
||||||
on(sizeObserver, scrollEventName, (event: Event) => {
|
on(sizeObserver, scrollEventName, (event: Event) => {
|
||||||
const directionCache = updateDirectionCache();
|
const directionCache = updateDirectionCache();
|
||||||
const { _value, _changed } = directionCache;
|
const { _value, _changed } = directionCache;
|
||||||
@@ -148,7 +149,7 @@ export const createSizeObserver = (
|
|||||||
// appearCallback is always needed on scroll-observer strategy to reset it
|
// appearCallback is always needed on scroll-observer strategy to reset it
|
||||||
if (appearCallback) {
|
if (appearCallback) {
|
||||||
addClass(sizeObserver, classNameSizeObserverAppear);
|
addClass(sizeObserver, classNameSizeObserverAppear);
|
||||||
offListeners.push(on(sizeObserver, animationStartEventName, appearCallback));
|
push(offListeners, on(sizeObserver, animationStartEventName, appearCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
prependChildren(target, sizeObserver);
|
prependChildren(target, sizeObserver);
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
import { WH, Cache, createDOM, offsetSize, runEach, prependChildren, removeElements, createCache, IntersectionObserverConstructor } from 'support';
|
import {
|
||||||
|
WH,
|
||||||
|
Cache,
|
||||||
|
createDOM,
|
||||||
|
offsetSize,
|
||||||
|
runEach,
|
||||||
|
prependChildren,
|
||||||
|
removeElements,
|
||||||
|
createCache,
|
||||||
|
push,
|
||||||
|
IntersectionObserverConstructor,
|
||||||
|
} from 'support';
|
||||||
import { createSizeObserver } from 'observers/sizeObserver';
|
import { createSizeObserver } from 'observers/sizeObserver';
|
||||||
import { classNameTrinsicObserver } from 'classnames';
|
import { classNameTrinsicObserver } from 'classnames';
|
||||||
|
|
||||||
@@ -35,9 +46,10 @@ export const createTrinsicObserver = (
|
|||||||
{ root: target }
|
{ root: target }
|
||||||
);
|
);
|
||||||
intersectionObserverInstance.observe(trinsicObserver);
|
intersectionObserverInstance.observe(trinsicObserver);
|
||||||
offListeners.push(() => intersectionObserverInstance.disconnect());
|
push(offListeners, () => intersectionObserverInstance.disconnect());
|
||||||
} else {
|
} else {
|
||||||
offListeners.push(
|
push(
|
||||||
|
offListeners,
|
||||||
createSizeObserver(trinsicObserver, () => {
|
createSizeObserver(trinsicObserver, () => {
|
||||||
const newSize = offsetSize(trinsicObserver);
|
const newSize = offsetSize(trinsicObserver);
|
||||||
const heightIntrinsicCache = updateHeightIntrinsicCache(0, newSize);
|
const heightIntrinsicCache = updateHeightIntrinsicCache(0, newSize);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { OSTarget, OSTargetObject, CSSDirection } from 'typings';
|
import { OSTarget, OSTargetObject, CSSDirection } from 'typings';
|
||||||
import { createStructureLifecycle } from 'lifecycles/structureLifecycle';
|
import { createStructureLifecycle } from 'lifecycles/structureLifecycle';
|
||||||
import { Cache, appendChildren, addClass, contents, is, isHTMLElement, createDiv, each } from 'support';
|
import { Cache, appendChildren, addClass, contents, is, isHTMLElement, createDiv, each, push } from 'support';
|
||||||
import { createSizeObserver } from 'observers/sizeObserver';
|
import { createSizeObserver } from 'observers/sizeObserver';
|
||||||
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
||||||
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
||||||
@@ -44,7 +44,7 @@ const OverlayScrollbars = (target: OSTarget, options?: any, extensions?: any): v
|
|||||||
const lifecycles: Lifecycle<any>[] = [];
|
const lifecycles: Lifecycle<any>[] = [];
|
||||||
const { host } = osTarget;
|
const { host } = osTarget;
|
||||||
|
|
||||||
lifecycles.push(createStructureLifecycle(osTarget));
|
push(lifecycles, createStructureLifecycle(osTarget));
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const onSizeChanged = (directionCache?: Cache<CSSDirection>) => {
|
const onSizeChanged = (directionCache?: Cache<CSSDirection>) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { each, runEach } from 'support/utils/array';
|
import { each, push, runEach } from 'support/utils/array';
|
||||||
|
|
||||||
let passiveEventsSupport: boolean;
|
let passiveEventsSupport: boolean;
|
||||||
const supportPassiveEvents = (): boolean => {
|
const supportPassiveEvents = (): boolean => {
|
||||||
@@ -69,7 +69,7 @@ export const on = (target: EventTarget, eventNames: string, listener: EventListe
|
|||||||
}
|
}
|
||||||
: listener;
|
: listener;
|
||||||
|
|
||||||
offListeners.push(off.bind(null, target, eventName, finalListener, capture));
|
push(offListeners, off.bind(null, target, eventName, finalListener, capture));
|
||||||
target.addEventListener(eventName, finalListener, nativeOptions);
|
target.addEventListener(eventName, finalListener, nativeOptions);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { each, from } from 'support/utils/array';
|
import { isElement } from 'support/utils/types';
|
||||||
|
import { push, from } from 'support/utils/array';
|
||||||
|
|
||||||
type InputElementType = Element | null | undefined;
|
type InputElementType = Element | Node | null | undefined;
|
||||||
type OutputElementType = Element | null;
|
type OutputElementType = Element | null;
|
||||||
|
|
||||||
const elmPrototype = Element.prototype;
|
const elmPrototype = Element.prototype;
|
||||||
@@ -12,12 +13,9 @@ const elmPrototype = Element.prototype;
|
|||||||
*/
|
*/
|
||||||
const find = (selector: string, elm?: InputElementType): Element[] => {
|
const find = (selector: string, elm?: InputElementType): Element[] => {
|
||||||
const arr: Array<Element> = [];
|
const arr: Array<Element> = [];
|
||||||
|
const rootElm = elm ? (isElement(elm) ? elm : null) : document;
|
||||||
|
|
||||||
each((elm || document).querySelectorAll(selector), (e: Element) => {
|
return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr;
|
||||||
arr.push(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +23,11 @@ const find = (selector: string, elm?: InputElementType): Element[] => {
|
|||||||
* @param selector The selector which has to be searched by.
|
* @param selector The selector which has to be searched by.
|
||||||
* @param elm The element from which the search shall be outgoing.
|
* @param elm The element from which the search shall be outgoing.
|
||||||
*/
|
*/
|
||||||
const findFirst = (selector: string, elm?: InputElementType): OutputElementType => (elm || document).querySelector(selector);
|
const findFirst = (selector: string, elm?: InputElementType): OutputElementType => {
|
||||||
|
const rootElm = elm ? (isElement(elm) ? elm : null) : document;
|
||||||
|
|
||||||
|
return rootElm ? rootElm.querySelector(selector) : null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the passed element is matching with the passed selector.
|
* Determines whether the passed element is matching with the passed selector.
|
||||||
@@ -33,12 +35,12 @@ const findFirst = (selector: string, elm?: InputElementType): OutputElementType
|
|||||||
* @param selector The selector which has to be compared with the passed element. Additional selectors: ':visible' and ':hidden'.
|
* @param selector The selector which has to be compared with the passed element. Additional selectors: ':visible' and ':hidden'.
|
||||||
*/
|
*/
|
||||||
const is = (elm: InputElementType, selector: string): boolean => {
|
const is = (elm: InputElementType, selector: string): boolean => {
|
||||||
if (elm) {
|
if (isElement(elm)) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
const fn: (...args: any) => boolean = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
||||||
return fn && fn.call(elm, selector);
|
return fn.call(elm, selector);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -51,17 +53,12 @@ const is = (elm: InputElementType, selector: string): boolean => {
|
|||||||
const children = (elm: InputElementType, selector?: string): ReadonlyArray<Element> => {
|
const children = (elm: InputElementType, selector?: string): ReadonlyArray<Element> => {
|
||||||
const childs: Array<Element> = [];
|
const childs: Array<Element> = [];
|
||||||
|
|
||||||
each(elm && elm.children, (child: Element) => {
|
return isElement(elm)
|
||||||
if (selector) {
|
? push(
|
||||||
if (is(child, selector)) {
|
childs,
|
||||||
childs.push(child);
|
from(elm.children).filter((child) => (selector ? is(child, selector) : child))
|
||||||
}
|
)
|
||||||
} else {
|
: childs;
|
||||||
childs.push(child);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return childs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +74,7 @@ const contents = (elm: InputElementType): ReadonlyArray<ChildNode> => (elm ? fro
|
|||||||
const parent = (elm: InputElementType): OutputElementType => (elm ? elm.parentElement : null);
|
const parent = (elm: InputElementType): OutputElementType => (elm ? elm.parentElement : null);
|
||||||
|
|
||||||
const closest = (elm: InputElementType, selector: string): OutputElementType => {
|
const closest = (elm: InputElementType, selector: string): OutputElementType => {
|
||||||
if (elm) {
|
if (isElement(elm)) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (elmPrototype.closest) {
|
if (elmPrototype.closest) {
|
||||||
@@ -85,7 +82,7 @@ const closest = (elm: InputElementType, selector: string): OutputElementType =>
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
if (is(elm, selector)) {
|
if (is(elm, selector)) {
|
||||||
return elm;
|
return elm as Element;
|
||||||
}
|
}
|
||||||
elm = parent(elm);
|
elm = parent(elm);
|
||||||
} while (elm !== null && elm.nodeType === 1);
|
} while (elm !== null && elm.nodeType === 1);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { each, hasOwnProperty, keys } from 'support/utils';
|
import { each, hasOwnProperty, keys, push, isEmptyObject } from 'support/utils';
|
||||||
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'support/utils/types';
|
import { type, isArray, isUndefined, isPlainObject, isString } from 'support/utils/types';
|
||||||
import { OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, Func, OptionsValidationResult, OptionsValidated } from 'support/options';
|
import { OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, Func, OptionsValidationResult, OptionsValidated } from 'support/options';
|
||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
|
||||||
@@ -93,13 +93,13 @@ const validateRecursive = <T extends PlainObject>(
|
|||||||
isValid = !!enumStringSplit.find((possibility) => possibility === optionsValue);
|
isValid = !!enumStringSplit.find((possibility) => possibility === optionsValue);
|
||||||
|
|
||||||
// build error message
|
// build error message
|
||||||
errorEnumStrings.push(...enumStringSplit);
|
push(errorEnumStrings, enumStringSplit);
|
||||||
} else {
|
} else {
|
||||||
isValid = optionsTemplateTypes[optionsValueType] === currTemplateType;
|
isValid = optionsTemplateTypes[optionsValueType] === currTemplateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// build error message
|
// build error message
|
||||||
errorPossibleTypes.push(isEnumString ? optionsTemplateTypes.string : typeString!);
|
push(errorPossibleTypes, isEnumString ? optionsTemplateTypes.string : typeString!);
|
||||||
|
|
||||||
// continue if invalid, break if valid
|
// continue if invalid, break if valid
|
||||||
return !isValid;
|
return !isValid;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isArrayLike } from 'support/utils/types';
|
import { isArrayLike, isString } from 'support/utils/types';
|
||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
|
||||||
type RunEachItem = ((...args: any) => any | any[]) | null | undefined;
|
type RunEachItem = ((...args: any) => any | any[]) | null | undefined;
|
||||||
@@ -55,6 +55,16 @@ export function each<T>(
|
|||||||
*/
|
*/
|
||||||
export const indexOf = <T = any>(arr: Array<T>, item: T, fromIndex?: number): number => arr.indexOf(item, fromIndex);
|
export const indexOf = <T = any>(arr: Array<T>, item: T, fromIndex?: number): number => arr.indexOf(item, fromIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushesh all given items into the given array and returns it.
|
||||||
|
* @param array The array the items shall be pushed into.
|
||||||
|
* @param items The items which shall be pushed into the array.
|
||||||
|
*/
|
||||||
|
export const push = <T>(array: Array<T>, items: T | ArrayLike<T>, arrayIsSingleItem?: boolean): Array<T> => {
|
||||||
|
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items as Array<T>) : array.push(items as T);
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a shallow-copied Array instance from an array-like or iterable object.
|
* Creates a shallow-copied Array instance from an array-like or iterable object.
|
||||||
* @param arr The object from which the array instance shall be created.
|
* @param arr The object from which the array instance shall be created.
|
||||||
@@ -65,7 +75,7 @@ export const from = <T = any>(arr: ArrayLike<T>) => {
|
|||||||
}
|
}
|
||||||
const result: Array<T> = [];
|
const result: Array<T> = [];
|
||||||
each(arr, (elm) => {
|
each(arr, (elm) => {
|
||||||
result.push(elm);
|
push(result, elm);
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,3 +72,14 @@ export function assignDeep<T, U, V, W, X, Y, Z>(
|
|||||||
// Return the modified object
|
// Return the modified object
|
||||||
return target as any;
|
return target as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given object is empty, false otherwise.
|
||||||
|
* @param obj The Object.
|
||||||
|
*/
|
||||||
|
export function isEmptyObject(obj: any): boolean {
|
||||||
|
/* eslint-disable no-restricted-syntax, guard-for-in */
|
||||||
|
for (const name in obj) return false;
|
||||||
|
return true;
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
import { hasOwnProperty } from 'support/utils/object';
|
||||||
|
|
||||||
|
const ElementNodeType = Node.ELEMENT_NODE;
|
||||||
|
|
||||||
export const type: (obj: any) => string = (obj) => {
|
export const type: (obj: any) => string = (obj) => {
|
||||||
if (obj === undefined) return `${obj}`;
|
if (obj === undefined) return `${obj}`;
|
||||||
@@ -47,7 +50,9 @@ export function isObject(obj: any): boolean {
|
|||||||
*/
|
*/
|
||||||
export function isArrayLike<T extends PlainObject = any>(obj: any): obj is ArrayLike<T> {
|
export function isArrayLike<T extends PlainObject = any>(obj: any): obj is ArrayLike<T> {
|
||||||
const length = !!obj && obj.length;
|
const length = !!obj && obj.length;
|
||||||
return isArray(obj) || (!isFunction(obj) && isNumber(length) && length > -1 && length % 1 == 0); // eslint-disable-line eqeqeq
|
const lengthCorrectFormat = isNumber(length) && length > -1 && length % 1 == 0; // eslint-disable-line eqeqeq
|
||||||
|
|
||||||
|
return isArray(obj) || (!isFunction(obj) && lengthCorrectFormat) ? (length > 0 && isObject(obj) ? length - 1 in obj : true) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,12 +63,13 @@ export function isPlainObject<T = any>(obj: any): obj is PlainObject<T> {
|
|||||||
if (!obj || !isObject(obj) || type(obj) !== 'object') return false;
|
if (!obj || !isObject(obj) || type(obj) !== 'object') return false;
|
||||||
|
|
||||||
let key;
|
let key;
|
||||||
const proto = 'prototype';
|
const cstr = 'constructor';
|
||||||
const { hasOwnProperty } = Object[proto];
|
const ctor = obj[cstr];
|
||||||
const hasOwnConstructor = hasOwnProperty.call(obj, 'constructor');
|
const ctorProto = ctor && ctor.prototype;
|
||||||
const hasIsPrototypeOf = obj.constructor && obj.constructor[proto] && hasOwnProperty.call(obj.constructor[proto], 'isPrototypeOf');
|
const hasOwnConstructor = hasOwnProperty(obj, cstr);
|
||||||
|
const hasIsPrototypeOf = ctorProto && hasOwnProperty(ctorProto, 'isPrototypeOf');
|
||||||
|
|
||||||
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
|
if (ctor && !hasOwnConstructor && !hasIsPrototypeOf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +79,7 @@ export function isPlainObject<T = any>(obj: any): obj is PlainObject<T> {
|
|||||||
}
|
}
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
return isUndefined(key) || hasOwnProperty.call(obj, key);
|
return isUndefined(key) || hasOwnProperty(obj, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,18 +87,15 @@ export function isPlainObject<T = any>(obj: any): obj is PlainObject<T> {
|
|||||||
* @param obj The object which shall be checked.
|
* @param obj The object which shall be checked.
|
||||||
*/
|
*/
|
||||||
export function isHTMLElement(obj: any): obj is HTMLElement {
|
export function isHTMLElement(obj: any): obj is HTMLElement {
|
||||||
const instaceOfRightHandSide = window.HTMLElement;
|
const instanceofObj = window.HTMLElement;
|
||||||
const doInstanceOf = isObject(instaceOfRightHandSide) || isFunction(instaceOfRightHandSide);
|
return obj ? (instanceofObj ? obj instanceof instanceofObj : obj.nodeType === ElementNodeType) : false;
|
||||||
return !!(doInstanceOf ? obj instanceof instaceOfRightHandSide : obj && isObject(obj) && obj.nodeType === 1 && isString(obj.nodeName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given object is empty, false otherwise.
|
* Checks whether the given object is a Element.
|
||||||
* @param obj The Object.
|
* @param obj The object which shall be checked.
|
||||||
*/
|
*/
|
||||||
export function isEmptyObject(obj: any): boolean {
|
export function isElement(obj: any): obj is Element {
|
||||||
/* eslint-disable no-restricted-syntax, guard-for-in */
|
const instanceofObj = window.Element;
|
||||||
for (const name in obj) return false;
|
return obj ? (instanceofObj ? obj instanceof instanceofObj : obj.nodeType === ElementNodeType) : false;
|
||||||
return true;
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { isString, isPlainObject, isEmptyObject } from 'support/utils/types';
|
import { isEmptyObject } from 'support/utils/object';
|
||||||
|
import { isString, isPlainObject } from 'support/utils/types';
|
||||||
import { style, hide, show, topRightBottomLeft } from 'support/dom/style';
|
import { style, hide, show, topRightBottomLeft } from 'support/dom/style';
|
||||||
|
|
||||||
describe('dom style', () => {
|
describe('dom style', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { find, findFirst, is, children, contents, parent, createDiv, liesBetween } from 'support/dom';
|
import { find, findFirst, is, children, contents, parent, createDiv, liesBetween, createDOM } from 'support/dom';
|
||||||
|
|
||||||
const slotElm = document.body;
|
const slotElm = document.body;
|
||||||
const testHTML = '<div id="parent" class="div-class"><div id="child" class="div-class"></div></div><p>2</p><input type="text" value="3"></input>abc';
|
const testHTML = '<div id="parent" class="div-class"><div id="child" class="div-class"></div></div><p>2</p><input type="text" value="3"></input>abc';
|
||||||
@@ -55,6 +55,10 @@ describe('dom traversal', () => {
|
|||||||
|
|
||||||
expect(nonExistent.length).toBe(0);
|
expect(nonExistent.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(find('div', createDOM('<div>textnodehere</div>')[0].firstChild)).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findFirst', () => {
|
describe('findFirst', () => {
|
||||||
@@ -96,6 +100,10 @@ describe('dom traversal', () => {
|
|||||||
|
|
||||||
expect(nonExistent).toBe(null);
|
expect(nonExistent).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(findFirst('div', createDOM('<div>textnodehere</div>')[0].firstChild)).toEqual(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('is', () => {
|
describe('is', () => {
|
||||||
@@ -147,6 +155,10 @@ describe('dom traversal', () => {
|
|||||||
expect(is(null, '.div-class')).toBe(false);
|
expect(is(null, '.div-class')).toBe(false);
|
||||||
expect(is(null, '.other-class')).toBe(false);
|
expect(is(null, '.other-class')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(is(createDOM('<div>textnodehere</div>')[0].firstChild, '.hi')).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('children', () => {
|
describe('children', () => {
|
||||||
@@ -169,6 +181,10 @@ describe('dom traversal', () => {
|
|||||||
expect(childs.length).toBe(1);
|
expect(childs.length).toBe(1);
|
||||||
expect(childs[0]).toBe(findFirst('input'));
|
expect(childs[0]).toBe(findFirst('input'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(children(createDOM('<div>textnodehere</div>')[0].firstChild, '.hi')).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('contents', () => {
|
describe('contents', () => {
|
||||||
@@ -184,6 +200,10 @@ describe('dom traversal', () => {
|
|||||||
|
|
||||||
expect(childs.length).toEqual(0);
|
expect(childs.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(contents(createDOM('<div>textnodehere</div>')[0].firstChild)).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parent', () => {
|
describe('parent', () => {
|
||||||
@@ -198,6 +218,10 @@ describe('dom traversal', () => {
|
|||||||
|
|
||||||
expect(p).toBeNull();
|
expect(p).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(parent(createDOM('<div>textnodehere</div>')[0].firstChild)?.nodeName).toEqual('DIV');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('liesBetween', () => {
|
describe('liesBetween', () => {
|
||||||
@@ -286,5 +310,9 @@ describe('dom traversal', () => {
|
|||||||
|
|
||||||
Element.prototype.closest = original;
|
Element.prototype.closest = original;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('text node', () => {
|
||||||
|
expect(liesBetween(createDOM('<div>textnodehere</div>')[0].firstChild, '.a', '.b')).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,87 @@
|
|||||||
import { each, from, indexOf, runEach } from 'support/utils/array';
|
import { push, each, from, indexOf, runEach } from 'support/utils/array';
|
||||||
|
|
||||||
describe('array utilities', () => {
|
describe('array utilities', () => {
|
||||||
|
describe('push', () => {
|
||||||
|
describe('single value', () => {
|
||||||
|
test('string', () => {
|
||||||
|
const arr: string[] = [];
|
||||||
|
const item = 'hi there';
|
||||||
|
|
||||||
|
expect(push(arr, item)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).toHaveLength(1);
|
||||||
|
expect(arr[0]).toBe(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('array like', () => {
|
||||||
|
const arr: string[] = [];
|
||||||
|
const item = ['tuple', 'elem'];
|
||||||
|
|
||||||
|
expect(push(arr, item, true)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).toHaveLength(1);
|
||||||
|
expect(arr[0]).toBe(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('array like fake', () => {
|
||||||
|
const arr: any[] = [];
|
||||||
|
const item = { length: 2 };
|
||||||
|
|
||||||
|
expect(push(arr, item)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).toHaveLength(1);
|
||||||
|
expect(arr[0]).toBe(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple values', () => {
|
||||||
|
test('string', () => {
|
||||||
|
const arr: string[] = [];
|
||||||
|
const items = 'hi there'.split('');
|
||||||
|
|
||||||
|
expect(push(arr, items)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).toHaveLength(items.length);
|
||||||
|
expect(arr).toEqual(items);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('array', () => {
|
||||||
|
const arr: string[] = [];
|
||||||
|
const items = ['tuple', 'elem'];
|
||||||
|
|
||||||
|
expect(push(arr, items)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).toHaveLength(2);
|
||||||
|
expect(arr[0]).toBe('tuple');
|
||||||
|
expect(arr[1]).toBe('elem');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('array like', () => {
|
||||||
|
const arr: string[] = [];
|
||||||
|
const items = { 0: 'zero', 1: 'one', 2: 'two', length: 3 };
|
||||||
|
|
||||||
|
expect(push(arr, items)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).toHaveLength(3);
|
||||||
|
expect(arr[0]).toBe('zero');
|
||||||
|
expect(arr[1]).toBe('one');
|
||||||
|
expect(arr[2]).toBe('two');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('array like query selector', () => {
|
||||||
|
document.body.innerHTML = '<div><p>testtext<h1></h1></p><div></div></div>';
|
||||||
|
const arr: Node[] = [];
|
||||||
|
const items = document.querySelectorAll('*');
|
||||||
|
|
||||||
|
expect(push(arr, items)).toBe(arr);
|
||||||
|
|
||||||
|
expect(arr).not.toHaveLength(0);
|
||||||
|
arr.forEach((node) => {
|
||||||
|
expect(node instanceof window.Element).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('each', () => {
|
describe('each', () => {
|
||||||
describe('each through Array', () => {
|
describe('each through Array', () => {
|
||||||
test('returns input', () => {
|
test('returns input', () => {
|
||||||
@@ -170,6 +251,13 @@ describe('array utilities', () => {
|
|||||||
expect(testFunc).toBeCalledTimes(arrLikeObj.length - 1);
|
expect(testFunc).toBeCalledTimes(arrLikeObj.length - 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('each through nothing', () => {
|
||||||
|
test('returns input', () => {
|
||||||
|
expect(each(null, () => {})).toBe(null);
|
||||||
|
expect(each(undefined, () => {})).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('from', () => {
|
describe('from', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { assignDeep, keys, hasOwnProperty } from 'support/utils/object';
|
import { assignDeep, keys, hasOwnProperty, isEmptyObject } from 'support/utils/object';
|
||||||
import { isPlainObject } from 'support/utils/types';
|
import { isPlainObject } from 'support/utils/types';
|
||||||
|
|
||||||
describe('object utilities', () => {
|
describe('object utilities', () => {
|
||||||
@@ -164,6 +164,20 @@ describe('object utilities', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isEmptyObject', () => {
|
||||||
|
test('empty object is empty', () => {
|
||||||
|
expect(isEmptyObject({})).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('filled object is not empty', () => {
|
||||||
|
expect(isEmptyObject({ a: 1, b: 2 })).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('created object is empty', () => {
|
||||||
|
expect(isEmptyObject(Object.create(null))).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('hasOwnProperty', () => {
|
test('hasOwnProperty', () => {
|
||||||
const obj = {
|
const obj = {
|
||||||
a: 1,
|
a: 1,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { createDOM } from 'support/dom/create';
|
||||||
import {
|
import {
|
||||||
type,
|
type,
|
||||||
isNumber,
|
isNumber,
|
||||||
@@ -10,7 +11,7 @@ import {
|
|||||||
isNull,
|
isNull,
|
||||||
isArrayLike,
|
isArrayLike,
|
||||||
isPlainObject,
|
isPlainObject,
|
||||||
isEmptyObject,
|
isElement,
|
||||||
isHTMLElement,
|
isHTMLElement,
|
||||||
} from 'support/utils/types';
|
} from 'support/utils/types';
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ const typeNameValueMap = {
|
|||||||
window,
|
window,
|
||||||
body: document.body,
|
body: document.body,
|
||||||
querySelectorAll: document.querySelectorAll('*'),
|
querySelectorAll: document.querySelectorAll('*'),
|
||||||
|
textNode: createDOM('<div>textnodehere</div>')[0].firstChild,
|
||||||
};
|
};
|
||||||
|
|
||||||
const testTypeFn = (typeFunc: Function, expectedTypeNameValueResultMap: any) => {
|
const testTypeFn = (typeFunc: Function, expectedTypeNameValueResultMap: any) => {
|
||||||
@@ -130,6 +132,7 @@ describe('types', () => {
|
|||||||
document: true,
|
document: true,
|
||||||
window: true,
|
window: true,
|
||||||
body: true,
|
body: true,
|
||||||
|
textNode: true,
|
||||||
querySelectorAll: true,
|
querySelectorAll: true,
|
||||||
functionConstructor: true,
|
functionConstructor: true,
|
||||||
arrayLikeObject: true,
|
arrayLikeObject: true,
|
||||||
@@ -177,30 +180,29 @@ describe('types', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('isEmptyObject', () => {
|
test('isElement', () => {
|
||||||
testTypeFn(isEmptyObject, {
|
const temp = window.Element;
|
||||||
objectEmpty: true,
|
|
||||||
objectCreate: true,
|
|
||||||
arrayEmpty: true,
|
|
||||||
|
|
||||||
newNumber: true,
|
testTypeFn(isElement, {
|
||||||
newBoolean: true,
|
body: true,
|
||||||
newFunction: true,
|
|
||||||
newArray: true,
|
|
||||||
|
|
||||||
null: true,
|
|
||||||
undefined: true,
|
|
||||||
booleanTrue: true,
|
|
||||||
booleanFalse: true,
|
|
||||||
void0: true,
|
|
||||||
number: true,
|
|
||||||
infinity: true,
|
|
||||||
functionConstructor: true,
|
|
||||||
function: true,
|
|
||||||
functionAsync: true,
|
|
||||||
functionArrow: true,
|
|
||||||
functionArrowAsync: true,
|
|
||||||
});
|
});
|
||||||
|
Array.from(document.querySelectorAll('*')).forEach((elm) => {
|
||||||
|
expect(isElement(elm)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
delete window.Element;
|
||||||
|
// @ts-ignore
|
||||||
|
window.Element = null;
|
||||||
|
|
||||||
|
testTypeFn(isElement, {
|
||||||
|
body: true,
|
||||||
|
});
|
||||||
|
Array.from(document.querySelectorAll('*')).forEach((elm) => {
|
||||||
|
expect(isElement(elm)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.Element = temp;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('isHTMLElement', () => {
|
test('isHTMLElement', () => {
|
||||||
@@ -213,6 +215,7 @@ describe('types', () => {
|
|||||||
expect(isHTMLElement(elm)).toBeTruthy();
|
expect(isHTMLElement(elm)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
delete window.HTMLElement;
|
delete window.HTMLElement;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.HTMLElement = null;
|
window.HTMLElement = null;
|
||||||
|
|||||||
@@ -28,10 +28,12 @@ const contentHostElmAttrChange: HTMLElement | null = document.querySelector('#co
|
|||||||
const targetElmsSlot = document.querySelector('#target .host-nest-item');
|
const targetElmsSlot = document.querySelector('#target .host-nest-item');
|
||||||
const targetContentElmsSlot = document.querySelector('#target .content .content-nest');
|
const targetContentElmsSlot = document.querySelector('#target .content .content-nest');
|
||||||
const targetContentBetweenElmsSlot = document.querySelector('#content-host');
|
const targetContentBetweenElmsSlot = document.querySelector('#content-host');
|
||||||
|
const imgElmsSlot = document.querySelector('#target .content-nest');
|
||||||
|
|
||||||
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetElms');
|
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetElms');
|
||||||
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentElms');
|
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentElms');
|
||||||
const addRemoveTargetContentBetweenElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentBetweenElms');
|
const addRemoveTargetContentBetweenElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentBetweenElms');
|
||||||
|
const addImgElms: HTMLButtonElement | null = document.querySelector('#addImgElms');
|
||||||
const setTargetAttr: HTMLSelectElement | null = document.querySelector('#setTargetAttr');
|
const setTargetAttr: HTMLSelectElement | null = document.querySelector('#setTargetAttr');
|
||||||
const setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector('#setFilteredTargetAttr');
|
const setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector('#setFilteredTargetAttr');
|
||||||
const setContentAttr: HTMLSelectElement | null = document.querySelector('#setContentAttr');
|
const setContentAttr: HTMLSelectElement | null = document.querySelector('#setContentAttr');
|
||||||
@@ -48,7 +50,7 @@ const startBtn: HTMLButtonElement | null = document.querySelector('#start');
|
|||||||
const targetElmObservations: DOMObserverResult[] = [];
|
const targetElmObservations: DOMObserverResult[] = [];
|
||||||
const targetElmContentElmObservations: DOMObserverResult[] = [];
|
const targetElmContentElmObservations: DOMObserverResult[] = [];
|
||||||
const getTotalObservations = () => targetElmObservations.length + targetElmContentElmObservations.length;
|
const getTotalObservations = () => targetElmObservations.length + targetElmContentElmObservations.length;
|
||||||
const getLast = <T>(arr: T[]): T => arr[arr.length - 1] || ({} as T);
|
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
||||||
const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObserverResult[]) => {
|
const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObserverResult[]) => {
|
||||||
interface Stat {
|
interface Stat {
|
||||||
total: number;
|
total: number;
|
||||||
@@ -229,6 +231,37 @@ const addRemoveTargetContentElmsFn = async () => {
|
|||||||
const addRemoveTargetContentBetweenElmsFn = async () => {
|
const addRemoveTargetContentBetweenElmsFn = async () => {
|
||||||
await addRemoveElementsTest(targetContentBetweenElmsSlot, targetElmContentElmObservations);
|
await addRemoveElementsTest(targetContentBetweenElmsSlot, targetElmContentElmObservations);
|
||||||
};
|
};
|
||||||
|
const addImgElmsFn = async () => {
|
||||||
|
const add = async () => {
|
||||||
|
const img = new Image(1, 1);
|
||||||
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
|
const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
||||||
|
const imgHolder = createDiv('img');
|
||||||
|
appendChildren(imgHolder, img);
|
||||||
|
|
||||||
|
before();
|
||||||
|
appendChildren(imgElmsSlot, imgHolder);
|
||||||
|
|
||||||
|
await waitForOrFailTest(() => {
|
||||||
|
after();
|
||||||
|
compare(2);
|
||||||
|
|
||||||
|
const mutationObserverObservation = getLast(targetElmContentElmObservations, 1);
|
||||||
|
should(mutationObserverObservation.contentChanged).equal(true);
|
||||||
|
should(mutationObserverObservation.styleChanged).equal(false);
|
||||||
|
should(mutationObserverObservation.changedTargetAttrs.length).equal(0);
|
||||||
|
|
||||||
|
const eventObservation = getLast(targetElmContentElmObservations);
|
||||||
|
should(eventObservation.contentChanged).equal(true);
|
||||||
|
should(eventObservation.styleChanged).equal(false);
|
||||||
|
should(eventObservation.changedTargetAttrs.length).equal(0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
await add();
|
||||||
|
await add();
|
||||||
|
await add();
|
||||||
|
};
|
||||||
const iterateTargetAttrChange = async () => {
|
const iterateTargetAttrChange = async () => {
|
||||||
await iterateAttrChange(setTargetAttr, targetElmObservations, (observation, selected) => {
|
await iterateAttrChange(setTargetAttr, targetElmObservations, (observation, selected) => {
|
||||||
const { changedTargetAttrs, styleChanged, contentChanged } = observation;
|
const { changedTargetAttrs, styleChanged, contentChanged } = observation;
|
||||||
@@ -270,6 +303,7 @@ const triggerBetweenSummaryChange = async () => {
|
|||||||
addRemoveTargetElms?.addEventListener('click', addRemoveTargetElmsFn);
|
addRemoveTargetElms?.addEventListener('click', addRemoveTargetElmsFn);
|
||||||
addRemoveTargetContentElms?.addEventListener('click', addRemoveTargetContentElmsFn);
|
addRemoveTargetContentElms?.addEventListener('click', addRemoveTargetContentElmsFn);
|
||||||
addRemoveTargetContentBetweenElms?.addEventListener('click', addRemoveTargetContentBetweenElmsFn);
|
addRemoveTargetContentBetweenElms?.addEventListener('click', addRemoveTargetContentBetweenElmsFn);
|
||||||
|
addImgElms?.addEventListener('click', addImgElmsFn);
|
||||||
setTargetAttr?.addEventListener('change', attrChangeListener(targetElm));
|
setTargetAttr?.addEventListener('change', attrChangeListener(targetElm));
|
||||||
setFilteredTargetAttr?.addEventListener('change', attrChangeListener(targetElm));
|
setFilteredTargetAttr?.addEventListener('change', attrChangeListener(targetElm));
|
||||||
setContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
setContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
||||||
@@ -305,6 +339,9 @@ createDOMObserver(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
_observeContent: true,
|
_observeContent: true,
|
||||||
|
_eventContentChange: () => {
|
||||||
|
return [['img', 'load']];
|
||||||
|
},
|
||||||
_ignoreContentChange: (mutation) => {
|
_ignoreContentChange: (mutation) => {
|
||||||
const { target, attributeName } = mutation;
|
const { target, attributeName } = mutation;
|
||||||
return attributeName ? !hasClass(target as Element, 'host') && liesBetween(target as Element, '.host', '.content') : false;
|
return attributeName ? !hasClass(target as Element, 'host') && liesBetween(target as Element, '.host', '.content') : false;
|
||||||
@@ -315,6 +352,8 @@ createDOMObserver(
|
|||||||
const start = async () => {
|
const start = async () => {
|
||||||
setTestResult(null);
|
setTestResult(null);
|
||||||
|
|
||||||
|
await addImgElmsFn();
|
||||||
|
|
||||||
await addRemoveTargetElmsFn();
|
await addRemoveTargetElmsFn();
|
||||||
await addRemoveTargetContentElmsFn();
|
await addRemoveTargetContentElmsFn();
|
||||||
await addRemoveTargetContentBetweenElmsFn();
|
await addRemoveTargetContentBetweenElmsFn();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<button id="addRemoveTargetElms">Target Elements</button>
|
<button id="addRemoveTargetElms">Target Elements</button>
|
||||||
<button id="addRemoveTargetContentElms">Content Elements</button>
|
<button id="addRemoveTargetContentElms">Content Elements</button>
|
||||||
<button id="addRemoveTargetContentBetweenElms">Content Between Elements</button>
|
<button id="addRemoveTargetContentBetweenElms">Content Between Elements</button>
|
||||||
|
<button id="addImgElms">Image Elements</button>
|
||||||
|
|
||||||
<label for="setTargetAttr">setTargetAttr</label>
|
<label for="setTargetAttr">setTargetAttr</label>
|
||||||
<select name="setTargetAttr" id="setTargetAttr">
|
<select name="setTargetAttr" id="setTargetAttr">
|
||||||
|
|||||||
@@ -75,3 +75,14 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
background: lime;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user