WIP: v2.0.0 intial commit

This commit is contained in:
Rene
2020-07-14 20:10:26 +02:00
parent 97abdf77ab
commit eedde6b82b
171 changed files with 4327 additions and 71194 deletions
+3
View File
@@ -0,0 +1,3 @@
import { jsAPI } from 'core/compatibility/vendors';
export const resizeObserver: any | undefined = jsAPI('ResizeObserver');
+7
View File
@@ -0,0 +1,7 @@
export const mouseButton: (event: MouseEvent) => number = (event) => {
const button: number = event.button;
if (!event.which && button !== undefined)
return (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));
else
return event.which;
}
+4
View File
@@ -0,0 +1,4 @@
export * from 'core/compatibility/vendors';
export * from 'core/compatibility/apis';
export * from 'core/compatibility/events';
+99
View File
@@ -0,0 +1,99 @@
import { each } from 'core/utils';
import { createDiv } from 'core/dom';
const firstLetterToUpper: (str: string) => string = (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const getDummyStyle: () => CSSStyleDeclaration = () => {
return createDiv().style;
}
//https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix
export const cssPrefixes: ReadonlyArray<string> = ['-webkit-', '-moz-', '-o-', '-ms-'];
export const jsPrefixes: ReadonlyArray<string> = ['WebKit', 'Moz', 'O', 'MS', 'webkit', 'moz', 'o', 'ms'];
export const jsCache: { [key: string]: any } = {};
export const cssCache: { [key: string]: string } = {};
/**
* Gets the name of the given CSS property with vendor prefix if it isn't supported without, or undefined if unsupported.
* @param name The name of the CSS property which shall be get.
*/
export const cssProperty: (name: string) => string | undefined = (name) => {
let result: string | undefined = cssCache[name];
if (cssCache.hasOwnProperty(name))
return result;
const uppercasedName: string = firstLetterToUpper(name);
const elmStyle: CSSStyleDeclaration = getDummyStyle();
each(cssPrefixes, (prefix: string) => {
const prefixWithoutDashes: string = prefix.replace(/-/g, '');
const resultPossibilities: Array<string> = [
name, //transition
prefix + name, //-webkit-transition
prefixWithoutDashes + uppercasedName, //webkitTransition
firstLetterToUpper(prefixWithoutDashes) + uppercasedName //WebkitTransition
];
result = resultPossibilities.find((resultPossibility: string) => elmStyle[resultPossibility] !== undefined);
return !result;
});
cssCache[name] = result;
return result;
}
/**
* Get the name of the given CSS property value(s), with vendor prefix if it isn't supported wuthout, or undefined if no value is supported.
* @param property The CSS property to which the CSS property value(s) belong.
* @param values The value(s) separated by spaces which shall be get.
* @param suffix A suffix which is added to each value in case the value is a function or something else more advanced.
*/
export const cssPropertyValue: (property: string, values: string, suffix?: string) => string | undefined = (property, values, suffix) => {
const name: string = property + ' ' + values;
let result: string | undefined = cssCache[name];
if (cssCache.hasOwnProperty(name))
return result;
const dummyStyle: CSSStyleDeclaration = getDummyStyle();
const possbleValues: Array<string> = values.split(' ');
const preparedSuffix: string = suffix || '';
const cssPrefixesWithFirstEmpty = [''].concat(cssPrefixes);
each(possbleValues, (possibleValue: string) => {
each(cssPrefixesWithFirstEmpty, (prefix: string) => {
const prop = prefix + possibleValue;
dummyStyle.cssText = property + ':' + prop + preparedSuffix;
if (dummyStyle.length) {
result = prop;
return false;
}
});
return !result;
});
cssCache[name] = result;
return result;
}
/**
* Get the requested JS function, object or constructor with vendor prefix if it isn't supported without or undefined if unsupported.
* @param name The name of the JS function, object or constructor.
*/
export const jsAPI: (name: string) => any = (name) => {
let result: any = jsCache[name] || window[name];
if (jsCache.hasOwnProperty(name))
return result;
each(jsPrefixes, (prefix: string) => {
result = result || window[prefix + firstLetterToUpper(name)];
return !result;
});
jsCache[name] = result;
return result;
}
+54
View File
@@ -0,0 +1,54 @@
/**
* Gets or sets a attribute with the given attribute of the given element depending whether the value attribute is given.
* Returns null if the element has no attribute with the given name.
* @param elm The element of which the attribute shall be get or set.
* @param attrName The attribute name which shall be get or set.
* @param value The value of the attribute which shall be set.
*/
export const attr: (elm: Element, attrName: string, value?: string) => string | null | void = (elm, attrName, value) => {
if (value === undefined)
return elm.getAttribute(attrName);
elm.setAttribute(attrName, value);
}
/**
* Removes the given attribute from the given element.
* @param elm The element of which the attribute shall be removed.
* @param attrName The attribute name.
*/
export const removeAttr: (elm: Element, attrName: string) => void = (elm, attrName) => {
elm.removeAttribute(attrName);
}
/**
* Gets or sets the scrollLeft value of the given element depending whether the value attribute is given.
* @param elm The element of which the scrollLeft value shall be get or set.
* @param value The scrollLeft value which shall be set.
*/
export const scrollLeft: (elm: HTMLElement, value?: number) => number | void = (elm, value) => {
if (value === undefined)
return elm.scrollLeft;
elm.scrollLeft = value;
}
/**
* Gets or sets the scrollTop value of the given element depending whether the value attribute is given.
* @param elm The element of which the scrollTop value shall be get or set.
* @param value The scrollTop value which shall be set.
*/
export const scrollTop: (elm: HTMLElement, value?: number) => number | void = (elm, value) => {
if (value === undefined)
return elm.scrollTop;
elm.scrollTop = value;
}
/**
* Gets or sets the value of the given input element depending whether the value attribute is given.
* @param elm The input element of which the value shall be get or set.
* @param value The value which shall be set.
*/
export const val: (elm: HTMLInputElement, value?: string) => string | void = (elm, value) => {
if (value === undefined)
return elm.value;
elm.value = value;
}
+59
View File
@@ -0,0 +1,59 @@
import { isString } from 'core/utils/types';
const rnothtmlwhite: RegExp = (/[^\x20\t\r\n\f]+/g);
/**
* Check whether the given element has the given class name.
* @param elm The element.
* @param className The class name.
*/
export const hasClass: (elm: Element, className: string) => boolean = (elm, className) => {
return elm.classList.contains(className);
}
/**
* Adds the given class name(s) to the given element.
* @param elm The element.
* @param className The class name(s) which shall be added. (separated by spaces)
*/
export const addClass: (elm: Element, className: string) => void = (elm, className) => {
let clazz: string;
let i: number = 0;
if (isString(className)) {
const classes: Array<string> = className.match(rnothtmlwhite) || [];
while ((clazz = classes[i++]))
elm.classList.add(clazz);
}
}
/**
* Removes the given class name(s) from the given element.
* @param elm The element.
* @param className The class name(s) which shall be removed. (separated by spaces)
*/
export const removeClass: (elm: Element, className: string) => void = (elm, className) => {
let clazz: string;
let i: number = 0;
if (isString(className)) {
const classes: Array<string> = className.match(rnothtmlwhite) || [];
while ((clazz = classes[i++]))
elm.classList.remove(clazz);
}
}
/**
* Adds or removes the given class name(s) from the given element depending on the given condition.
* Condition true means add class name(s), false means remove class name(s).
* @param elm The element.
* @param className The class name(s) which shall be added or removed. (separated by spaces)
*/
export const conditionalClass: (elm: Element, className: string, condition: boolean) => void = (elm, className, condition) => {
if (condition) {
addClass(elm, className);
}
else {
removeClass(elm, className);
}
}
+14
View File
@@ -0,0 +1,14 @@
import { each } from 'core/utils/arrays';
import { contents } from 'core/dom/traversal';
import { removeElements } from 'core/dom/manipulation';
export const createDiv: () => HTMLDivElement = () => {
return document.createElement('div');
}
export const createDOM: (html: string) => ReadonlyArray<Node> = (html) => {
const elm = createDiv();
elm.innerHTML = html.trim();
return each(contents(elm), (elm) => removeElements(elm));
}
+7
View File
@@ -0,0 +1,7 @@
export * from 'core/dom/attributes';
export * from 'core/dom/classes';
export * from 'core/dom/create';
export * from 'core/dom/style';
export * from 'core/dom/manipulation';
export * from 'core/dom/offset';
export * from 'core/dom/traversal';
+91
View File
@@ -0,0 +1,91 @@
import { isArrayLike, isHTMLElement } from 'core/utils/types';
import { each } from 'core/utils/arrays';
import { parent } from 'core/dom/traversal';
type NodeCollection = ArrayLike<Node> | Node | undefined | null;
/**
* Inserts Nodes before the given preferredAnchor element.
* @param parent The parent of the preferredAnchor element or the element which shall be the parent of the inserted Nodes.
* @param preferredAnchor The element before which the Nodes shall be inserted or null if the elements shall be appended at the end.
* @param insertedElms The Nodes which shall be inserted.
*/
const before: (parent: Node | null, preferredAnchor: Node | null, insertedElms: NodeCollection) => void = (parent, preferredAnchor, insertedElms) => {
if (insertedElms) {
let anchor: Node | null = preferredAnchor;
let fragment: DocumentFragment | Node | undefined | null;
// parent must be defined
if (parent) {
if (isArrayLike(insertedElms)) {
fragment = document.createDocumentFragment();
// append all insertedElms to the fragment and if one of these is the anchor, change the anchor
each(insertedElms, (insertedElm) => {
if (insertedElm === anchor) {
anchor = insertedElm.previousSibling;
}
fragment!.appendChild(insertedElm);
});
}
else {
fragment = insertedElms;
}
// if the preferred anchor isn't null set it to a valid anchor
if (preferredAnchor) {
if (!anchor) {
anchor = parent.firstChild;
}
else if (anchor !== preferredAnchor) {
anchor = anchor.nextSibling;
}
}
parent.insertBefore(fragment, anchor);
}
}
}
/**
* Appends the given children at the end of the given Node.
* @param node The Node to which the children shall be appended.
* @param children The Nodes which shall be appended.
*/
export const appendChildren: (node: Node | null, children: NodeCollection) => void = (node, children) => { before(node, null, children) };
/**
* Prepends the given children at the start of the given Node.
* @param node The Node to which the children shall be prepended.
* @param children The Nodes which shall be prepended.
*/
export const prependChildren: (node: Node | null, children: NodeCollection) => void = (node, children) => { before(node, node && node.firstChild, children) };
/**
* Inserts the given Nodes before the given Node.
* @param node The Node before which the given Nodes shall be inserted.
* @param insertedNodes The Nodes which shall be inserted.
*/
export const insertBefore: (node: Node | null, insertedNodes: NodeCollection) => void = (node, insertedNodes) => { before(parent(node), node, insertedNodes) };
/**
* Inserts the given Nodes after the given Node.
* @param node The Node after which the given Nodes shall be inserted.
* @param insertedNodes The Nodes which shall be inserted.
*/
export const insertAfter: (node: Node | null, insertedNodes: NodeCollection) => void = (node, insertedNodes) => { before(parent(node), node && node.nextSibling, insertedNodes) };
/**
* Removes the given Nodes from their parent.
* @param nodes The Nodes which shall be removed.
*/
export const removeElements: (nodes: NodeCollection) => void = (nodes) => {
if (isArrayLike(nodes)) {
each(Array.from(nodes), (e) => removeElements(e));
}
else if (nodes) {
const parentNode = nodes.parentNode;
if (parentNode)
parentNode.removeChild(nodes);
}
}
+14
View File
@@ -0,0 +1,14 @@
export const offset = (elm: HTMLElement) => {
const rect = elm.getBoundingClientRect();
return {
top: rect.top + window.pageXOffset,
left: rect.left + window.pageYOffset
};
}
export const position = (elm: HTMLElement) => {
return {
top: elm.offsetTop,
left: elm.offsetLeft
};
}
+62
View File
@@ -0,0 +1,62 @@
import { isString, isNumber, isUndefined } from 'core/utils/types';
type cssStyleObj = { [key: string]: string | number };
const cssNumber = {
animationIterationCount: true,
columnCount: true,
fillOpacity: true,
flexGrow: true,
flexShrink: true,
fontWeight: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
widows: true,
zIndex: true,
zoom: true
};
const setCSSVal: (elm: HTMLElement, prop: string, val: string | number) => void = (elm, prop, val) => {
try {
if (elm.style[prop] !== undefined) {
elm.style[prop] = parseCSSVal(prop, val);
}
} catch (e) { }
}
const parseCSSVal: (prop: string, val: string | number) => string | number = (prop, val) => {
return !cssNumber[prop.toLowerCase()] && isNumber(val) ? val + 'px' : val;
}
export function style(elm: HTMLElement, styles: string | cssStyleObj): string;
export function style(elm: HTMLElement, styles: string | cssStyleObj, val: string | number): void;
export function style(elm: HTMLElement, styles: string | cssStyleObj, val?: string | number): string | void {
const getCptStyle: Function = window.getComputedStyle;
if (isString(styles)) {
if (isUndefined(val)) {
const cptStyle: CSSStyleDeclaration = getCptStyle(elm, null);
//https://bugzilla.mozilla.org/show_bug.cgi?id=548397 can be null sometimes if iframe with display: none (firefox only!)
return cptStyle != null ? cptStyle.getPropertyValue(styles) : elm.style[styles];
}
else {
setCSSVal(elm, styles, val);
}
}
else {
for (const key in styles)
setCSSVal(elm, key, styles[key]);
}
}
export const hide: (elm: HTMLElement) => void = (elm) => {
elm.style.display = 'none';
}
export const show: (elm: HTMLElement) => void = (elm) => {
elm.style.display = 'block';
}
+52
View File
@@ -0,0 +1,52 @@
import { each } from 'core/utils/arrays';
const elementIsVisible: (elm: HTMLElement) => boolean = (elm) => {
return !!(elm.offsetWidth || elm.offsetHeight || elm.getClientRects().length);
}
export const find: (selector: string, elm?: Element | null) => ReadonlyArray<Element> = (selector, elm?) => {
const arr: Array<Element> = [];
each((elm || document).querySelectorAll(selector), (e: Element) => {
arr.push(e);
});
return arr;
}
export const findFirst: (selector: string, elm?: Element | null) => Element | null = (selector, elm?) => {
return (elm || document).querySelector(selector);
}
export const is: (elm: Element | null, selector: string) => boolean = (elm, selector) => {
if (elm) {
if (selector === ':visible')
return elementIsVisible(elm as HTMLElement);
if (selector === ':hidden')
return !elementIsVisible(elm as HTMLElement);
if (elm.matches(selector))
return true;
}
return false;
}
export const children: (elm: Element | null, selector?: string) => ReadonlyArray<Element> = (elm, selector?) => {
const children: Array<Element> = [];
each(elm && elm.children, (child: Element) => {
if (selector) {
if (child.matches(selector))
children.push(child);
}
else
children.push(child);
});
return children;
}
export const contents: (elm: Element | null) => ReadonlyArray<ChildNode> = (elm) => {
return elm ? Array.from<ChildNode>(elm.childNodes) : [];
}
export const parent: (elm: Node | null) => Node | null = (elm) => elm ? elm.parentElement : null;
+2
View File
@@ -0,0 +1,2 @@
export * from 'core/options/validation';
export * from 'core/options/transformation';
+26
View File
@@ -0,0 +1,26 @@
import { OptionsTemplate, OptionsAndOptionsTemplate, PlainObject, OptionsTemplateTypes } from "core/typings";
import { isArray, isObject } from "core/utils/types";
import { each } from "core/utils/arrays";
/**
* Transforms the given OptionsAndOptionsTemplate<T> object to its corresponding generic (T) Object or its corresponding Template object.
* @param optionsWithOptionsTemplate The OptionsAndOptionsTemplate<T> object which shall be converted.
* @param toTemplate True if the given OptionsAndOptionsTemplate<T> shall be converted to its corresponding Template object.
*/
export function transform<T extends Required<T>>(optionsWithOptionsTemplate: OptionsAndOptionsTemplate<T>): T;
export function transform<T extends Required<T>>(optionsWithOptionsTemplate: OptionsAndOptionsTemplate<T>, toTemplate: true | void): OptionsTemplate<T>;
export function transform<T extends Required<T>>(optionsWithOptionsTemplate: OptionsAndOptionsTemplate<T>, toTemplate?: true | void): OptionsTemplate<T> | T {
const result: any = {};
each(Object.keys(optionsWithOptionsTemplate), (key: Extract<keyof T, string>) => {
const val: PlainObject | OptionsTemplateTypes | Array<OptionsTemplateTypes> = optionsWithOptionsTemplate[key];
/* istanbul ignore else */
if (isArray(val))
result[key] = val[toTemplate ? 1 : 0];
else if (isObject(val))
result[key] = transform(val as OptionsAndOptionsTemplate<typeof val>, toTemplate);
});
return result;
};
+164
View File
@@ -0,0 +1,164 @@
import { each, indexOf } from 'core/utils/arrays';
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'core/utils/types';
import { PlainObject, OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, OptionsValidated, Func, OptionsValidatedResult } from 'core/typings';
const stringify = JSON.stringify;
/**
* A prefix and suffix tuple which serves as recognition pattern for template types.
*/
const templateTypePrefixSuffix: readonly [string, string] = ['__TPL_', '_TYPE__'];
/**
* A object which serves as a mapping for "normal" types and template types.
* Key = normal type string
* value = template type string
*/
const optionsTemplateTypes: OptionsTemplateTypesDictionary = [
'boolean',
'number',
'string',
'array',
'object',
'function',
'null'
].reduce((result, item) => {
result[item] = templateTypePrefixSuffix[0] + item + templateTypePrefixSuffix[1];
return result;
}, {} as OptionsTemplateTypesDictionary);
/**
* Validates the given options object according to the given template object and returns a object which looks like:
* {
* foreign : a object which consists of properties which aren't defined inside the template. (foreign properties)
* validated : a object which consists only of valid properties. (property name is inside the template and value has a correct type)
* }
* @param options The options object which shall be validated.
* @param template The template according to which the options object shall be validated.
* @param optionsDiff When provided the returned validated object will only have properties which are different to this objects properties.
* Example (assume all properties are valid to the template):
* Options object : { a: 'a', b: 'b', c: 'c' }
* optionsDiff object : { a: 'a', b: 'b', c: undefined }
* Returned validated object : { c: 'c' }
* Because the value of the properties a and b didn't change, they aren't included in the returned object.
* Without the optionsDiff object the returned validated object would be: { a: 'a', b: 'b', c: 'c' }
* @param doWriteErrors True if errors shall be logged into the console, false otherwise.
* @param propPath The propertyPath which lead to this object. (used for error logging)
*/
const validateRecursive = function <T extends PlainObject>(options: T, template: OptionsTemplate<Required<T>>, optionsDiff: OptionsValidated<T>, doWriteErrors?: boolean, propPath?: string): OptionsValidatedResult<T> {
const validatedOptions: OptionsValidated<T> = {};
const optionsCopy: T = Object.assign({}, options);
const props = Object.keys(template).filter(prop => options.hasOwnProperty(prop));
each(props, (prop: Extract<keyof T, string>) => {
const optionsDiffValue: any = isUndefined(optionsDiff[prop]) ? {} : optionsDiff[prop];
const optionsValue: any = options[prop];
const templateValue: PlainObject | string | OptionsTemplateTypes | Array<OptionsTemplateTypes> = template[prop];
const templateIsComplex = isPlainObject(templateValue);
const propPrefix = propPath ? propPath + '.' : '';
//if the template has a object as value, it means that the options are complex (verschachtelt)
if (templateIsComplex && isPlainObject(optionsValue)) {
const validatedResult = validateRecursive(optionsValue, templateValue as PlainObject, optionsDiffValue, doWriteErrors, propPrefix + prop);
validatedOptions[prop] = validatedResult.validated;
optionsCopy[prop] = validatedResult.foreign as any;
each([optionsCopy, validatedOptions], (value) => {
if (isEmptyObject(value[prop])) {
delete value[prop];
}
});
}
else if (!templateIsComplex) {
let isValid = false;
const errorEnumStrings: Array<string> = [];
const errorPossibleTypes: Array<string> = [];
const optionsValueType = type(optionsValue);
const templateValueArr: Array<string | OptionsTemplateTypes> = !isArray(templateValue) ? [templateValue as string | OptionsTemplateTypes] : templateValue as Array<OptionsTemplateTypes>;
each(templateValueArr, (currTemplateType) => {
//if currType value isn't inside possibleTemplateTypes we assume its a enum string value
const isEnumString = indexOf(Object.values(optionsTemplateTypes), currTemplateType) < 0;
if (isEnumString && isString(optionsValue)) {
//split it into a array which contains all possible values for example: ["yes", "no", "maybe"]
const enumStringSplit = currTemplateType.split(' ');
isValid = !!enumStringSplit.find(possibility => possibility === optionsValue);
// build error message
errorEnumStrings.push(...enumStringSplit);
}
else {
isValid = optionsTemplateTypes[optionsValueType] === currTemplateType;
}
// build error message
errorPossibleTypes.push(isEnumString ? optionsTemplateTypes.string : currTemplateType);
// continue if invalid, break if valid
return !isValid;
});
if (isValid) {
const doStringifyComparison = isArray(optionsValue) || isPlainObject(optionsValue);
if (doStringifyComparison ? stringify(optionsValue) !== stringify(optionsDiffValue) : optionsValue !== optionsDiffValue) {
validatedOptions[prop] = optionsValue;
}
}
else if (doWriteErrors) {
console.warn(`The option "${propPrefix}${prop}" wasn't set, because it doesn't accept the type [ ${optionsValueType.toUpperCase()} ] with the value of "${optionsValue}".\r\n` +
`Accepted types are: [ ${errorPossibleTypes.join(', ').toUpperCase()} ].\r\n` +
(errorEnumStrings.length > 0 ? `\r\nValid strings are: [ ${errorEnumStrings.join(', ')} ].` : ''))
}
delete optionsCopy[prop];
}
});
return {
foreign: optionsCopy,
validated: validatedOptions
};
};
/**
* Validates the given options object according to the given template object and returns a object which looks like:
* {
* foreign : a object which consists of properties which aren't defined inside the template. (foreign properties)
* validated : a object which consists only of valid properties. (property name is inside the template and value has a correct type)
* }
* @param options The options object which shall be validated.
* @param template The template according to which the options object shall be validated.
* @param optionsDiff When provided the returned validated object will only have properties which are different to this objects properties.
* Example (assume all properties are valid to the template):
* Options object : { a: 'a', b: 'b', c: 'c' }
* optionsDiff object : { a: 'a', b: 'b', c: undefined }
* Returned validated object : { c: 'c' }
* Because the value of the properties a and b didn't change, they aren't included in the returned object.
* Without the optionsDiff object the returned validated object would be: { a: 'a', b: 'b', c: 'c' }
* @param doWriteErrors True if errors shall be logged into the console, false otherwise.
*/
const validate = function <T extends PlainObject>(options: T, template: OptionsTemplate<Required<T>>, optionsDiff?: OptionsValidated<T>, doWriteErrors?: boolean): OptionsValidatedResult<T> {
/*
if (!isEmptyObject(foreign) && doWriteErrors)
console.warn(`The following options are discarded due to invalidity:\r\n ${window.JSON.stringify(foreign, null, 2)}`);
//add values, which aren't specified in the template, to the finished validated object to prevent them from being discarded
if (keepForeignProps) {
Object.assign(result.validated, foreign);
}
*/
return validateRecursive(options, template, optionsDiff || {}, doWriteErrors || false);
};
export { validate, optionsTemplateTypes };
type OptionsTemplateTypesDictionary = {
readonly boolean: OptionsTemplateType<boolean>;
readonly number: OptionsTemplateType<number>;
readonly string: OptionsTemplateType<string>;
readonly array: OptionsTemplateType<Array<any>>;
readonly object: OptionsTemplateType<object>;
readonly function: OptionsTemplateType<Func>;
readonly null: OptionsTemplateType<null>;
}
+36
View File
@@ -0,0 +1,36 @@
export type PlainObject<T = any> = { [name: string]: T };
// Options Template Typings:
export type Func = (this: any, ...args: any[]) => any;
export type OptionsTemplateType<T extends OptionsTemplateNativeTypes> = ExtractPropsKey<OptionsTemplateTypeMap, T>;
export type OptionsTemplateTypes = keyof OptionsTemplateTypeMap;
export type OptionsTemplateNativeTypes = OptionsTemplateTypeMap[keyof OptionsTemplateTypeMap];
export type OptionsTemplateValue<T extends OptionsTemplateNativeTypes = string> = T extends string ? string extends T ? OptionsTemplateValueNonEnum<T> : string : OptionsTemplateValueNonEnum<T>;
export type OptionsTemplate<T extends Required<T>> = {
[P in keyof T]: PlainObject extends T[P] ? OptionsTemplate<Required<T[P]>> : T[P] extends OptionsTemplateNativeTypes ? OptionsTemplateValue<T[P]> : never
};
export type OptionsValidated<T> = {
[P in keyof T]?: OptionsValidated<T[P]>;
};
export type OptionsValidatedResult<T> = {
readonly foreign: PlainObject;
readonly validated: OptionsValidated<T>;
}
// Options With Options Template Typings:
export type OptionsAndOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
export type OptionsAndOptionsTemplate<T extends Required<T>> = {
[P in keyof T]: PlainObject extends T[P] ? OptionsAndOptionsTemplate<Required<T[P]>> : T[P] extends OptionsTemplateNativeTypes ? OptionsAndOptionsTemplateValue<T[P]> : never
}
type OptionsTemplateTypeMap = {
'__TPL_boolean_TYPE__': boolean;
'__TPL_number_TYPE__': number;
'__TPL_string_TYPE__': string;
'__TPL_array_TYPE__': Array<any>
'__TPL_function_TYPE__': Func
'__TPL_null_TYPE__': null;
'__TPL_object_TYPE__': object;
}
type ExtractPropsKey<T, TProps extends T[keyof T]> = {
[P in keyof T]: TProps extends T[P] ? P : never;
}[keyof T];
type OptionsTemplateValueNonEnum<T extends OptionsTemplateNativeTypes> = OptionsTemplateType<T> | [OptionsTemplateType<T>, ...Array<OptionsTemplateTypes>];
+44
View File
@@ -0,0 +1,44 @@
import { isArrayLike } from 'core/utils/types';
import { PlainObject } from 'core/typings';
/**
* Iterates through a array or object
* @param arrayLikeOrObject The array or object through which shall be iterated.
* @param callback The function which is responsible for the iteration.
* If the function returns true its treated like a "continue" statement.
* If the function returns false its treated like a "break" statement.
*/
export function each<T>(array: Array<T> | ReadonlyArray<T>, callback: (value: T, indexOrKey: number, source: Array<T>) => boolean | void): Array<T> | ReadonlyArray<T>;
export function each<T>(array: Array<T> | ReadonlyArray<T> | null, callback: (value: T, indexOrKey: number, source: Array<T>) => boolean | void): Array<T> | ReadonlyArray<T> | null;
export function each<T>(arrayLikeObject: ArrayLike<T>, callback: (value: T, indexOrKey: number, source: ArrayLike<T>) => boolean | void): ArrayLike<T>;
export function each<T>(arrayLikeObject: ArrayLike<T> | null, callback: (value: T, indexOrKey: number, source: ArrayLike<T>) => boolean | void): ArrayLike<T> | null;
export function each(obj: PlainObject, callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | void): PlainObject;
export function each(obj: PlainObject | null, callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | void): PlainObject | null;
export function each<T>(source: ArrayLike<T> | PlainObject | null, callback: (value: T | any, indexOrKey: any, source: any) => boolean | void): Array<T> | ReadonlyArray<T> | ArrayLike<T> | PlainObject | null {
let i: number | string = 0;
if (isArrayLike(source)) {
for (; i < source.length; i++) {
if (callback(source[i], i, source) === false)
break;
}
}
else if (source) {
for (i in source) {
if (callback(source[i], i, source) === false)
break;
}
}
return source;
};
/**
* Returns the index of the given inside the given array or -1 if the given item isn't part of the given array.
* @param arr The array.
* @param item The item.
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
*/
export const indexOf: <T = any>(arr: Array<T>, item: T, fromIndex?: number) => number = (arr, item, fromIndex) => {
return arr.indexOf(item, fromIndex);
}
+59
View File
@@ -0,0 +1,59 @@
import { isArray, isFunction, isPlainObject, isNull } from 'core/utils/types';
import { each } from 'core/utils/arrays';
// https://github.com/jquery/jquery/blob/master/src/core.js#L116
export function extend<T, U>(target: T, object1: U): T & U;
export function extend<T, U, V>(target: T, object1: U, object2: V): T & U & V;
export function extend<T, U, V, W>(target: T, object1: U, object2: V, object3: W): T & U & V & W;
export function extend<T, U, V, W, X>(target: T, object1: U, object2: V, object3: W, object4: X): T & U & V & W & X;
export function extend<T, U, V, W, X, Y>(target: T, object1: U, object2: V, object3: W, object4: X, object5: Y): T & U & V & W & X & Y;
export function extend<T, U, V, W, X, Y, Z>(target: T, object1?: U, object2?: V, object3?: W, object4?: X, object5?: Y, object6?: Z): T & U & V & W & X & Y & Z {
const sources: Array<any> = [object1, object2, object3, object4, object5, object6];
// Handle case when target is a string or something (possible in deep copy)
if ((typeof target !== "object" || isNull(target)) && !isFunction(target)) {
target = {} as T;
}
each(sources, (source) => {
// Only deal with non-null/undefined values
if (source != null) {
// Extend the base object
for (const name in source) {
const copy: any = source[name];
// Prevent Object.prototype pollution
// Prevent never-ending loop
if (name === "__proto__" || target === copy) {
continue;
}
const copyIsArray = isArray(copy);
// Recurse if we're merging plain objects or arrays
if (copy && (isPlainObject(copy) || copyIsArray)) {
const src = target[name];
let clone: any = src;
// Ensure proper type for the source value
if (copyIsArray && !isArray(src)) {
clone = [];
} else if (!copyIsArray && !isPlainObject(src)) {
clone = {};
}
// Never move original objects, clone them
target[name] = extend(clone, copy) as any;
// Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
});
// Return the modified object
return target as any;
}
+3
View File
@@ -0,0 +1,3 @@
export * from 'core/utils/arrays';
export * from 'core/utils/extend';
export * from 'core/utils/types';
+96
View File
@@ -0,0 +1,96 @@
import { PlainObject } from 'core/typings';
export const type: (obj: any) => string = (obj) => {
if (obj === undefined)
return obj + '';
if (obj === null)
return obj + '';
return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
}
export function isNumber(obj: any): obj is number {
return typeof obj === 'number';
};
export function isString(obj: any): obj is string {
return typeof obj === 'string';
}
export function isBoolean(obj: any): obj is boolean {
return typeof obj === 'boolean';
}
export function isObject(obj: any): boolean {
return typeof obj === 'object' && !isArray(obj) && !isNull(obj);
}
export function isFunction(obj: any): obj is 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> {
return Array.isArray(obj);
}
/**
* Returns true if the given object is array like, false otherwise.
* @param obj The Object
*/
export function isArrayLike<T extends PlainObject = any>(obj: any): obj is ArrayLike<T> {
const length = !!obj && obj.length;
return isArray(obj) || (!isFunction(obj) && isNumber(length) && length > -1 && length % 1 == 0);
}
/**
* Returns true if the given object is a "plain" (e.g. { key: value }) object, false otherwise.
* @param obj The Object.
*/
export function isPlainObject<T = any>(obj: any): obj is PlainObject<T> {
if (!obj || !isObject(obj) || type(obj) !== 'object')
return false;
let key;
const proto = 'prototype';
const hasOwnProperty = Object[proto].hasOwnProperty;
const hasOwnConstructor = hasOwnProperty.call(obj, 'constructor');
const hasIsPrototypeOf = obj.constructor && obj.constructor[proto] && hasOwnProperty.call(obj.constructor[proto], 'isPrototypeOf');
if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
return false;
}
for (key in obj) { /**/ }
return isUndefined(key) || hasOwnProperty.call(obj, key);
};
/**
* Checks whether the given object is a HTMLElement.
* @param obj The object which shall be checked.
*/
export function isHTMLElement(obj: any): obj is HTMLElement {
const instaceOfRightHandSide = window.HTMLElement;
const doInstanceOf = isObject(instaceOfRightHandSide) || isFunction(instaceOfRightHandSide);
return !!(
doInstanceOf ? obj instanceof instaceOfRightHandSide : (obj && isObject(obj) && obj.nodeType === 1 && isString(obj.nodeName))
);
}
/**
* Returns true if the given object is empty, false otherwise.
* @param obj The Object.
*/
export function isEmptyObject(obj: any): boolean {
for (let name in obj)
return false;
return true;
};
+33
View File
@@ -0,0 +1,33 @@
import { createDOM } from 'core/dom';
export * from 'core/compatibility';
export * from 'core/utils';
export * from 'core/dom';
export * from 'core/options';
export * from 'instances';
window['hi'] = createDOM(`\
<div class="os-host">\
<div class="os-resize-observer-host"></div>\
<div class="os-padding">\
<div class="os-viewport">\
<div class="os-content">\
fdfhdfgh\
</div>\
</div>\
</div>\
<div class="os-scrollbar os-scrollbar-horizontal">\
<div class="os-scrollbar-track">\
<div class="os-scrollbar-handle"></div>\
</div>\
</div>\
<div class="os-scrollbar os-scrollbar-vertical">\
<div class="os-scrollbar-track">\
<div class="os-scrollbar-handle"></div>\
</div>\
</div>\
<div class="os-scrollbar-corner"></div>\
</div>`);
+52
View File
@@ -0,0 +1,52 @@
const targets: Set<Element> = new Set();
const targetInstanceMap: WeakMap<Element, any> = new WeakMap();
/**
* Adds the given OverlayScrollbars instance to the given element.
* @param target The element which is the target of the OverlayScrollbars instance.
* @param osInstance The OverlayScrollbars instance.
*/
export const addInstance: (target: Element, osInstance: any) => void = (target, osInstance) => {
targetInstanceMap.set(target, osInstance);
targets.add(target);
}
/**
* Removes a OverlayScrollbars instance from the given element.
* @param target The element from which its OverlayScrollbars instance shall be removed.
*/
export const removeInstance: (target: Element) => void = (target) => {
targetInstanceMap.delete(target);
targets.delete(target);
}
/**
* Gets the OverlayScrollbars from the given element or undefined if it doesn't have one.
* @param target The element of which its OverlayScrollbars instance shall be get.
*/
export const getInstance: (target: Element) => any = (target) => {
return targetInstanceMap.get(target);
}
/**
* Gets a Map which represents all active OverayScrollbars instances.
* The Key is the ekement and the value is the instance.
*/
export const allInstances: () => ReadonlyMap<Element, any> = () => {
const validTargetInstanceMap: Map<Element, any> = new Map();
targets.forEach((target: Element) => {
/* istanbul ignore else */
if (targetInstanceMap.has(target)) {
validTargetInstanceMap.set(target, targetInstanceMap.get(target))
}
});
targets.clear();
validTargetInstanceMap.forEach((instance: any, validTarget: Element) => {
targets.add(validTarget);
});
return validTargetInstanceMap;
}
+82
View File
@@ -0,0 +1,82 @@
import { OptionsTemplate, OptionsTemplateValue, OptionsAndOptionsTemplateValue, OptionsAndOptionsTemplate, Func } from "core/typings";
import { optionsTemplateTypes as oTypes, transform } from "core/options";
import { OverlayScrollbars } from "typings";
const classNameAllowedValues: OptionsTemplateValue<string | null> = [oTypes.string, oTypes.null];
const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number;
const booleanNullAllowedValues: OptionsTemplateValue<boolean | null> = [oTypes.boolean, oTypes.null];
const stringArrayNullAllowedValues: OptionsTemplateValue<string | Array<string> | null> = [oTypes.string, oTypes.array, oTypes.null];
const booleanTrueTemplate: OptionsAndOptionsTemplateValue<boolean> = [true, oTypes.boolean];
const booleanFalseTemplate: OptionsAndOptionsTemplateValue<boolean> = [false, oTypes.boolean];
const callbackTemplate: OptionsAndOptionsTemplateValue<Func | null> = [null, [oTypes.function, oTypes.null]];
const resizeAllowedValues: OptionsTemplateValue<OverlayScrollbars.ResizeBehavior> = 'none both horizontal vertical';
const overflowBehaviorAllowedValues: OptionsTemplateValue<OverlayScrollbars.OverflowBehavior> = 'visible-hidden visible-scroll scroll hidden';
const scrollbarsVisibilityAllowedValues: OptionsTemplateValue<OverlayScrollbars.VisibilityBehavior> = 'visible hidden auto';
const scrollbarsAutoHideAllowedValues: OptionsTemplateValue<OverlayScrollbars.AutoHideBehavior> = 'never scroll leavemove';
/**
* A object which serves as "default options object" and "options template object".
* I combined these two into one object so that I don't have to define two separate big objects, instead I define one big object.
*
* The property value is a tuple:
* the first value is the default value
* the second value is the template value
* Example:
* {
* a: ['default', [Type.string, Type.null]],
* b: [250, Type.number]
* }
* Property "a" has a default value of 'default' and it can be a string or null
* Property "b" has a default value of 250 and it can be number
*/
const defaultOptionsWithTemplate: OptionsAndOptionsTemplate<Required<OverlayScrollbars.Options>> = {
className: ['os-theme-dark', classNameAllowedValues], //null || string
resize: ['none', resizeAllowedValues], //none || both || horizontal || vertical || n || b || h || v
sizeAutoCapable: booleanTrueTemplate, //true || false
clipAlways: booleanTrueTemplate, //true || false
normalizeRTL: booleanTrueTemplate, //true || false
paddingAbsolute: booleanFalseTemplate, //true || false
autoUpdate: [null, booleanNullAllowedValues], //true || false || null
autoUpdateInterval: [33, numberAllowedValues], //number
updateOnLoad: [['img'], stringArrayNullAllowedValues], //string || array || null
nativeScrollbarsOverlaid: {
showNativeScrollbars: booleanFalseTemplate, //true || false
initialize: booleanFalseTemplate //true || false
},
overflowBehavior: {
x: ['scroll', overflowBehaviorAllowedValues], //visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
y: ['scroll', overflowBehaviorAllowedValues] //visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
},
scrollbars: {
visibility: ['auto', scrollbarsVisibilityAllowedValues], //visible || hidden || auto || v || h || a
autoHide: ['never', scrollbarsAutoHideAllowedValues], //never || scroll || leave || move || n || s || l || m
autoHideDelay: [800, numberAllowedValues], //number
dragScrolling: booleanTrueTemplate, //true || false
clickScrolling: booleanFalseTemplate, //true || false
touchSupport: booleanTrueTemplate, //true || false
snapHandle: booleanFalseTemplate //true || false
},
textarea: {
dynWidth: booleanFalseTemplate, //true || false
dynHeight: booleanFalseTemplate, //true || false
inheritedAttrs: [['style', 'class'], stringArrayNullAllowedValues], //string || array || null
},
callbacks: {
onInitialized: callbackTemplate, //null || function
onInitializationWithdrawn: callbackTemplate, //null || function
onDestroyed: callbackTemplate, //null || function
onScrollStart: callbackTemplate, //null || function
onScroll: callbackTemplate, //null || function
onScrollStop: callbackTemplate, //null || function
onOverflowChanged: callbackTemplate, //null || function
onOverflowAmountChanged: callbackTemplate, //null || function
onDirectionChanged: callbackTemplate, //null || function
onContentSizeChanged: callbackTemplate, //null || function
onHostSizeChanged: callbackTemplate, //null || function
onUpdated: callbackTemplate //null || function
}
}
export const optionsTemplate: OptionsTemplate<Required<OverlayScrollbars.Options>> = transform(defaultOptionsWithTemplate, true);
export const defaultOptions: OverlayScrollbars.Options = transform(defaultOptionsWithTemplate);
+370
View File
@@ -0,0 +1,370 @@
export namespace OverlayScrollbars {
export type ResizeBehavior = "none" | "both" | "horizontal" | "vertical";
export type OverflowBehavior = "hidden" | "scroll" | "visible-hidden" | "visible-scroll";
export type VisibilityBehavior = "visible" | "hidden" | "auto";
export type AutoHideBehavior = "never" | "scroll" | "leave" | "move";
export type ScrollBehavior = "always" | "ifneeded" | "never";
export type BlockBehavior = "begin" | "end" | "center" | "nearest";
export type Easing = string | null | undefined;
export type Margin = number | boolean;
export type Position = number | string;
export type Extensions = string | ReadonlyArray<string> | { [extensionName: string]: {} };
export type BasicEventCallback = (this: OverlayScrollbars) => void;
export type ScrollEventCallback = (this: OverlayScrollbars, args?: UIEvent) => void;
export type OverflowChangedCallback = (this: OverlayScrollbars, args?: OverflowChangedArgs) => void;
export type OverflowAmountChangedCallback = (this: OverlayScrollbars, args?: OverflowAmountChangedArgs) => void;
export type DirectionChangedCallback = (this: OverlayScrollbars, args?: DirectionChangedArgs) => void;
export type SizeChangedCallback = (this: OverlayScrollbars, args?: SizeChangedArgs) => void;
export type UpdatedCallback = (this: OverlayScrollbars, args?: UpdatedArgs) => void;
export type Coordinates = { x?: Position; y?: Position }
| { l?: Position; t?: Position }
| { left?: Position; top?: Position }
| [Position, Position]
| Position
| HTMLElement
| JQuery
| {
el: HTMLElement | JQuery;
scroll?: ScrollBehavior | { x?: ScrollBehavior; y?: ScrollBehavior } | [ScrollBehavior, ScrollBehavior];
block?: BlockBehavior | { x?: BlockBehavior; y?: BlockBehavior } | [BlockBehavior, BlockBehavior];
margin?: Margin
| {
top?: Margin;
right?: Margin;
bottom?: Margin;
left?: Margin;
}
| [Margin, Margin]
| [Margin, Margin, Margin, Margin];
};
export interface OverflowChangedArgs {
x: boolean;
y: boolean;
xScrollable: boolean;
yScrollable: boolean;
clipped: boolean;
}
export interface OverflowAmountChangedArgs {
x: number;
y: number;
}
export interface DirectionChangedArgs {
isRTL: number;
dir: string;
}
export interface SizeChangedArgs {
width: number;
height: number;
}
export interface UpdatedArgs {
forced: boolean;
}
export interface Options {
className?: string | null;
resize?: ResizeBehavior;
sizeAutoCapable?: boolean;
clipAlways?: boolean;
normalizeRTL?: boolean;
paddingAbsolute?: boolean;
autoUpdate?: boolean | null;
autoUpdateInterval?: number;
updateOnLoad?: string | ReadonlyArray<string> | null;
nativeScrollbarsOverlaid?: {
showNativeScrollbars?: boolean;
initialize?: boolean;
};
overflowBehavior?: {
x?: OverflowBehavior;
y?: OverflowBehavior;
};
scrollbars?: {
visibility?: VisibilityBehavior;
autoHide?: AutoHideBehavior;
autoHideDelay?: number;
dragScrolling?: boolean;
clickScrolling?: boolean;
touchSupport?: boolean;
snapHandle?: boolean;
};
textarea?: {
dynWidth?: boolean;
dynHeight?: boolean;
inheritedAttrs?: string | ReadonlyArray<string> | null;
};
callbacks?: {
onInitialized?: BasicEventCallback | null;
onInitializationWithdrawn?: BasicEventCallback | null;
onDestroyed?: BasicEventCallback | null;
onScrollStart?: ScrollEventCallback | null;
onScroll?: ScrollEventCallback | null;
onScrollStop?: ScrollEventCallback | null;
onOverflowChanged?: OverflowChangedCallback | null;
onOverflowAmountChanged?: OverflowAmountChangedCallback | null;
onDirectionChanged?: DirectionChangedCallback | null;
onContentSizeChanged?: SizeChangedCallback | null;
onHostSizeChanged?: SizeChangedCallback | null;
onUpdated?: UpdatedCallback | null;
};
}
export interface ScrollInfo {
position: {
x: number;
y: number;
};
ratio: {
x: number;
y: number;
};
max: {
x: number;
y: number;
};
handleOffset: {
x: number;
y: number;
};
handleLength: {
x: number;
y: number;
};
handleLengthRatio: {
x: number;
y: number;
};
trackLength: {
x: number;
y: number;
};
snappedHandleOffset: {
x: number;
y: number;
};
isRTL: boolean;
isRTLNormalized: boolean;
}
export interface Elements {
target: HTMLElement;
host: HTMLElement;
padding: HTMLElement;
viewport: HTMLElement;
content: HTMLElement;
scrollbarHorizontal: {
scrollbar: HTMLElement;
track: HTMLElement;
handle: HTMLElement;
};
scrollbarVertical: {
scrollbar: HTMLElement;
track: HTMLElement;
handle: HTMLElement;
};
scrollbarCorner: HTMLElement;
}
export interface State {
destroyed: boolean;
sleeping: boolean;
autoUpdate: boolean;
widthAuto: boolean;
heightAuto: boolean;
documentMixed: boolean;
padding: {
t: number;
r: number;
b: number;
l: number;
};
overflowAmount: {
x: number;
y: number;
};
hideOverflow: {
x: boolean;
y: boolean;
xs: boolean;
ys: boolean;
};
hasOverflow: {
x: boolean;
y: boolean;
};
contentScrollSize: {
width: number;
height: number;
};
viewportSize: {
width: number;
height: number;
};
hostSize: {
width: number;
height: number;
};
}
export interface Extension {
contract(global: any): boolean;
added(options?: {}): void;
removed(): void;
on(callbackName: string, callbackArgs?: UIEvent | OverflowChangedArgs | OverflowAmountChangedArgs | DirectionChangedArgs | SizeChangedArgs | UpdatedArgs): void;
}
export interface ExtensionInfo {
name: string;
extensionFactory: (this: OverlayScrollbars, defaultOptions: {}, compatibility: Compatibility, framework: any) => Extension;
defaultOptions?: {};
}
export interface Globals {
defaultOptions: {};
autoUpdateLoop: boolean;
autoUpdateRecommended: boolean;
supportMutationObserver: boolean;
supportResizeObserver: boolean;
supportPassiveEvents: boolean;
supportTransform: boolean;
supportTransition: boolean;
restrictedMeasuring: boolean;
nativeScrollbarStyling: boolean;
cssCalc: string | null;
nativeScrollbarSize: {
x: number;
y: number;
};
nativeScrollbarIsOverlaid: {
x: boolean;
y: boolean;
};
overlayScrollbarDummySize: {
x: number;
y: number;
};
rtlScrollBehavior: {
i: boolean;
n: boolean;
};
}
export interface Compatibility {
wW(): number;
wH(): number;
mO(): any;
rO(): any;
rAF(): (callback: (...args: any[]) => any) => number;
cAF(): (requestID: number) => void;
now(): number;
stpP(event: Event): void;
prvD(event: Event): void;
page(event: MouseEvent): { x: number, y: number };
mBtn(event: MouseEvent): number;
inA<T>(item: T, array: T[]): number;
isA(obj: any): boolean;
type(obj: any): string;
bind(func: (...args: any[]) => any, thisObj: any, ...args: any[]): any;
}
}
interface OverlayScrollbars {
options(): OverlayScrollbars.Options;
options(options: OverlayScrollbars.Options): void;
options(optionName: string): any;
options(optionName: string, optionValue: {} | null): void;
update(force?: boolean): void;
sleep(): void;
scroll(): OverlayScrollbars.ScrollInfo;
scroll(
coordinates: OverlayScrollbars.Coordinates,
duration?: number,
easing?: OverlayScrollbars.Easing | { x?: OverlayScrollbars.Easing; y?: OverlayScrollbars.Easing } | [OverlayScrollbars.Easing, OverlayScrollbars.Easing],
complete?: (...args: any[]) => any
): void;
scroll(coordinates: OverlayScrollbars.Coordinates, options: {}): void;
scrollStop(): OverlayScrollbars;
getElements(): OverlayScrollbars.Elements;
getElements(elementName: string): any;
getState(): OverlayScrollbars.State;
getState(stateProperty: string): any;
destroy(): void;
ext(): {};
ext(extensionName: string): OverlayScrollbars.Extension;
addExt(extensionName: string, options: {}): OverlayScrollbars.Extension;
removeExt(extensionName: string): boolean;
}
interface OverlayScrollbarsStatic {
(
element: HTMLElement | Element | JQuery,
options: OverlayScrollbars.Options,
extensions?: OverlayScrollbars.Extensions
): OverlayScrollbars;
(
element: HTMLElement | Element | JQuery | null
): OverlayScrollbars | undefined;
(
elements: NodeListOf<Element> | ReadonlyArray<Element> | JQuery,
options: OverlayScrollbars.Options,
extensions?: OverlayScrollbars.Extensions
): OverlayScrollbars | OverlayScrollbars[] | undefined;
(
elements: NodeListOf<Element> | ReadonlyArray<Element> | JQuery,
filter?: string | ((element: Element, instance: OverlayScrollbars) => boolean)
): OverlayScrollbars | OverlayScrollbars[] | undefined;
globals(): OverlayScrollbars.Globals;
defaultOptions(): OverlayScrollbars.Options;
defaultOptions(newDefaultOptions: OverlayScrollbars.Options): void;
extension(): { [index: number]: OverlayScrollbars.ExtensionInfo; length: number };
extension(extensionName: string): OverlayScrollbars.ExtensionInfo;
extension(
extensionName: string,
extensionFactory: (this: OverlayScrollbars, defaultOptions: {},
compatibility: OverlayScrollbars.Compatibility, framework: any) => OverlayScrollbars.Extension,
defaultOptions?: {}
): void;
extension(extensionName: string, extensionFactory: null | undefined): void;
valid(osInstance: any): boolean;
}