mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-01 01:04:06 +03:00
improve domObserver
This commit is contained in:
+267
-57
@@ -1,7 +1,13 @@
|
||||
const ElementNodeType = Node.ELEMENT_NODE;
|
||||
const { toString, hasOwnProperty } = Object.prototype;
|
||||
function isUndefined(obj) {
|
||||
return obj === undefined;
|
||||
}
|
||||
function isNull(obj) {
|
||||
return obj === null;
|
||||
}
|
||||
const type = (obj) => {
|
||||
return obj === undefined || obj === null
|
||||
return isUndefined(obj) || isNull(obj)
|
||||
? `${obj}`
|
||||
: toString
|
||||
.call(obj)
|
||||
@@ -17,12 +23,6 @@ function isString(obj) {
|
||||
function isFunction(obj) {
|
||||
return typeof obj === 'function';
|
||||
}
|
||||
function isUndefined(obj) {
|
||||
return obj === undefined;
|
||||
}
|
||||
function isNull(obj) {
|
||||
return obj === null;
|
||||
}
|
||||
function isArray(obj) {
|
||||
return Array.isArray(obj);
|
||||
}
|
||||
@@ -121,6 +121,7 @@ function each(source, callback) {
|
||||
|
||||
return source;
|
||||
}
|
||||
const indexOf = (arr, item, fromIndex) => arr.indexOf(item, fromIndex);
|
||||
const push = (array, items, arrayIsSingleItem) => {
|
||||
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
|
||||
return array;
|
||||
@@ -136,6 +137,7 @@ const from = (arr) => {
|
||||
});
|
||||
return result;
|
||||
};
|
||||
const isEmptyArray = (array) => array && array.length === 0;
|
||||
const runEach = (arr, p1) => {
|
||||
const runFn = (fn) => fn && fn(p1);
|
||||
|
||||
@@ -148,6 +150,12 @@ const runEach = (arr, p1) => {
|
||||
|
||||
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) => {
|
||||
if (isElement(elm)) {
|
||||
const fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
||||
@@ -260,7 +268,7 @@ const getBoundingClientRect = (elm) => elm.getBoundingClientRect();
|
||||
let passiveEventsSupport;
|
||||
|
||||
const supportPassiveEvents = () => {
|
||||
if (passiveEventsSupport === undefined) {
|
||||
if (isUndefined(passiveEventsSupport)) {
|
||||
passiveEventsSupport = false;
|
||||
|
||||
try {
|
||||
@@ -373,6 +381,75 @@ function isEmptyObject(obj) {
|
||||
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 = {
|
||||
animationiterationcount: 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) {
|
||||
var module = { exports: {} };
|
||||
return fn(module, module.exports), module.exports;
|
||||
@@ -599,7 +629,7 @@ const validateRecursive = (options, template, optionsDiff, doWriteErrors, propPa
|
||||
typeString = key;
|
||||
}
|
||||
});
|
||||
const isEnumString = typeString === undefined;
|
||||
const isEnumString = isUndefined(typeString);
|
||||
|
||||
if (isEnumString && isString(optionsValue)) {
|
||||
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) => {
|
||||
if (isHTMLElement(target)) {
|
||||
const isTextarea = is(target, 'textarea');
|
||||
@@ -1197,7 +1395,7 @@ const normalizeTarget = (target) => {
|
||||
const OverlayScrollbars = (target, options, extensions) => {
|
||||
const osTarget = normalizeTarget(target);
|
||||
const lifecycles = [];
|
||||
const { host } = osTarget;
|
||||
const { host, content } = osTarget;
|
||||
push(lifecycles, createStructureLifecycle(osTarget));
|
||||
|
||||
const onSizeChanged = (directionCache) => {
|
||||
@@ -1223,6 +1421,18 @@ const OverlayScrollbars = (target, options, extensions) => {
|
||||
_direction: true,
|
||||
});
|
||||
createTrinsicObserver(host, onTrinsicChanged);
|
||||
createDOMObserver(host, () => {
|
||||
return null;
|
||||
});
|
||||
createDOMObserver(
|
||||
content,
|
||||
() => {
|
||||
return null;
|
||||
},
|
||||
{
|
||||
_observeContent: true,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
toString = _Object$prototype.toString,
|
||||
hasOwnProperty = _Object$prototype.hasOwnProperty;
|
||||
function isUndefined(obj) {
|
||||
return obj === undefined;
|
||||
}
|
||||
function isNull(obj) {
|
||||
return obj === null;
|
||||
}
|
||||
var type = function type(obj) {
|
||||
return obj === undefined || obj === null
|
||||
return isUndefined(obj) || isNull(obj)
|
||||
? '' + obj
|
||||
: toString
|
||||
.call(obj)
|
||||
@@ -28,12 +34,6 @@
|
||||
function isFunction(obj) {
|
||||
return typeof obj === 'function';
|
||||
}
|
||||
function isUndefined(obj) {
|
||||
return obj === undefined;
|
||||
}
|
||||
function isNull(obj) {
|
||||
return obj === null;
|
||||
}
|
||||
function isArray(obj) {
|
||||
return Array.isArray(obj);
|
||||
}
|
||||
@@ -136,6 +136,9 @@
|
||||
|
||||
return source;
|
||||
}
|
||||
var indexOf = function indexOf(arr, item, fromIndex) {
|
||||
return arr.indexOf(item, fromIndex);
|
||||
};
|
||||
var push = function push(array, items, arrayIsSingleItem) {
|
||||
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
|
||||
return array;
|
||||
@@ -151,6 +154,9 @@
|
||||
});
|
||||
return result;
|
||||
};
|
||||
var isEmptyArray = function isEmptyArray(array) {
|
||||
return array && array.length === 0;
|
||||
};
|
||||
var runEach = function runEach(arr, p1) {
|
||||
var runFn = function runFn(fn) {
|
||||
return fn && fn(p1);
|
||||
@@ -165,6 +171,12 @@
|
||||
|
||||
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) {
|
||||
if (isElement(elm)) {
|
||||
var fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
|
||||
@@ -292,7 +304,7 @@
|
||||
var passiveEventsSupport;
|
||||
|
||||
var supportPassiveEvents = function supportPassiveEvents() {
|
||||
if (passiveEventsSupport === undefined) {
|
||||
if (isUndefined(passiveEventsSupport)) {
|
||||
passiveEventsSupport = false;
|
||||
|
||||
try {
|
||||
@@ -423,6 +435,81 @@
|
||||
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 = {
|
||||
animationiterationcount: 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) {
|
||||
var module = { exports: {} };
|
||||
return fn(module, module.exports), module.exports;
|
||||
@@ -666,7 +700,7 @@
|
||||
typeString = key;
|
||||
}
|
||||
});
|
||||
var isEnumString = typeString === undefined;
|
||||
var isEnumString = isUndefined(typeString);
|
||||
|
||||
if (isEnumString && isString(optionsValue)) {
|
||||
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) {
|
||||
if (isHTMLElement(target)) {
|
||||
var isTextarea = is(target, 'textarea');
|
||||
@@ -1338,7 +1553,8 @@
|
||||
var OverlayScrollbars = function OverlayScrollbars(target, options, extensions) {
|
||||
var osTarget = normalizeTarget(target);
|
||||
var lifecycles = [];
|
||||
var host = osTarget.host;
|
||||
var host = osTarget.host,
|
||||
content = osTarget.content;
|
||||
push(lifecycles, createStructureLifecycle(osTarget));
|
||||
|
||||
var onSizeChanged = function onSizeChanged(directionCache) {
|
||||
@@ -1364,6 +1580,18 @@
|
||||
_direction: true,
|
||||
});
|
||||
createTrinsicObserver(host, onTrinsicChanged);
|
||||
createDOMObserver(host, function () {
|
||||
return null;
|
||||
});
|
||||
createDOMObserver(
|
||||
content,
|
||||
function () {
|
||||
return null;
|
||||
},
|
||||
{
|
||||
_observeContent: true,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
export type DOMOvserverEventContentChangeResult = Array<[StringNullUndefined, StringNullUndefined] | null | undefined>; // [selector, eventname]
|
||||
export type DOMOvserverEventContentChange = () => DOMOvserverEventContentChangeResult;
|
||||
export type DOMObserverEventContentChange =
|
||||
| Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined>
|
||||
| false
|
||||
| null
|
||||
| undefined;
|
||||
export type DOMObserverIgnoreContentChange = (
|
||||
mutation: MutationRecord,
|
||||
domObserverTarget: HTMLElement,
|
||||
domObserverOptions: DOMObserverOptions | undefined
|
||||
) => boolean | null | undefined;
|
||||
export interface DOMObserverOptions {
|
||||
_observeContent?: boolean;
|
||||
_attributes?: string[];
|
||||
_observeContent?: boolean; // do observe children and trigger content change
|
||||
_attributes?: string[]; // observed attributes
|
||||
_styleChangingAttributes?: string[]; // list of attributes that trigger a content change if changed
|
||||
_eventContentChange?: DOMObserverEventContentChange; // [selector, eventname]
|
||||
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
||||
_eventContentChange?: DOMOvserverEventContentChange;
|
||||
}
|
||||
export interface DOMObserver {
|
||||
_disconnect: () => void;
|
||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
||||
_update: () => void;
|
||||
}
|
||||
|
||||
const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
||||
const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];
|
||||
// const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
||||
// 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 =>
|
||||
oldValue !== attr(mutationTarget as HTMLElement, attributeName);
|
||||
|
||||
@@ -31,42 +120,26 @@ export const createDOMObserver = (
|
||||
options?: DOMObserverOptions
|
||||
): DOMObserver => {
|
||||
let isConnected = false;
|
||||
const { _observeContent, _attributes, _ignoreContentChange, _eventContentChange } = options || {};
|
||||
const eventContentChangeCallback = debounce(() => {
|
||||
if (isConnected) {
|
||||
callback([], false, true);
|
||||
}
|
||||
});
|
||||
const refreshEventContentChange = (getElements: (selector: string) => Node[]) => {
|
||||
if (_eventContentChange) {
|
||||
const eventContentChanges = _eventContentChange();
|
||||
const eventElmList = eventContentChanges.reduce<Array<[string, Node[]]>>((arr, item) => {
|
||||
if (item) {
|
||||
const selector = item[0];
|
||||
const eventName = item[1];
|
||||
const elements = eventName && selector && getElements(selector);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
const { _observeContent, _attributes, _styleChangingAttributes, _eventContentChange, _ignoreContentChange } = options || {};
|
||||
const {
|
||||
_updateElements: updateEventContentChangeElements,
|
||||
_destroy: destroyEventContentChange,
|
||||
_update: updateEventContentChange,
|
||||
} = createEventContentChange(
|
||||
target,
|
||||
_observeContent && _eventContentChange,
|
||||
new Map<Node, string>(),
|
||||
debounce(() => {
|
||||
if (isConnected) {
|
||||
callback([], false, true);
|
||||
}
|
||||
}, 80)
|
||||
);
|
||||
|
||||
// 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 targetChangedAttrs: string[] = [];
|
||||
const totalAddedNodes: Node[] = [];
|
||||
@@ -80,7 +153,7 @@ export const createDOMObserver = (
|
||||
const targetIsMutationTarget = target === mutationTarget;
|
||||
const attributeChanged = isAttributesType && isString(attributeName) && getAttributeChanged(mutationTarget, attributeName!, oldValue);
|
||||
const targetAttrChanged = attributeChanged && targetIsMutationTarget && !_observeContent;
|
||||
const styleChangingAttrChanged = indexOf(styleChangingAttributes, attributeName) > -1 && attributeChanged;
|
||||
const styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
|
||||
|
||||
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
||||
|
||||
@@ -101,7 +174,7 @@ export const createDOMObserver = (
|
||||
});
|
||||
|
||||
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
||||
refreshEventContentChange((selector) =>
|
||||
updateEventContentChangeElements((selector) =>
|
||||
totalAddedNodes.reduce<Node[]>((arr, node) => {
|
||||
push(arr, find(selector, node));
|
||||
return is(node, selector) ? push(arr, node) : arr;
|
||||
@@ -114,6 +187,7 @@ export const createDOMObserver = (
|
||||
};
|
||||
const mutationObserver: MutationObserver = new MutationObserverConstructor!(observerCallback);
|
||||
|
||||
// Connect
|
||||
mutationObserver.observe(target, {
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
@@ -122,17 +196,18 @@ export const createDOMObserver = (
|
||||
childList: _observeContent,
|
||||
characterData: _observeContent,
|
||||
});
|
||||
|
||||
isConnected = true;
|
||||
|
||||
if (_observeContent) {
|
||||
refreshEventContentChange((selector) => find(selector, target));
|
||||
}
|
||||
|
||||
return {
|
||||
_disconnect: () => {
|
||||
mutationObserver.disconnect();
|
||||
isConnected = false;
|
||||
if (isConnected) {
|
||||
destroyEventContentChange();
|
||||
mutationObserver.disconnect();
|
||||
isConnected = false;
|
||||
}
|
||||
},
|
||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => {
|
||||
updateEventContentChange(isConnected && _observeContent && newEventContentChange);
|
||||
},
|
||||
_update: () => {
|
||||
if (isConnected) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createStructureLifecycle } from 'lifecycles/structureLifecycle';
|
||||
import { Cache, appendChildren, addClass, contents, is, isHTMLElement, createDiv, each, push } from 'support';
|
||||
import { createSizeObserver } from 'observers/sizeObserver';
|
||||
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
||||
import { createDOMObserver } from 'observers/domObserver';
|
||||
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
||||
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 osTarget: OSTargetObject = normalizeTarget(target);
|
||||
const lifecycles: Lifecycle<any>[] = [];
|
||||
const { host } = osTarget;
|
||||
const { host, content } = 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 });
|
||||
createTrinsicObserver(host, onTrinsicChanged);
|
||||
createDOMObserver(host, () => {
|
||||
return null;
|
||||
});
|
||||
createDOMObserver(
|
||||
content,
|
||||
() => {
|
||||
return null;
|
||||
},
|
||||
{ _observeContent: true }
|
||||
);
|
||||
};
|
||||
|
||||
export { OverlayScrollbars };
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { isUndefined } from 'support/utils/types';
|
||||
import { each, push, runEach } from 'support/utils/array';
|
||||
|
||||
let passiveEventsSupport: boolean;
|
||||
const supportPassiveEvents = (): boolean => {
|
||||
if (passiveEventsSupport === undefined) {
|
||||
if (isUndefined(passiveEventsSupport)) {
|
||||
passiveEventsSupport = false;
|
||||
try {
|
||||
/* eslint-disable */
|
||||
|
||||
@@ -86,7 +86,7 @@ const validateRecursive = <T extends PlainObject>(
|
||||
typeString = key;
|
||||
}
|
||||
});
|
||||
const isEnumString = typeString === undefined;
|
||||
const isEnumString = isUndefined(typeString);
|
||||
if (isEnumString && isString(optionsValue)) {
|
||||
// split it into a array which contains all possible values for example: ["yes", "no", "maybe"]
|
||||
const enumStringSplit = currTemplateType.split(' ');
|
||||
|
||||
@@ -3,8 +3,16 @@ import { PlainObject } from 'typings';
|
||||
const ElementNodeType = Node.ELEMENT_NODE;
|
||||
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) => {
|
||||
return obj === undefined || obj === null
|
||||
return isUndefined(obj) || isNull(obj)
|
||||
? `${obj}`
|
||||
: toString
|
||||
.call(obj)
|
||||
@@ -28,14 +36,6 @@ export function isFunction(obj: any): obj is (...args: Array<unknown>) => unknow
|
||||
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> {
|
||||
return Array.isArray(obj);
|
||||
}
|
||||
|
||||
+166
-57
@@ -4,7 +4,7 @@ import should from 'should';
|
||||
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
||||
import { timeout } from '@/testing-browser/timeout';
|
||||
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';
|
||||
|
||||
@@ -29,11 +29,13 @@ const targetElmsSlot = document.querySelector('#target .host-nest-item');
|
||||
const targetContentElmsSlot = document.querySelector('#target .content .content-nest');
|
||||
const targetContentBetweenElmsSlot = document.querySelector('#content-host');
|
||||
const imgElmsSlot = document.querySelector('#target .content-nest');
|
||||
const transitionElmsSlot = document.querySelector('#content-host .content');
|
||||
|
||||
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetElms');
|
||||
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentElms');
|
||||
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 setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector('#setFilteredTargetAttr');
|
||||
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 attrs = ['id', 'class', 'style', 'open'];
|
||||
const contentChangeArr: Array<[string, string | ((elms: Node[]) => string)]> = [['img', 'load']];
|
||||
const targetElmObservations: 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 getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
||||
const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObserverResult[]) => {
|
||||
@@ -231,8 +273,7 @@ const addRemoveTargetContentElmsFn = async () => {
|
||||
const addRemoveTargetContentBetweenElmsFn = async () => {
|
||||
await addRemoveElementsTest(targetContentBetweenElmsSlot, targetElmContentElmObservations);
|
||||
};
|
||||
const addImgElmsFn = async () => {
|
||||
/*
|
||||
const addRemoveImgElmsFn = async () => {
|
||||
const add = async () => {
|
||||
const img = new Image(1, 1);
|
||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||
@@ -263,21 +304,124 @@ const addImgElmsFn = async () => {
|
||||
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);
|
||||
const imgHolder = createDiv('img');
|
||||
appendChildren(imgHolder, img);
|
||||
// test event content change debounce
|
||||
const addMultiple = async () => {
|
||||
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();
|
||||
add();
|
||||
add();
|
||||
await addMultiple();
|
||||
|
||||
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 () => {
|
||||
await iterateAttrChange(setTargetAttr, targetElmObservations, (observation, selected) => {
|
||||
@@ -320,7 +464,8 @@ const triggerBetweenSummaryChange = async () => {
|
||||
addRemoveTargetElms?.addEventListener('click', addRemoveTargetElmsFn);
|
||||
addRemoveTargetContentElms?.addEventListener('click', addRemoveTargetContentElmsFn);
|
||||
addRemoveTargetContentBetweenElms?.addEventListener('click', addRemoveTargetContentBetweenElmsFn);
|
||||
addImgElms?.addEventListener('click', addImgElmsFn);
|
||||
addRemoveImgElms?.addEventListener('click', addRemoveImgElmsFn);
|
||||
addRemoveTransitionElms?.addEventListener('click', addRemoveTransitionElmsFn);
|
||||
setTargetAttr?.addEventListener('change', attrChangeListener(targetElm));
|
||||
setFilteredTargetAttr?.addEventListener('change', attrChangeListener(targetElm));
|
||||
setContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
|
||||
@@ -330,62 +475,26 @@ setFilteredContentBetweenAttr?.addEventListener('change', attrChangeListener(con
|
||||
setContentHostElmAttr?.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 () => {
|
||||
setTestResult(null);
|
||||
|
||||
await addImgElmsFn();
|
||||
|
||||
await addRemoveTargetElmsFn();
|
||||
await addRemoveTargetContentElmsFn();
|
||||
await addRemoveTargetContentBetweenElmsFn();
|
||||
|
||||
await iterateTargetAttrChange();
|
||||
await iterateContentAttrChange();
|
||||
|
||||
await addRemoveTransitionElmsFn();
|
||||
|
||||
await iterateContentBetweenAttrChange();
|
||||
await iterateContentHostElmAttrChange();
|
||||
|
||||
await triggerContentSummaryChange();
|
||||
await triggerBetweenSummaryChange();
|
||||
|
||||
await addRemoveImgElmsFn();
|
||||
|
||||
setTestResult(true);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<button id="addRemoveTargetElms">Target Elements</button>
|
||||
<button id="addRemoveTargetContentElms">Content 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>
|
||||
<select name="setTargetAttr" id="setTargetAttr">
|
||||
|
||||
@@ -88,3 +88,22 @@ body {
|
||||
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;
|
||||
export declare type DOMOvserverEventContentChangeResult = Array<[StringNullUndefined, StringNullUndefined] | null | undefined>;
|
||||
export declare type DOMOvserverEventContentChange = () => DOMOvserverEventContentChangeResult;
|
||||
export declare type DOMObserverEventContentChange = Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined> | false | null | undefined;
|
||||
export declare type DOMObserverIgnoreContentChange = (mutation: MutationRecord, domObserverTarget: HTMLElement, domObserverOptions: DOMObserverOptions | undefined) => boolean | null | undefined;
|
||||
export interface DOMObserverOptions {
|
||||
_observeContent?: boolean;
|
||||
_attributes?: string[];
|
||||
_styleChangingAttributes?: string[];
|
||||
_eventContentChange?: DOMObserverEventContentChange;
|
||||
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
||||
_eventContentChange?: DOMOvserverEventContentChange;
|
||||
}
|
||||
export interface DOMObserver {
|
||||
_disconnect: () => void;
|
||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
||||
_update: () => void;
|
||||
}
|
||||
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/equal';
|
||||
export * from 'support/utils/function';
|
||||
export * from 'support/utils/lexicon';
|
||||
export * from 'support/utils/object';
|
||||
export * from 'support/utils/types';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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 function isNumber(obj: any): obj is number;
|
||||
export declare function isString(obj: any): obj is string;
|
||||
export declare function isBoolean(obj: any): obj is boolean;
|
||||
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 isObject(obj: any): boolean;
|
||||
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();
|
||||
if (elm) {
|
||||
if (typeof result === 'boolean') {
|
||||
if (result) {
|
||||
if (elm.getAttribute('class') === 'failed') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
elm.setAttribute('class', result ? 'passed' : 'failed');
|
||||
} else {
|
||||
elm.removeAttribute('class');
|
||||
|
||||
Reference in New Issue
Block a user