UNPKG

admin-lte

Version:

Responsive open source admin dashboard and control panel.

922 lines (841 loc) 319 kB
/*! * OverlayScrollbars * https://github.com/KingSora/OverlayScrollbars * * Version: 1.10.0 * * Copyright KingSora | Rene Haas. * https://github.com/KingSora * * Released under the MIT license. * Date: 11.10.2019 */ (function (global, factory) { if (typeof define === 'function' && define.amd) define(['jquery'], function(framework) { return factory(global, global.document, undefined, framework); }); else if (typeof module === 'object' && typeof module.exports === 'object') module.exports = factory(global, global.document, undefined, require('jquery')); else factory(global, global.document, undefined, global.jQuery); }(typeof window !== 'undefined' ? window : this, function(window, document, undefined, framework) { 'use strict'; var PLUGINNAME = 'OverlayScrollbars'; var TYPES = { o : 'object', f : 'function', a : 'array', s : 'string', b : 'boolean', n : 'number', u : 'undefined', z : 'null' //d : 'date', //e : 'error', //r : 'regexp', //y : 'symbol' }; var LEXICON = { c : 'class', s : 'style', i : 'id', l : 'length', p : 'prototype', oH : 'offsetHeight', cH : 'clientHeight', sH : 'scrollHeight', oW : 'offsetWidth', cW : 'clientWidth', sW : 'scrollWidth', hOP : 'hasOwnProperty', bCR : 'getBoundingClientRect' }; var VENDORS = (function() { //https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix var jsCache = { }; var cssCache = { }; var cssPrefixes = ['-webkit-', '-moz-', '-o-', '-ms-']; var jsPrefixes = ['WebKit', 'Moz', 'O', 'MS']; function firstLetterToUpper(str) { return str.charAt(0).toUpperCase() + str.slice(1); } return { _cssPrefixes: cssPrefixes, _jsPrefixes: jsPrefixes, _cssProperty : function(name) { var result = cssCache[name]; if(cssCache[LEXICON.hOP](name)) return result; var uppercasedName = firstLetterToUpper(name); var elmStyle = document.createElement('div')[LEXICON.s]; var resultPossibilities; var i = 0; var v; var currVendorWithoutDashes; for (; i < cssPrefixes.length; i++) { currVendorWithoutDashes = cssPrefixes[i].replace(/-/g, ''); resultPossibilities = [ name, //transition cssPrefixes[i] + name, //-webkit-transition currVendorWithoutDashes + uppercasedName, //webkitTransition firstLetterToUpper(currVendorWithoutDashes) + uppercasedName //WebkitTransition ]; for(v = 0; v < resultPossibilities[LEXICON.l]; v++) { if(elmStyle[resultPossibilities[v]] !== undefined) { result = resultPossibilities[v]; break; } } } cssCache[name] = result; return result; }, _jsAPI : function(name, isInterface, fallback) { var i = 0; var result = jsCache[name]; if(!jsCache[LEXICON.hOP](name)) { result = window[name]; for(; i < jsPrefixes[LEXICON.l]; i++) result = result || window[(isInterface ? jsPrefixes[i] : jsPrefixes[i].toLowerCase()) + firstLetterToUpper(name)]; jsCache[name] = result; } return result || fallback; } } })(); var COMPATIBILITY = (function() { function windowSize(x) { return x ? window.innerWidth || document.documentElement[LEXICON.cW] || document.body[LEXICON.cW] : window.innerHeight || document.documentElement[LEXICON.cH] || document.body[LEXICON.cH]; } function bind(func, thisObj) { if (typeof func != TYPES.f) { throw "Can't bind function!"; // closest thing possible to the ECMAScript 5 // internal IsCallable function //throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var proto = LEXICON.p; var aArgs = Array[proto].slice.call(arguments, 2); var fNOP = function() {}; var fBound = function() { return func.apply(this instanceof fNOP ? this : thisObj, aArgs.concat(Array[proto].slice.call(arguments))); }; if (func[proto]) fNOP[proto] = func[proto]; // Function.prototype doesn't have a prototype property fBound[proto] = new fNOP(); return fBound; } return { /** * Gets the current window width. * @returns {Number|number} The current window width in pixel. */ wW: bind(windowSize, 0, true), /** * Gets the current window height. * @returns {Number|number} The current window height in pixel. */ wH: bind(windowSize, 0), /** * Gets the MutationObserver Object or undefined if not supported. * @returns {MutationObserver|*|undefined} The MutationsObserver Object or undefined. */ mO: bind(VENDORS._jsAPI, 0, 'MutationObserver', true), /** * Gets the ResizeObserver Object or undefined if not supported. * @returns {MutationObserver|*|undefined} The ResizeObserver Object or undefined. */ rO: bind(VENDORS._jsAPI, 0, 'ResizeObserver', true), /** * Gets the RequestAnimationFrame method or it's corresponding polyfill. * @returns {*|Function} The RequestAnimationFrame method or it's corresponding polyfill. */ rAF: bind(VENDORS._jsAPI, 0, 'requestAnimationFrame', false, function (func) { return window.setTimeout(func, 1000 / 60); }), /** * Gets the CancelAnimationFrame method or it's corresponding polyfill. * @returns {*|Function} The CancelAnimationFrame method or it's corresponding polyfill. */ cAF: bind(VENDORS._jsAPI, 0, 'cancelAnimationFrame', false, function (id) { return window.clearTimeout(id); }), /** * Gets the current time. * @returns {number} The current time. */ now: function() { return Date.now && Date.now() || new Date().getTime(); }, /** * Stops the propagation of the given event. * @param event The event of which the propagation shall be stoped. */ stpP: function(event) { if(event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true; }, /** * Prevents the default action of the given event. * @param event The event of which the default action shall be prevented. */ prvD: function(event) { if(event.preventDefault && event.cancelable) event.preventDefault(); else event.returnValue = false; }, /** * Gets the pageX and pageY values of the given mouse event. * @param event The mouse event of which the pageX and pageX shall be got. * @returns {{x: number, y: number}} x = pageX value, y = pageY value. */ page: function(event) { event = event.originalEvent || event; var strPage = 'page'; var strClient = 'client'; var strX = 'X'; var strY = 'Y'; var target = event.target || event.srcElement || document; var eventDoc = target.ownerDocument || document; var doc = eventDoc.documentElement; var body = eventDoc.body; //if touch event return return pageX/Y of it if(event.touches !== undefined) { var touch = event.touches[0]; return { x : touch[strPage + strX], y : touch[strPage + strY] } } // Calculate pageX/Y if not native supported if (!event[strPage + strX] && event[strClient + strX] && event[strClient + strX] != null) { return { x : event[strClient + strX] + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0), y : event[strClient + strY] + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0) } } return { x : event[strPage + strX], y : event[strPage + strY] }; }, /** * Gets the clicked mouse button of the given mouse event. * @param event The mouse event of which the clicked button shal be got. * @returns {number} The number of the clicked mouse button. (0 : none | 1 : leftButton | 2 : middleButton | 3 : rightButton) */ mBtn: function(event) { var button = event.button; if (!event.which && button !== undefined) return (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); else return event.which; }, /** * Checks whether a item is in the given array and returns its index. * @param item The item of which the position in the array shall be determined. * @param arr The array. * @returns {number} The zero based index of the item or -1 if the item isn't in the array. */ inA : function(item, arr) { for (var i = 0; i < arr[LEXICON.l]; i++) //Sometiems in IE a "SCRIPT70" Permission denied error occurs if HTML elements in a iFrame are compared try { if (arr[i] === item) return i; } catch(e) { } return -1; }, /** * Returns true if the given value is a array. * @param arr The potential array. * @returns {boolean} True if the given value is a array, false otherwise. */ isA: function(arr) { var def = Array.isArray; return def ? def(arr) : this.type(arr) == TYPES.a; }, /** * Determine the internal JavaScript [[Class]] of the given object. * @param obj The object of which the type shall be determined. * @returns {string} The type of the given object. */ type: function(obj) { if (obj === undefined) return obj + ''; if (obj === null) return obj + ''; return Object[LEXICON.p].toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase(); }, bind: bind /** * Gets the vendor-prefixed CSS property by the given name. * For example the given name is "transform" and you're using a old Firefox browser then the returned value would be "-moz-transform". * If the browser doesn't need a vendor-prefix, then the returned string is the given name. * If the browser doesn't support the given property name at all (not even with a vendor-prefix) the returned value is null. * @param propName The unprefixed CSS property name. * @returns {string|null} The vendor-prefixed CSS property or null if the browser doesn't support the given CSS property. cssProp: function(propName) { return VENDORS._cssProperty(propName); } */ } })(); var MATH = Math; var JQUERY = framework; var EASING = framework.easing; var FRAMEWORK = framework; var INSTANCES = (function() { var _targets = [ ]; var _instancePropertyString = '__overlayScrollbars__'; /** * Register, unregister or get a certain (or all) instances. * Register: Pass the target and the instance. * Unregister: Pass the target and null. * Get Instance: Pass the target from which the instance shall be got. * Get Targets: Pass no arguments. * @param target The target to which the instance shall be registered / from which the instance shall be unregistered / the instance shall be got * @param instance The instance. * @returns {*|void} Returns the instance from the given target. */ return function (target, instance) { var argLen = arguments[LEXICON.l]; if(argLen < 1) { //return all targets return _targets; } else { if(instance) { //register instance target[_instancePropertyString] = instance; _targets.push(target); } else { var index = COMPATIBILITY.inA(target, _targets); if (index > -1) { if(argLen > 1) { //unregister instance delete target[_instancePropertyString]; _targets.splice(index, 1); } else { //get instance from target return _targets[index][_instancePropertyString]; } } } } } })(); var PLUGIN = (function() { var _plugin; var _pluginsGlobals; var _pluginsAutoUpdateLoop; var _pluginsExtensions = [ ]; var _pluginsOptions = (function() { var type = COMPATIBILITY.type; var possibleTemplateTypes = [ TYPES.b, //boolean TYPES.n, //number TYPES.s, //string TYPES.a, //array TYPES.o, //object TYPES.f, //function TYPES.z //null ]; var restrictedStringsSplit = ' '; var restrictedStringsPossibilitiesSplit = ':'; var classNameAllowedValues = [TYPES.z, TYPES.s]; var numberAllowedValues = TYPES.n; var booleanNullAllowedValues = [TYPES.z, TYPES.b]; var booleanTrueTemplate = [true, TYPES.b]; var booleanFalseTemplate = [false, TYPES.b]; var callbackTemplate = [null, [TYPES.z, TYPES.f]]; var inheritedAttrsTemplate = [['style', 'class'], [TYPES.s, TYPES.a, TYPES.z]]; var resizeAllowedValues = 'n:none b:both h:horizontal v:vertical'; var overflowBehaviorAllowedValues = 'v-h:visible-hidden v-s:visible-scroll s:scroll h:hidden'; var scrollbarsVisibilityAllowedValues = 'v:visible h:hidden a:auto'; var scrollbarsAutoHideAllowedValues = 'n:never s:scroll l:leave m:move'; var optionsDefaultsAndTemplate = { 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 nativeScrollbarsOverlaid: { showNativeScrollbars: booleanFalseTemplate, //true || false initialize: booleanTrueTemplate //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 : inheritedAttrsTemplate //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 } }; var convert = function(template) { var recursive = function(obj) { var key; var val; var valType; for(key in obj) { if(!obj[LEXICON.hOP](key)) continue; val = obj[key]; valType = type(val); if(valType == TYPES.a) obj[key] = val[template ? 1 : 0]; else if(valType == TYPES.o) obj[key] = recursive(val); } return obj; }; return recursive(FRAMEWORK.extend(true, { }, optionsDefaultsAndTemplate)); }; return { _defaults : convert(), _template : convert(true), /** * Validates the passed object by the passed template. * @param obj The object which shall be validated. * @param template The template which defines the allowed values and types. * @param writeErrors True if errors shall be logged to the console. * @param diffObj If a object is passed then only valid differences to this object will be returned. * @returns {{}} A object which contains two objects called "default" and "prepared" which contains only the valid properties of the passed original object and discards not different values compared to the passed diffObj. */ _validate : function (obj, template, writeErrors, diffObj) { var validatedOptions = { }; var validatedOptionsPrepared = { }; var objectCopy = FRAMEWORK.extend(true, { }, obj); var inArray = FRAMEWORK.inArray; var isEmptyObj = FRAMEWORK.isEmptyObject; var checkObjectProps = function(data, template, diffData, validatedOptions, validatedOptionsPrepared, prevPropName) { for (var prop in template) { if (template[LEXICON.hOP](prop) && data[LEXICON.hOP](prop)) { var isValid = false; var isDiff = false; var templateValue = template[prop]; var templateValueType = type(templateValue); var templateIsComplex = templateValueType == TYPES.o; var templateTypes = type(templateValue) != TYPES.a ? [ templateValue ] : templateValue; var dataDiffValue = diffData[prop]; var dataValue = data[prop]; var dataValueType = type(dataValue); var propPrefix = prevPropName ? prevPropName + '.' : ''; var error = "The option \"" + propPrefix + prop + "\" wasn't set, because"; var errorPossibleTypes = [ ]; var errorRestrictedStrings = [ ]; var restrictedStringValuesSplit; var restrictedStringValuesPossibilitiesSplit; var isRestrictedValue; var mainPossibility; var currType; var i; var v; var j; dataDiffValue = dataDiffValue === undefined ? { } : dataDiffValue; //if the template has a object as value, it means that the options are complex (verschachtelt) if(templateIsComplex && dataValueType == TYPES.o) { validatedOptions[prop] = { }; validatedOptionsPrepared[prop] = { }; checkObjectProps(dataValue, templateValue, dataDiffValue, validatedOptions[prop], validatedOptionsPrepared[prop], propPrefix + prop); FRAMEWORK.each([ data, validatedOptions, validatedOptionsPrepared ], function(index, value) { if(isEmptyObj(value[prop])) { delete value[prop]; } }); } else if(!templateIsComplex) { for(i = 0; i < templateTypes[LEXICON.l]; i++) { currType = templateTypes[i]; templateValueType = type(currType); //if currtype is string and starts with restrictedStringPrefix and end with restrictedStringSuffix isRestrictedValue = templateValueType == TYPES.s && inArray(currType, possibleTemplateTypes) === -1; if(isRestrictedValue) { errorPossibleTypes.push(TYPES.s); //split it into a array which contains all possible values for example: ["y:yes", "n:no", "m:maybe"] restrictedStringValuesSplit = currType.split(restrictedStringsSplit); errorRestrictedStrings = errorRestrictedStrings.concat(restrictedStringValuesSplit); for(v = 0; v < restrictedStringValuesSplit[LEXICON.l]; v++) { //split the possible values into their possibiliteis for example: ["y", "yes"] -> the first is always the mainPossibility restrictedStringValuesPossibilitiesSplit = restrictedStringValuesSplit[v].split(restrictedStringsPossibilitiesSplit); mainPossibility = restrictedStringValuesPossibilitiesSplit[0]; for(j = 0; j < restrictedStringValuesPossibilitiesSplit[LEXICON.l]; j++) { //if any possibility matches with the dataValue, its valid if(dataValue === restrictedStringValuesPossibilitiesSplit[j]) { isValid = true; break; } } if(isValid) break; } } else { errorPossibleTypes.push(currType); if(dataValueType === currType) { isValid = true; break; } } } if(isValid) { isDiff = dataValue !== dataDiffValue; if(isDiff) validatedOptions[prop] = dataValue; if(isRestrictedValue ? inArray(dataDiffValue, restrictedStringValuesPossibilitiesSplit) < 0 : isDiff) validatedOptionsPrepared[prop] = isRestrictedValue ? mainPossibility : dataValue; } else if(writeErrors) { console.warn(error + " it doesn't accept the type [ " + dataValueType.toUpperCase() + " ] with the value of \"" + dataValue + "\".\r\n" + "Accepted types are: [ " + errorPossibleTypes.join(', ').toUpperCase() + " ]." + (errorRestrictedStrings[length] > 0 ? "\r\nValid strings are: [ " + errorRestrictedStrings.join(', ').split(restrictedStringsPossibilitiesSplit).join(', ') + " ]." : '')); } delete data[prop]; } } } }; checkObjectProps(objectCopy, template, diffObj || { }, validatedOptions, validatedOptionsPrepared); //add values which aren't specified in the template to the finished validated object to prevent them from being discarded /* if(keepForeignProps) { FRAMEWORK.extend(true, validatedOptions, objectCopy); FRAMEWORK.extend(true, validatedOptionsPrepared, objectCopy); } */ if(!isEmptyObj(objectCopy) && writeErrors) console.warn('The following options are discarded due to invalidity:\r\n' + window.JSON.stringify(objectCopy, null, 2)); return { _default : validatedOptions, _prepared : validatedOptionsPrepared }; } } }()); /** * Initializes the object which contains global information about the plugin and each instance of it. */ function initOverlayScrollbarsStatics() { if(!_pluginsGlobals) _pluginsGlobals = new OverlayScrollbarsGlobals(_pluginsOptions._defaults); if(!_pluginsAutoUpdateLoop) _pluginsAutoUpdateLoop = new OverlayScrollbarsAutoUpdateLoop(_pluginsGlobals); } /** * The global object for the OverlayScrollbars objects. It contains resources which every OverlayScrollbars object needs. This object is initialized only once: if the first OverlayScrollbars object gets initialized. * @param defaultOptions * @constructor */ function OverlayScrollbarsGlobals(defaultOptions) { var _base = this; var strOverflow = 'overflow'; var strHidden = 'hidden'; var strScroll = 'scroll'; var bodyElement = FRAMEWORK('body'); var scrollbarDummyElement = FRAMEWORK('<div id="os-dummy-scrollbar-size"><div></div></div>'); var scrollbarDummyElement0 = scrollbarDummyElement[0]; var dummyContainerChild = FRAMEWORK(scrollbarDummyElement.children('div').eq(0)); bodyElement.append(scrollbarDummyElement); scrollbarDummyElement.hide().show(); //fix IE8 bug (incorrect measuring) var nativeScrollbarSize = calcNativeScrollbarSize(scrollbarDummyElement0); var nativeScrollbarIsOverlaid = { x: nativeScrollbarSize.x === 0, y: nativeScrollbarSize.y === 0 }; var msie = (function() { var ua = window.navigator.userAgent; var strIndexOf = 'indexOf'; var strSubString = 'substring'; var msie = ua[strIndexOf]('MSIE '); var trident = ua[strIndexOf]('Trident/'); var edge = ua[strIndexOf]('Edge/'); var rv = ua[strIndexOf]('rv:'); var result; var parseIntFunc = parseInt; // IE 10 or older => return version number if (msie > 0) result = parseIntFunc(ua[strSubString](msie + 5, ua[strIndexOf]('.', msie)), 10); // IE 11 => return version number else if (trident > 0) result = parseIntFunc(ua[strSubString](rv + 3, ua[strIndexOf]('.', rv)), 10); // Edge (IE 12+) => return version number else if (edge > 0) result = parseIntFunc(ua[strSubString](edge + 5, ua[strIndexOf]('.', edge)), 10); // other browser return result; })(); FRAMEWORK.extend(_base, { defaultOptions : defaultOptions, msie : msie, autoUpdateLoop : false, autoUpdateRecommended : !COMPATIBILITY.mO(), nativeScrollbarSize : nativeScrollbarSize, nativeScrollbarIsOverlaid : nativeScrollbarIsOverlaid, nativeScrollbarStyling : (function() { var result = false; scrollbarDummyElement.addClass('os-viewport-native-scrollbars-invisible'); try { result = (scrollbarDummyElement.css('scrollbar-width') === 'none' && (msie > 9 || !msie)) || window.getComputedStyle(scrollbarDummyElement0, '::-webkit-scrollbar').getPropertyValue('display') === 'none'; } catch (ex) { } //fix opera bug: scrollbar styles will only appear if overflow value is scroll or auto during the activation of the style. //and set overflow to scroll //scrollbarDummyElement.css(strOverflow, strHidden).hide().css(strOverflow, strScroll).show(); //return (scrollbarDummyElement0[LEXICON.oH] - scrollbarDummyElement0[LEXICON.cH]) === 0 && (scrollbarDummyElement0[LEXICON.oW] - scrollbarDummyElement0[LEXICON.cW]) === 0; return result; })(), overlayScrollbarDummySize : { x: 30, y: 30 }, cssCalc : (function() { var dummyStyle = document.createElement('div')[LEXICON.s]; var strCalc = 'calc'; var i = -1; var prop; for(; i < VENDORS._cssPrefixes[LEXICON.l]; i++) { prop = i < 0 ? strCalc : VENDORS._cssPrefixes[i] + strCalc; dummyStyle.cssText = 'width:' + prop + '(1px);'; if (dummyStyle[LEXICON.l]) return prop; } return null; })(), restrictedMeasuring : (function() { //https://bugzilla.mozilla.org/show_bug.cgi?id=1439305 scrollbarDummyElement.css(strOverflow, strHidden); var scrollSize = { w : scrollbarDummyElement0[LEXICON.sW], h : scrollbarDummyElement0[LEXICON.sH] }; scrollbarDummyElement.css(strOverflow, 'visible'); var scrollSize2 = { w : scrollbarDummyElement0[LEXICON.sW], h : scrollbarDummyElement0[LEXICON.sH] }; return (scrollSize.w - scrollSize2.w) !== 0 || (scrollSize.h - scrollSize2.h) !== 0; })(), rtlScrollBehavior : (function() { scrollbarDummyElement.css({ 'overflow-y' : strHidden, 'overflow-x' : strScroll, 'direction' : 'rtl' }).scrollLeft(0); var dummyContainerOffset = scrollbarDummyElement.offset(); var dummyContainerChildOffset = dummyContainerChild.offset(); scrollbarDummyElement.scrollLeft(999); var dummyContainerScrollOffsetAfterScroll = dummyContainerChild.offset(); return { //origin direction = determines if the zero scroll position is on the left or right side //'i' means 'invert' (i === true means that the axis must be inverted to be correct) //true = on the left side //false = on the right side i : dummyContainerOffset.left === dummyContainerChildOffset.left, //negative = determines if the maximum scroll is positive or negative //'n' means 'negate' (n === true means that the axis must be negated to be correct) //true = negative //false = positive n : dummyContainerChildOffset.left - dummyContainerScrollOffsetAfterScroll.left === 0 }; })(), supportTransform : VENDORS._cssProperty('transform') !== undefined, supportTransition : VENDORS._cssProperty('transition') !== undefined, supportPassiveEvents : (function() { var supportsPassive = false; try { window.addEventListener('test', null, Object.defineProperty({ }, 'passive', { get: function() { supportsPassive = true; } })); } catch (e) { } return supportsPassive; })(), supportResizeObserver : !!COMPATIBILITY.rO(), supportMutationObserver : !!COMPATIBILITY.mO() }); scrollbarDummyElement.removeAttr(LEXICON.s).remove(); //Catch zoom event: (function () { if(nativeScrollbarIsOverlaid.x && nativeScrollbarIsOverlaid.y) return; var abs = MATH.abs; var windowWidth = COMPATIBILITY.wW(); var windowHeight = COMPATIBILITY.wH(); var windowDpr = getWindowDPR(); var onResize = function() { if(INSTANCES().length > 0) { var newW = COMPATIBILITY.wW(); var newH = COMPATIBILITY.wH(); var deltaW = newW - windowWidth; var deltaH = newH - windowHeight; if (deltaW === 0 && deltaH === 0) return; var deltaWRatio = MATH.round(newW / (windowWidth / 100.0)); var deltaHRatio = MATH.round(newH / (windowHeight / 100.0)); var absDeltaW = abs(deltaW); var absDeltaH = abs(deltaH); var absDeltaWRatio = abs(deltaWRatio); var absDeltaHRatio = abs(deltaHRatio); var newDPR = getWindowDPR(); var deltaIsBigger = absDeltaW > 2 && absDeltaH > 2; var difference = !differenceIsBiggerThanOne(absDeltaWRatio, absDeltaHRatio); var dprChanged = newDPR !== windowDpr && windowDpr > 0; var isZoom = deltaIsBigger && difference && dprChanged; var oldScrollbarSize = _base.nativeScrollbarSize; var newScrollbarSize; if (isZoom) { bodyElement.append(scrollbarDummyElement); newScrollbarSize = _base.nativeScrollbarSize = calcNativeScrollbarSize(scrollbarDummyElement[0]); scrollbarDummyElement.remove(); if(oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) { FRAMEWORK.each(INSTANCES(), function () { if(INSTANCES(this)) INSTANCES(this).update('zoom'); }); } } windowWidth = newW; windowHeight = newH; windowDpr = newDPR; } }; function differenceIsBiggerThanOne(valOne, valTwo) { var absValOne = abs(valOne); var absValTwo = abs(valTwo); return !(absValOne === absValTwo || absValOne + 1 === absValTwo || absValOne - 1 === absValTwo); } function getWindowDPR() { var dDPI = window.screen.deviceXDPI || 0; var sDPI = window.screen.logicalXDPI || 1; return window.devicePixelRatio || (dDPI / sDPI); } FRAMEWORK(window).on('resize', onResize); })(); function calcNativeScrollbarSize(measureElement) { return { x: measureElement[LEXICON.oH] - measureElement[LEXICON.cH], y: measureElement[LEXICON.oW] - measureElement[LEXICON.cW] }; } } /** * The object which manages the auto update loop for all OverlayScrollbars objects. This object is initialized only once: if the first OverlayScrollbars object gets initialized. * @constructor */ function OverlayScrollbarsAutoUpdateLoop(globals) { var _base = this; var _inArray = FRAMEWORK.inArray; var _getNow = COMPATIBILITY.now; var _strAutoUpdate = 'autoUpdate'; var _strAutoUpdateInterval = _strAutoUpdate + 'Interval'; var _strLength = LEXICON.l; var _loopingInstances = [ ]; var _loopingInstancesIntervalCache = [ ]; var _loopIsActive = false; var _loopIntervalDefault = 33; var _loopInterval = _loopIntervalDefault; var _loopTimeOld = _getNow(); var _loopID; /** * The auto update loop which will run every 50 milliseconds or less if the update interval of a instance is lower than 50 milliseconds. */ var loop = function() { if(_loopingInstances[_strLength] > 0 && _loopIsActive) { _loopID = COMPATIBILITY.rAF()(function () { loop(); }); var timeNew = _getNow(); var timeDelta = timeNew - _loopTimeOld; var lowestInterval; var instance; var instanceOptions; var instanceAutoUpdateAllowed; var instanceAutoUpdateInterval; var now; if (timeDelta > _loopInterval) { _loopTimeOld = timeNew - (timeDelta % _loopInterval); lowestInterval = _loopIntervalDefault; for(var i = 0; i < _loopingInstances[_strLength]; i++) { instance = _loopingInstances[i]; if (instance !== undefined) { instanceOptions = instance.options(); instanceAutoUpdateAllowed = instanceOptions[_strAutoUpdate]; instanceAutoUpdateInterval = MATH.max(1, instanceOptions[_strAutoUpdateInterval]); now = _getNow(); if ((instanceAutoUpdateAllowed === true || instanceAutoUpdateAllowed === null) && (now - _loopingInstancesIntervalCache[i]) > instanceAutoUpdateInterval) { instance.update('auto'); _loopingInstancesIntervalCache[i] = new Date(now += instanceAutoUpdateInterval); } lowestInterval = MATH.max(1, MATH.min(lowestInterval, instanceAutoUpdateInterval)); } } _loopInterval = lowestInterval; } } else { _loopInterval = _loopIntervalDefault; } }; /** * Add OverlayScrollbars instance to the auto update loop. Only successful if the instance isn't already added. * @param instance The instance which shall be updated in a loop automatically. */ _base.add = function(instance) { if(_inArray(instance, _loopingInstances) === -1) { _loopingInstances.push(instance); _loopingInstancesIntervalCache.push(_getNow()); if (_loopingInstances[_strLength] > 0 && !_loopIsActive) { _loopIsActive = true; globals.autoUpdateLoop = _loopIsActive; loop(); } } }; /** * Remove OverlayScrollbars instance from the auto update loop. Only successful if the instance was added before. * @param instance The instance which shall be updated in a loop automatically. */ _base.remove = function(instance) { var index = _inArray(instance, _loopingInstances); if(index > -1) { //remove from loopingInstances list _loopingInstancesIntervalCache.splice(index, 1); _loopingInstances.splice(index, 1); //correct update loop behavior if (_loopingInstances[_strLength] === 0 && _loopIsActive) { _loopIsActive = false; globals.autoUpdateLoop = _loopIsActive; if(_loopID !== undefined) { COMPATIBILITY.cAF()(_loopID); _loopID = -1; } } } }; } /** * A object whic