mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-22 21:30:37 +03:00
improve domObserver
This commit is contained in:
+267
-57
@@ -1,7 +1,13 @@
|
|||||||
const ElementNodeType = Node.ELEMENT_NODE;
|
const ElementNodeType = Node.ELEMENT_NODE;
|
||||||
const { toString, hasOwnProperty } = Object.prototype;
|
const { toString, hasOwnProperty } = Object.prototype;
|
||||||
|
function isUndefined(obj) {
|
||||||
|
return obj === undefined;
|
||||||
|
}
|
||||||
|
function isNull(obj) {
|
||||||
|
return obj === null;
|
||||||
|
}
|
||||||
const type = (obj) => {
|
const type = (obj) => {
|
||||||
return obj === undefined || obj === null
|
return isUndefined(obj) || isNull(obj)
|
||||||
? `${obj}`
|
? `${obj}`
|
||||||
: toString
|
: toString
|
||||||
.call(obj)
|
.call(obj)
|
||||||
@@ -17,12 +23,6 @@ function isString(obj) {
|
|||||||
function isFunction(obj) {
|
function isFunction(obj) {
|
||||||
return typeof obj === 'function';
|
return typeof obj === 'function';
|
||||||
}
|
}
|
||||||
function isUndefined(obj) {
|
|
||||||
return obj === undefined;
|
|
||||||
}
|
|
||||||
function isNull(obj) {
|
|
||||||
return obj === null;
|
|
||||||
}
|
|
||||||
function isArray(obj) {
|
function isArray(obj) {
|
||||||
return Array.isArray(obj);
|
return Array.isArray(obj);
|
||||||
}
|
}
|
||||||
@@ -121,6 +121,7 @@ function each(source, callback) {
|
|||||||
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
const indexOf = (arr, item, fromIndex) => arr.indexOf(item, fromIndex);
|
||||||
const push = (array, items, arrayIsSingleItem) => {
|
const push = (array, items, arrayIsSingleItem) => {
|
||||||
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
|
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
|
||||||
return array;
|
return array;
|
||||||
@@ -136,6 +137,7 @@ const from = (arr) => {
|
|||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
const isEmptyArray = (array) => array && array.length === 0;
|
||||||
const runEach = (arr, p1) => {
|
const runEach = (arr, p1) => {
|
||||||
const runFn = (fn) => fn && fn(p1);
|
const runFn = (fn) => fn && fn(p1);
|
||||||
|
|
||||||
@@ -148,6 +150,12 @@ const runEach = (arr, p1) => {
|
|||||||
|
|
||||||
const elmPrototype = Element.prototype;
|
const elmPrototype = Element.prototype;
|
||||||
|
|
||||||
|
const find = (selector, elm) => {
|
||||||
|
const arr = [];
|
||||||
|
const rootElm = elm ? (isElement(elm) ? elm : null) : document;
|
||||||
|
return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr;
|
||||||
|
};
|
||||||
|
|
||||||
const is = (elm, selector) => {
|
const is = (elm, selector) => {
|
||||||
if (isElement(elm)) {
|
if (isElement(elm)) {
|
||||||
const fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
const fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
||||||
@@ -260,7 +268,7 @@ const getBoundingClientRect = (elm) => elm.getBoundingClientRect();
|
|||||||
let passiveEventsSupport;
|
let passiveEventsSupport;
|
||||||
|
|
||||||
const supportPassiveEvents = () => {
|
const supportPassiveEvents = () => {
|
||||||
if (passiveEventsSupport === undefined) {
|
if (isUndefined(passiveEventsSupport)) {
|
||||||
passiveEventsSupport = false;
|
passiveEventsSupport = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -373,6 +381,75 @@ function isEmptyObject(obj) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const firstLetterToUpper = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
|
||||||
|
const getDummyStyle = () => createDiv().style;
|
||||||
|
|
||||||
|
const cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];
|
||||||
|
const jsPrefixes = ['WebKit', 'Moz', 'O', 'MS', 'webkit', 'moz', 'o', 'ms'];
|
||||||
|
const jsCache = {};
|
||||||
|
const cssCache = {};
|
||||||
|
const cssProperty = (name) => {
|
||||||
|
let result = cssCache[name];
|
||||||
|
|
||||||
|
if (hasOwnProperty$1(cssCache, name)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uppercasedName = firstLetterToUpper(name);
|
||||||
|
const elmStyle = getDummyStyle();
|
||||||
|
each(cssPrefixes, (prefix) => {
|
||||||
|
const prefixWithoutDashes = prefix.replace(/-/g, '');
|
||||||
|
const resultPossibilities = [name, prefix + name, prefixWithoutDashes + uppercasedName, firstLetterToUpper(prefixWithoutDashes) + uppercasedName];
|
||||||
|
result = resultPossibilities.find((resultPossibility) => elmStyle[resultPossibility] !== undefined);
|
||||||
|
return !result;
|
||||||
|
});
|
||||||
|
cssCache[name] = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const jsAPI = (name) => {
|
||||||
|
let result = jsCache[name] || window[name];
|
||||||
|
|
||||||
|
if (hasOwnProperty$1(jsCache, name)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
each(jsPrefixes, (prefix) => {
|
||||||
|
result = result || window[prefix + firstLetterToUpper(name)];
|
||||||
|
return !result;
|
||||||
|
});
|
||||||
|
jsCache[name] = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MutationObserverConstructor = jsAPI('MutationObserver');
|
||||||
|
const IntersectionObserverConstructor = jsAPI('IntersectionObserver');
|
||||||
|
const ResizeObserverConstructor = jsAPI('ResizeObserver');
|
||||||
|
const cAF = jsAPI('cancelAnimationFrame');
|
||||||
|
const rAF = jsAPI('requestAnimationFrame');
|
||||||
|
|
||||||
|
const debounce = (functionToDebounce, timeout, maxWait) => {
|
||||||
|
let timeoutId;
|
||||||
|
let lastCallTime;
|
||||||
|
const hasTimeout = isNumber(timeout) && timeout > 0;
|
||||||
|
const hasMaxWait = isNumber(maxWait) && maxWait > 0;
|
||||||
|
const cancel = hasTimeout ? window.clearTimeout : cAF;
|
||||||
|
const set = hasTimeout ? window.setTimeout : rAF;
|
||||||
|
|
||||||
|
const setFn = function setFn(args) {
|
||||||
|
lastCallTime = hasMaxWait ? performance.now() : 0;
|
||||||
|
timeoutId && cancel(timeoutId);
|
||||||
|
functionToDebounce.apply(this, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
const boundSetFn = setFn.bind(this, arguments);
|
||||||
|
const forceCall = hasMaxWait ? performance.now() - lastCallTime >= maxWait : false;
|
||||||
|
timeoutId && cancel(timeoutId);
|
||||||
|
timeoutId = forceCall ? boundSetFn() : set(boundSetFn, timeout);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const cssNumber = {
|
const cssNumber = {
|
||||||
animationiterationcount: 1,
|
animationiterationcount: 1,
|
||||||
columncount: 1,
|
columncount: 1,
|
||||||
@@ -481,53 +558,6 @@ const createCache = (update, options) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const firstLetterToUpper = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
||||||
|
|
||||||
const getDummyStyle = () => createDiv().style;
|
|
||||||
|
|
||||||
const cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];
|
|
||||||
const jsPrefixes = ['WebKit', 'Moz', 'O', 'MS', 'webkit', 'moz', 'o', 'ms'];
|
|
||||||
const jsCache = {};
|
|
||||||
const cssCache = {};
|
|
||||||
const cssProperty = (name) => {
|
|
||||||
let result = cssCache[name];
|
|
||||||
|
|
||||||
if (hasOwnProperty$1(cssCache, name)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uppercasedName = firstLetterToUpper(name);
|
|
||||||
const elmStyle = getDummyStyle();
|
|
||||||
each(cssPrefixes, (prefix) => {
|
|
||||||
const prefixWithoutDashes = prefix.replace(/-/g, '');
|
|
||||||
const resultPossibilities = [name, prefix + name, prefixWithoutDashes + uppercasedName, firstLetterToUpper(prefixWithoutDashes) + uppercasedName];
|
|
||||||
result = resultPossibilities.find((resultPossibility) => elmStyle[resultPossibility] !== undefined);
|
|
||||||
return !result;
|
|
||||||
});
|
|
||||||
cssCache[name] = result;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
const jsAPI = (name) => {
|
|
||||||
let result = jsCache[name] || window[name];
|
|
||||||
|
|
||||||
if (hasOwnProperty$1(jsCache, name)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
each(jsPrefixes, (prefix) => {
|
|
||||||
result = result || window[prefix + firstLetterToUpper(name)];
|
|
||||||
return !result;
|
|
||||||
});
|
|
||||||
jsCache[name] = result;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const MutationObserverConstructor = jsAPI('MutationObserver');
|
|
||||||
const IntersectionObserverConstructor = jsAPI('IntersectionObserver');
|
|
||||||
const ResizeObserverConstructor = jsAPI('ResizeObserver');
|
|
||||||
const cAF = jsAPI('cancelAnimationFrame');
|
|
||||||
const rAF = jsAPI('requestAnimationFrame');
|
|
||||||
|
|
||||||
function createCommonjsModule(fn) {
|
function createCommonjsModule(fn) {
|
||||||
var module = { exports: {} };
|
var module = { exports: {} };
|
||||||
return fn(module, module.exports), module.exports;
|
return fn(module, module.exports), module.exports;
|
||||||
@@ -599,7 +629,7 @@ const validateRecursive = (options, template, optionsDiff, doWriteErrors, propPa
|
|||||||
typeString = key;
|
typeString = key;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const isEnumString = typeString === undefined;
|
const isEnumString = isUndefined(typeString);
|
||||||
|
|
||||||
if (isEnumString && isString(optionsValue)) {
|
if (isEnumString && isString(optionsValue)) {
|
||||||
const enumStringSplit = currTemplateType.split(' ');
|
const enumStringSplit = currTemplateType.split(' ');
|
||||||
@@ -1160,6 +1190,174 @@ const createTrinsicObserver = (target, onTrinsicChangedCallback) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createEventContentChange = (target, eventContentChange, map, callback) => {
|
||||||
|
let eventContentChangeRef;
|
||||||
|
|
||||||
|
const addEvent = (elm, eventName) => {
|
||||||
|
const entry = map.get(elm);
|
||||||
|
const newEntry = isUndefined(entry);
|
||||||
|
|
||||||
|
const registerEvent = () => {
|
||||||
|
map.set(elm, eventName);
|
||||||
|
on(elm, eventName, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!newEntry && eventName !== entry) {
|
||||||
|
off(elm, entry, callback);
|
||||||
|
registerEvent();
|
||||||
|
} else if (newEntry) {
|
||||||
|
registerEvent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const _destroy = () => {
|
||||||
|
map.forEach((eventName, elm) => {
|
||||||
|
off(elm, eventName, callback);
|
||||||
|
});
|
||||||
|
map.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
const _updateElements = (getElements) => {
|
||||||
|
if (eventContentChangeRef) {
|
||||||
|
const eventElmList = eventContentChangeRef.reduce((arr, item) => {
|
||||||
|
if (item) {
|
||||||
|
const selector = item[0];
|
||||||
|
const eventName = item[1];
|
||||||
|
const elements = eventName && selector && (getElements ? getElements(selector) : find(selector, target));
|
||||||
|
|
||||||
|
if (elements) {
|
||||||
|
push(arr, [elements, isFunction(eventName) ? eventName(elements) : eventName], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}, []);
|
||||||
|
each(eventElmList, (item) => {
|
||||||
|
const elements = item[0];
|
||||||
|
const eventName = item[1];
|
||||||
|
each(elements, (elm) => {
|
||||||
|
addEvent(elm, eventName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const _update = (newEventContentChange) => {
|
||||||
|
eventContentChangeRef = newEventContentChange;
|
||||||
|
|
||||||
|
_destroy();
|
||||||
|
|
||||||
|
_updateElements();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (eventContentChange) {
|
||||||
|
_update(eventContentChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_destroy,
|
||||||
|
_updateElements,
|
||||||
|
_update,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAttributeChanged = (mutationTarget, attributeName, oldValue) => oldValue !== attr(mutationTarget, attributeName);
|
||||||
|
|
||||||
|
const createDOMObserver = (target, callback, options) => {
|
||||||
|
let isConnected = false;
|
||||||
|
const { _observeContent, _attributes, _styleChangingAttributes, _ignoreContentChange, _eventContentChange } = options || {};
|
||||||
|
const {
|
||||||
|
_updateElements: updateEventContentChangeElements,
|
||||||
|
_destroy: destroyEventContentChange,
|
||||||
|
_update: updateEventContentChange,
|
||||||
|
} = createEventContentChange(
|
||||||
|
target,
|
||||||
|
_observeContent && _eventContentChange,
|
||||||
|
new Map(),
|
||||||
|
debounce(() => {
|
||||||
|
if (isConnected) {
|
||||||
|
callback([], false, true);
|
||||||
|
}
|
||||||
|
}, 80)
|
||||||
|
);
|
||||||
|
const finalAttributes = _attributes || [];
|
||||||
|
const finalStyleChangingAttributes = _styleChangingAttributes || [];
|
||||||
|
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
|
||||||
|
|
||||||
|
const observerCallback = (mutations) => {
|
||||||
|
const targetChangedAttrs = [];
|
||||||
|
const totalAddedNodes = [];
|
||||||
|
let targetStyleChanged = false;
|
||||||
|
let contentChanged = false;
|
||||||
|
let childListChanged = false;
|
||||||
|
each(mutations, (mutation) => {
|
||||||
|
const { attributeName, target: mutationTarget, type, oldValue, addedNodes } = mutation;
|
||||||
|
const isAttributesType = type === 'attributes';
|
||||||
|
const isChildListType = type === 'childList';
|
||||||
|
const targetIsMutationTarget = target === mutationTarget;
|
||||||
|
const attributeChanged = isAttributesType && isString(attributeName) && getAttributeChanged(mutationTarget, attributeName, oldValue);
|
||||||
|
const targetAttrChanged = attributeChanged && targetIsMutationTarget && !_observeContent;
|
||||||
|
const styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
|
||||||
|
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
||||||
|
|
||||||
|
if (targetAttrChanged) {
|
||||||
|
push(targetChangedAttrs, attributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_observeContent) {
|
||||||
|
const notOnlyAttrChanged = !isAttributesType;
|
||||||
|
const contentAttrChanged = isAttributesType && styleChangingAttrChanged && !targetIsMutationTarget;
|
||||||
|
const contentFinalChanged =
|
||||||
|
(notOnlyAttrChanged || contentAttrChanged) && (_ignoreContentChange ? !_ignoreContentChange(mutation, target, options) : _observeContent);
|
||||||
|
push(totalAddedNodes, addedNodes);
|
||||||
|
contentChanged = contentChanged || contentFinalChanged;
|
||||||
|
childListChanged = childListChanged || isChildListType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
||||||
|
updateEventContentChangeElements((selector) =>
|
||||||
|
totalAddedNodes.reduce((arr, node) => {
|
||||||
|
push(arr, find(selector, node));
|
||||||
|
return is(node, selector) ? push(arr, node) : arr;
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged || contentChanged) {
|
||||||
|
callback(targetChangedAttrs, targetStyleChanged, contentChanged);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mutationObserver = new MutationObserverConstructor(observerCallback);
|
||||||
|
mutationObserver.observe(target, {
|
||||||
|
attributes: true,
|
||||||
|
attributeOldValue: true,
|
||||||
|
attributeFilter: observedAttributes,
|
||||||
|
subtree: _observeContent,
|
||||||
|
childList: _observeContent,
|
||||||
|
characterData: _observeContent,
|
||||||
|
});
|
||||||
|
isConnected = true;
|
||||||
|
return {
|
||||||
|
_disconnect: () => {
|
||||||
|
if (isConnected) {
|
||||||
|
destroyEventContentChange();
|
||||||
|
mutationObserver.disconnect();
|
||||||
|
isConnected = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_updateEventContentChange: (newEventContentChange) => {
|
||||||
|
updateEventContentChange(isConnected && _observeContent && newEventContentChange);
|
||||||
|
},
|
||||||
|
_update: () => {
|
||||||
|
if (isConnected) {
|
||||||
|
observerCallback(mutationObserver.takeRecords());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeTarget = (target) => {
|
const normalizeTarget = (target) => {
|
||||||
if (isHTMLElement(target)) {
|
if (isHTMLElement(target)) {
|
||||||
const isTextarea = is(target, 'textarea');
|
const isTextarea = is(target, 'textarea');
|
||||||
@@ -1197,7 +1395,7 @@ const normalizeTarget = (target) => {
|
|||||||
const OverlayScrollbars = (target, options, extensions) => {
|
const OverlayScrollbars = (target, options, extensions) => {
|
||||||
const osTarget = normalizeTarget(target);
|
const osTarget = normalizeTarget(target);
|
||||||
const lifecycles = [];
|
const lifecycles = [];
|
||||||
const { host } = osTarget;
|
const { host, content } = osTarget;
|
||||||
push(lifecycles, createStructureLifecycle(osTarget));
|
push(lifecycles, createStructureLifecycle(osTarget));
|
||||||
|
|
||||||
const onSizeChanged = (directionCache) => {
|
const onSizeChanged = (directionCache) => {
|
||||||
@@ -1223,6 +1421,18 @@ const OverlayScrollbars = (target, options, extensions) => {
|
|||||||
_direction: true,
|
_direction: true,
|
||||||
});
|
});
|
||||||
createTrinsicObserver(host, onTrinsicChanged);
|
createTrinsicObserver(host, onTrinsicChanged);
|
||||||
|
createDOMObserver(host, () => {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
createDOMObserver(
|
||||||
|
content,
|
||||||
|
() => {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_observeContent: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var index = () => {
|
var index = () => {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+291
-63
@@ -11,8 +11,14 @@
|
|||||||
var _Object$prototype = Object.prototype,
|
var _Object$prototype = Object.prototype,
|
||||||
toString = _Object$prototype.toString,
|
toString = _Object$prototype.toString,
|
||||||
hasOwnProperty = _Object$prototype.hasOwnProperty;
|
hasOwnProperty = _Object$prototype.hasOwnProperty;
|
||||||
|
function isUndefined(obj) {
|
||||||
|
return obj === undefined;
|
||||||
|
}
|
||||||
|
function isNull(obj) {
|
||||||
|
return obj === null;
|
||||||
|
}
|
||||||
var type = function type(obj) {
|
var type = function type(obj) {
|
||||||
return obj === undefined || obj === null
|
return isUndefined(obj) || isNull(obj)
|
||||||
? '' + obj
|
? '' + obj
|
||||||
: toString
|
: toString
|
||||||
.call(obj)
|
.call(obj)
|
||||||
@@ -28,12 +34,6 @@
|
|||||||
function isFunction(obj) {
|
function isFunction(obj) {
|
||||||
return typeof obj === 'function';
|
return typeof obj === 'function';
|
||||||
}
|
}
|
||||||
function isUndefined(obj) {
|
|
||||||
return obj === undefined;
|
|
||||||
}
|
|
||||||
function isNull(obj) {
|
|
||||||
return obj === null;
|
|
||||||
}
|
|
||||||
function isArray(obj) {
|
function isArray(obj) {
|
||||||
return Array.isArray(obj);
|
return Array.isArray(obj);
|
||||||
}
|
}
|
||||||
@@ -136,6 +136,9 @@
|
|||||||
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
var indexOf = function indexOf(arr, item, fromIndex) {
|
||||||
|
return arr.indexOf(item, fromIndex);
|
||||||
|
};
|
||||||
var push = function push(array, items, arrayIsSingleItem) {
|
var push = function push(array, items, arrayIsSingleItem) {
|
||||||
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
|
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
|
||||||
return array;
|
return array;
|
||||||
@@ -151,6 +154,9 @@
|
|||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
var isEmptyArray = function isEmptyArray(array) {
|
||||||
|
return array && array.length === 0;
|
||||||
|
};
|
||||||
var runEach = function runEach(arr, p1) {
|
var runEach = function runEach(arr, p1) {
|
||||||
var runFn = function runFn(fn) {
|
var runFn = function runFn(fn) {
|
||||||
return fn && fn(p1);
|
return fn && fn(p1);
|
||||||
@@ -165,6 +171,12 @@
|
|||||||
|
|
||||||
var elmPrototype = Element.prototype;
|
var elmPrototype = Element.prototype;
|
||||||
|
|
||||||
|
var find = function find(selector, elm) {
|
||||||
|
var arr = [];
|
||||||
|
var rootElm = elm ? (isElement(elm) ? elm : null) : document;
|
||||||
|
return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr;
|
||||||
|
};
|
||||||
|
|
||||||
var is = function is(elm, selector) {
|
var is = function is(elm, selector) {
|
||||||
if (isElement(elm)) {
|
if (isElement(elm)) {
|
||||||
var fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
var fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
||||||
@@ -292,7 +304,7 @@
|
|||||||
var passiveEventsSupport;
|
var passiveEventsSupport;
|
||||||
|
|
||||||
var supportPassiveEvents = function supportPassiveEvents() {
|
var supportPassiveEvents = function supportPassiveEvents() {
|
||||||
if (passiveEventsSupport === undefined) {
|
if (isUndefined(passiveEventsSupport)) {
|
||||||
passiveEventsSupport = false;
|
passiveEventsSupport = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -423,6 +435,81 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var firstLetterToUpper = function firstLetterToUpper(str) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getDummyStyle = function getDummyStyle() {
|
||||||
|
return createDiv().style;
|
||||||
|
};
|
||||||
|
|
||||||
|
var cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];
|
||||||
|
var jsPrefixes = ['WebKit', 'Moz', 'O', 'MS', 'webkit', 'moz', 'o', 'ms'];
|
||||||
|
var jsCache = {};
|
||||||
|
var cssCache = {};
|
||||||
|
var cssProperty = function cssProperty(name) {
|
||||||
|
var result = cssCache[name];
|
||||||
|
|
||||||
|
if (hasOwnProperty$1(cssCache, name)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uppercasedName = firstLetterToUpper(name);
|
||||||
|
var elmStyle = getDummyStyle();
|
||||||
|
each(cssPrefixes, function (prefix) {
|
||||||
|
var prefixWithoutDashes = prefix.replace(/-/g, '');
|
||||||
|
var resultPossibilities = [name, prefix + name, prefixWithoutDashes + uppercasedName, firstLetterToUpper(prefixWithoutDashes) + uppercasedName];
|
||||||
|
result = resultPossibilities.find(function (resultPossibility) {
|
||||||
|
return elmStyle[resultPossibility] !== undefined;
|
||||||
|
});
|
||||||
|
return !result;
|
||||||
|
});
|
||||||
|
cssCache[name] = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var jsAPI = function jsAPI(name) {
|
||||||
|
var result = jsCache[name] || window[name];
|
||||||
|
|
||||||
|
if (hasOwnProperty$1(jsCache, name)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
each(jsPrefixes, function (prefix) {
|
||||||
|
result = result || window[prefix + firstLetterToUpper(name)];
|
||||||
|
return !result;
|
||||||
|
});
|
||||||
|
jsCache[name] = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
var MutationObserverConstructor = jsAPI('MutationObserver');
|
||||||
|
var IntersectionObserverConstructor = jsAPI('IntersectionObserver');
|
||||||
|
var ResizeObserverConstructor = jsAPI('ResizeObserver');
|
||||||
|
var cAF = jsAPI('cancelAnimationFrame');
|
||||||
|
var rAF = jsAPI('requestAnimationFrame');
|
||||||
|
|
||||||
|
var debounce = function debounce(functionToDebounce, timeout, maxWait) {
|
||||||
|
var timeoutId;
|
||||||
|
var lastCallTime;
|
||||||
|
var hasTimeout = isNumber(timeout) && timeout > 0;
|
||||||
|
var hasMaxWait = isNumber(maxWait) && maxWait > 0;
|
||||||
|
var cancel = hasTimeout ? window.clearTimeout : cAF;
|
||||||
|
var set = hasTimeout ? window.setTimeout : rAF;
|
||||||
|
|
||||||
|
var setFn = function setFn(args) {
|
||||||
|
lastCallTime = hasMaxWait ? performance.now() : 0;
|
||||||
|
timeoutId && cancel(timeoutId);
|
||||||
|
functionToDebounce.apply(this, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
var boundSetFn = setFn.bind(this, arguments);
|
||||||
|
var forceCall = hasMaxWait ? performance.now() - lastCallTime >= maxWait : false;
|
||||||
|
timeoutId && cancel(timeoutId);
|
||||||
|
timeoutId = forceCall ? boundSetFn() : set(boundSetFn, timeout);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var cssNumber = {
|
var cssNumber = {
|
||||||
animationiterationcount: 1,
|
animationiterationcount: 1,
|
||||||
columncount: 1,
|
columncount: 1,
|
||||||
@@ -540,59 +627,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var firstLetterToUpper = function firstLetterToUpper(str) {
|
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
var getDummyStyle = function getDummyStyle() {
|
|
||||||
return createDiv().style;
|
|
||||||
};
|
|
||||||
|
|
||||||
var cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-'];
|
|
||||||
var jsPrefixes = ['WebKit', 'Moz', 'O', 'MS', 'webkit', 'moz', 'o', 'ms'];
|
|
||||||
var jsCache = {};
|
|
||||||
var cssCache = {};
|
|
||||||
var cssProperty = function cssProperty(name) {
|
|
||||||
var result = cssCache[name];
|
|
||||||
|
|
||||||
if (hasOwnProperty$1(cssCache, name)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
var uppercasedName = firstLetterToUpper(name);
|
|
||||||
var elmStyle = getDummyStyle();
|
|
||||||
each(cssPrefixes, function (prefix) {
|
|
||||||
var prefixWithoutDashes = prefix.replace(/-/g, '');
|
|
||||||
var resultPossibilities = [name, prefix + name, prefixWithoutDashes + uppercasedName, firstLetterToUpper(prefixWithoutDashes) + uppercasedName];
|
|
||||||
result = resultPossibilities.find(function (resultPossibility) {
|
|
||||||
return elmStyle[resultPossibility] !== undefined;
|
|
||||||
});
|
|
||||||
return !result;
|
|
||||||
});
|
|
||||||
cssCache[name] = result;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var jsAPI = function jsAPI(name) {
|
|
||||||
var result = jsCache[name] || window[name];
|
|
||||||
|
|
||||||
if (hasOwnProperty$1(jsCache, name)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
each(jsPrefixes, function (prefix) {
|
|
||||||
result = result || window[prefix + firstLetterToUpper(name)];
|
|
||||||
return !result;
|
|
||||||
});
|
|
||||||
jsCache[name] = result;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
var MutationObserverConstructor = jsAPI('MutationObserver');
|
|
||||||
var IntersectionObserverConstructor = jsAPI('IntersectionObserver');
|
|
||||||
var ResizeObserverConstructor = jsAPI('ResizeObserver');
|
|
||||||
var cAF = jsAPI('cancelAnimationFrame');
|
|
||||||
var rAF = jsAPI('requestAnimationFrame');
|
|
||||||
|
|
||||||
function createCommonjsModule(fn) {
|
function createCommonjsModule(fn) {
|
||||||
var module = { exports: {} };
|
var module = { exports: {} };
|
||||||
return fn(module, module.exports), module.exports;
|
return fn(module, module.exports), module.exports;
|
||||||
@@ -666,7 +700,7 @@
|
|||||||
typeString = key;
|
typeString = key;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var isEnumString = typeString === undefined;
|
var isEnumString = isUndefined(typeString);
|
||||||
|
|
||||||
if (isEnumString && isString(optionsValue)) {
|
if (isEnumString && isString(optionsValue)) {
|
||||||
var enumStringSplit = currTemplateType.split(' ');
|
var enumStringSplit = currTemplateType.split(' ');
|
||||||
@@ -1298,6 +1332,187 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var createEventContentChange = function createEventContentChange(target, eventContentChange, map, callback) {
|
||||||
|
var eventContentChangeRef;
|
||||||
|
|
||||||
|
var addEvent = function addEvent(elm, eventName) {
|
||||||
|
var entry = map.get(elm);
|
||||||
|
var newEntry = isUndefined(entry);
|
||||||
|
|
||||||
|
var registerEvent = function registerEvent() {
|
||||||
|
map.set(elm, eventName);
|
||||||
|
on(elm, eventName, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!newEntry && eventName !== entry) {
|
||||||
|
off(elm, entry, callback);
|
||||||
|
registerEvent();
|
||||||
|
} else if (newEntry) {
|
||||||
|
registerEvent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var _destroy = function _destroy() {
|
||||||
|
map.forEach(function (eventName, elm) {
|
||||||
|
off(elm, eventName, callback);
|
||||||
|
});
|
||||||
|
map.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
var _updateElements = function _updateElements(getElements) {
|
||||||
|
if (eventContentChangeRef) {
|
||||||
|
var eventElmList = eventContentChangeRef.reduce(function (arr, item) {
|
||||||
|
if (item) {
|
||||||
|
var selector = item[0];
|
||||||
|
var eventName = item[1];
|
||||||
|
var elements = eventName && selector && (getElements ? getElements(selector) : find(selector, target));
|
||||||
|
|
||||||
|
if (elements) {
|
||||||
|
push(arr, [elements, isFunction(eventName) ? eventName(elements) : eventName], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}, []);
|
||||||
|
each(eventElmList, function (item) {
|
||||||
|
var elements = item[0];
|
||||||
|
var eventName = item[1];
|
||||||
|
each(elements, function (elm) {
|
||||||
|
addEvent(elm, eventName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var _update = function _update(newEventContentChange) {
|
||||||
|
eventContentChangeRef = newEventContentChange;
|
||||||
|
|
||||||
|
_destroy();
|
||||||
|
|
||||||
|
_updateElements();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (eventContentChange) {
|
||||||
|
_update(eventContentChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_destroy: _destroy,
|
||||||
|
_updateElements: _updateElements,
|
||||||
|
_update: _update,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var getAttributeChanged = function getAttributeChanged(mutationTarget, attributeName, oldValue) {
|
||||||
|
return oldValue !== attr(mutationTarget, attributeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createDOMObserver = function createDOMObserver(target, callback, options) {
|
||||||
|
var isConnected = false;
|
||||||
|
|
||||||
|
var _ref = options || {},
|
||||||
|
_observeContent = _ref._observeContent,
|
||||||
|
_attributes = _ref._attributes,
|
||||||
|
_styleChangingAttributes = _ref._styleChangingAttributes,
|
||||||
|
_ignoreContentChange = _ref._ignoreContentChange,
|
||||||
|
_eventContentChange = _ref._eventContentChange;
|
||||||
|
|
||||||
|
var _createEventContentCh = createEventContentChange(
|
||||||
|
target,
|
||||||
|
_observeContent && _eventContentChange,
|
||||||
|
new Map(),
|
||||||
|
debounce(function () {
|
||||||
|
if (isConnected) {
|
||||||
|
callback([], false, true);
|
||||||
|
}
|
||||||
|
}, 80)
|
||||||
|
),
|
||||||
|
updateEventContentChangeElements = _createEventContentCh._updateElements,
|
||||||
|
destroyEventContentChange = _createEventContentCh._destroy,
|
||||||
|
updateEventContentChange = _createEventContentCh._update;
|
||||||
|
|
||||||
|
var finalAttributes = _attributes || [];
|
||||||
|
var finalStyleChangingAttributes = _styleChangingAttributes || [];
|
||||||
|
var observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
|
||||||
|
|
||||||
|
var observerCallback = function observerCallback(mutations) {
|
||||||
|
var targetChangedAttrs = [];
|
||||||
|
var totalAddedNodes = [];
|
||||||
|
var targetStyleChanged = false;
|
||||||
|
var contentChanged = false;
|
||||||
|
var childListChanged = false;
|
||||||
|
each(mutations, function (mutation) {
|
||||||
|
var attributeName = mutation.attributeName,
|
||||||
|
mutationTarget = mutation.target,
|
||||||
|
type = mutation.type,
|
||||||
|
oldValue = mutation.oldValue,
|
||||||
|
addedNodes = mutation.addedNodes;
|
||||||
|
var isAttributesType = type === 'attributes';
|
||||||
|
var isChildListType = type === 'childList';
|
||||||
|
var targetIsMutationTarget = target === mutationTarget;
|
||||||
|
var attributeChanged = isAttributesType && isString(attributeName) && getAttributeChanged(mutationTarget, attributeName, oldValue);
|
||||||
|
var targetAttrChanged = attributeChanged && targetIsMutationTarget && !_observeContent;
|
||||||
|
var styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
|
||||||
|
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
||||||
|
|
||||||
|
if (targetAttrChanged) {
|
||||||
|
push(targetChangedAttrs, attributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_observeContent) {
|
||||||
|
var notOnlyAttrChanged = !isAttributesType;
|
||||||
|
var contentAttrChanged = isAttributesType && styleChangingAttrChanged && !targetIsMutationTarget;
|
||||||
|
var contentFinalChanged =
|
||||||
|
(notOnlyAttrChanged || contentAttrChanged) && (_ignoreContentChange ? !_ignoreContentChange(mutation, target, options) : _observeContent);
|
||||||
|
push(totalAddedNodes, addedNodes);
|
||||||
|
contentChanged = contentChanged || contentFinalChanged;
|
||||||
|
childListChanged = childListChanged || isChildListType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
||||||
|
updateEventContentChangeElements(function (selector) {
|
||||||
|
return totalAddedNodes.reduce(function (arr, node) {
|
||||||
|
push(arr, find(selector, node));
|
||||||
|
return is(node, selector) ? push(arr, node) : arr;
|
||||||
|
}, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged || contentChanged) {
|
||||||
|
callback(targetChangedAttrs, targetStyleChanged, contentChanged);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var mutationObserver = new MutationObserverConstructor(observerCallback);
|
||||||
|
mutationObserver.observe(target, {
|
||||||
|
attributes: true,
|
||||||
|
attributeOldValue: true,
|
||||||
|
attributeFilter: observedAttributes,
|
||||||
|
subtree: _observeContent,
|
||||||
|
childList: _observeContent,
|
||||||
|
characterData: _observeContent,
|
||||||
|
});
|
||||||
|
isConnected = true;
|
||||||
|
return {
|
||||||
|
_disconnect: function _disconnect() {
|
||||||
|
if (isConnected) {
|
||||||
|
destroyEventContentChange();
|
||||||
|
mutationObserver.disconnect();
|
||||||
|
isConnected = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_updateEventContentChange: function _updateEventContentChange(newEventContentChange) {
|
||||||
|
updateEventContentChange(isConnected && _observeContent && newEventContentChange);
|
||||||
|
},
|
||||||
|
_update: function _update() {
|
||||||
|
if (isConnected) {
|
||||||
|
observerCallback(mutationObserver.takeRecords());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var normalizeTarget = function normalizeTarget(target) {
|
var normalizeTarget = function normalizeTarget(target) {
|
||||||
if (isHTMLElement(target)) {
|
if (isHTMLElement(target)) {
|
||||||
var isTextarea = is(target, 'textarea');
|
var isTextarea = is(target, 'textarea');
|
||||||
@@ -1338,7 +1553,8 @@
|
|||||||
var OverlayScrollbars = function OverlayScrollbars(target, options, extensions) {
|
var OverlayScrollbars = function OverlayScrollbars(target, options, extensions) {
|
||||||
var osTarget = normalizeTarget(target);
|
var osTarget = normalizeTarget(target);
|
||||||
var lifecycles = [];
|
var lifecycles = [];
|
||||||
var host = osTarget.host;
|
var host = osTarget.host,
|
||||||
|
content = osTarget.content;
|
||||||
push(lifecycles, createStructureLifecycle(osTarget));
|
push(lifecycles, createStructureLifecycle(osTarget));
|
||||||
|
|
||||||
var onSizeChanged = function onSizeChanged(directionCache) {
|
var onSizeChanged = function onSizeChanged(directionCache) {
|
||||||
@@ -1364,6 +1580,18 @@
|
|||||||
_direction: true,
|
_direction: true,
|
||||||
});
|
});
|
||||||
createTrinsicObserver(host, onTrinsicChanged);
|
createTrinsicObserver(host, onTrinsicChanged);
|
||||||
|
createDOMObserver(host, function () {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
createDOMObserver(
|
||||||
|
content,
|
||||||
|
function () {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_observeContent: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var index = function () {
|
var index = function () {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,27 +1,116 @@
|
|||||||
import { each, debounce, indexOf, isString, MutationObserverConstructor, isEmptyArray, on, off, attr, is, find, push } from 'support';
|
import {
|
||||||
|
each,
|
||||||
|
debounce,
|
||||||
|
indexOf,
|
||||||
|
isString,
|
||||||
|
MutationObserverConstructor,
|
||||||
|
isEmptyArray,
|
||||||
|
on,
|
||||||
|
off,
|
||||||
|
attr,
|
||||||
|
is,
|
||||||
|
find,
|
||||||
|
push,
|
||||||
|
isUndefined,
|
||||||
|
isFunction,
|
||||||
|
} from 'support';
|
||||||
|
|
||||||
type StringNullUndefined = string | null | undefined;
|
type StringNullUndefined = string | null | undefined;
|
||||||
|
export type DOMObserverEventContentChange =
|
||||||
export type DOMOvserverEventContentChangeResult = Array<[StringNullUndefined, StringNullUndefined] | null | undefined>; // [selector, eventname]
|
| Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined>
|
||||||
export type DOMOvserverEventContentChange = () => DOMOvserverEventContentChangeResult;
|
| false
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
export type DOMObserverIgnoreContentChange = (
|
export type DOMObserverIgnoreContentChange = (
|
||||||
mutation: MutationRecord,
|
mutation: MutationRecord,
|
||||||
domObserverTarget: HTMLElement,
|
domObserverTarget: HTMLElement,
|
||||||
domObserverOptions: DOMObserverOptions | undefined
|
domObserverOptions: DOMObserverOptions | undefined
|
||||||
) => boolean | null | undefined;
|
) => boolean | null | undefined;
|
||||||
export interface DOMObserverOptions {
|
export interface DOMObserverOptions {
|
||||||
_observeContent?: boolean;
|
_observeContent?: boolean; // do observe children and trigger content change
|
||||||
_attributes?: string[];
|
_attributes?: string[]; // observed attributes
|
||||||
|
_styleChangingAttributes?: string[]; // list of attributes that trigger a content change if changed
|
||||||
|
_eventContentChange?: DOMObserverEventContentChange; // [selector, eventname]
|
||||||
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
||||||
_eventContentChange?: DOMOvserverEventContentChange;
|
|
||||||
}
|
}
|
||||||
export interface DOMObserver {
|
export interface DOMObserver {
|
||||||
_disconnect: () => void;
|
_disconnect: () => void;
|
||||||
|
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
||||||
_update: () => void;
|
_update: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
// const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
||||||
const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];
|
// const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];
|
||||||
|
|
||||||
|
const createEventContentChange = (
|
||||||
|
target: Element,
|
||||||
|
eventContentChange: DOMObserverEventContentChange,
|
||||||
|
map: Map<Node, string>,
|
||||||
|
callback: (...args: any) => any
|
||||||
|
) => {
|
||||||
|
let eventContentChangeRef: DOMObserverEventContentChange;
|
||||||
|
const addEvent = (elm: Node, eventName: string) => {
|
||||||
|
const entry = map.get(elm);
|
||||||
|
const newEntry = isUndefined(entry);
|
||||||
|
const registerEvent = () => {
|
||||||
|
map.set(elm, eventName);
|
||||||
|
on(elm, eventName, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!newEntry && eventName !== entry) {
|
||||||
|
off(elm, entry!, callback);
|
||||||
|
registerEvent();
|
||||||
|
} else if (newEntry) {
|
||||||
|
registerEvent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const _destroy = () => {
|
||||||
|
map.forEach((eventName: string, elm: Node) => {
|
||||||
|
off(elm, eventName, callback);
|
||||||
|
});
|
||||||
|
map.clear();
|
||||||
|
};
|
||||||
|
const _updateElements = (getElements?: (selector: string) => Node[]) => {
|
||||||
|
if (eventContentChangeRef) {
|
||||||
|
const eventElmList = eventContentChangeRef.reduce<Array<[Node[], string]>>((arr, item) => {
|
||||||
|
if (item) {
|
||||||
|
const selector = item[0];
|
||||||
|
const eventName = item[1];
|
||||||
|
const elements = eventName && selector && (getElements ? getElements(selector) : find(selector, target));
|
||||||
|
|
||||||
|
if (elements) {
|
||||||
|
push(arr, [elements, isFunction(eventName) ? eventName(elements) : eventName!], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
each(eventElmList, (item) => {
|
||||||
|
const elements = item[0];
|
||||||
|
const eventName = item[1];
|
||||||
|
|
||||||
|
each(elements, (elm) => {
|
||||||
|
addEvent(elm, eventName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const _update = (newEventContentChange: DOMObserverEventContentChange) => {
|
||||||
|
eventContentChangeRef = newEventContentChange;
|
||||||
|
_destroy();
|
||||||
|
_updateElements();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (eventContentChange) {
|
||||||
|
_update(eventContentChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_destroy,
|
||||||
|
_updateElements,
|
||||||
|
_update,
|
||||||
|
};
|
||||||
|
};
|
||||||
const getAttributeChanged = (mutationTarget: Node, attributeName: string, oldValue: string | null): boolean =>
|
const getAttributeChanged = (mutationTarget: Node, attributeName: string, oldValue: string | null): boolean =>
|
||||||
oldValue !== attr(mutationTarget as HTMLElement, attributeName);
|
oldValue !== attr(mutationTarget as HTMLElement, attributeName);
|
||||||
|
|
||||||
@@ -31,42 +120,26 @@ export const createDOMObserver = (
|
|||||||
options?: DOMObserverOptions
|
options?: DOMObserverOptions
|
||||||
): DOMObserver => {
|
): DOMObserver => {
|
||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
const { _observeContent, _attributes, _ignoreContentChange, _eventContentChange } = options || {};
|
const { _observeContent, _attributes, _styleChangingAttributes, _eventContentChange, _ignoreContentChange } = options || {};
|
||||||
const eventContentChangeCallback = debounce(() => {
|
const {
|
||||||
if (isConnected) {
|
_updateElements: updateEventContentChangeElements,
|
||||||
callback([], false, true);
|
_destroy: destroyEventContentChange,
|
||||||
}
|
_update: updateEventContentChange,
|
||||||
});
|
} = createEventContentChange(
|
||||||
const refreshEventContentChange = (getElements: (selector: string) => Node[]) => {
|
target,
|
||||||
if (_eventContentChange) {
|
_observeContent && _eventContentChange,
|
||||||
const eventContentChanges = _eventContentChange();
|
new Map<Node, string>(),
|
||||||
const eventElmList = eventContentChanges.reduce<Array<[string, Node[]]>>((arr, item) => {
|
debounce(() => {
|
||||||
if (item) {
|
if (isConnected) {
|
||||||
const selector = item[0];
|
callback([], false, true);
|
||||||
const eventName = item[1];
|
}
|
||||||
const elements = eventName && selector && getElements(selector);
|
}, 80)
|
||||||
|
);
|
||||||
if (elements) {
|
|
||||||
push(arr, [eventName!, elements], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
each(eventElmList, (item) => {
|
|
||||||
const eventName = item[0];
|
|
||||||
const elements = item[1];
|
|
||||||
|
|
||||||
each(elements, (elm) => {
|
|
||||||
off(elm, eventName, eventContentChangeCallback);
|
|
||||||
on(elm, eventName, eventContentChangeCallback);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// MutationObserver
|
// MutationObserver
|
||||||
const observedAttributes = (_attributes || []).concat(styleChangingAttributes); // TODO: observer textarea attrs if textarea
|
const finalAttributes = _attributes || [];
|
||||||
|
const finalStyleChangingAttributes = _styleChangingAttributes || [];
|
||||||
|
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes); // TODO: observer textarea attrs if textarea
|
||||||
const observerCallback = (mutations: MutationRecord[]) => {
|
const observerCallback = (mutations: MutationRecord[]) => {
|
||||||
const targetChangedAttrs: string[] = [];
|
const targetChangedAttrs: string[] = [];
|
||||||
const totalAddedNodes: Node[] = [];
|
const totalAddedNodes: Node[] = [];
|
||||||
@@ -80,7 +153,7 @@ export const createDOMObserver = (
|
|||||||
const targetIsMutationTarget = target === mutationTarget;
|
const targetIsMutationTarget = target === mutationTarget;
|
||||||
const attributeChanged = isAttributesType && isString(attributeName) && getAttributeChanged(mutationTarget, attributeName!, oldValue);
|
const attributeChanged = isAttributesType && isString(attributeName) && getAttributeChanged(mutationTarget, attributeName!, oldValue);
|
||||||
const targetAttrChanged = attributeChanged && targetIsMutationTarget && !_observeContent;
|
const targetAttrChanged = attributeChanged && targetIsMutationTarget && !_observeContent;
|
||||||
const styleChangingAttrChanged = indexOf(styleChangingAttributes, attributeName) > -1 && attributeChanged;
|
const styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
|
||||||
|
|
||||||
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
||||||
|
|
||||||
@@ -101,7 +174,7 @@ export const createDOMObserver = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
||||||
refreshEventContentChange((selector) =>
|
updateEventContentChangeElements((selector) =>
|
||||||
totalAddedNodes.reduce<Node[]>((arr, node) => {
|
totalAddedNodes.reduce<Node[]>((arr, node) => {
|
||||||
push(arr, find(selector, node));
|
push(arr, find(selector, node));
|
||||||
return is(node, selector) ? push(arr, node) : arr;
|
return is(node, selector) ? push(arr, node) : arr;
|
||||||
@@ -114,6 +187,7 @@ export const createDOMObserver = (
|
|||||||
};
|
};
|
||||||
const mutationObserver: MutationObserver = new MutationObserverConstructor!(observerCallback);
|
const mutationObserver: MutationObserver = new MutationObserverConstructor!(observerCallback);
|
||||||
|
|
||||||
|
// Connect
|
||||||
mutationObserver.observe(target, {
|
mutationObserver.observe(target, {
|
||||||
attributes: true,
|
attributes: true,
|
||||||
attributeOldValue: true,
|
attributeOldValue: true,
|
||||||
@@ -122,17 +196,18 @@ export const createDOMObserver = (
|
|||||||
childList: _observeContent,
|
childList: _observeContent,
|
||||||
characterData: _observeContent,
|
characterData: _observeContent,
|
||||||
});
|
});
|
||||||
|
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
|
|
||||||
if (_observeContent) {
|
|
||||||
refreshEventContentChange((selector) => find(selector, target));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_disconnect: () => {
|
_disconnect: () => {
|
||||||
mutationObserver.disconnect();
|
if (isConnected) {
|
||||||
isConnected = false;
|
destroyEventContentChange();
|
||||||
|
mutationObserver.disconnect();
|
||||||
|
isConnected = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => {
|
||||||
|
updateEventContentChange(isConnected && _observeContent && newEventContentChange);
|
||||||
},
|
},
|
||||||
_update: () => {
|
_update: () => {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createStructureLifecycle } from 'lifecycles/structureLifecycle';
|
|||||||
import { Cache, appendChildren, addClass, contents, is, isHTMLElement, createDiv, each, push } 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 { createDOMObserver } from 'observers/domObserver';
|
||||||
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
||||||
import { classNameHost, classNamePadding, classNameViewport, classNameContent } from 'classnames';
|
import { classNameHost, classNamePadding, classNameViewport, classNameContent } from 'classnames';
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ const normalizeTarget = (target: OSTarget): OSTargetObject => {
|
|||||||
const OverlayScrollbars = (target: OSTarget, options?: any, extensions?: any): void => {
|
const OverlayScrollbars = (target: OSTarget, options?: any, extensions?: any): void => {
|
||||||
const osTarget: OSTargetObject = normalizeTarget(target);
|
const osTarget: OSTargetObject = normalizeTarget(target);
|
||||||
const lifecycles: Lifecycle<any>[] = [];
|
const lifecycles: Lifecycle<any>[] = [];
|
||||||
const { host } = osTarget;
|
const { host, content } = osTarget;
|
||||||
|
|
||||||
push(lifecycles, createStructureLifecycle(osTarget));
|
push(lifecycles, createStructureLifecycle(osTarget));
|
||||||
|
|
||||||
@@ -66,6 +67,16 @@ const OverlayScrollbars = (target: OSTarget, options?: any, extensions?: any): v
|
|||||||
|
|
||||||
createSizeObserver(host, onSizeChanged, { _appear: true, _direction: true });
|
createSizeObserver(host, onSizeChanged, { _appear: true, _direction: true });
|
||||||
createTrinsicObserver(host, onTrinsicChanged);
|
createTrinsicObserver(host, onTrinsicChanged);
|
||||||
|
createDOMObserver(host, () => {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
createDOMObserver(
|
||||||
|
content,
|
||||||
|
() => {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
{ _observeContent: true }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { OverlayScrollbars };
|
export { OverlayScrollbars };
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import { isUndefined } from 'support/utils/types';
|
||||||
import { each, push, 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 => {
|
||||||
if (passiveEventsSupport === undefined) {
|
if (isUndefined(passiveEventsSupport)) {
|
||||||
passiveEventsSupport = false;
|
passiveEventsSupport = false;
|
||||||
try {
|
try {
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ const validateRecursive = <T extends PlainObject>(
|
|||||||
typeString = key;
|
typeString = key;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const isEnumString = typeString === undefined;
|
const isEnumString = isUndefined(typeString);
|
||||||
if (isEnumString && isString(optionsValue)) {
|
if (isEnumString && isString(optionsValue)) {
|
||||||
// split it into a array which contains all possible values for example: ["yes", "no", "maybe"]
|
// split it into a array which contains all possible values for example: ["yes", "no", "maybe"]
|
||||||
const enumStringSplit = currTemplateType.split(' ');
|
const enumStringSplit = currTemplateType.split(' ');
|
||||||
|
|||||||
@@ -3,8 +3,16 @@ import { PlainObject } from 'typings';
|
|||||||
const ElementNodeType = Node.ELEMENT_NODE;
|
const ElementNodeType = Node.ELEMENT_NODE;
|
||||||
const { toString, hasOwnProperty } = Object.prototype;
|
const { toString, hasOwnProperty } = Object.prototype;
|
||||||
|
|
||||||
|
export function isUndefined(obj: any): obj is undefined {
|
||||||
|
return obj === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNull(obj: any): obj is null {
|
||||||
|
return obj === null;
|
||||||
|
}
|
||||||
|
|
||||||
export const type: (obj: any) => string = (obj) => {
|
export const type: (obj: any) => string = (obj) => {
|
||||||
return obj === undefined || obj === null
|
return isUndefined(obj) || isNull(obj)
|
||||||
? `${obj}`
|
? `${obj}`
|
||||||
: toString
|
: toString
|
||||||
.call(obj)
|
.call(obj)
|
||||||
@@ -28,14 +36,6 @@ export function isFunction(obj: any): obj is (...args: Array<unknown>) => unknow
|
|||||||
return typeof obj === 'function';
|
return typeof obj === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isUndefined(obj: any): obj is undefined {
|
|
||||||
return obj === undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNull(obj: any): obj is null {
|
|
||||||
return obj === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isArray(obj: any): obj is Array<any> {
|
export function isArray(obj: any): obj is Array<any> {
|
||||||
return Array.isArray(obj);
|
return Array.isArray(obj);
|
||||||
}
|
}
|
||||||
|
|||||||
+166
-57
@@ -4,7 +4,7 @@ import should from 'should';
|
|||||||
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
||||||
import { timeout } from '@/testing-browser/timeout';
|
import { timeout } from '@/testing-browser/timeout';
|
||||||
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
||||||
import { appendChildren, createDiv, removeElements, children, isArray, isNumber, liesBetween, hasClass } from 'support';
|
import { appendChildren, createDiv, removeElements, children, isArray, isNumber, liesBetween, hasClass, addClass, removeClass, on } from 'support';
|
||||||
|
|
||||||
import { createDOMObserver } from 'observers/domObserver';
|
import { createDOMObserver } from 'observers/domObserver';
|
||||||
|
|
||||||
@@ -29,11 +29,13 @@ 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 imgElmsSlot = document.querySelector('#target .content-nest');
|
||||||
|
const transitionElmsSlot = document.querySelector('#content-host .content');
|
||||||
|
|
||||||
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 addRemoveImgElms: HTMLButtonElement | null = document.querySelector('#addRemoveImgElms');
|
||||||
|
const addRemoveTransitionElms: HTMLButtonElement | null = document.querySelector('#addRemoveTransitionElms');
|
||||||
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');
|
||||||
@@ -47,8 +49,48 @@ const summaryBetween: HTMLElement | null = document.querySelector('#summary-betw
|
|||||||
|
|
||||||
const startBtn: HTMLButtonElement | null = document.querySelector('#start');
|
const startBtn: HTMLButtonElement | null = document.querySelector('#start');
|
||||||
|
|
||||||
|
const attrs = ['id', 'class', 'style', 'open'];
|
||||||
|
const contentChangeArr: Array<[string, string | ((elms: Node[]) => string)]> = [['img', 'load']];
|
||||||
const targetElmObservations: DOMObserverResult[] = [];
|
const targetElmObservations: DOMObserverResult[] = [];
|
||||||
const targetElmContentElmObservations: DOMObserverResult[] = [];
|
const targetElmContentElmObservations: DOMObserverResult[] = [];
|
||||||
|
|
||||||
|
createDOMObserver(
|
||||||
|
document.querySelector('#target') as HTMLElement,
|
||||||
|
(changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => {
|
||||||
|
targetElmObservations.push({ changedTargetAttrs, styleChanged, contentChanged });
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (targetChangesCountSlot) {
|
||||||
|
targetChangesCountSlot.textContent = `${targetElmObservations.length}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_styleChangingAttributes: attrs,
|
||||||
|
_attributes: attrs.concat(['data-target']),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const { _updateEventContentChange } = createDOMObserver(
|
||||||
|
document.querySelector('#target .content') as HTMLElement,
|
||||||
|
(changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => {
|
||||||
|
targetElmContentElmObservations.push({ changedTargetAttrs, styleChanged, contentChanged });
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (contentChangesCountSlot) {
|
||||||
|
contentChangesCountSlot.textContent = `${targetElmContentElmObservations.length}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_observeContent: true,
|
||||||
|
_styleChangingAttributes: attrs,
|
||||||
|
_attributes: attrs,
|
||||||
|
_eventContentChange: contentChangeArr,
|
||||||
|
_ignoreContentChange: (mutation) => {
|
||||||
|
const { target, attributeName } = mutation;
|
||||||
|
return attributeName ? !hasClass(target as Element, 'host') && liesBetween(target as Element, '.host', '.content') : false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const getTotalObservations = () => targetElmObservations.length + targetElmContentElmObservations.length;
|
const getTotalObservations = () => targetElmObservations.length + targetElmContentElmObservations.length;
|
||||||
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} 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[]) => {
|
||||||
@@ -231,8 +273,7 @@ const addRemoveTargetContentElmsFn = async () => {
|
|||||||
const addRemoveTargetContentBetweenElmsFn = async () => {
|
const addRemoveTargetContentBetweenElmsFn = async () => {
|
||||||
await addRemoveElementsTest(targetContentBetweenElmsSlot, targetElmContentElmObservations);
|
await addRemoveElementsTest(targetContentBetweenElmsSlot, targetElmContentElmObservations);
|
||||||
};
|
};
|
||||||
const addImgElmsFn = async () => {
|
const addRemoveImgElmsFn = async () => {
|
||||||
/*
|
|
||||||
const add = async () => {
|
const add = async () => {
|
||||||
const img = new Image(1, 1);
|
const img = new Image(1, 1);
|
||||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
@@ -263,21 +304,124 @@ const addImgElmsFn = async () => {
|
|||||||
await add();
|
await add();
|
||||||
await add();
|
await add();
|
||||||
await add();
|
await add();
|
||||||
*/
|
|
||||||
const add = async () => {
|
|
||||||
const img = new Image(1, 1);
|
|
||||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
|
||||||
|
|
||||||
//const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
// test event content change debounce
|
||||||
const imgHolder = createDiv('img');
|
const addMultiple = async () => {
|
||||||
appendChildren(imgHolder, img);
|
const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
||||||
|
const addMultipleItem = () => {
|
||||||
|
const img = new Image(1, 1);
|
||||||
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
|
|
||||||
appendChildren(imgElmsSlot, imgHolder);
|
const imgHolder = createDiv('img');
|
||||||
|
appendChildren(imgHolder, img);
|
||||||
|
|
||||||
|
appendChildren(imgElmsSlot, imgHolder);
|
||||||
|
};
|
||||||
|
|
||||||
|
before();
|
||||||
|
|
||||||
|
addMultipleItem();
|
||||||
|
addMultipleItem();
|
||||||
|
addMultipleItem();
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
add();
|
await addMultiple();
|
||||||
add();
|
|
||||||
add();
|
removeElements(document.querySelectorAll('.img'));
|
||||||
|
|
||||||
|
await timeout(250);
|
||||||
|
};
|
||||||
|
const addRemoveTransitionElmsFn = async () => {
|
||||||
|
const startTransition = async (elm: Element, expectTransitionEndContentChange: boolean) => {
|
||||||
|
await timeout(50); // time for css to apply class a bit later to trigger transition
|
||||||
|
|
||||||
|
const { before: beforeTransition, after: afterTransition, compare: compareTransition } = changedThrough(targetElmContentElmObservations);
|
||||||
|
beforeTransition();
|
||||||
|
removeClass(elm, 'resetTransition'); // IE...
|
||||||
|
addClass(elm, 'active');
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
on(
|
||||||
|
elm,
|
||||||
|
'transitionend',
|
||||||
|
async () => {
|
||||||
|
await waitForOrFailTest(() => {
|
||||||
|
afterTransition();
|
||||||
|
compareTransition(expectTransitionEndContentChange ? 2 : 1); // 2 because 1: added class mutation and 2: transition end event
|
||||||
|
|
||||||
|
const eventObservation = getLast(targetElmContentElmObservations);
|
||||||
|
should(eventObservation.contentChanged).equal(true);
|
||||||
|
should(eventObservation.styleChanged).equal(false);
|
||||||
|
should(eventObservation.changedTargetAttrs.length).equal(0);
|
||||||
|
resolve(1);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ _once: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
removeClass(elm, 'active');
|
||||||
|
addClass(elm, 'resetTransition'); // IE...
|
||||||
|
};
|
||||||
|
const add = async (expectTransitionEndContentChange: boolean) => {
|
||||||
|
const elm = createDiv(`transition ${expectTransitionEndContentChange ? 'highlight' : ''}`);
|
||||||
|
const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
||||||
|
|
||||||
|
before();
|
||||||
|
|
||||||
|
appendChildren(transitionElmsSlot, elm);
|
||||||
|
|
||||||
|
await waitForOrFailTest(() => {
|
||||||
|
after();
|
||||||
|
compare(1);
|
||||||
|
|
||||||
|
const eventObservation = getLast(targetElmContentElmObservations);
|
||||||
|
should(eventObservation.contentChanged).equal(true);
|
||||||
|
should(eventObservation.styleChanged).equal(false);
|
||||||
|
should(eventObservation.changedTargetAttrs.length).equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
await startTransition(elm, expectTransitionEndContentChange && true);
|
||||||
|
_updateEventContentChange(contentChangeArr);
|
||||||
|
await startTransition(elm, expectTransitionEndContentChange && false);
|
||||||
|
|
||||||
|
removeElements(elm);
|
||||||
|
|
||||||
|
await timeout(250);
|
||||||
|
};
|
||||||
|
|
||||||
|
await add(false);
|
||||||
|
|
||||||
|
_updateEventContentChange(
|
||||||
|
contentChangeArr.concat([
|
||||||
|
[
|
||||||
|
'.transition',
|
||||||
|
(elms) => {
|
||||||
|
elms.forEach((elm) => {
|
||||||
|
should(hasClass(elm as Element, 'transition')).equal(true);
|
||||||
|
});
|
||||||
|
return 'transitionend';
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await add(true);
|
||||||
};
|
};
|
||||||
const iterateTargetAttrChange = async () => {
|
const iterateTargetAttrChange = async () => {
|
||||||
await iterateAttrChange(setTargetAttr, targetElmObservations, (observation, selected) => {
|
await iterateAttrChange(setTargetAttr, targetElmObservations, (observation, selected) => {
|
||||||
@@ -320,7 +464,8 @@ 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);
|
addRemoveImgElms?.addEventListener('click', addRemoveImgElmsFn);
|
||||||
|
addRemoveTransitionElms?.addEventListener('click', addRemoveTransitionElmsFn);
|
||||||
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));
|
||||||
@@ -330,62 +475,26 @@ setFilteredContentBetweenAttr?.addEventListener('change', attrChangeListener(con
|
|||||||
setContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
setContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
||||||
setFilteredContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
setFilteredContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
|
||||||
|
|
||||||
createDOMObserver(
|
|
||||||
document.querySelector('#target') as HTMLElement,
|
|
||||||
(changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => {
|
|
||||||
targetElmObservations.push({ changedTargetAttrs, styleChanged, contentChanged });
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (targetChangesCountSlot) {
|
|
||||||
targetChangesCountSlot.textContent = `${targetElmObservations.length}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_attributes: ['data-target'],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
createDOMObserver(
|
|
||||||
document.querySelector('#target .content') as HTMLElement,
|
|
||||||
(changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => {
|
|
||||||
targetElmContentElmObservations.push({ changedTargetAttrs, styleChanged, contentChanged });
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (contentChangesCountSlot) {
|
|
||||||
contentChangesCountSlot.textContent = `${targetElmContentElmObservations.length}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_observeContent: true,
|
|
||||||
_eventContentChange: () => {
|
|
||||||
return [
|
|
||||||
['img', 'load'],
|
|
||||||
['iframe', 'load'],
|
|
||||||
];
|
|
||||||
},
|
|
||||||
_ignoreContentChange: (mutation) => {
|
|
||||||
const { target, attributeName } = mutation;
|
|
||||||
return attributeName ? !hasClass(target as Element, 'host') && liesBetween(target as Element, '.host', '.content') : false;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
setTestResult(null);
|
setTestResult(null);
|
||||||
|
|
||||||
await addImgElmsFn();
|
|
||||||
|
|
||||||
await addRemoveTargetElmsFn();
|
await addRemoveTargetElmsFn();
|
||||||
await addRemoveTargetContentElmsFn();
|
await addRemoveTargetContentElmsFn();
|
||||||
await addRemoveTargetContentBetweenElmsFn();
|
await addRemoveTargetContentBetweenElmsFn();
|
||||||
|
|
||||||
await iterateTargetAttrChange();
|
await iterateTargetAttrChange();
|
||||||
await iterateContentAttrChange();
|
await iterateContentAttrChange();
|
||||||
|
|
||||||
|
await addRemoveTransitionElmsFn();
|
||||||
|
|
||||||
await iterateContentBetweenAttrChange();
|
await iterateContentBetweenAttrChange();
|
||||||
await iterateContentHostElmAttrChange();
|
await iterateContentHostElmAttrChange();
|
||||||
|
|
||||||
await triggerContentSummaryChange();
|
await triggerContentSummaryChange();
|
||||||
await triggerBetweenSummaryChange();
|
await triggerBetweenSummaryChange();
|
||||||
|
|
||||||
|
await addRemoveImgElmsFn();
|
||||||
|
|
||||||
setTestResult(true);
|
setTestResult(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<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>
|
<button id="addRemoveImgElms">Image Elements</button>
|
||||||
|
<button id="addRemoveTransitionElms">Transition Elements</button>
|
||||||
|
|
||||||
<label for="setTargetAttr">setTargetAttr</label>
|
<label for="setTargetAttr">setTargetAttr</label>
|
||||||
<select name="setTargetAttr" id="setTargetAttr">
|
<select name="setTargetAttr" id="setTargetAttr">
|
||||||
|
|||||||
@@ -88,3 +88,22 @@ body {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transition {
|
||||||
|
width: 0;
|
||||||
|
height: 50px;
|
||||||
|
background: gray;
|
||||||
|
|
||||||
|
&.highlight {
|
||||||
|
background: cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.resetTransition {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
transition: width 2s ease;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
declare type StringNullUndefined = string | null | undefined;
|
declare type StringNullUndefined = string | null | undefined;
|
||||||
export declare type DOMOvserverEventContentChangeResult = Array<[StringNullUndefined, StringNullUndefined] | null | undefined>;
|
export declare type DOMObserverEventContentChange = Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined> | false | null | undefined;
|
||||||
export declare type DOMOvserverEventContentChange = () => DOMOvserverEventContentChangeResult;
|
|
||||||
export declare type DOMObserverIgnoreContentChange = (mutation: MutationRecord, domObserverTarget: HTMLElement, domObserverOptions: DOMObserverOptions | undefined) => boolean | null | undefined;
|
export declare type DOMObserverIgnoreContentChange = (mutation: MutationRecord, domObserverTarget: HTMLElement, domObserverOptions: DOMObserverOptions | undefined) => boolean | null | undefined;
|
||||||
export interface DOMObserverOptions {
|
export interface DOMObserverOptions {
|
||||||
_observeContent?: boolean;
|
_observeContent?: boolean;
|
||||||
_attributes?: string[];
|
_attributes?: string[];
|
||||||
|
_styleChangingAttributes?: string[];
|
||||||
|
_eventContentChange?: DOMObserverEventContentChange;
|
||||||
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
||||||
_eventContentChange?: DOMOvserverEventContentChange;
|
|
||||||
}
|
}
|
||||||
export interface DOMObserver {
|
export interface DOMObserver {
|
||||||
_disconnect: () => void;
|
_disconnect: () => void;
|
||||||
|
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
||||||
_update: () => void;
|
_update: () => void;
|
||||||
}
|
}
|
||||||
export declare const createDOMObserver: (target: HTMLElement, callback: (targetChangedAttrs: string[], targetStyleChanged: boolean, contentChanged: boolean) => any, options?: DOMObserverOptions | undefined) => DOMObserver;
|
export declare const createDOMObserver: (target: HTMLElement, callback: (targetChangedAttrs: string[], targetStyleChanged: boolean, contentChanged: boolean) => any, options?: DOMObserverOptions | undefined) => DOMObserver;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export declare const debounce: (functionToDebounce: (...args: any) => any, timeout?: number | undefined, maxWait?: number | undefined) => () => void;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from 'support/utils/array';
|
export * from 'support/utils/array';
|
||||||
export * from 'support/utils/equal';
|
export * from 'support/utils/equal';
|
||||||
|
export * from 'support/utils/function';
|
||||||
export * from 'support/utils/lexicon';
|
export * from 'support/utils/lexicon';
|
||||||
export * from 'support/utils/object';
|
export * from 'support/utils/object';
|
||||||
export * from 'support/utils/types';
|
export * from 'support/utils/types';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
export declare function isUndefined(obj: any): obj is undefined;
|
||||||
|
export declare function isNull(obj: any): obj is null;
|
||||||
export declare const type: (obj: any) => string;
|
export declare const type: (obj: any) => string;
|
||||||
export declare function isNumber(obj: any): obj is number;
|
export declare function isNumber(obj: any): obj is number;
|
||||||
export declare function isString(obj: any): obj is string;
|
export declare function isString(obj: any): obj is string;
|
||||||
export declare function isBoolean(obj: any): obj is boolean;
|
export declare function isBoolean(obj: any): obj is boolean;
|
||||||
export declare function isFunction(obj: any): obj is (...args: Array<unknown>) => unknown;
|
export declare function isFunction(obj: any): obj is (...args: Array<unknown>) => unknown;
|
||||||
export declare function isUndefined(obj: any): obj is undefined;
|
|
||||||
export declare function isNull(obj: any): obj is null;
|
|
||||||
export declare function isArray(obj: any): obj is Array<any>;
|
export declare function isArray(obj: any): obj is Array<any>;
|
||||||
export declare function isObject(obj: any): boolean;
|
export declare function isObject(obj: any): boolean;
|
||||||
export declare function isArrayLike<T extends PlainObject = any>(obj: any): obj is ArrayLike<T>;
|
export declare function isArrayLike<T extends PlainObject = any>(obj: any): obj is ArrayLike<T>;
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ export const setTestResult = (result: boolean | null) => {
|
|||||||
const elm = getTestResultElm();
|
const elm = getTestResultElm();
|
||||||
if (elm) {
|
if (elm) {
|
||||||
if (typeof result === 'boolean') {
|
if (typeof result === 'boolean') {
|
||||||
|
if (result) {
|
||||||
|
if (elm.getAttribute('class') === 'failed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
elm.setAttribute('class', result ? 'passed' : 'failed');
|
elm.setAttribute('class', result ? 'passed' : 'failed');
|
||||||
} else {
|
} else {
|
||||||
elm.removeAttribute('class');
|
elm.removeAttribute('class');
|
||||||
|
|||||||
Reference in New Issue
Block a user