/*!
 * OverlayScrollbars
 * https://github.com/KingSora/OverlayScrollbars
 *
 * Version: 1.5.3
 *
 * Copyright KingSora.
 * https://github.com/KingSora
 *
 * Released under the MIT license.
 * Date: 07.11.2018
 */

(function (global, factory) {
    if (typeof define === 'function' && define.amd)
        define(function() { return factory(global, global.document, undefined); });
    else if (typeof module === 'object' && typeof module.exports === 'object')
        module.exports = factory(global, global.document, undefined);
    else
        factory(global, global.document, undefined);
}(typeof window !== 'undefined' ? window : this,
    function(window, document, undefined) {
        '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'
        };
        var COMPATIBILITY = {
            /**
             * Gets the current window width.
             * @returns {Number|number} The current window width in pixel.
             */
            wW: function() {
                return window.innerWidth || document.documentElement[LEXICON.cW] || document.body[LEXICON.cW];
            },

            /**
             * Gets the current window height.
             * @returns {Number|number} The current window height in pixel.
             */
            wH: function() {
                return window.innerHeight || document.documentElement[LEXICON.cH] || document.body[LEXICON.cH];
            },

            /**
             * Gets the MutationObserver Object or undefined if not supported.
             * @returns {MutationObserver|*|undefined} The MutationsObserver Object or undefined.
             */
            mO: function() {
                return window.MutationObserver || window.WebKitMutationObserver || window.WebkitMutationObserver || window.MozMutationObserver || undefined;
            },

            /**
             * Gets the ResizeObserver Object or undefined if not supported.
             * @returns {MutationObserver|*|undefined} The ResizeObserver Object or undefined.
             */
            rO: function() {
                return window.ResizeObserver || window.WebKitResizeObserver || window.WebkitResizeObserver || window.MozResizeObserver || undefined;
            },

            /**
             * Gets the RequestAnimationFrame method or it's corresponding polyfill.
             * @returns {*|Function} The RequestAnimationFrame method or it's corresponding polyfill.
             */
            rAF: function() {
                return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || 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: function() {
                return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function (id) {
                        return window.clearTimeout(id);
                    };
            },

            /**
             * Gets the current time.
             * @returns {number} The current time.
             */
            now: function() {
                return 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. (1 : leftButton | 2 : middleButton | 3 : rightButton)
             */
            mBtn: function(event) {
                if (!event.which && event.button !== undefined)
                    return (event.button & 1 ? 1 : (event.button & 2 ? 3 : (event.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: function(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;
            }
        };
        
        var JQUERY = window.jQuery;
        var EASING = (function() {
            var _math = window.Math;
            var _easingsMath = {
                p : _math.PI,
                c : _math.cos,
                s : _math.sin,
                w : _math.pow,
                t : _math.sqrt,
                n : _math.asin,
                a : _math.abs,
                o : 1.70158
            };
            
            /*
             x : current percent (0 - 1),
             t : current time (duration * percent),
             b : start value (from),
             c : end value (to),
             d : duration

             easingName : function(x, t, b, c, d) { return easedValue; }
             */

            return {
                swing: function (x, t, b, c, d) {
                    return 0.5 - _easingsMath.c(x * _easingsMath.p) / 2;
                },
                linear: function(x, t, b, c, d) {
                    return x;
                },
                easeInQuad: function (x, t, b, c, d) {
                    return c*(t/=d)*t + b;
                },
                easeOutQuad: function (x, t, b, c, d) {
                    return -c *(t/=d)*(t-2) + b;
                },
                easeInOutQuad: function (x, t, b, c, d) {
                    return ((t/=d/2) < 1) ? c/2*t*t + b : -c/2 * ((--t)*(t-2) - 1) + b;
                },
                easeInCubic: function (x, t, b, c, d) {
                    return c*(t/=d)*t*t + b;
                },
                easeOutCubic: function (x, t, b, c, d) {
                    return c*((t=t/d-1)*t*t + 1) + b;
                },
                easeInOutCubic: function (x, t, b, c, d) {
                    return ((t/=d/2) < 1) ? c/2*t*t*t + b : c/2*((t-=2)*t*t + 2) + b;
                },
                easeInQuart: function (x, t, b, c, d) {
                    return c*(t/=d)*t*t*t + b;
                },
                easeOutQuart: function (x, t, b, c, d) {
                    return -c * ((t=t/d-1)*t*t*t - 1) + b;
                },
                easeInOutQuart: function (x, t, b, c, d) {
                    return ((t/=d/2) < 1) ? c/2*t*t*t*t + b : -c/2 * ((t-=2)*t*t*t - 2) + b;
                },
                easeInQuint: function (x, t, b, c, d) {
                    return c*(t/=d)*t*t*t*t + b;
                },
                easeOutQuint: function (x, t, b, c, d) {
                    return c*((t=t/d-1)*t*t*t*t + 1) + b;
                },
                easeInOutQuint: function (x, t, b, c, d) {
                    return ((t/=d/2) < 1) ? c/2*t*t*t*t*t + b : c/2*((t-=2)*t*t*t*t + 2) + b;
                },
                easeInSine: function (x, t, b, c, d) {
                    return -c * _easingsMath.c(t/d * (_easingsMath.p/2)) + c + b;
                },
                easeOutSine: function (x, t, b, c, d) {
                    return c * _easingsMath.s(t/d * (_easingsMath.p/2)) + b;
                },
                easeInOutSine: function (x, t, b, c, d) {
                    return -c/2 * (_easingsMath.c(_easingsMath.p*t/d) - 1) + b;
                },
                easeInExpo: function (x, t, b, c, d) {
                    return (t==0) ? b : c * _easingsMath.w(2, 10 * (t/d - 1)) + b;
                },
                easeOutExpo: function (x, t, b, c, d) {
                    return (t==d) ? b+c : c * (-_easingsMath.w(2, -10 * t/d) + 1) + b;
                },
                easeInOutExpo: function (x, t, b, c, d) {
                    if (t==0) return b;
                    if (t==d) return b+c;
                    if ((t/=d/2) < 1) return c/2 * _easingsMath.w(2, 10 * (t - 1)) + b;
                    return c/2 * (-_easingsMath.w(2, -10 * --t) + 2) + b;
                },
                easeInCirc: function (x, t, b, c, d) {
                    return -c * (_easingsMath.t(1 - (t/=d)*t) - 1) + b;
                },
                easeOutCirc: function (x, t, b, c, d) {
                    return c * _easingsMath.t(1 - (t=t/d-1)*t) + b;
                },
                easeInOutCirc: function (x, t, b, c, d) {
                    return ((t/=d/2) < 1) ? -c/2 * (_easingsMath.t(1 - t*t) - 1) + b : c/2 * (_easingsMath.t(1 - (t-=2)*t) + 1) + b;
                },
                easeInElastic: function (x, t, b, c, d) {
                    var s=_easingsMath.o;var p=0;var a=c;
                    if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
                    if (a < _easingsMath.a(c)) { a=c; var s=p/4; }
                    else var s = p/(2*_easingsMath.p) * _easingsMath.n (c/a);
                    return -(a*_easingsMath.w(2,10*(t-=1)) * _easingsMath.s( (t*d-s)*(2*_easingsMath.p)/p )) + b;
                },
                easeOutElastic: function (x, t, b, c, d) {
                    var s=_easingsMath.o;var p=0;var a=c;
                    if (t==0) return b;  
                    if ((t/=d)==1) return b+c;  
                    if (!p) p=d*.3;
                    if (a < _easingsMath.a(c)) { a=c; var s=p/4; }
                    else var s = p/(2*_easingsMath.p) * _easingsMath.n (c/a);
                    return a*_easingsMath.w(2,-10*t) * _easingsMath.s( (t*d-s)*(2*_easingsMath.p)/p ) + c + b;
                },
                easeInOutElastic: function (x, t, b, c, d) {
                    var s=_easingsMath.o;var p=0;var a=c;
                    if (t==0) return b;  
                    if ((t/=d/2)==2) return b+c;  
                    if (!p) p=d*(.3*1.5);
                    if (a < _easingsMath.a(c)) { a=c; var s=p/4; }
                    else var s = p/(2*_easingsMath.p) * _easingsMath.n (c/a);
                    if (t < 1) return -.5*(a*_easingsMath.w(2,10*(t-=1)) * _easingsMath.s( (t*d-s)*(2*_easingsMath.p)/p )) + b;
                    return a*_easingsMath.w(2,-10*(t-=1)) * _easingsMath.s( (t*d-s)*(2*_easingsMath.p)/p )*.5 + c + b;
                },
                easeInBack: function (x, t, b, c, d, s) {
                    s = s || _easingsMath.o;
                    return c*(t/=d)*t*((s+1)*t - s) + b;
                },
                easeOutBack: function (x, t, b, c, d, s) {
                    s = s || _easingsMath.o;
                    return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
                },
                easeInOutBack: function (x, t, b, c, d, s) {
                    s = s || _easingsMath.o;
                    return ((t/=d/2) < 1) ? c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b : c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
                },
                easeInBounce: function (x, t, b, c, d) {
                    return c - this.easeOutBounce (x, d-t, 0, c, d) + b;
                },
                easeOutBounce: function (x, t, b, c, d) {
                    var o = 7.5625;
                    if ((t/=d) < (1/2.75)) {
                        return c*(o*t*t) + b;
                    } else if (t < (2/2.75)) {
                        return c*(o*(t-=(1.5/2.75))*t + .75) + b;
                    } else if (t < (2.5/2.75)) {
                        return c*(o*(t-=(2.25/2.75))*t + .9375) + b;
                    } else {
                        return c*(o*(t-=(2.625/2.75))*t + .984375) + b;
                    }
                },
                easeInOutBounce: function (x, t, b, c, d) {
                    return (t < d/2) ? this.easeInBounce (x, t*2, 0, c, d) * .5 + b : this.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
                }
            };
            /*
             *
             * TERMS OF USE - EASING EQUATIONS
             * 
             * Open source under the BSD License. 
             * 
             * Copyright Ã‚Â© 2001 Robert Penner
             * All rights reserved.
             * 
             * Redistribution and use in source and binary forms, with or without modification, 
             * are permitted provided that the following conditions are met:
             * 
             * Redistributions of source code must retain the above copyright notice, this list of 
             * conditions and the following disclaimer.
             * Redistributions in binary form must reproduce the above copyright notice, this list 
             * of conditions and the following disclaimer in the documentation and/or other materials 
             * provided with the distribution.
             * 
             * Neither the name of the author nor the names of contributors may be used to endorse 
             * or promote products derived from this software without specific prior written permission.
             * 
             * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
             * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
             * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
             *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
             *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
             *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
             * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
             *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
             * OF THE POSSIBILITY OF SUCH DAMAGE. 
             *
             */
        })();
        var FRAMEWORK = (function() {
            var _rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
            var _strSpace = ' ';
            var _strEmpty = '';
            var _strScrollLeft = 'scrollLeft';
            var _strScrollTop = 'scrollTop';
            var _animations = [ ];
            var _type = COMPATIBILITY.type;
            var _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
            };

            var extend = function() {
                var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {},
                    i = 1,
                    length = arguments[LEXICON.l],
                    deep = false;

                // Handle a deep copy situation
                if (_type(target) == TYPES.b) {
                    deep = target;
                    target = arguments[1] || {};
                    // skip the boolean and the target
                    i = 2;
                }

                // Handle case when target is a string or something (possible in deep copy)
                if (_type(target) != TYPES.o && !_type(target) == TYPES.f) {
                    target = {};
                }

                // extend jQuery itself if only one argument is passed
                if (length === i) {
                    target = FakejQuery;
                    --i;
                }

                for (; i < length; i++) {
                    // Only deal with non-null/undefined values
                    if ((options = arguments[i]) != null) {
                        // Extend the base object
                        for (name in options) {
                            src = target[name];
                            copy = options[name];

                            // Prevent never-ending loop
                            if (target === copy) {
                                continue;
                            }

                            // Recurse if we're merging plain objects or arrays
                            if (deep && copy && (isPlainObject(copy) || (copyIsArray = COMPATIBILITY.isA(copy)))) {
                                if (copyIsArray) {
                                    copyIsArray = false;
                                    clone = src && COMPATIBILITY.isA(src) ? src : [];

                                } else {
                                    clone = src && isPlainObject(src) ? src : {};
                                }

                                // Never move original objects, clone them
                                target[name] = extend(deep, clone, copy);

                                // Don't bring in undefined values
                            } else if (copy !== undefined) {
                                target[name] = copy;
                            }
                        }
                    }
                }

                // Return the modified object
                return target;
            };

            var inArray = function(item, arr, fromIndex) {
                for (var i = fromIndex || 0; i < arr[LEXICON.l]; i++)
                    if (arr[i] === item)
                        return i;
                return -1;
            }
            
            var isFunction = function(obj) {
                return _type(obj) == TYPES.f;
            };

            var isEmptyObject = function(obj) {
                for (var name in obj )
                    return false;
                return true;
            };

            var isPlainObject = function(obj) {
                if (!obj || _type(obj) != TYPES.o)
                    return false;

                var key;
                var proto = LEXICON.p;
                var hasOwnProperty = Object[proto].hasOwnProperty;
                var hasOwnConstructor = hasOwnProperty.call(obj, 'constructor');
                var hasIsPrototypeOf = obj.constructor && obj.constructor[proto] && hasOwnProperty.call(obj.constructor[proto], 'isPrototypeOf');

                if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) {
                    return false;
                }


                for (key in obj) { /**/ }

                return _type(key) == TYPES.u || hasOwnProperty.call(obj, key);
            };

            var each = function(obj, callback) {
                var i = 0;

                if (isArrayLike(obj)) {
                    for (; i < obj[LEXICON.l]; i++) {
                        if (callback.call(obj[i], i, obj[i]) === false)
                            break;
                    }
                }
                else {
                    for (i in obj) {
                        if (callback.call(obj[i], i, obj[i]) === false)
                            break;
                    }
                }

                return obj;
            };

            var FakejQuery = function (selector) {
                if(arguments[LEXICON.l] === 0)
                    return this;

                var base = new FakejQuery();
                var elements = selector;
                var i = 0;
                var elms;
                var el;

                if(_type(selector) == TYPES.s) {
                    elements = [ ];
                    if(selector.charAt(0) === '<') {
                        el = document.createElement('div');
                        el.innerHTML = selector;
                        elms = el.children;
                    }
                    else {
                        elms = document.querySelectorAll(selector);
                    }

                    for(; i < elms[LEXICON.l]; i++)
                        elements.push(elms[i]);
                }

                if(elements) {
                    if(_type(elements) != TYPES.s && !isArrayLike(elements))
                        elements = [ elements ];

                    for(i = 0; i < elements[LEXICON.l]; i++)
                        base[i] = elements[i];

                    base[LEXICON.l] = elements[LEXICON.l];
                }

                return base;
            };

            function isArrayLike(obj) {
                var length = !!obj && [LEXICON.l] in obj && obj[LEXICON.l];
                var t = _type(obj);
                //|| obj === window = self added!
                return isFunction(t) || obj === window ? false : (t == TYPES.a || length === 0 || _type(length) == TYPES.n && length > 0 && (length - 1) in obj);
            }

            function stripAndCollapse(value) {
                var tokens = value.match(_rnothtmlwhite) || [];
                return tokens.join(_strSpace);
            }

            function matches(elem, selector) {
                var nodeList = (elem.parentNode || document).querySelectorAll(selector) || [];
                var i = nodeList[LEXICON.l];

                while (i--)
                    if (nodeList[i] == elem)
                        return true;

                return false;
            }

            function insertAdjacentElement(el, strategy, child) {
                if(_type(child) == TYPES.a) {
                    for(var i = 0; i < child[LEXICON.l]; i++)
                        insertAdjacentElement(el, strategy, child[i]);
                }
                else if(_type(child) == TYPES.s)
                    el.insertAdjacentHTML(strategy, child);
                else
                    el.insertAdjacentElement(strategy, child.nodeType ? child : child[0]);
            }

            function setCSSVal(el, prop, val) {
                try {
                    if(el[LEXICON.s][prop] !== undefined)
                        el[LEXICON.s][prop] = parseCSSVal(prop, val);
                } catch(e) { }
            }

            function parseCSSVal(prop, val) {
                if(!_cssNumber[prop.toLowerCase()] && _type(val) == TYPES.n)
                    val += 'px';
                return val;
            }

            function startNextAnimationInQ(animObj, removeFromQ) {
                var index;
                var nextAnim;
                if(removeFromQ !== false)
                    animObj.q.splice(0, 1);
                if(animObj.q[LEXICON.l] > 0) {
                    nextAnim = animObj.q[0];
                    animate(animObj.el, nextAnim.props, nextAnim.duration, nextAnim.easing, nextAnim.complete, true);
                }
                else {
                    index = inArray(animObj, _animations);
                    if(index > -1)
                        _animations.splice(index, 1);
                }
            }

            function setAnimationValue(el, prop, value) {
                if(prop === _strScrollLeft || prop === _strScrollTop)
                    el[prop] = value;
                else
                    setCSSVal(el, prop, value);
            }

            function animate(el, props, options, easing, complete, guaranteedNext) {
                var hasOptions = isPlainObject(options);
                var from = { };
                var to = { };
                var i = 0;
                var key;
                var animObj;
                var start;
                var progress;
                var step;
                var specialEasing;
                var duration;
                if(hasOptions) {
                    easing = options.easing;
                    start = options.start;
                    progress = options.progress;
                    step = options.step;
                    specialEasing = options.specialEasing;
                    complete = options.complete;
                    duration = options.duration;
                }
                else
                    duration = options;
                specialEasing = specialEasing || { };
                duration = duration || 400;
                easing = easing || 'swing';
                guaranteedNext = guaranteedNext || false;

                for(; i < _animations[LEXICON.l]; i++) {
                    if(_animations[i].el === el) {
                        animObj = _animations[i];
                        break;
                    }
                }

                if(!animObj) {
                    animObj = {
                        el : el,
                        q : []
                    };
                    _animations.push(animObj);
                }

                for (key in props) {
                    if(key === _strScrollLeft || key === _strScrollTop)
                        from[key] = el[key];
                    else
                        from[key] = FakejQuery(el).css(key);
                }

                for (key in from) {
                    if(from[key] !== props[key] && props[key] !== undefined)
                        to[key] = props[key];
                }

                if(!isEmptyObject(to)) {
                    var timeNow;
                    var end;
                    var percent;
                    var fromVal;
                    var toVal;
                    var easedVal;
                    var timeStart;
                    var frame;
                    var elapsed;
                    var qPos = guaranteedNext ? 0 : inArray(qObj, animObj.q);
                    var qObj = {
                        props : to,
                        duration : hasOptions ? options : duration,
                        easing : easing,
                        complete : complete
                    };
                    if (qPos === -1) {
                        qPos = animObj.q[LEXICON.l];
                        animObj.q.push(qObj);
                    }

                    if(qPos === 0) {
                        if(duration > 0) {
                            timeStart = COMPATIBILITY.now();
                            frame = function() {
                                timeNow = COMPATIBILITY.now();
                                elapsed = (timeNow - timeStart);
                                end = qObj.stop || elapsed >= duration;
                                percent = 1 - ((Math.max(0, timeStart + duration - timeNow) / duration) || 0);

                                for(key in to) {
                                    fromVal = parseFloat(from[key]);
                                    toVal = parseFloat(to[key]);
                                    easedVal = (toVal - fromVal) * EASING[specialEasing[key] || easing](percent, percent * duration, 0, 1, duration) + fromVal;
                                    setAnimationValue(el, key, easedVal);
                                    if(isFunction(step)) {
                                        step(easedVal, {
                                            elem : el,
                                            prop : key,
                                            start : fromVal,
                                            now : easedVal,
                                            end : toVal,
                                            pos : percent,
                                            options : {
                                                easing : easing,
                                                speacialEasing : specialEasing,
                                                duration : duration,
                                                complete : complete,
                                                step : step
                                            },
                                            startTime : timeStart
                                        });
                                    }
                                }

                                if(isFunction(progress))
                                    progress({ }, percent, Math.max(0, duration - elapsed));

                                if (end) {
                                    startNextAnimationInQ(animObj);
                                    if(isFunction(complete))
                                        complete();
                                }
                                else
                                    qObj.frame = COMPATIBILITY.rAF()(frame);
                            };
                            qObj.frame = COMPATIBILITY.rAF()(frame);
                        }
                        else {
                            for(key in to)
                                setAnimationValue(el, key, to[key]);
                            startNextAnimationInQ(animObj);
                        }
                    }
                }
                else if(guaranteedNext)
                    startNextAnimationInQ(animObj);
            }

            function stop(el, clearQ, jumpToEnd) {
                var animObj;
                var qObj;
                var key;
                var i = 0;
                for(; i < _animations[LEXICON.l]; i++) {
                    animObj = _animations[i];
                    if(animObj.el === el) {
                        if(animObj.q[LEXICON.l] > 0) {
                            qObj = animObj.q[0];
                            qObj.stop = true;
                            COMPATIBILITY.cAF()(qObj.frame);
                            animObj.q.splice(0, 1);

                            if(jumpToEnd)
                                for(key in qObj.props)
                                    setAnimationValue(el, key, qObj.props[key]);

                            if(clearQ)
                                animObj.q = [ ];
                            else
                                startNextAnimationInQ(animObj, false);
                        }
                        break;
                    }
                }
            }

            FakejQuery[LEXICON.p] = {

                //EVENTS:

                on : function(eventName, handler) {
                    eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [_strEmpty];

                    var eventNameLength = eventName[LEXICON.l];
                    var i = 0;
                    var el;
                    return this.each(function() {
                        el = this;
                        try {
                            if (el.addEventListener) {
                                for (; i < eventNameLength; i++)
                                    el.addEventListener(eventName[i], handler);
                            }
                            else if(el.detachEvent) {
                                for (; i < eventNameLength; i++)
                                    el.attachEvent('on' + eventName[i], handler);
                            }
                        } catch (e) { }
                    });
                },

                off : function(eventName, handler) {
                    eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [_strEmpty];

                    var eventNameLength = eventName[LEXICON.l];
                    var i = 0;
                    var el;
                    return this.each(function() {
                        el = this;
                        try {
                            if (el.removeEventListener) {
                                for (; i < eventNameLength; i++)
                                    el.removeEventListener(eventName[i], handler);
                            }
                            else if(el.detachEvent) {
                                for (; i < eventNameLength; i++)
                                    el.detachEvent('on' + eventName[i], handler);
                            }
                        } catch (e) { }
                    });
                },

                one : function (eventName, handler) {
                    eventName = (eventName || _strEmpty).match(_rnothtmlwhite) || [_strEmpty];
                    return this.each(function() {
                        var el = FakejQuery(this);
                        FakejQuery.each(eventName, function(i, oneEventName) {
                            var oneHandler = function(e) {
                                handler.call(this, e);
                                el.off(oneEventName, oneHandler);
                            };
                            el.on(oneEventName, oneHandler);
                        });
                    });
                },

                trigger : function(eventName) {
                    var el;
                    var event;
                    return this.each(function() {
                        el = this;
                        if (document.createEvent) {
                            event = document.createEvent('HTMLEvents');
                            event.initEvent(eventName, true, false);
                            el.dispatchEvent(event);
                        }
                        else {
                            el.fireEvent("on" + eventName);
                        }
                    });
                },

                //DOM NODE INSERTING / REMOVING:

                append : function(child) {
                    return this.each(function() { insertAdjacentElement(this, 'beforeend', child); });
                },

                prepend : function(child) {
                    return this.each(function() { insertAdjacentElement(this, 'afterbegin', child); });
                },

                before : function(child) {
                    return this.each(function() { insertAdjacentElement(this, 'beforebegin', child); });
                },

                after : function(child) {
                    return this.each(function() { insertAdjacentElement(this, 'afterend', child); });
                },

                remove : function() {
                    return this.each(function() {
                        var el = this;
                        var parentNode = el.parentNode;
                        if(parentNode != null)
                            parentNode.removeChild(el);
                    });
                },

                unwrap : function() {
                    var parents = [ ];
                    var i;
                    var el;
                    var parent;

                    this.each(function() {
                        parent = this.parentNode;
                        if(inArray(parent, parents) === - 1)
                            parents.push(parent);
                    });

                    for(i = 0; i < parents[LEXICON.l]; i++) {
                        el = parents[i];
                        parent = el.parentNode;
                        while (el.firstChild)
                            parent.insertBefore(el.firstChild, el);
                        parent.removeChild(el);
                    }

                    return this;
                },

                wrapAll : function(wrapperHTML) {
                    var i;
                    var nodes = this;
                    var wrapper = FakejQuery(wrapperHTML)[0];
                    var deepest = wrapper;
                    var parent = nodes[0].parentNode;
                    var previousSibling = nodes[0].previousSibling;
                    while(deepest.childNodes[LEXICON.l] > 0)
                        deepest = deepest.childNodes[0];

                    for (i = 0; nodes[LEXICON.l] - i; deepest.firstChild === nodes[0] && i++)
                        deepest.appendChild(nodes[i]);

                    var nextSibling = previousSibling ? previousSibling.nextSibling : parent.firstChild;
                    parent.insertBefore(wrapper, nextSibling);

                    return this;
                },

                wrapInner : function(wrapperHTML) {
                    return this.each(function() {
                        var el = FakejQuery(this);
                        var contents = el.contents();

                        if (contents[LEXICON.l])
                            contents.wrapAll(wrapperHTML);
                        else
                            el.append(wrapperHTML);
                    });
                },

                wrap : function(wrapperHTML) {
                    return this.each(function() { FakejQuery(this).wrapAll(wrapperHTML); });
                },


                //DOM NODE MANIPULATION / INFORMATION:

                css : function(styles, val) {
                    var el;
                    var key;
                    var cptStyle;
                    var getCptStyle = window.getComputedStyle;
                    if(_type(styles) == TYPES.s) {
                        if(val === undefined) {
                            el = this[0];
                            cptStyle = getCptStyle ? getCptStyle(el, null) : el.currentStyle[styles];

                            //https://bugzilla.mozilla.org/show_bug.cgi?id=548397 can be null sometimes if iframe with display: none (firefox only!)
                            return getCptStyle ? cptStyle != null ? cptStyle.getPropertyValue(styles) : el[LEXICON.s][styles] : cptStyle;
                        }
                        else {
                            return this.each(function() {
                                setCSSVal(this, styles, val);
                            });
                        }
                    }
                    else {
                        return this.each(function() {
                            for(key in styles)
                                setCSSVal(this, key, styles[key]);
                        });
                    }
                },

                hasClass : function(className) {
                    var elem, i = 0;
                    var classNamePrepared = _strSpace + className + _strSpace;
                    var classList;

                    while ((elem = this[ i++ ])) {
                        classList = elem.classList;
                        if(classList && classList.contains(className))
                            return true;
                        else if (elem.nodeType === 1 && (_strSpace + stripAndCollapse(elem.className + _strEmpty) + _strSpace).indexOf(classNamePrepared) > -1)
                            return true;
                    }

                    return false;
                },

                addClass : function(className) {
                    var classes;
                    var elem;
                    var cur;
                    var curValue;
                    var clazz;
                    var finalValue;
                    var supportClassList;
                    var elmClassList;
                    var i = 0;
                    var v = 0;

                    if (className) {
                        classes = className.match( _rnothtmlwhite ) || [];

                        while ((elem = this[i++])) {
                            elmClassList = elem.classList;
                            if(supportClassList === undefined)
                                supportClassList = elmClassList !== undefined;

                            if(supportClassList) {
                                while ((clazz = classes[v++]))
                                    elmClassList.add(clazz);
                            }
                            else {
                                curValue = elem.className + _strEmpty;
                                cur = elem.nodeType === 1 && (_strSpace + stripAndCollapse(curValue) + _strSpace);

                                if (cur) {
                                    while ((clazz = classes[v++]))
                                        if (cur.indexOf(_strSpace + clazz + _strSpace) < 0)
                                            cur += clazz + _strSpace;

                                    finalValue = stripAndCollapse(cur);
                                    if (curValue !== finalValue)
                                        elem.className = finalValue;
                                }
                            }
                        }
                    }

                    return this;
                },

                removeClass : function(className) {
                    var classes;
                    var elem;
                    var cur;
                    var curValue;
                    var clazz;
                    var finalValue;
                    var supportClassList;
                    var elmClassList;
                    var i = 0;
                    var v = 0;

                    if (className) {
                        classes = className.match(_rnothtmlwhite) || [];

                        while ((elem = this[i++])) {
                            elmClassList = elem.classList;
                            if(supportClassList === undefined)
                                supportClassList = elmClassList !== undefined;

                            if(supportClassList) {
                                while ((clazz = classes[v++]))
                                    elmClassList.remove(clazz);
                            }
                            else {
                                curValue = elem.className + _strEmpty;
                                cur = elem.nodeType === 1 && (_strSpace + stripAndCollapse(curValue) + _strSpace);

                                if (cur) {
                                    while ((clazz = classes[v++]))
                                        while (cur.indexOf(_strSpace + clazz + _strSpace) > -1)
                                            cur = cur.replace(_strSpace + clazz + _strSpace, _strSpace);

                                    finalValue = stripAndCollapse(cur);
                                    if (curValue !== finalValue)
                                        elem.className = finalValue;
                                }
                            }
                        }
                    }

                    return this;
                },

                hide : function() {
                    return this.each(function() { this[LEXICON.s].display = 'none'; });
                },

                show : function() {
                    return this.each(function() { this[LEXICON.s].display = 'block'; });
                },

                attr : function(attrName, value) {
                    var i = 0;
                    var el;
                    while (el = this[i++]) {
                        if(value === undefined)
                            return el.getAttribute(attrName);
                        el.setAttribute(attrName, value);
                    }
                    return this;
                },

                removeAttr : function(attrName) {
                    return this.each(function() { this.removeAttribute(attrName); });
                },

                offset : function() {
                    var el = this[0];
                    var rect = el.getBoundingClientRect();
                    var scrollLeft = window.pageXOffset || document.documentElement[_strScrollLeft];
                    var scrollTop = window.pageYOffset || document.documentElement[_strScrollTop];
                    return {
                        top: rect.top + scrollTop,
                        left: rect.left + scrollLeft
                    };
                },

                position : function() {
                    var el = this[0];
                    return {
                        top: el.offsetTop,
                        left: el.offsetLeft
                    };
                },

                scrollLeft : function(value) {
                    var i = 0;
                    var el;
                    while (el = this[i++]) {
                        if(value === undefined)
                            return el[_strScrollLeft];
                        el[_strScrollLeft] = value;
                    }
                    return this;
                },

                scrollTop : function(value) {
                    var i = 0;
                    var el;
                    while (el = this[i++]) {
                        if(value === undefined)
                            return el[_strScrollTop];
                        el[_strScrollTop] = value;
                    }
                    return this;
                },

                val : function(value) {
                    var el = this[0];
                    if(!value)
                        return el.value;
                    el.value = value;
                    return this;
                },


                //DOM TRAVERSAL / FILTERING:

                first : function() {
                    return this.eq(0);
                },

                last : function() {
                    return this.eq(-1);
                },

                eq : function(index) {
                    return FakejQuery(this[index >= 0 ? index : this[LEXICON.l] + index]);
                },

                find : function(selector) {
                    var children = [ ];
                    var i;
                    this.each(function() {
                        var el = this;
                        var ch = el.querySelectorAll(selector);
                        for(i = 0; i < ch[LEXICON.l]; i++)
                            children.push(ch[i]);
                    });
                    return FakejQuery(children);
                },

                children : function(selector) {
                    var children = [ ];
                    var el;
                    var ch;
                    var i;

                    this.each(function() {
                        ch = this.children;
                        for(i = 0; i < ch[LEXICON.l]; i++) {
                            el = ch[i];
                            if(selector) {
                                if((el.matches && el.matches(selector)) || matches(el, selector))
                                    children.push(el);
                            }
                            else
                                children.push(el);
                        }
                    });
                    return FakejQuery(children);
                },

                parent : function(selector) {
                    var parents = [ ];
                    var parent;
                    this.each(function() {
                        parent = this.parentNode;
                        if(selector ? FakejQuery(parent).is(selector) : true)
                            parents.push(parent);
                    });
                    return FakejQuery(parents);
                },

                is : function(selector) {
                    var el;
                    var i;
                    for(i = 0; i < this[LEXICON.l]; i++) {
                        el = this[i];
                        if(selector === ":visible")
                            return !!(el[LEXICON.oW] || el[LEXICON.oH] || el.getClientRects()[LEXICON.l]);
                        if(selector === ":hidden")
                            return !!!(el[LEXICON.oW] || el[LEXICON.oH] || el.getClientRects()[LEXICON.l]);
                        if((el.matches && el.matches(selector)) || matches(el, selector))
                            return true;
                    }
                    return false;
                },

                contents : function() {
                    var contents = [ ];
                    var childs;
                    var i;

                    this.each(function() {
                        childs = this.childNodes;
                        for(i = 0; i < childs[LEXICON.l]; i++)
                            contents.push(childs[i]);
                    });

                    return FakejQuery(contents);
                },

                each : function(callback) {
                    return each(this, callback);
                },


                //ANIMATION:

                animate : function(props, duration, easing, complete) {
                    return this.each(function() { animate(this, props, duration, easing, complete); });
                },

                stop : function(clearQ, jump) {
                    return this.each(function() { stop(this, clearQ, jump); });
                }
            };

            extend(FakejQuery, {
                extend : extend,
                inArray : inArray,
                isEmptyObject : isEmptyObject,
                isPlainObject : isPlainObject,
                each : each
            });

            return FakejQuery;
        })();
        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 _pluginsGlobals;
            var _pluginsAutoUpdateLoop;
            var _pluginsExtensions = [ ];
            var _pluginsOptions = (function() {
                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 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
                    },
                    textarea: {
                        dynWidth: booleanFalseTemplate,                             //true || false
                        dynHeight: booleanFalseTemplate                             //true || false
                    },
                    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.hasOwnProperty(key))
                                continue;
                            val = obj[key];
                            valType = COMPATIBILITY.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
                    d : convert(),

                    //template
                    t : convert(true),

                    //validate
                    /**
                     * 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 usePreparedValues True if the validated main values shall be returned in the validated object, false otherwise.
                     * @param keepForeignProps True if properties which aren't in the template shall be added to the validated object, false otherwise.
                     * @returns {{}} A object which contains only the valid properties of the passed original object.
                     */
                    v : function (obj, template, writeErrors, usePreparedValues, keepForeignProps) {
                        var validatedOptions = { };
                        var objectCopy = FRAMEWORK.extend(true, { }, obj);
                        var checkObjectProps = function(data, template, validatedOptions, prevPropName) {
                            for (var prop in template) {
                                if (template.hasOwnProperty(prop) && data.hasOwnProperty(prop)) {
                                    var isValid = false;
                                    var templateValue = template[prop];
                                    var templateValueType = COMPATIBILITY.type(templateValue);
                                    var templateTypes = COMPATIBILITY.type(templateValue) != TYPES.a ? [ templateValue ] : templateValue;
                                    var dataValue = data[prop];
                                    var dataValueType = COMPATIBILITY.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;

                                    //if the template has a object as value, it means that the options are complex (verschachtelt)
                                    if(templateValueType == TYPES.o) {
                                        validatedOptions[prop] = { };
                                        checkObjectProps(dataValue, templateValue, validatedOptions[prop], propPrefix + prop);
                                        if(FRAMEWORK.isEmptyObject(dataValue))
                                            delete data[prop];
                                    }
                                    else {
                                        for(i = 0; i < templateTypes.length; i++) {
                                            currType = templateTypes[i];
                                            templateValueType = COMPATIBILITY.type(currType);
                                            //if currtype is string and starts with restrictedStringPrefix and end with restrictedStringSuffix
                                            isRestrictedValue = templateValueType == TYPES.s && FRAMEWORK.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.length; 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.length; 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) {
                                            validatedOptions[prop] = isRestrictedValue && usePreparedValues ? 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, validatedOptions);

                        //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);
                        else if(!FRAMEWORK.isEmptyObject(objectCopy) && writeErrors)
                            console.warn("The following options are discarded due to invalidity:\r\n" + JSON.stringify(objectCopy, null, 2));

                        return validatedOptions;
                    }
                }
            }());

            /**
             * Initializes the object which contains global information about the plugin and each instance of it.
             */
            function initOverlayScrollbarsStatics() {
                if(!_pluginsGlobals)
                    _pluginsGlobals = new OverlayScrollbarsGlobals(_pluginsOptions.d);
                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="hs-dummy-scrollbar-size"><div style="height: 200%; width: 200%; margin: 10px 0;"></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
                };

                FRAMEWORK.extend(_base, {
                    defaultOptions : defaultOptions,
                    autoUpdateLoop : false,
                    autoUpdateRecommended : !COMPATIBILITY.mO(),
                    nativeScrollbarSize : nativeScrollbarSize,
                    nativeScrollbarIsOverlaid : nativeScrollbarIsOverlaid,
                    nativeScrollbarStyling : (function() {
                        scrollbarDummyElement.addClass('os-viewport-native-scrollbars-invisible');
                        //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;
                    })(),
                    overlayScrollbarDummySize : { x: 30, y: 30 },
                    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 parseInt = window.parseInt;

                        // IE 10 or older => return version number
                        if (msie > 0)
                            result = parseInt(ua[strSubString](msie + 5, ua[strIndexOf]('.', msie)), 10);

                        // IE 11 => return version number
                        else if (trident > 0)
                            result = parseInt(ua[strSubString](rv + 3, ua[strIndexOf]('.', rv)), 10);

                        // Edge (IE 12+) => return version number
                        else if (edge > 0)
                            result = parseInt(ua[strSubString](edge + 5, ua[strIndexOf]('.', edge)), 10);

                        // other browser
                        return result;
                    })(),
                    cssCalc : (function() {
                        var dummy = document.createElement('div');
                        var props = ['calc', '-webkit-calc', '-moz-calc', '-o-calc'];
                        var i;
                        var prop;

                        for (i = 0; i < props.length; ++i) {
                            prop = props[i];
                            dummy[LEXICON.s].cssText = 'width:' + prop + '(1px);';
                            if (dummy[LEXICON.s].length)
                                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 : detectCSSFeature('transform'),
                    supportTransition : detectCSSFeature('transition'),
                    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 detectCSSFeature(featurename) {
                    var feature = false;
                    var domPrefixes = 'Webkit Moz ms O'.split(' ');
                    var elm = document.createElement('div');
                    var featurenameCapital = null;
                    var i = 0;

                    featurename = featurename.toLowerCase();

                    if (elm[LEXICON.s][featurename] !== undefined)
                        feature = true;

                    if (!feature) {
                        featurenameCapital = featurename.charAt(0).toUpperCase() + featurename.substr(1);
                        for (; i < domPrefixes.length; i++) {
                            if (elm[LEXICON.s][domPrefixes[i] + featurenameCapital] !== undefined) {
                                feature = true;
                                break;
                            }
                        }
                    }
                    return feature;
                }

                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 _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  = COMPATIBILITY.now();
                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 = COMPATIBILITY.now();
                        var timeDelta = timeNew - _loopTimeOld;

                        if (timeDelta > _loopInterval) {
                            _loopTimeOld = timeNew - (timeDelta % _loopInterval);
                            var lowestInterval = _loopIntervalDefault;
                            for(var i = 0; i < _loopingInstances[_strLength]; i++) {
                                var instance = _loopingInstances[i];
                                if (instance !== undefined) {
                                    var instanceOptions = instance.options();
                                    var instanceAutoUpdateAllowed = instanceOptions[_strAutoUpdate];
                                    var instanceAutoUpdateInterval = Math.max(1, instanceOptions[_strAutoUpdateInterval]);
                                    var now = COMPATIBILITY.now();
                                    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(FRAMEWORK.inArray(instance, _loopingInstances) === -1) {
                        _loopingInstances.push(instance);
                        _loopingInstancesIntervalCache.push(COMPATIBILITY.now());
                        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 = FRAMEWORK.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 which manages the scrollbars visibility of the target element.
             * @param pluginTargetElement The element from which the scrollbars shall be hidden.
             * @param options The custom options.
             * @param extensions The custom extensions.
             * @param globals
             * @param autoUpdateLoop
             * @returns {*}
             * @constructor
             */
            function OverlayScrollbarsInstance(pluginTargetElement, options, extensions, globals, autoUpdateLoop) {
                //if passed element is no HTML element: skip and return
                if(!isHTMLElement(pluginTargetElement))
                    return;

                //if passed element is already initialized: set passed options if there are any and return its instance
                if(INSTANCES(pluginTargetElement)) {
                    var inst = INSTANCES(pluginTargetElement);
                    inst.options(options);
                    return inst;
                }

                //make correct instanceof
                var _base = new window[PLUGINNAME]();
                var _frameworkProto = FRAMEWORK[LEXICON.p];

                //globals:
                var _nativeScrollbarIsOverlaid;
                var _overlayScrollbarDummySize;
                var _rtlScrollBehavior;
                var _autoUpdateRecommended;
                var _msieVersion;
                var _nativeScrollbarStyling;
                var _cssCalc;
                var _nativeScrollbarSize;
                var _supportTransition;
                var _supportTransform;
                var _supportPassiveEvents;
                var _supportResizeObserver;
                var _restrictedMeasuring;

                //general readonly:
                var _initialized;
                var _destroyed;
                var _isTextarea;
                var _isBody;
                var _documentMixed;

                //general:
                var _isBorderBox;
                var _sizeAutoObserverAdded;
                var _paddingX;
                var _paddingY;
                var _borderX;
                var _borderY;
                var _marginX;
                var _marginY;
                var _isRTL;
                var _isSleeping;
                var _contentBorderSize = { };
                var _scrollHorizontalInfo = { };
                var _scrollVerticalInfo = { };
                var _viewportSize = { };
                var _nativeScrollbarMinSize = { };

                //scroll
                var _scrollStopDelay = 175;
                var _scrollStopTimeoutId;

                //naming:
                var _strMinusHidden = '-hidden';
                var _strMarginMinus = 'margin-';
                var _strPaddingMinus = 'padding-';
                var _strBorderMinus = 'border-';
                var _strTop = 'top';
                var _strRight = 'right';
                var _strBottom = 'bottom';
                var _strLeft = 'left';
                var _strMinMinus = 'min-';
                var _strMaxMinus = 'max-';
                var _strWidth = 'width';
                var _strHeight = 'height';
                var _strFloat = 'float';
                var _strEmpty = '';
                var _strAuto = 'auto';
                var _strScroll = 'scroll';
                var _strHundredPercent = '100%';
                var _strX = 'x';
                var _strY = 'y';
                var _strDot = '.';
                var _strSpace = ' ';
                var _strScrollbar = 'scrollbar';
                var _strMinusHorizontal = '-horizontal';
                var _strMinusVertical = '-vertical';
                var _strScrollLeft = _strScroll + 'Left';
                var _strScrollTop = _strScroll + 'Top';
                var _strMouseTouchDownEvent = 'mousedown touchstart';
                var _strMouseTouchUpEvent = 'mouseup touchend touchcancel';
                var _strMouseTouchMoveEvent = 'mousemove touchmove';
                var _strMouseTouchEnter = 'mouseenter';
                var _strMouseTouchLeave = 'mouseleave';
                var _strKeyDownEvent = 'keydown';
                var _strKeyUpEvent = 'keyup';
                var _strSelectStartEvent = 'selectstart';
                var _strTransitionEndEvent = 'transitionend webkitTransitionEnd oTransitionEnd';
                var _strResizeObserverProperty = '__overlayScrollbarsRO__';

                //class names:
                var _cassNamesPrefix = 'os-';
                var _classNameHTMLElement = _cassNamesPrefix + 'html';
                var _classNameHostElement = _cassNamesPrefix + 'host';
                var _classNameHostTextareaElement = _classNameHostElement + '-textarea';
                var _classNameHostScrollbarHorizontalHidden = _classNameHostElement + '-' + _strScrollbar + _strMinusHorizontal + _strMinusHidden;
                var _classNameHostScrollbarVerticalHidden = _classNameHostElement + '-' + _strScrollbar + _strMinusVertical + _strMinusHidden;
                var _classNameHostTransition = _classNameHostElement + '-transition';
                var _classNameHostRTL = _classNameHostElement + '-rtl';
                var _classNameHostResizeDisabled = _classNameHostElement + '-resize-disabled';
                var _classNameHostScrolling = _classNameHostElement + '-scrolling';
                var _classNameHostOverflow = _classNameHostElement + '-overflow';
                var _classNameHostOverflowX = _classNameHostOverflow + '-x';
                var _classNameHostOverflowY = _classNameHostOverflow + '-y';
                var _classNameTextareaElement = _cassNamesPrefix + 'textarea';
                var _classNameTextareaCoverElement = _classNameTextareaElement + '-cover';
                var _classNamePaddingElement = _cassNamesPrefix + 'padding';
                var _classNameViewportElement = _cassNamesPrefix + 'viewport';
                var _classNameViewportNativeScrollbarsInvisible = _classNameViewportElement + '-native-scrollbars-invisible';
                var _classNameViewportNativeScrollbarsOverlaid = _classNameViewportElement + '-native-scrollbars-overlaid';
                var _classNameContentElement = _cassNamesPrefix + 'content';
                var _classNameContentArrangeElement = _cassNamesPrefix + 'content-arrange';
                var _classNameContentGlueElement = _cassNamesPrefix + 'content-glue';
                var _classNameSizeAutoObserverElement = _cassNamesPrefix + 'size-auto-observer';
                var _classNameResizeObserverElement = _cassNamesPrefix + 'resize-observer';
                var _classNameResizeObserverItemElement = _cassNamesPrefix + 'resize-observer-item';
                var _classNameResizeObserverItemFinalElement = _classNameResizeObserverItemElement + '-final';
                var _classNameTextInherit = _cassNamesPrefix + 'text-inherit';
                var _classNameScrollbar = _cassNamesPrefix + _strScrollbar;
                var _classNameScrollbarTrack = _classNameScrollbar + '-track';
                var _classNameScrollbarTrackOff = _classNameScrollbarTrack + '-off';
                var _classNameScrollbarHandle = _classNameScrollbar + '-handle';
                var _classNameScrollbarHandleOff = _classNameScrollbarHandle + '-off';
                var _classNameScrollbarUnusable = _classNameScrollbar + '-unusable';
                var _classNameScrollbarAutoHidden = _classNameScrollbar + '-' + _strAuto + _strMinusHidden;
                var _classNameScrollbarCorner = _classNameScrollbar + '-corner';
                var _classNameScrollbarCornerResize = _classNameScrollbarCorner + '-resize';
                var _classNameScrollbarCornerResizeB = _classNameScrollbarCornerResize + '-both';
                var _classNameScrollbarCornerResizeH = _classNameScrollbarCornerResize + _strMinusHorizontal;
                var _classNameScrollbarCornerResizeV = _classNameScrollbarCornerResize + _strMinusVertical;
                var _classNameScrollbarHorizontal = _classNameScrollbar + _strMinusHorizontal;
                var _classNameScrollbarVertical = _classNameScrollbar + _strMinusVertical;
                var _classNameDragging = _cassNamesPrefix + 'dragging';
                var _classNameThemeNone = _cassNamesPrefix + 'theme-none';

                //options:
                var _defaultOptions;
                var _currentOptions;
                var _currentPreparedOptions;

                //extensions:
                var _extensions = { };
                var _extensionsPrivateMethods = "added removed on contract";

                //update
                var _lastUpdateTime;
                var _swallowedUpdateParams = { };
                var _swallowedUpdateTimeout;
                var _swallowUpdateLag = 42;
                var _imgs = [ ];

                //DOM elements:
                var _windowElement;
                var _documentElement;
                var _htmlElement;
                var _bodyElement;
                var _targetElement;                     //the target element of this OverlayScrollbars object
                var _hostElement;                         //the host element of this OverlayScrollbars object -> may be the same as targetElement
                var _sizeAutoObserverElement;             //observes size auto changes
                var _sizeObserverElement;               //observes size and padding changes
                var _contentGlueElement;                 //has always the size of the content element
                var _paddingElement;                     //manages the padding
                var _viewportElement;                     //is the viewport of our scrollbar model
                var _contentArrangeElement;             //is needed for correct sizing of the content element (only if native scrollbars are overlays)
                var _contentElement;                     //the element which holds the content
                var _textareaCoverElement;              //only applied if target is a textarea element. Used for correct size calculation and for prevention of uncontrolled scrolling
                var _scrollbarCornerElement;
                var _scrollbarHorizontalElement;
                var _scrollbarHorizontalTrackElement;
                var _scrollbarHorizontalHandleElement;
                var _scrollbarVerticalElement;
                var _scrollbarVerticalTrackElement;
                var _scrollbarVerticalHandleElement;

                //Cache:
                var _hostSizeCache;
                var _contentScrollSizeCache;
                var _arrangeContentSizeCache;
                var _hasOverflowCache;
                var _hideOverflowCache;
                var _widthAutoCache;
                var _heightAutoCache;
                var _cssMaxValueCache;
                var _cssBoxSizingCache;
                var _cssPaddingCache;
                var _cssBorderCache;
                var _cssMarginCache;
                var _cssDirectionCache;
                var _cssDirectionDetectedCache;
                var _paddingAbsoluteCache;
                var _clipAlwaysCache;
                var _contentGlueSizeCache;
                var _overflowBehaviorCache;
                var _overflowAmountCache;
                var _ignoreOverlayScrollbarHidingCache;
                var _autoUpdateCache;
                var _sizeAutoCapableCache;
                var _textareaAutoWrappingCache;
                var _textareaInfoCache;
                var _updateAutoHostElementIdCache;
                var _updateAutoHostElementClassCache;
                var _updateAutoHostElementStyleCache;
                var _updateAutoHostElementVisibleCache;
                var _updateAutoTargetElementRowsCache;
                var _updateAutoTargetElementColsCache;
                var _updateAutoTargetElementWrapCache;
                var _contentElementScrollSizeChangeDetectedCache;
                var _hostElementSizeChangeDetectedCache;
                var _scrollbarsVisibilityCache;
                var _scrollbarsAutoHideCache;
                var _scrollbarsClickScrollingCache;
                var _scrollbarsDragScrollingCache;
                var _resizeCache;
                var _normalizeRTLCache;
                var _classNameCache;
                var _oldClassName;
                var _textareaDynHeightCache;
                var _textareaDynWidthCache;
                var _bodyMinSizeCache;
                var _viewportScrollSizeCache;
                var _displayIsHiddenCache;

                //MutationObserver:
                var _mutationObserverContentLag = 11;
                var _mutationObserverHost;
                var _mutationObserverContent;
                var _mutationObserverConnected;
                var _supportMutationObserver;

                //textarea:
                var _textareaKeyDownRestrictedKeyCodes = [
                    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 123,    //F1 to F12
                    33, 34,                                                   //page up, page down
                    37, 38, 39, 40,                                           //left, up, right, down arrows
                    16, 17, 18, 19, 20, 144                                   //Shift, Ctrl, Alt, Pause, CapsLock, NumLock
                ];
                var _textareaKeyDownKeyCodesList = [ ];
                var _textareaUpdateIntervalID;
                var _textareaHasFocus;

                //scrollbars:
                var _scrollbarsAutoHideTimeoutId;
                var _scrollbarsAutoHideMoveTimeoutId;
                var _scrollbarsAutoHideDelay;
                var _scrollbarsAutoHideNever;
                var _scrollbarsAutoHideScroll;
                var _scrollbarsAutoHideMove;
                var _scrollbarsAutoHideLeave;
                var _scrollbarsTouchSupport;
                var _scrollbarsAutoHideFlagScrollAndHovered;

                //resize
                var _resizeReconnectMutationObserver;
                var _resizeNone;
                var _resizeBoth;
                var _resizeHorizontal;
                var _resizeVertical;
                var _resizeDragStartPosition = { };
                var _resizeDragStartSize = { };


                //==== Add / Remove Passive Event Listener ====//

                /**
                 * Adds a passive event listener to the given element.
                 * @param element The element to which the event listener shall be applied.
                 * @param eventNames The name(s) of the event listener.
                 * @param listener The listener method which shall be called.
                 */
                function addPassiveEventListener(element, eventNames, listener) {
                    var events = eventNames.split(_strSpace);
                    for (var i = 0; i < events.length; i++)
                        element[0].addEventListener(events[i], listener, {passive: true});
                }

                /**
                 * Removes a passive event listener to the given element.
                 * @param element The element from which the event listener shall be removed.
                 * @param eventNames The name(s) of the event listener.
                 * @param listener The listener method which shall be removed.
                 */
                function removePassiveEventListener(element, eventNames, listener) {
                    var events = eventNames.split(_strSpace);
                    for (var i = 0; i < events.length; i++)
                        element[0].removeEventListener(events[i], listener, {passive: true});
                }


                //==== Add / Remove / Freeze / Unfreeze Resize Observer ====//

                /**
                 * Adds a resize observer to the given element.
                 * @param targetElement The element to which the resize observer shall be applied.
                 * @param onElementResizedCallback The callback which is fired every time the resize observer registers a size change.
                 */
                function addResizeObserver(targetElement, onElementResizedCallback) {
                    var constMaximum = 3333333;
                    var resizeObserver = COMPATIBILITY.rO();
                    var strAnimationStartEvent = 'animationstart mozAnimationStart webkitAnimationStart MSAnimationStart';
                    var strChildNodes = 'childNodes';
                    var callback = function () {
                        targetElement[_strScrollTop](constMaximum)[_strScrollLeft](_isRTL ? _rtlScrollBehavior.n ? -constMaximum : _rtlScrollBehavior.i ? 0 : constMaximum : constMaximum);
                        onElementResizedCallback();
                    };
                    if (_supportResizeObserver) {
                        var element = targetElement.append(generateDiv(_classNameResizeObserverElement + ' observed')).contents()[0];
                        var observer = element[_strResizeObserverProperty] = new resizeObserver(callback);
                        observer.observe(element);
                    }
                    else {
                        if (_msieVersion > 9 || !_autoUpdateRecommended) {
                            targetElement.prepend(
                                generateDiv(_classNameResizeObserverElement,
                                    generateDiv({ className : _classNameResizeObserverItemElement, dir : "ltr" },
                                        generateDiv(_classNameResizeObserverItemElement,
                                            generateDiv(_classNameResizeObserverItemFinalElement)
                                        ) +
                                        generateDiv(_classNameResizeObserverItemElement,
                                            generateDiv({ className : _classNameResizeObserverItemFinalElement, style : 'width: 200%; height: 200%' })
                                        )
                                    )
                                )
                            );

                            var observerElement = targetElement[0][strChildNodes][0][strChildNodes][0];
                            var shrinkElement = FRAMEWORK(observerElement[strChildNodes][1]);
                            var expandElement = FRAMEWORK(observerElement[strChildNodes][0]);
                            var expandElementChild = FRAMEWORK(expandElement[0][strChildNodes][0]);
                            var widthCache = observerElement[LEXICON.oW];
                            var heightCache = observerElement[LEXICON.oH];
                            var isDirty;
                            var rAFId;
                            var currWidth;
                            var currHeight;
                            var factor = 2;
                            var nativeScrollbarSize = globals.nativeScrollbarSize; //care don't make changes to this object!!!
                            var reset = function () {
                                /*
                                 var sizeResetWidth = observerElement[LEXICON.oW] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;
                                 var sizeResetHeight = observerElement[LEXICON.oH] + nativeScrollbarSize.x * factor + nativeScrollbarSize.y * factor + _overlayScrollbarDummySize.x + _overlayScrollbarDummySize.y;
                                 var expandChildCSS = {};
                                 expandChildCSS[_strWidth] = sizeResetWidth;
                                 expandChildCSS[_strHeight] = sizeResetHeight;
                                 expandElementChild.css(expandChildCSS);


                                 expandElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);
                                 shrinkElement[_strScrollLeft](sizeResetWidth)[_strScrollTop](sizeResetHeight);
                                 */
                                expandElement[_strScrollLeft](constMaximum)[_strScrollTop](constMaximum);
                                shrinkElement[_strScrollLeft](constMaximum)[_strScrollTop](constMaximum);
                            };
                            var onResized = function () {
                                rAFId = 0;
                                if (!isDirty)
                                    return;

                                widthCache = currWidth;
                                heightCache = currHeight;
                                callback();
                            };
                            var onScroll = function (event) {
                                currWidth = observerElement[LEXICON.oW];
                                currHeight = observerElement[LEXICON.oH];
                                isDirty = currWidth != widthCache || currHeight != heightCache;

                                if (event && isDirty && !rAFId) {
                                    COMPATIBILITY.cAF()(rAFId);
                                    rAFId = COMPATIBILITY.rAF()(onResized);
                                }
                                else if(!event)
                                    onResized();

                                reset();
                                if (event) {
                                    COMPATIBILITY.prvD(event);
                                    COMPATIBILITY.stpP(event);
                                }
                                return false;
                            };
                            var expandChildCSS = {};
                            var observerElementCSS = {};

                            observerElementCSS[_strTop] = (-((nativeScrollbarSize.y + 1) * factor));
                            observerElementCSS[_strRight] = (nativeScrollbarSize.x * -factor);
                            observerElementCSS[_strBottom] = (nativeScrollbarSize.y * -factor);
                            observerElementCSS[_strLeft] = (-((nativeScrollbarSize.x + 1) * factor));

                            FRAMEWORK(observerElement).css(observerElementCSS);
                            expandElement.on(_strScroll, onScroll);
                            shrinkElement.on(_strScroll, onScroll);
                            targetElement.on(strAnimationStartEvent, function () {
                                onScroll(false);
                            });
                            //lets assume that the divs will never be that large and a constant value is enough
                            expandChildCSS[_strWidth] = constMaximum;
                            expandChildCSS[_strHeight] = constMaximum;
                            expandElementChild.css(expandChildCSS);

                            reset();
                        }
                        else {
                            var doc = _documentElement[0];
                            var attachEvent = doc.attachEvent;
                            var isIE = _msieVersion !== undefined;
                            if (attachEvent) {
                                targetElement.prepend(generateDiv(_classNameResizeObserverElement));
                                findFirst(targetElement, _strDot + _classNameResizeObserverElement)[0].attachEvent('onresize', callback);
                            }
                            else {
                                var obj = doc.createElement(TYPES.o);
                                obj.setAttribute('tabindex', '-1');
                                obj.setAttribute(LEXICON.c, _classNameResizeObserverElement);
                                obj.onload = function () {
                                    var wnd = this.contentDocument.defaultView;
                                    wnd.addEventListener('resize', callback);
                                    wnd.document.documentElement.style.display = 'none';
                                };
                                obj.type = 'text/html';
                                if (isIE)
                                    targetElement.prepend(obj);
                                obj.data = 'about:blank';
                                if (!isIE)
                                    targetElement.prepend(obj);
                                targetElement.on(strAnimationStartEvent, callback);
                            }
                        }
                    }

                    //direction change detection:
                    if (targetElement[0] === _sizeObserverElement[0]) {
                        var directionChanged = function () {
                            var dir = _hostElement.css('direction');
                            var css = {};
                            var scrollLeftValue = 0;
                            var result = false;
                            if (dir !== _cssDirectionDetectedCache) {
                                if (dir === 'ltr') {
                                    css[_strLeft] = 0;
                                    css[_strRight] = _strAuto;
                                    scrollLeftValue = constMaximum;
                                }
                                else {
                                    css[_strLeft] = _strAuto;
                                    css[_strRight] = 0;
                                    scrollLeftValue = _rtlScrollBehavior.n ? -constMaximum : _rtlScrollBehavior.i ? 0 : constMaximum;
                                }
                                _sizeObserverElement.children().eq(0).css(css);
                                targetElement[_strScrollLeft](scrollLeftValue)[_strScrollTop](constMaximum);
                                _cssDirectionDetectedCache = dir;
                                result = true;
                            }
                            return result;
                        };
                        directionChanged();
                        targetElement.on(_strScroll, function (event) {
                            if (directionChanged())
                                update();
                            COMPATIBILITY.prvD(event);
                            COMPATIBILITY.stpP(event);
                            return false;
                        });
                    }
                }

                /**
                 * Removes a resize observer from the given element.
                 * @param targetElement The element to which the target resize observer is applied.
                 */
                function removeResizeObserver(targetElement) {
                    if (_supportResizeObserver) {
                        var element = targetElement.contents()[0];
                        element[_strResizeObserverProperty].disconnect();
                        delete element[_strResizeObserverProperty];
                    }
                    else {
                        remove(targetElement.children(_strDot + _classNameResizeObserverElement).eq(0));
                    }
                }

                /**
                 * Freezes the given resize observer.
                 * @param targetElement The element to which the target resize observer is applied.
                 */
                function freezeResizeObserver(targetElement) {
                    if (targetElement !== undefined) {
                        /*
                        if (_supportResizeObserver) {
                            var element = targetElement.contents()[0];
                            element[_strResizeObserverProperty].unobserve(element);
                        }
                        else {
                            targetElement = targetElement.children(_strDot + _classNameResizeObserverElement).eq(0);
                            var w = targetElement.css(_strWidth);
                            var h = targetElement.css(_strHeight);
                            var css = {};
                            css[_strWidth] = w;
                            css[_strHeight] = h;
                            targetElement.css(css);
                        }
                        */
                    }
                }

                /**
                 * Unfreezes the given resize observer.
                 * @param targetElement The element to which the target resize observer is applied.
                 */
                function unfreezeResizeObserver(targetElement) {
                    if (targetElement !== undefined) {
                        /*
                        if (_supportResizeObserver) {
                            var element = targetElement.contents()[0];
                            element[_strResizeObserverProperty].observe(element);
                        }
                        else {
                            var css = { };
                            css[_strHeight] = _strEmpty;
                            css[_strWidth] = _strEmpty;
                            targetElement.children(_strDot + _classNameResizeObserverElement).eq(0).css(css);
                        }
                        */
                    }
                }


                //==== Connect / Disconnect Mutation Observer ====//

                /**
                 * Connects the MutationObservers if they are supported.
                 */
                function mutationObserversConnect() {
                    if (_supportMutationObserver && !_mutationObserverConnected) {
                        _mutationObserverHost.observe(_hostElement[0], {
                            attributes: true,
                            attributeOldValue: true,
                            attributeFilter: [LEXICON.i, LEXICON.c, LEXICON.s]
                        });

                        _mutationObserverContent.observe(_isTextarea ? _targetElement[0] : _contentElement[0], {
                            attributes: true,
                            attributeOldValue: true,
                            subtree: !_isTextarea,
                            childList: !_isTextarea,
                            characterData: !_isTextarea,
                            attributeFilter: _isTextarea ? ['wrap', 'cols', 'rows'] : [LEXICON.i, LEXICON.c, LEXICON.s]
                        });

                        _mutationObserverConnected = true;
                    }
                }

                /**
                 * Disconnects the MutationObservers if they are supported.
                 */
                function mutationObserversDisconnect() {
                    if (_supportMutationObserver && _mutationObserverConnected) {
                        _mutationObserverHost.disconnect();
                        _mutationObserverContent.disconnect();

                        _mutationObserverConnected = false;
                    }
                }


                //==== Events of elements ====//

                /**
                 * This method gets called every time the host element gets resized. IMPORTANT: Padding changes are detected too!!
                 * It refreshes the hostResizedEventArgs and the hostSizeResizeCache.
                 * If there are any size changes, the update method gets called.
                 */
                function hostOnResized() {
                    if (_isSleeping)
                        return;
                    var measureElement = _sizeObserverElement[0];
                    var hostSize = {
                        w: measureElement[LEXICON.sW],
                        h: measureElement[LEXICON.sH]
                    };
                    if (_initialized) {
                        var changed = checkCacheDouble(hostSize, _hostElementSizeChangeDetectedCache);
                        _hostElementSizeChangeDetectedCache = hostSize;
                        if (changed)
                            update(true, false);
                    }
                    else {
                        _hostElementSizeChangeDetectedCache = hostSize;
                    }
                }

                /**
                 * The mouse enter event of the host element. This event is only needed for the autoHide feature.
                 */
                function hostOnMouseEnter() {
                    if (_scrollbarsAutoHideLeave)
                        refreshScrollbarsAutoHide(true);
                }

                /**
                 * The mouse leave event of the host element. This event is only needed for the autoHide feature.
                 */
                function hostOnMouseLeave() {
                    if (_scrollbarsAutoHideLeave && !_bodyElement.hasClass(_classNameDragging))
                        refreshScrollbarsAutoHide(false);
                }

                /**
                 * The mouse move event of the host element. This event is only needed for the autoHide "move" feature.
                 */
                function hostOnMouseMove() {
                    if (_scrollbarsAutoHideMove) {
                        refreshScrollbarsAutoHide(true);
                        clearTimeout(_scrollbarsAutoHideMoveTimeoutId);
                        _scrollbarsAutoHideMoveTimeoutId = setTimeout(function () {
                            if (_scrollbarsAutoHideMove && !_destroyed)
                                refreshScrollbarsAutoHide(false);
                        }, 100);
                    }
                }

                /**
                 * The scroll event of the viewport element. That is the main scroll event. It controls also the "scroll", "scrollStart" and "scrollStop" callbacks.
                 * @param event The scroll event.
                 */
                function viewportOnScroll(event) {
                    if (_isSleeping)
                        return;

                    if (_scrollStopTimeoutId !== undefined)
                        clearTimeout(_scrollStopTimeoutId);
                    else {
                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
                            refreshScrollbarsAutoHide(true);

                        if (!nativeOverlayScrollbarsAreActive())
                            addClass(_hostElement, _classNameHostScrolling);

                        callCallback("onScrollStart", event);
                    }

                    refreshScrollbarHandleOffset(true, _viewportElement[_strScrollLeft]());
                    refreshScrollbarHandleOffset(false, _viewportElement[_strScrollTop]());
                    callCallback("onScroll", event);

                    _scrollStopTimeoutId = setTimeout(function () {
                        if(!_destroyed) {
                            viewportOnScrollStop();
                            callCallback("onScrollStop", event);
                        }
                    }, _scrollStopDelay);
                }

                /**
                 * This method gets called if the scroll event stopped for a specified amount of time.
                 */
                function viewportOnScrollStop() {
                    clearTimeout(_scrollStopTimeoutId);
                    _scrollStopTimeoutId = undefined;
                    if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
                        refreshScrollbarsAutoHide(false);

                    if (!nativeOverlayScrollbarsAreActive())
                        removeClass(_hostElement, _classNameHostScrolling);
                }

                /**
                 * The key input event of the textarea element.
                 */
                function textareaOnInput() {
                    textareaUpdate();
                    _base.update(_strAuto);
                }

                /**
                 * The key down event of the textarea element. Is only applied if the input event isn't fully supported.
                 * @param event The key down event.
                 */
                function textareaOnKeyDown(event) {
                    var keyCode = event.keyCode;
                    if (textareaIsRestrictedKeyCode(keyCode))
                        return;
                    if (_textareaKeyDownKeyCodesList.length === 0) {
                        var action = function () {
                            textareaUpdate();
                            _base.update(_strAuto);
                        };
                        action();
                        _textareaUpdateIntervalID = setInterval(action, 1000 / 60);
                    }
                    if (FRAMEWORK.inArray(keyCode, _textareaKeyDownKeyCodesList) === -1)
                        _textareaKeyDownKeyCodesList.push(keyCode);
                }

                /**
                 * The key up event of the textarea element. Is only applied if the input event isn't fully supported.
                 * @param event The key up event.
                 */
                function textareaOnKeyUp(event) {
                    var keyCode = event.keyCode;
                    if (textareaIsRestrictedKeyCode(keyCode))
                        return;
                    var index = FRAMEWORK.inArray(keyCode, _textareaKeyDownKeyCodesList);
                    if (index > -1)
                        _textareaKeyDownKeyCodesList.splice(index, 1);
                    if (_textareaKeyDownKeyCodesList.length === 0) {
                        textareaUpdate();
                        _base.update(_strAuto);
                        clearInterval(_textareaUpdateIntervalID);
                    }
                }

                /**
                 * The drop event of the textarea element.
                 */
                function textareaOnDrop() {
                    setTimeout(function () {
                        if(!_destroyed) {
                            textareaUpdate();
                            _base.update(_strAuto);
                        }
                    }, 50);
                }

                /**
                 * The focus event of the textarea element.
                 */
                function textareaOnFocus() {
                    _textareaHasFocus = true;
                }

                /**
                 * The focus out event of the textarea element.
                 */
                function textareaOnFocusOut() {
                    _textareaHasFocus = false;
                    clearInterval(_textareaUpdateIntervalID);
                    _textareaKeyDownKeyCodesList = [ ];
                    textareaUpdate();
                    _base.update(_strAuto);
                }

                /**
                 * The scroll event of the textarea element.
                 * @param event The scroll event.
                 */
                function textareaOnScroll(event) {
                    _targetElement[_strScrollLeft](_rtlScrollBehavior.i && _normalizeRTLCache ? 9999999 : 0);
                    _targetElement[_strScrollTop](0);
                    COMPATIBILITY.prvD(event);
                    COMPATIBILITY.stpP(event);
                    return false;
                }

                /**
                 * The mouse down event of the scrollbar corner element.
                 * @param event The mouse down event.
                 */
                function scrollbarCornerOnMouseDown(event) {
                    if (_isSleeping)
                        return;

                    var originalEvent = event.originalEvent || event;
                    var isTouchEvent = originalEvent.touches !== undefined;

                    if (COMPATIBILITY.mBtn(event) === 1 || isTouchEvent) {
                        if (_mutationObserverConnected) {
                            _resizeReconnectMutationObserver = true;
                            mutationObserversDisconnect();
                        }

                        _resizeDragStartPosition = COMPATIBILITY.page(event);

                        _resizeDragStartSize.w = _hostElement[0][LEXICON.oW] - (!_isBorderBox ? _paddingX : 0);
                        _resizeDragStartSize.h = _hostElement[0][LEXICON.oH] - (!_isBorderBox ? _paddingY : 0);

                        _documentElement.on(_strSelectStartEvent, documentOnSelectStart)
                            .on(_strMouseTouchMoveEvent, scrollbarCornerOnResize)
                            .on(_strMouseTouchUpEvent, scrollbarCornerOnResized);

                        addClass(_bodyElement, _classNameDragging);
                        if (_scrollbarCornerElement.setCapture)
                            _scrollbarCornerElement.setCapture();

                        COMPATIBILITY.prvD(event);
                        COMPATIBILITY.stpP(event);
                    }
                }

                /**
                 * The mouse move event if the scrollbar corner element is resizable and gets dragged.
                 * @param event The mouse move event.
                 */
                function scrollbarCornerOnResize(event) {
                    var pageOffset = COMPATIBILITY.page(event);
                    var hostElementCSS = { };
                    if (_resizeHorizontal || _resizeBoth)
                        hostElementCSS[_strWidth] = (_resizeDragStartSize.w + pageOffset.x - _resizeDragStartPosition.x);
                    if (_resizeVertical || _resizeBoth)
                        hostElementCSS[_strHeight] = (_resizeDragStartSize.h + pageOffset.y - _resizeDragStartPosition.y);
                    _hostElement.css(hostElementCSS);
                    COMPATIBILITY.stpP(event);
                }

                /**
                 * The mouse up event if the scrollbar corner element is resizable and was dragged and now the mouse button is released.
                 * @param event The mouse up event.
                 */
                function scrollbarCornerOnResized(event) {
                    var eventIsTrusted = event !== undefined;

                    _documentElement.off(_strSelectStartEvent, documentOnSelectStart)
                        .off(_strMouseTouchMoveEvent, scrollbarCornerOnResize)
                        .off(_strMouseTouchUpEvent, scrollbarCornerOnResized);

                    removeClass(_bodyElement, _classNameDragging);
                    if (_scrollbarCornerElement.releaseCapture)
                        _scrollbarCornerElement.releaseCapture();

                    if (eventIsTrusted) {
                        if (_resizeReconnectMutationObserver)
                            mutationObserversConnect();
                        _base.update(_strAuto);
                    }
                    _resizeReconnectMutationObserver = false;
                }

                /**
                 * Prevents text from deselection if attached to the document element on the mousedown event of a DOM element.
                 * @param event The select start event.
                 */
                function documentOnSelectStart(event) {
                    COMPATIBILITY.prvD(event);
                    return false;
                }

                /**
                 * A callback which will be called after a img element has downloaded its src asynchronous.
                 */
                function imgOnLoad() {
                    update();
                }


                //==== Update Detection ====//

                /**
                 * Measures the min width and min height of the body element and refreshes the related cache.
                 * @returns {boolean} True if the min width or min height has changed, false otherwise.
                 */
                function bodyMinSizeChanged() {
                    var bodyMinSize = {};
                    if (_isBody && _contentArrangeElement) {
                        bodyMinSize.w = parseToZeroOrNumber(_contentArrangeElement.css(_strMinMinus + _strWidth));
                        bodyMinSize.h = parseToZeroOrNumber(_contentArrangeElement.css(_strMinMinus + _strHeight));
                        bodyMinSize.c = checkCacheDouble(bodyMinSize, _bodyMinSizeCache);
                        bodyMinSize.f = true; //flag for "measured at least once"
                    }
                    _bodyMinSizeCache = bodyMinSize;
                    return bodyMinSize.c || false;
                }

                /**
                 * Returns true if the class names really changed (new class without plugin host prefix)
                 * @param oldCassNames The old ClassName string.
                 * @param newClassNames The new ClassName string.
                 * @returns {boolean} True if the class names has really changed, false otherwise.
                 */
                function hostClassNamesChanged(oldCassNames, newClassNames) {
                    var currClasses = (newClassNames !== undefined && newClassNames !== null) ? newClassNames.split(_strSpace) : _strEmpty;
                    var oldClasses = (oldCassNames !== undefined && oldCassNames !== null) ? oldCassNames.split(_strSpace) : _strEmpty;
                    if (currClasses === _strEmpty && oldClasses === _strEmpty)
                        return false;
                    var diff = getArrayDifferences(oldClasses, currClasses);
                    var changed = false;
                    var oldClassNames = _oldClassName !== undefined && _oldClassName !== null ? _oldClassName.split(_strSpace) : [_strEmpty];
                    var currClassNames = _classNameCache !== undefined && _classNameCache !== null ? _classNameCache.split(_strSpace) : [_strEmpty];

                    //remove none theme from diff list to prevent update
                    var idx = FRAMEWORK.inArray(_classNameThemeNone, diff);
                    var curr = diff[i];
                    var i;
                    var v;
                    var o;
                    var c;

                    if (idx > -1)
                        diff.splice(idx, 1);

                    for (i = 0; i < diff.length; i++) {
                        curr = diff[i];
                        if (curr.indexOf(_classNameHostElement) !== 0) {
                            o = true;
                            c = true;
                            for (v = 0; v < oldClassNames.length; v++) {
                                if (curr === oldClassNames[v]) {
                                    o = false;
                                    break;
                                }
                            }
                            for (v = 0; v < currClassNames.length; v++) {
                                if (curr === currClassNames[v]) {
                                    c = false;
                                    break;
                                }
                            }
                            if (o && c) {
                                changed = true;
                                break;
                            }
                        }

                    }
                    return changed;
                }

                /**
                 * Returns true if the given mutation is not from a from the plugin generated element. If the target element is a textarea the mutation is always unknown.
                 * @param mutation The mutation which shall be checked.
                 * @returns {boolean} True if the mutation is from a unknown element, false otherwise.
                 */
                function isUnknownMutation(mutation) {
                    var attributeName = mutation.attributeName;
                    var mutationTarget = mutation.target;
                    var mutationType = mutation.type;
                    var strClosest = 'closest';

                    if (mutationTarget === _contentElement[0])
                        return attributeName === null;
                    if (mutationType === 'attributes' && (attributeName === LEXICON.c || attributeName === LEXICON.s) && !_isTextarea) {
                        //only do it of browser support it natively
                        if (typeof mutationTarget[strClosest] != TYPES.f)
                            return true;
                        if (mutationTarget[strClosest](_strDot + _classNameResizeObserverElement) !== null ||
                            mutationTarget[strClosest](_strDot + _classNameScrollbar) !== null ||
                            mutationTarget[strClosest](_strDot + _classNameScrollbarCorner) !== null)
                            return false;
                    }
                    return true;
                }

                /**
                 * Returns true if the content size was changed since the last time this method was called.
                 * @returns {boolean} True if the content size was changed, false otherwise.
                 */
                function updateAutoContentSizeChanged() {
                    if (_isSleeping)
                        return false;

                    var float;
                    var textareaValueLength = _isTextarea && _widthAutoCache && !_textareaAutoWrappingCache ? _targetElement.val().length : 0;
                    var setCSS = !_mutationObserverConnected && _widthAutoCache && !_isTextarea;
                    var viewportScrollSize = { };
                    var css = { };

                    //fix for https://bugzilla.mozilla.org/show_bug.cgi?id=1439305, it only works with "clipAlways : true"
                    //it can work with "clipAlways : false" too, but we had to set the overflow of the viewportElement to hidden every time before measuring
                    if(_restrictedMeasuring) {
                        viewportScrollSize = {
                            x : _viewportElement[0][LEXICON.sW],
                            y : _viewportElement[0][LEXICON.sH]
                        }
                    }
                    if (setCSS) {
                        float = _contentElement.css(_strFloat);
                        css[_strFloat] = _isRTL ? _strRight : _strLeft;
                        css[_strWidth] = _strAuto;
                        _contentElement.css(css);
                    }
                    var contentElementScrollSize = {
                        w: getContentMeasureElement()[LEXICON.sW] + textareaValueLength,
                        h: getContentMeasureElement()[LEXICON.sH] + textareaValueLength
                    };
                    if (setCSS) {
                        css[_strFloat] = float;
                        css[_strWidth] = _strHundredPercent;
                        _contentElement.css(css);
                    }

                    var bodyMinSizeC = bodyMinSizeChanged();
                    var changed = checkCacheDouble(contentElementScrollSize, _contentElementScrollSizeChangeDetectedCache);
                    var viewportScrollSizeChanged = checkCacheDouble(viewportScrollSize, _viewportScrollSizeCache, _strX, _strY);
                    _contentElementScrollSizeChangeDetectedCache = contentElementScrollSize;
                    _viewportScrollSizeCache = viewportScrollSize;

                    return changed || bodyMinSizeC || viewportScrollSizeChanged;
                }

                /**
                 * Returns true if the host element attributes (id, class, style) was changed since the last time this method was called.
                 * @returns {boolean}
                 */
                function meaningfulAttrsChanged() {
                    if (_isSleeping || _mutationObserverConnected)
                        return false;

                    var hostElementId = _hostElement.attr(LEXICON.i) || _strEmpty;
                    var hostElementIdChanged = checkCacheSingle(hostElementId, _updateAutoHostElementIdCache);
                    var hostElementClass = _hostElement.attr(LEXICON.c) || _strEmpty;
                    var hostElementClassChanged = checkCacheSingle(hostElementClass, _updateAutoHostElementClassCache);
                    var hostElementStyle = _hostElement.attr(LEXICON.s) || _strEmpty;
                    var hostElementStyleChanged = checkCacheSingle(hostElementStyle, _updateAutoHostElementStyleCache);
                    var hostElementVisible = _hostElement.is(':visible') || _strEmpty;
                    var hostElementVisibleChanged = checkCacheSingle(hostElementVisible, _updateAutoHostElementVisibleCache);
                    var targetElementRows = _isTextarea ? (_targetElement.attr('rows') || _strEmpty) : _strEmpty;
                    var targetElementRowsChanged = checkCacheSingle(targetElementRows, _updateAutoTargetElementRowsCache);
                    var targetElementCols = _isTextarea ? (_targetElement.attr('cols') || _strEmpty) : _strEmpty;
                    var targetElementColsChanged = checkCacheSingle(targetElementCols, _updateAutoTargetElementColsCache);
                    var targetElementWrap = _isTextarea ? (_targetElement.attr('wrap') || _strEmpty) : _strEmpty;
                    var targetElementWrapChanged = checkCacheSingle(targetElementWrap, _updateAutoTargetElementWrapCache);

                    _updateAutoHostElementIdCache = hostElementId;
                    if (hostElementClassChanged)
                        hostElementClassChanged = hostClassNamesChanged(_updateAutoHostElementClassCache, hostElementClass);
                    _updateAutoHostElementClassCache = hostElementClass;
                    _updateAutoHostElementStyleCache = hostElementStyle;
                    _updateAutoHostElementVisibleCache = hostElementVisible;
                    _updateAutoTargetElementRowsCache = targetElementRows;
                    _updateAutoTargetElementColsCache = targetElementCols;
                    _updateAutoTargetElementWrapCache = targetElementWrap;

                    return hostElementIdChanged || hostElementClassChanged || hostElementStyleChanged || hostElementVisibleChanged || targetElementRowsChanged || targetElementColsChanged || targetElementWrapChanged;
                }

                /**
                 * Checks is a CSS Property of a child element is affecting the scroll size of the content.
                 * @param propertyName The CSS property name.
                 * @returns {boolean} True if the property is affecting the content scroll size, false otherwise.
                 */
                function isSizeAffectingCSSProperty(propertyName) {
                    if (!_initialized)
                        return true;
                    var flexGrow = 'flex-grow';
                    var flexShrink = 'flex-shrink';
                    var flexBasis = 'flex-basis';
                    var affectingPropsX = [
                        _strWidth,
                        _strMinMinus + _strWidth,
                        _strMaxMinus + _strWidth,
                        _strMarginMinus + _strLeft,
                        _strMarginMinus + _strRight,
                        _strLeft,
                        _strRight,
                        'font-weight',
                        'word-spacing',
                        flexGrow,
                        flexShrink,
                        flexBasis
                    ];
                    var affectingPropsXContentBox = [
                        _strPaddingMinus + _strLeft,
                        _strPaddingMinus + _strRight,
                        _strBorderMinus + _strLeft + _strWidth,
                        _strBorderMinus + _strRight + _strWidth
                    ];
                    var affectingPropsY = [
                        _strHeight,
                        _strMinMinus + _strHeight,
                        _strMaxMinus + _strHeight,
                        _strMarginMinus + _strTop,
                        _strMarginMinus + _strBottom,
                        _strTop,
                        _strBottom,
                        'line-height',
                        flexGrow,
                        flexShrink,
                        flexBasis
                    ];
                    var affectingPropsYContentBox = [
                        _strPaddingMinus + _strTop,
                        _strPaddingMinus + _strBottom,
                        _strBorderMinus + _strTop + _strWidth,
                        _strBorderMinus + _strBottom + _strWidth
                    ];
                    var _strS = 's';
                    var _strVS = 'v-s';
                    var checkX = _overflowBehaviorCache.x === _strS || _overflowBehaviorCache.x === _strVS;
                    var checkY = _overflowBehaviorCache.y === _strS || _overflowBehaviorCache.y === _strVS;
                    var sizeIsAffected = false;
                    var checkPropertyName = function (arr, name) {
                        for (var i = 0; i < arr.length; i++) {
                            if (arr[i] === name)
                                return true;
                        }
                        return false;
                    };

                    if (checkY) {
                        sizeIsAffected = checkPropertyName(affectingPropsY, propertyName);
                        if (!sizeIsAffected && !_isBorderBox)
                            sizeIsAffected = checkPropertyName(affectingPropsYContentBox, propertyName);
                    }
                    if (checkX && !sizeIsAffected) {
                        sizeIsAffected = checkPropertyName(affectingPropsX, propertyName);
                        if (!sizeIsAffected && !_isBorderBox)
                            sizeIsAffected = checkPropertyName(affectingPropsXContentBox, propertyName);
                    }
                    return sizeIsAffected;
                }


                //==== Update ====//

                /**
                 * Updates the variables and size of the textarea element, and manages the scroll on new line or new character.
                 */
                function textareaUpdate() {
                    if (_isSleeping)
                        return;

                    var wrapAttrOff = !_textareaAutoWrappingCache;
                    var minWidth = _viewportSize.w - (!_isBorderBox && !_paddingAbsoluteCache && _widthAutoCache ? _paddingY + _borderY : 0);
                    var minHeight = _viewportSize.h - (!_isBorderBox && !_paddingAbsoluteCache && _heightAutoCache ? _paddingY + _borderY : 0);
                    var css = { };
                    var doMeasure = _widthAutoCache || wrapAttrOff;
                    var measureElement = _targetElement[0];
                    var origWidth;
                    var width;
                    var origHeight;
                    var height;

                    //reset min size
                    css[_strMinMinus + _strWidth] = _strEmpty;
                    css[_strMinMinus + _strHeight] = _strEmpty;

                    //set width auto
                    css[_strWidth] = _strAuto;
                    _targetElement.css(css);

                    //measure width
                    origWidth = measureElement[LEXICON.oW];
                    width = doMeasure ? Math.max(origWidth, measureElement[LEXICON.sW] - 1) : 1;
                    width += (_widthAutoCache ? _marginX + (!_isBorderBox ? wrapAttrOff ? 0 : _paddingX + _borderX : 0) : 0);

                    //set measured width and height auto
                    css[_strWidth] = _widthAutoCache ? width : _strHundredPercent;
                    css[_strHeight] = _strAuto; //_strAuto
                    _targetElement.css(css);

                    //measure height
                    origHeight = measureElement[LEXICON.oH];
                    height = Math.max(origHeight, measureElement[LEXICON.sH] - 1);

                    //append correct size values
                    css[_strWidth] = width;
                    css[_strHeight] = height;
                    _textareaCoverElement.css(css);

                    //apply min width / min height to prevent textarea collapsing
                    css[_strMinMinus + _strWidth] = minWidth + (!_isBorderBox && _widthAutoCache ? _paddingX + _borderX : 0);
                    css[_strMinMinus + _strHeight] = minHeight + (!_isBorderBox && _heightAutoCache ? _paddingY + _borderY : 0);
                    _targetElement.css(css);

                    return {
                        ow: origWidth,
                        oh: origHeight,
                        dw: width,
                        dh: height
                    };
                }

                /**
                 * Updates the plugin and DOM to the current options.
                 * This method should only be called if a update is 100% required.
                 * @param hostSizeChanged True if this method was called due to a host size change.
                 * @param contentSizeChanged True if this method was called due to a content size change.
                 * @param force True if every property shall be updated and the cache shall be ignored.
                 * @param preventSwallowing True if this method shall be executed event if it could be swallowed.
                 */
                function update(hostSizeChanged, contentSizeChanged, force, preventSwallowing) {
                    var now = COMPATIBILITY.now();
                    var swallow = _swallowUpdateLag > 0 && _initialized && (now - _lastUpdateTime) < _swallowUpdateLag && (!_heightAutoCache && !_widthAutoCache) && !preventSwallowing;
                    var displayIsHidden = _hostElement.is(':hidden');
                    var displayIsHiddenChanged = checkCacheSingle(displayIsHidden, _displayIsHiddenCache, force);
                    _displayIsHiddenCache = displayIsHidden;
                    clearTimeout(_swallowedUpdateTimeout);
                    
                    if (swallow) {
                        _swallowedUpdateParams.h = hostSizeChanged;
                        _swallowedUpdateParams.c = contentSizeChanged;
                        _swallowedUpdateParams.f = force;
                        _swallowedUpdateTimeout = setTimeout(update, _swallowUpdateLag);
                    }

                    //abort update due to:
                    //destroyed
                    //swallowing
                    //sleeping
                    //host is hidden or has false display
                    if (_destroyed || swallow || _isSleeping || (_initialized && !force && displayIsHidden) || _hostElement.css('display') === 'inline')
                        return;

                    _lastUpdateTime = now;
                    hostSizeChanged = hostSizeChanged || _swallowedUpdateParams.h;
                    contentSizeChanged = contentSizeChanged || _swallowedUpdateParams.c;
                    force = force || _swallowedUpdateParams.f;
                    _swallowedUpdateParams = {};

                    hostSizeChanged = hostSizeChanged === undefined ? false : hostSizeChanged;
                    contentSizeChanged = contentSizeChanged === undefined ? false : contentSizeChanged;
                    force = force === undefined ? false : force;

                    //if scrollbar styling is possible and native scrollbars aren't overlaid the scrollbar styling will be applied which hides the native scrollbars completely.
                    if (_nativeScrollbarStyling && !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)) {
                        //native scrollbars are hidden, so change the values to zero
                        _nativeScrollbarSize.x = 0;
                        _nativeScrollbarSize.y = 0;
                    }
                    else {
                        //refresh native scrollbar size (in case of zoom)
                        _nativeScrollbarSize = extend(true, {}, globals.nativeScrollbarSize);
                    }

                    // Scrollbar padding is needed for firefox, because firefox hides scrollbar automatically if the size of the div is too small.
                    // The calculation: [scrollbar size +3 *3]
                    // (+3 because of possible decoration e.g. borders, margins etc., but only if native scrollbar is NOT a overlaid scrollbar)
                    // (*3 because (1)increase / (2)decrease -button and (3)resize handle)
                    _nativeScrollbarMinSize = {
                        x: (_nativeScrollbarSize.x + (_nativeScrollbarIsOverlaid.x ? 0 : 3)) * 3,
                        y: (_nativeScrollbarSize.y + (_nativeScrollbarIsOverlaid.y ? 0 : 3)) * 3
                    };

                    freezeResizeObserver(_sizeObserverElement);
                    freezeResizeObserver(_sizeAutoObserverElement);

                    //save current scroll offset
                    var currScroll = {
                        l: _viewportElement[_strScrollLeft](),
                        t: _viewportElement[_strScrollTop]()
                    };
                    var currentPreparedOptionsScrollbars = _currentPreparedOptions.scrollbars;
                    var currentPreparedOptionsTextarea = _currentPreparedOptions.textarea;

                    //scrollbars visibility:
                    var scrollbarsVisibility = currentPreparedOptionsScrollbars.visibility;
                    var scrollbarsVisibilityChanged = checkCacheSingle(scrollbarsVisibility, _scrollbarsVisibilityCache, force);

                    //scrollbars autoHide:
                    var scrollbarsAutoHide = currentPreparedOptionsScrollbars.autoHide;
                    var scrollbarsAutoHideChanged = checkCacheSingle(scrollbarsAutoHide, _scrollbarsAutoHideCache, force);

                    //scrollbars click scrolling
                    var scrollbarsClickScrolling = currentPreparedOptionsScrollbars.clickScrolling;
                    var scrollbarsClickScrollingChanged = checkCacheSingle(scrollbarsClickScrolling, _scrollbarsClickScrollingCache, force);

                    //scrollbars drag scrolling
                    var scrollbarsDragScrolling = currentPreparedOptionsScrollbars.dragScrolling;
                    var scrollbarsDragScrollingChanged = checkCacheSingle(scrollbarsDragScrolling, _scrollbarsDragScrollingCache, force);

                    //className
                    var className = _currentPreparedOptions.className;
                    var classNameChanged = checkCacheSingle(className, _classNameCache, force);

                    //resize
                    var resize = _currentPreparedOptions.resize;
                    var resizeChanged = checkCacheSingle(resize, _resizeCache, force) && !_isBody; //body can't be resized since the window itself acts as resize possibility.

                    //textarea AutoWrapping
                    var textareaAutoWrapping = _isTextarea ? _targetElement.attr('wrap') !== 'off' : false;
                    var textareaAutoWrappingChanged = checkCacheSingle(textareaAutoWrapping, _textareaAutoWrappingCache, force);

                    //paddingAbsolute
                    var paddingAbsolute = _currentPreparedOptions.paddingAbsolute;
                    var paddingAbsoluteChanged = checkCacheSingle(paddingAbsolute, _paddingAbsoluteCache, force);

                    //clipAlways
                    var clipAlways = _currentPreparedOptions.clipAlways;
                    var clipAlwaysChanged = checkCacheSingle(clipAlways, _clipAlwaysCache, force);

                    //sizeAutoCapable
                    var sizeAutoCapable = _currentPreparedOptions.sizeAutoCapable && !_isBody; //body can never be size auto, because it shall be always as big as the viewport.
                    var sizeAutoCapableChanged = checkCacheSingle(sizeAutoCapable, _sizeAutoCapableCache, force);

                    //showNativeScrollbars
                    var ignoreOverlayScrollbarHiding = _currentPreparedOptions.nativeScrollbarsOverlaid.showNativeScrollbars;
                    var ignoreOverlayScrollbarHidingChanged = checkCacheSingle(ignoreOverlayScrollbarHiding, _ignoreOverlayScrollbarHidingCache);

                    //autoUpdate
                    var autoUpdate = _currentPreparedOptions.autoUpdate;
                    var autoUpdateChanged = checkCacheSingle(autoUpdate, _autoUpdateCache);

                    //overflowBehavior
                    var overflowBehavior = _currentPreparedOptions.overflowBehavior;
                    var overflowBehaviorChanged = checkCacheDouble(overflowBehavior, _overflowBehaviorCache, _strX, _strY, force);

                    //dynWidth:
                    var textareaDynWidth = currentPreparedOptionsTextarea.dynWidth;
                    var textareaDynWidthChanged = checkCacheSingle(_textareaDynWidthCache, textareaDynWidth);

                    //dynHeight:
                    var textareaDynHeight = currentPreparedOptionsTextarea.dynHeight;
                    var textareaDynHeightChanged = checkCacheSingle(_textareaDynHeightCache, textareaDynHeight);

                    //scrollbars visibility
                    _scrollbarsAutoHideNever = scrollbarsAutoHide === 'n';
                    _scrollbarsAutoHideScroll = scrollbarsAutoHide === 's';
                    _scrollbarsAutoHideMove = scrollbarsAutoHide === 'm';
                    _scrollbarsAutoHideLeave = scrollbarsAutoHide === 'l';

                    //scrollbars autoHideDelay
                    _scrollbarsAutoHideDelay = currentPreparedOptionsScrollbars.autoHideDelay;

                    //scrollbars support touch
                    _scrollbarsTouchSupport = currentPreparedOptionsScrollbars.touchSupport;

                    //old className
                    _oldClassName = _classNameCache;

                    //resize
                    _resizeNone = resize === 'n';
                    _resizeBoth = resize === 'b';
                    _resizeHorizontal = resize === 'h';
                    _resizeVertical = resize === 'v';

                    //normalizeRTL
                    _normalizeRTLCache = _currentPreparedOptions.normalizeRTL;

                    //ignore overlay scrollbar hiding
                    ignoreOverlayScrollbarHiding = ignoreOverlayScrollbarHiding && (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y);

                    //refresh options cache
                    _scrollbarsVisibilityCache = scrollbarsVisibility;
                    _scrollbarsAutoHideCache = scrollbarsAutoHide;
                    _scrollbarsClickScrollingCache = scrollbarsClickScrolling;
                    _scrollbarsDragScrollingCache = scrollbarsDragScrolling;
                    _classNameCache = className;
                    _resizeCache = resize;
                    _textareaAutoWrappingCache = textareaAutoWrapping;
                    _paddingAbsoluteCache = paddingAbsolute;
                    _clipAlwaysCache = clipAlways;
                    _sizeAutoCapableCache = sizeAutoCapable;
                    _ignoreOverlayScrollbarHidingCache = ignoreOverlayScrollbarHiding;
                    _autoUpdateCache = autoUpdate;
                    _overflowBehaviorCache = extend(true, {}, overflowBehavior);
                    _textareaDynWidthCache = textareaDynWidth;
                    _textareaDynHeightCache = textareaDynHeight;
                    _hasOverflowCache = _hasOverflowCache || { x: false, y: false };

                    //set correct class name to the host element
                    if (classNameChanged) {
                        removeClass(_hostElement, _oldClassName + _strSpace + _classNameThemeNone);
                        addClass(_hostElement, className !== undefined && className !== null && className.length > 0 ? className : _classNameThemeNone);
                    }

                    //set correct auto Update
                    if (autoUpdateChanged) {
                        if (autoUpdate === true) {
                            mutationObserversDisconnect();
                            autoUpdateLoop.add(_base);
                        }
                        else if (autoUpdate === null) {
                            if (_autoUpdateRecommended) {
                                mutationObserversDisconnect();
                                autoUpdateLoop.add(_base);
                            }
                            else {
                                autoUpdateLoop.remove(_base);
                                mutationObserversConnect();
                            }
                        }
                        else {
                            autoUpdateLoop.remove(_base);
                            mutationObserversConnect();
                        }
                    }

                    //activate or deactivate size auto capability
                    if (sizeAutoCapableChanged) {
                        if (sizeAutoCapable) {
                            if (_contentGlueElement === undefined) {
                                _contentGlueElement = FRAMEWORK(generateDiv(_classNameContentGlueElement));
                                _paddingElement.before(_contentGlueElement);
                            }
                            if (_sizeAutoObserverAdded) {
                                _sizeAutoObserverElement.show();
                            }
                            else {
                                _sizeAutoObserverElement = FRAMEWORK(generateDiv(_classNameSizeAutoObserverElement));
                                _contentGlueElement.before(_sizeAutoObserverElement);
                                var oldSize = {w: -1, h: -1};
                                addResizeObserver(_sizeAutoObserverElement, function () {
                                    var newSize = {
                                        w: _sizeAutoObserverElement[0][LEXICON.oW],
                                        h: _sizeAutoObserverElement[0][LEXICON.oH]
                                    };
                                    if (checkCacheDouble(newSize, oldSize)) {
                                        if (_initialized && (_heightAutoCache && newSize.h > 0) || (_widthAutoCache && newSize.w > 0)) {
                                            update();
                                        }
                                        else if (_initialized && (!_heightAutoCache && newSize.h === 0) || (!_widthAutoCache && newSize.w === 0)) {
                                            update();
                                        }
                                    }
                                    oldSize = newSize;
                                });
                                _sizeAutoObserverAdded = true;
                                //fix heightAuto detector bug if height is fixed but contentHeight is 0.
                                //the probability this bug will ever happen is very very low, thats why its ok if we use calc which isn't supported in IE8.
                                if (_cssCalc !== null)
                                    _sizeAutoObserverElement.css(_strHeight, _cssCalc + '(100% + 1px)');
                            }
                        }
                        else {
                            if (_sizeAutoObserverAdded)
                                _sizeAutoObserverElement.hide();
                        }
                    }

                    //if force, update all resizeObservers too
                    if (force) {
                        _sizeObserverElement.find('*').trigger(_strScroll);
                        if (_sizeAutoObserverAdded)
                            _sizeAutoObserverElement.find('*').trigger(_strScroll);
                    }

                    //detect direction:
                    var cssDirection = _hostElement.css('direction');
                    var cssDirectionChanged = checkCacheSingle(cssDirection, _cssDirectionCache, force);

                    //detect box-sizing:
                    var boxSizing = _hostElement.css('box-sizing');
                    var boxSizingChanged = checkCacheSingle(boxSizing, _cssBoxSizingCache, force);

                    //detect padding:
                    var padding = {
                        c: force,
                        t: parseToZeroOrNumber(_hostElement.css(_strPaddingMinus + _strTop)),
                        r: parseToZeroOrNumber(_hostElement.css(_strPaddingMinus + _strRight)),
                        b: parseToZeroOrNumber(_hostElement.css(_strPaddingMinus + _strBottom)),
                        l: parseToZeroOrNumber(_hostElement.css(_strPaddingMinus + _strLeft))
                    };

                    //width + height auto detecting var:
                    var sizeAutoObserverElementBCRect;
                    //exception occurs in IE8 sometimes (unknown exception)
                    try {
                        sizeAutoObserverElementBCRect = _sizeAutoObserverAdded ? _sizeAutoObserverElement[0].getBoundingClientRect() : null;
                    } catch (ex) {
                        return;
                    }

                    _isRTL = cssDirection === 'rtl';
                    _isBorderBox = (boxSizing === 'border-box');
                    var isRTLLeft = _isRTL ? _strLeft : _strRight;
                    var isRTLRight = _isRTL ? _strRight : _strLeft;
                    var hostElement = _hostElement[0];
                    var paddingElement = _paddingElement[0];

                    //detect width auto:
                    var widthAutoResizeDetection = false;
                    var widthAutoObserverDetection = (_sizeAutoObserverAdded && (_hostElement.css(_strFloat) !== 'none' /*|| _isTextarea */)) ? (Math.round(sizeAutoObserverElementBCRect.right - sizeAutoObserverElementBCRect.left) === 0) && (!paddingAbsolute ? (hostElement[LEXICON.cW] - _paddingX) > 0 : true) : false;
                    if (sizeAutoCapable && !widthAutoObserverDetection) {
                        var tmpCurrHostWidth = hostElement[LEXICON.oW];
                        var tmpCurrContentGlueWidth = _contentGlueElement.css(_strWidth);
                        _contentGlueElement.css(_strWidth, _strAuto);

                        var tmpNewHostWidth = hostElement[LEXICON.oW];
                        _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);
                        widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;
                        if (!widthAutoResizeDetection) {
                            _contentGlueElement.css(_strWidth, tmpCurrHostWidth + 1);
                            tmpNewHostWidth = hostElement[LEXICON.oW];
                            _contentGlueElement.css(_strWidth, tmpCurrContentGlueWidth);
                            widthAutoResizeDetection = tmpCurrHostWidth !== tmpNewHostWidth;
                        }
                    }
                    var widthAuto = (widthAutoObserverDetection || widthAutoResizeDetection) && sizeAutoCapable && !displayIsHidden;
                    var widthAutoChanged = checkCacheSingle(widthAuto, _widthAutoCache, force);
                    var wasWidthAuto = !widthAuto && _widthAutoCache;

                    //detect height auto:
                    var heightAuto = _sizeAutoObserverAdded && !displayIsHidden ? (Math.round(sizeAutoObserverElementBCRect.bottom - sizeAutoObserverElementBCRect.top) === 0) /* && (!paddingAbsolute && (_msieVersion > 9 || !_msieVersion) ? true : true) */ : false;
                    var heightAutoChanged = checkCacheSingle(heightAuto, _heightAutoCache, force);
                    var wasHeightAuto = !heightAuto && _heightAutoCache;

                    //detect border:
                    //we need the border only if border box and auto size
                    var strMinusWidth = '-' + _strWidth;
                    var updateBorderX = (widthAuto && _isBorderBox) || !_isBorderBox;
                    var updateBorderY = (heightAuto && _isBorderBox) || !_isBorderBox;
                    var border = {
                        c: force,
                        t: updateBorderY ? parseToZeroOrNumber(_hostElement.css(_strBorderMinus + _strTop + strMinusWidth), true) : 0,
                        r: updateBorderX ? parseToZeroOrNumber(_hostElement.css(_strBorderMinus + _strRight + strMinusWidth), true) : 0,
                        b: updateBorderY ? parseToZeroOrNumber(_hostElement.css(_strBorderMinus + _strBottom + strMinusWidth), true) : 0,
                        l: updateBorderX ? parseToZeroOrNumber(_hostElement.css(_strBorderMinus + _strLeft + strMinusWidth), true) : 0
                    };

                    //detect margin:
                    var margin = {
                        c: force,
                        t: parseToZeroOrNumber(_hostElement.css(_strMarginMinus + _strTop)),
                        r: parseToZeroOrNumber(_hostElement.css(_strMarginMinus + _strRight)),
                        b: parseToZeroOrNumber(_hostElement.css(_strMarginMinus + _strBottom)),
                        l: parseToZeroOrNumber(_hostElement.css(_strMarginMinus + _strLeft))
                    };

                    //detect css max width & height:
                    var cssMaxValue = {
                        h: String(_hostElement.css(_strMaxMinus + _strHeight)),
                        w: String(_hostElement.css(_strMaxMinus + _strWidth))
                    };

                    //vars to apply correct css
                    var contentElementCSS = { };
                    var contentGlueElementCSS = { };
                    
                    //funcs
                    var getHostSize = function() { 
                        return {
                            w: hostElement[LEXICON.cW],
                            h: hostElement[LEXICON.cH]
                        };
                    };
                    var getViewportSize = function() { 
                        var brect = paddingElement.getBoundingClientRect()
                        return brect[_strWidth] 
                        ? {
                            w: brect[_strWidth],
                            h: brect[_strHeight]
                        } : {
                            w: paddingElement[LEXICON.oW],
                            h: paddingElement[LEXICON.oH]
                        };
                    };
                    
                    //set info for padding
                    _paddingX = padding.l + padding.r;
                    _paddingY = padding.t + padding.b;
                    padding.ax = paddingAbsolute ? _paddingX : 0;
                    padding.ay = paddingAbsolute ? _paddingY : 0;
                    padding.c = checkCacheTRBL(padding, _cssPaddingCache);
                    
                    //set info for border
                    _borderX = border.l + border.r;
                    _borderY = border.t + border.b;
                    border.c = checkCacheTRBL(border, _cssBorderCache);

                    //set info for margin
                    _marginX = margin.l + margin.r;
                    _marginY = margin.t + margin.b;
                    margin.c = checkCacheTRBL(margin, _cssMarginCache);

                    //set info for css max value
                    cssMaxValue.ih = parseToZeroOrNumber(cssMaxValue.h); //ih = integer height
                    cssMaxValue.iw = parseToZeroOrNumber(cssMaxValue.w); //iw = integer width
                    cssMaxValue.ch = cssMaxValue.h.indexOf('px') > -1; //ch = correct height
                    cssMaxValue.cw = cssMaxValue.w.indexOf('px') > -1; //cw = correct width
                    cssMaxValue.c = checkCacheDouble(cssMaxValue, _cssMaxValueCache, force);

                    //refresh cache
                    _cssDirectionCache = cssDirection;
                    _cssBoxSizingCache = boxSizing;
                    _widthAutoCache = widthAuto;
                    _heightAutoCache = heightAuto;
                    _cssPaddingCache = padding;
                    _cssBorderCache = border;
                    _cssMarginCache = margin;
                    _cssMaxValueCache = cssMaxValue;

                    //IEFix direction changed
                    if (cssDirectionChanged && _sizeAutoObserverAdded)
                        _sizeAutoObserverElement.css(_strFloat, isRTLRight);

                    //apply padding:
                    if (padding.c || cssDirectionChanged || paddingAbsoluteChanged || widthAutoChanged || heightAutoChanged || boxSizingChanged || sizeAutoCapableChanged) {
                        var paddingElementCSS = {};
                        var textareaCSS = {};
                        setTopRightBottomLeft(contentGlueElementCSS, _strMarginMinus, [-padding.t, -padding.r, -padding.b, -padding.l]);
                        if (paddingAbsolute) {
                            setTopRightBottomLeft(paddingElementCSS, _strEmpty, [padding.t, padding.r, padding.b, padding.l]);
                            if (_isTextarea)
                                setTopRightBottomLeft(textareaCSS, _strPaddingMinus);
                            else
                                setTopRightBottomLeft(contentElementCSS, _strPaddingMinus);
                        }
                        else {
                            setTopRightBottomLeft(paddingElementCSS, _strEmpty);
                            if (_isTextarea)
                                setTopRightBottomLeft(textareaCSS, _strPaddingMinus, [padding.t, padding.r, padding.b, padding.l]);
                            else
                                setTopRightBottomLeft(contentElementCSS, _strPaddingMinus, [padding.t, padding.r, padding.b, padding.l]);
                        }
                        _paddingElement.css(paddingElementCSS);
                        _targetElement.css(textareaCSS);
                    }

                    //viewport size is padding container because it never has padding, margin and a border.
                    _viewportSize = getViewportSize();

                    //update Textarea
                    var textareaSize = _isTextarea ? textareaUpdate() : false;

                    //fix height auto / width auto in cooperation with current padding & boxSizing behavior:
                    if (heightAuto && (heightAutoChanged || paddingAbsoluteChanged || boxSizingChanged || cssMaxValue.c || padding.c || border.c)) {
                        if (cssMaxValue.cw)
                            contentElementCSS[_strMaxMinus + _strHeight] =
                                (cssMaxValue.ch ? (cssMaxValue.ih - padding.ay + (_isBorderBox ? -_borderY : _paddingY)) 
                                : _strEmpty);
                        contentElementCSS[_strHeight] = _strAuto;
                    } 
                    else if (heightAutoChanged || paddingAbsoluteChanged) {
                        contentElementCSS[_strMaxMinus + _strHeight] = _strEmpty;
                        contentElementCSS[_strHeight] = _strHundredPercent;
                    }
                    if (widthAuto && (widthAutoChanged || paddingAbsoluteChanged || boxSizingChanged || cssMaxValue.c || padding.c || border.c || cssDirectionChanged)) {
                        if (cssMaxValue.cw)
                            contentElementCSS[_strMaxMinus + _strWidth] =
                                (cssMaxValue.cw ? (cssMaxValue.iw - padding.ax + (_isBorderBox ? -_borderX : _paddingX)) +
                                (_nativeScrollbarIsOverlaid.y /*&& _hasOverflowCache.y && widthAuto */ ? _overlayScrollbarDummySize.y : 0) 
                                : _strEmpty);
                        contentElementCSS[_strWidth] = _strAuto;
                        contentGlueElementCSS[_strMaxMinus + _strWidth] = _strHundredPercent; //IE Fix
                    } 
                    else if (widthAutoChanged || paddingAbsoluteChanged) {
                        contentElementCSS[_strMaxMinus + _strWidth] = _strEmpty;
                        contentElementCSS[_strWidth] = _strHundredPercent;
                        contentElementCSS[_strFloat] = _strEmpty;
                        contentGlueElementCSS[_strMaxMinus + _strWidth] = _strEmpty; //IE Fix
                    }
                    if (widthAuto) {
                        if (!cssMaxValue.cw)
                            contentElementCSS[_strMaxMinus + _strWidth] = _strEmpty;
                        contentGlueElementCSS[_strWidth] = _isTextarea && textareaDynWidth ? textareaSize.dw : _strAuto;

                        contentElementCSS[_strWidth] = _strAuto;
                        contentElementCSS[_strFloat] = isRTLRight;
                    }
                    if (heightAuto) {
                        if (!cssMaxValue.ch)
                            contentElementCSS[_strMaxMinus + _strHeight] = _strEmpty;
                        //fix dyn height collapse bug: (doesn't works for width!)
                        //contentGlueElementCSS[_strHeight] = _isTextarea && textareaDynHeight ? textareaSize.dh : _strAuto;
                        contentGlueElementCSS[_strHeight] = _isTextarea ? textareaDynHeight ? textareaSize.dh : _strAuto : _contentElement[0][LEXICON.cH];
                    }
                    if (sizeAutoCapable)
                        _contentGlueElement.css(contentGlueElementCSS);
                    _contentElement.css(contentElementCSS);

                    //CHECKPOINT HERE ~
                    contentElementCSS = {};
                    contentGlueElementCSS = {};
                    
                    //if [content(host) client / scroll size, or target element direction, or content(host) max-sizes] changed, or force is true
                    if (hostSizeChanged || contentSizeChanged || cssDirectionChanged || boxSizingChanged || paddingAbsoluteChanged || widthAutoChanged || widthAuto || heightAutoChanged || heightAuto || cssMaxValue.c || ignoreOverlayScrollbarHidingChanged || overflowBehaviorChanged || clipAlwaysChanged || resizeChanged || scrollbarsVisibilityChanged || textareaDynWidthChanged || textareaDynHeightChanged || textareaAutoWrappingChanged || force) {
                        var strOverflow = 'overflow';
                        var strOverflowX = strOverflow + '-x';
                        var strOverflowY = strOverflow + '-y';
                        var strHidden = 'hidden';
                        var strVisible = 'visible';
                        //decide whether the content overflow must get hidden for correct overflow measuring, it MUST be always hidden if the height is auto
                        var hideOverflow4CorrectMeasuring = _restrictedMeasuring ?
                        (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) || //it must be hidden if native scrollbars are overlaid
                        (_viewportSize.w < _nativeScrollbarMinSize.y || _viewportSize.h < _nativeScrollbarMinSize.x) || //it must be hidden if host-element is too small
                        heightAuto || displayIsHiddenChanged //it must be hidden if height is auto or display was change
                            : heightAuto; //if there is not the restricted Measuring bug, it must be hidden if the height is auto

                        //Reset the viewport (very important for natively overlaid scrollbars and zoom change
                        var viewportElementResetCSS = {};
                        var resetXTmp = _hasOverflowCache.y && _hideOverflowCache.ys && !ignoreOverlayScrollbarHiding ? (_nativeScrollbarIsOverlaid.y ? _viewportElement.css(isRTLLeft) : -_nativeScrollbarSize.y) : 0;
                        var resetBottomTmp = _hasOverflowCache.x && _hideOverflowCache.xs && !ignoreOverlayScrollbarHiding ? (_nativeScrollbarIsOverlaid.x ? _viewportElement.css(_strBottom) : -_nativeScrollbarSize.x) : 0;
                        setTopRightBottomLeft(viewportElementResetCSS, _strEmpty);
                        _viewportElement.css(viewportElementResetCSS);
                        if(hideOverflow4CorrectMeasuring)
                            _contentElement.css(strOverflow, strHidden);

                        //measure several sizes:
                        var contentMeasureElement = getContentMeasureElement();
                        //in Firefox content element has to have overflow hidden, else element margins aren't calculated properly, this element prevents this bug, but only if scrollbars aren't overlaid
                        var contentMeasureElementGuaranty = _restrictedMeasuring && !hideOverflow4CorrectMeasuring ? _viewportElement[0] : contentMeasureElement;
                        var clientSize = {
                            w: contentMeasureElement[LEXICON.cW],
                            h: contentMeasureElement[LEXICON.cH]
                        };
                        var scrollSize = {
                            w: Math.max(contentMeasureElement[LEXICON.sW], contentMeasureElementGuaranty[LEXICON.sW]),
                            h: Math.max(contentMeasureElement[LEXICON.sH], contentMeasureElementGuaranty[LEXICON.sH])
                        };
                        var contentClientSize = {
                            w: _isTextarea && textareaSize && !textareaDynWidth ? textareaSize.ow : widthAuto ? clientSize.w : scrollSize.w,
                            h: _isTextarea && textareaSize && !textareaDynHeight ? textareaSize.oh : heightAuto ? clientSize.h : scrollSize.h
                        };

                        //apply the correct viewport style and measure viewport size
                        viewportElementResetCSS[_strBottom] = wasHeightAuto ? _strEmpty : resetBottomTmp;
                        viewportElementResetCSS[isRTLLeft] = wasWidthAuto ? _strEmpty : resetXTmp;
                        _viewportElement.css(viewportElementResetCSS);
                        //viewport size is padding container because it never has padding, margin and a border.
                        _viewportSize = getViewportSize();
                        
                        //measure and correct several sizes
                        //has to be clientSize because offsetSize respect borders.
                        var hostSize = getHostSize();
                        var contentGlueSize = {
                            w: Math.max(contentClientSize.w + padding.ax, hostSize.w - _paddingX) - (textareaDynWidth ? (_isTextarea && widthAuto ? _marginX + (!_isBorderBox ? _paddingX + _borderX : 0) : 0) : 0),
                            h: Math.max(contentClientSize.h + padding.ay, hostSize.h - _paddingY)
                        };
                        contentGlueSize.c = checkCacheDouble(contentGlueSize, _contentGlueSizeCache, force);
                        _contentGlueSizeCache = contentGlueSize;

                        //apply correct contentGlue size
                        if (sizeAutoCapable) {
                            //size contentGlue correctly to make sure the element has correct size if the sizing switches to auto
                            if (contentGlueSize.c || (heightAuto || widthAuto)) {
                                contentGlueElementCSS[_strWidth] = contentGlueSize.w;
                                contentGlueElementCSS[_strHeight] = contentGlueSize.h;
                            }

                            var maxWidth = contentGlueElementCSS[_strWidth] + (_isBorderBox ? _borderX : -_paddingX);
                            var maxHeight = contentGlueElementCSS[_strHeight] + (_isBorderBox ? _borderY : -_paddingX);
                            var textareaCoverCSS = {};

                            //make contentGlue size -1 if element is not auto sized, to make sure that a resize event happens when the element shrinks
                            if (!widthAuto || (!widthAuto && border.c))
                                contentGlueElementCSS[_strWidth] = hostSize.w - (_isBorderBox ? 0 : _paddingX + _borderX) - 1 - _marginX;
                            if (!heightAuto || (!heightAuto && border.c))
                                contentGlueElementCSS[_strHeight] = hostSize.h - (_isBorderBox ? 0 : _paddingY + _borderY) - 1 - _marginY;

                            //if size is auto and host is same size as max size, make content glue size +1 to make sure size changes will be detected
                            if (cssMaxValue.cw && cssMaxValue.iw === maxWidth)
                                contentGlueElementCSS[_strWidth] = maxWidth + (_isBorderBox ? 0 : _paddingX) + 1;
                            if (cssMaxValue.ch && cssMaxValue.ih === maxHeight)
                                contentGlueElementCSS[_strHeight] = maxHeight + (_isBorderBox ? 0 : _paddingY) + 1;

                            //if size is auto and host is smaller than size as min size, make content glue size -1 to make sure size changes will be detected (this is only needed if padding is 0)
                            if (widthAuto && (clientSize.w < _viewportSize.w || _isTextarea && !textareaAutoWrapping) && _paddingX === 0) {
                                if (_isTextarea)
                                    textareaCoverCSS[_strWidth] = parseToZeroOrNumber(_textareaCoverElement.css(_strWidth)) - 1;
                                contentGlueElementCSS[_strWidth] -= 1;
                            }
                            if (heightAuto && (clientSize.h < _viewportSize.h || _isTextarea) && _paddingY === 0) {
                                if (_isTextarea)
                                    textareaCoverCSS[_strHeight] = parseToZeroOrNumber(_textareaCoverElement.css(_strHeight)) - 1;
                                contentGlueElementCSS[_strHeight] -= 1;
                            }

                            //make sure content glue size at least 1
                            if (contentClientSize.h > 0) {
                                contentGlueElementCSS[_strWidth] = Math.max(1, contentGlueElementCSS[_strWidth]);
                                contentGlueElementCSS[_strHeight] = Math.max(1, contentGlueElementCSS[_strHeight]);
                            }

                            if (_isTextarea)
                                _textareaCoverElement.css(textareaCoverCSS);
                            _contentGlueElement.css(contentGlueElementCSS);
                        }
                        if (widthAuto)
                            contentElementCSS[_strWidth] = _strHundredPercent;
                        if (widthAuto && !_isBorderBox && !_mutationObserverConnected)
                            contentElementCSS[_strFloat] = 'none';


                        //apply and reset content style
                        _contentElement.css(contentElementCSS);
                        contentElementCSS = {};

                        //measure again, but this time all correct sizes:
                        var contentBCRect = contentMeasureElement.getBoundingClientRect();
                        var contentBCRectW = contentBCRect[_strWidth] || 0;
                        var contentBCRectH = contentBCRect[_strHeight] || 0;
                        var contentScrollSize = {
                            w: Math.max(contentMeasureElement[LEXICON.sW], contentMeasureElementGuaranty[LEXICON.sW]) + parseToZeroOrNumber(contentBCRectW) - contentBCRectW,
                            h: Math.max(contentMeasureElement[LEXICON.sH], contentMeasureElementGuaranty[LEXICON.sH]) + parseToZeroOrNumber(contentBCRectH) - contentBCRectH
                        };
                        contentScrollSize.c = contentSizeChanged = checkCacheDouble(contentScrollSize, _contentScrollSizeCache, force);
                        _contentScrollSizeCache = contentScrollSize;

                        if(hideOverflow4CorrectMeasuring)
                            _contentElement.css(strOverflow, _strEmpty);
                        
                        //refresh viewport size after correct measuring
                        _viewportSize = getViewportSize();
                        
                        //has to be clientSize because offsetSize respect borders.
                        hostSize = getHostSize();
                        hostSizeChanged = checkCacheDouble(hostSize, _hostSizeCache);
                        _hostSizeCache = hostSize;
                        
                        var overflowBehaviorIsVS = {
                            x: overflowBehavior.x === 'v-s',
                            y: overflowBehavior.y === 'v-s'
                        };
                        var overflowBehaviorIsVH = {
                            x: overflowBehavior.x === 'v-h',
                            y: overflowBehavior.y === 'v-h'
                        };
                        var overflowBehaviorIsS = {
                            x: overflowBehavior.x === 's',
                            y: overflowBehavior.y === 's'
                        };
                        var overflowAmount = {
                            x: Math.max(0, Math.round((contentScrollSize.w - _viewportSize.w) * 100) / 100),
                            y: Math.max(0, Math.round((contentScrollSize.h - _viewportSize.h) * 100) / 100)
                        };
                        var hideOverflowForceTextarea = _isTextarea && (_viewportSize.w === 0 || _viewportSize.h === 0);
                        var hideOverflowForceRounding = (_viewportElement[0].scrollLeftMax === 0 && overflowAmount.x > 0 && overflowAmount.x < 1) || (_viewportElement[0].scrollTopMax === 0 && overflowAmount.y > 0 && overflowAmount.y < 1);
                        if (hideOverflowForceTextarea || hideOverflowForceRounding)
                            overflowAmount.x = overflowAmount.y = 0;
                        var hasOverflow = {
                            x: overflowAmount.x > 0,
                            y: overflowAmount.y > 0
                        };
                        //hideOverflow:
                        //x || y : true === overflow is hidden by "overflow: scroll" OR "overflow: hidden"
                        //xs || ys : true === overflow is hidden by "overflow: scroll"
                        var hideOverflow = {x: hasOverflow.x, y: hasOverflow.y};
                        if (overflowBehaviorIsVS.x || overflowBehaviorIsVH.x)
                            hideOverflow.x = (hasOverflow.y && !overflowBehaviorIsVS.y && !overflowBehaviorIsVH.y);
                        if (overflowBehaviorIsVS.y || overflowBehaviorIsVH.y)
                            hideOverflow.y = (hasOverflow.x && !overflowBehaviorIsVS.x && !overflowBehaviorIsVH.x);
                        hideOverflow.xs = hideOverflow.x ? (overflowBehaviorIsS.x || overflowBehaviorIsVS.x) : false;
                        hideOverflow.ys = hideOverflow.y ? (overflowBehaviorIsS.y || overflowBehaviorIsVS.y) : false;

                        var canScroll = {
                            x: hasOverflow.x && hideOverflow.xs,
                            y: hasOverflow.y && hideOverflow.ys
                        };
                        var previousOverflow = _overflowAmountCache;
                        overflowAmount.c = checkCacheDouble(overflowAmount, _overflowAmountCache, _strX, _strY, force);
                        _overflowAmountCache = overflowAmount;
                        hasOverflow.c = checkCacheDouble(hasOverflow, _hasOverflowCache, _strX, _strY, force);
                        _hasOverflowCache = hasOverflow;
                        hideOverflow.c = checkCacheDouble(hideOverflow, _hideOverflowCache, _strX, _strY, force);
                        _hideOverflowCache = hideOverflow;

                        //if native scrollbar is overlay at x OR y axis, prepare DOM
                        if (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) {
                            var borderDesign = 'px solid transparent';
                            var contentArrangeElementCSS = { };
                            var arrangeContent = { };
                            var arrangeChanged = force;
                            if (hasOverflow.x || hasOverflow.y) {
                                arrangeContent.w = _nativeScrollbarIsOverlaid.y && hasOverflow.y ? contentScrollSize.w + _overlayScrollbarDummySize.y : _strEmpty;
                                arrangeContent.h = _nativeScrollbarIsOverlaid.x && hasOverflow.x ? contentScrollSize.h + _overlayScrollbarDummySize.x : _strEmpty;
                                arrangeChanged = checkCacheSingle(arrangeContent, _arrangeContentSizeCache, force);
                                _arrangeContentSizeCache = arrangeContent;
                            }

                            if (hasOverflow.c || hideOverflow.c || contentScrollSize.c || cssDirectionChanged || widthAutoChanged || heightAutoChanged || widthAuto || heightAuto || ignoreOverlayScrollbarHidingChanged) {
                                contentElementCSS[_strMarginMinus + isRTLRight] = contentElementCSS[_strBorderMinus + isRTLRight] = _strEmpty;
                                if (_nativeScrollbarIsOverlaid.x && hasOverflow.x && hideOverflow.xs) {
                                    contentElementCSS[_strMarginMinus + _strBottom] = heightAuto ? (ignoreOverlayScrollbarHiding ? _strEmpty : _overlayScrollbarDummySize.x) : _strEmpty;
                                    contentElementCSS[_strBorderMinus + _strBottom] = (!heightAuto && !ignoreOverlayScrollbarHiding) ? (_overlayScrollbarDummySize.x + borderDesign) : _strEmpty;
                                }
                                else {
                                    arrangeContent.h = 
                                    contentElementCSS[_strMarginMinus + _strBottom] = 
                                    contentElementCSS[_strBorderMinus + _strBottom] = _strEmpty;
                                    arrangeChanged = true;
                                }
                                if (_nativeScrollbarIsOverlaid.y && hasOverflow.y && hideOverflow.ys) {
                                    contentElementCSS[_strMarginMinus + isRTLLeft] = widthAuto ? (ignoreOverlayScrollbarHiding ? _strEmpty : _overlayScrollbarDummySize.y) : _strEmpty;
                                    contentElementCSS[_strBorderMinus + isRTLLeft] = (/* !widthAuto && */ !ignoreOverlayScrollbarHiding) ? (_overlayScrollbarDummySize.y + borderDesign) : _strEmpty;
                                }
                                else {
                                    arrangeContent.w = 
                                    contentElementCSS[_strMarginMinus + isRTLLeft] = 
                                    contentElementCSS[_strBorderMinus + isRTLLeft] = _strEmpty;
                                    arrangeChanged = true;
                                }
                            }
                            if (ignoreOverlayScrollbarHiding) {
                                arrangeContent.w = arrangeContent.h = _strEmpty;
                                arrangeChanged = true;
                            }
                            if (arrangeChanged) {
                                contentArrangeElementCSS[_strWidth] = hideOverflow.y ? arrangeContent.w : _strEmpty;
                                contentArrangeElementCSS[_strHeight] = hideOverflow.x ? arrangeContent.h : _strEmpty;

                                if (!_contentArrangeElement) {
                                    _contentArrangeElement = FRAMEWORK(generateDiv(_classNameContentArrangeElement));
                                    _viewportElement.prepend(_contentArrangeElement);
                                }
                                _contentArrangeElement.css(contentArrangeElementCSS);
                            }
                            _contentElement.css(contentElementCSS);
                        }

                        var viewportElementCSS = {};
                        var paddingElementCSS = {};
                        if (hostSizeChanged || hasOverflow.c || hideOverflow.c || contentScrollSize.c || overflowBehaviorChanged || boxSizingChanged || ignoreOverlayScrollbarHidingChanged || cssDirectionChanged || clipAlwaysChanged || heightAutoChanged) {
                            viewportElementCSS[isRTLRight] = _strEmpty;
                            var resetScrollbarHidingX = function () {
                                viewportElementCSS[_strBottom] = _strEmpty;
                                _contentBorderSize.h = 0;
                            };
                            var resetScrollbarHidingY = function () {
                                viewportElementCSS[isRTLLeft] = _strEmpty;
                                _contentBorderSize.w = 0;
                            };
                            if (hasOverflow.x && hideOverflow.xs) {
                                viewportElementCSS[strOverflowX] = _strScroll;
                                if (!ignoreOverlayScrollbarHiding) {
                                    viewportElementCSS[_strBottom] = -(_nativeScrollbarIsOverlaid.x ? _overlayScrollbarDummySize.x : _nativeScrollbarSize.x);
                                    _contentBorderSize.h = _nativeScrollbarIsOverlaid.x ? _overlayScrollbarDummySize.y : 0;
                                }
                                else
                                    resetScrollbarHidingX();
                            } else {
                                viewportElementCSS[strOverflowX] = _strEmpty;
                                resetScrollbarHidingX();
                            }
                            if (hasOverflow.y && hideOverflow.ys) {
                                viewportElementCSS[strOverflowY] = _strScroll;
                                if (!ignoreOverlayScrollbarHiding) {
                                    viewportElementCSS[isRTLLeft] = -(_nativeScrollbarIsOverlaid.y ? _overlayScrollbarDummySize.y : _nativeScrollbarSize.y);
                                    _contentBorderSize.w = _nativeScrollbarIsOverlaid.y ? _overlayScrollbarDummySize.x : 0;
                                }
                                else
                                    resetScrollbarHidingY();
                            } else {
                                viewportElementCSS[strOverflowY] = _strEmpty;
                                resetScrollbarHidingY();
                            }

                            // if the scroll container is too small and if there is any overflow with not overlay scrollbar, make viewport element greater in size (Firefox hide Scrollbars fix)
                            // because firefox starts hiding scrollbars on too small elements
                            // with this behavior the overflow calculation may be incorrect or the scrollbars would appear suddenly
                            // https://bugzilla.mozilla.org/show_bug.cgi?id=292284
                            if ((_viewportSize.h < _nativeScrollbarMinSize.x || _viewportSize.w < _nativeScrollbarMinSize.y)
                                && ((hasOverflow.x && hideOverflow.x && !_nativeScrollbarIsOverlaid.x) || (hasOverflow.y && hideOverflow.y && !_nativeScrollbarIsOverlaid.y))) {
                                viewportElementCSS[_strPaddingMinus + _strTop] = _nativeScrollbarMinSize.x;
                                viewportElementCSS[_strMarginMinus + _strTop] = -_nativeScrollbarMinSize.x;

                                viewportElementCSS[_strPaddingMinus + isRTLRight] = _nativeScrollbarMinSize.y;
                                viewportElementCSS[_strMarginMinus + isRTLRight] = -_nativeScrollbarMinSize.y;
                            }
                            else {
                                viewportElementCSS[_strPaddingMinus + _strTop] = 
                                viewportElementCSS[_strMarginMinus + _strTop] = 
                                viewportElementCSS[_strPaddingMinus + isRTLRight] = 
                                viewportElementCSS[_strMarginMinus + isRTLRight] = _strEmpty;
                            }
                            viewportElementCSS[_strPaddingMinus + isRTLLeft] = 
                            viewportElementCSS[_strMarginMinus + isRTLLeft] = _strEmpty;

                            //if there is any overflow (x OR y axis) and this overflow shall be hidden, make overflow hidden, else overflow visible
                            if ((hasOverflow.x && hideOverflow.x) || (hasOverflow.y && hideOverflow.y) || hideOverflowForceTextarea) {
                                //only hide if is Textarea
                                if (_isTextarea && hideOverflowForceTextarea) {
                                    paddingElementCSS[strOverflowX] = 
                                    paddingElementCSS[strOverflowY] = strHidden;
                                }
                            }
                            else {
                                if (!clipAlways || (overflowBehaviorIsVH.x || overflowBehaviorIsVS.x || overflowBehaviorIsVH.y || overflowBehaviorIsVS.y)) {
                                    //only un-hide if Textarea
                                    if (_isTextarea) {
                                        paddingElementCSS[strOverflowX] = 
                                        paddingElementCSS[strOverflowY] = _strEmpty;
                                    }
                                    viewportElementCSS[strOverflowX] = 
                                    viewportElementCSS[strOverflowY] = strVisible;
                                }
                            }

                            _paddingElement.css(paddingElementCSS);
                            _viewportElement.css(viewportElementCSS);
                            viewportElementCSS = { };

                            //force soft redraw in webkit because without the scrollbars will may appear because DOM wont be redrawn under special conditions
                            if ((hasOverflow.c || boxSizingChanged || widthAutoChanged || heightAutoChanged) && !(_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y)) {
                                var element = _contentElement[0];
                                var elementStyle = element[LEXICON.s];
                                var dump;
                                elementStyle.webkitTransform = 'scale(1)';
                                elementStyle.display = 'run-in';
                                dump = element[LEXICON.oH];
                                elementStyle.display = _strEmpty; //|| dump; //use dump to prevent it from deletion if minify
                                elementStyle.webkitTransform = _strEmpty;
                            }
                            //force hard redraw in webkit if native overlaid scrollbars shall appear
                            if (ignoreOverlayScrollbarHidingChanged && ignoreOverlayScrollbarHiding) {
                                //_hostElement.hide();
                                //var dump = hostElement[LEXICON.oH];
                                //_hostElement.show();
                            }
                        }

                        //change to direction RTL and width auto Bugfix in Webkit
                        //without this fix, the DOM still thinks the scrollbar is LTR and thus the content is shifted to the left
                        contentElementCSS = {};
                        if (cssDirectionChanged || widthAutoChanged || heightAutoChanged) {
                            if (_isRTL && widthAuto) {
                                var floatTmp = _contentElement.css(_strFloat);
                                var posLeftWithoutFloat = Math.round(_contentElement.css(_strFloat, _strEmpty).css(_strLeft, _strEmpty).position().left);
                                _contentElement.css(_strFloat, floatTmp);
                                var posLeftWithFloat = Math.round(_contentElement.position().left);

                                if (posLeftWithoutFloat !== posLeftWithFloat)
                                    contentElementCSS[_strLeft] = posLeftWithoutFloat;
                            }
                            else {
                                contentElementCSS[_strLeft] = _strEmpty;
                            }
                        }
                        _contentElement.css(contentElementCSS);

                        //scrollbars management:
                        var scrollbarsVisibilityVisible = scrollbarsVisibility === 'v';
                        var scrollbarsVisibilityHidden = scrollbarsVisibility === 'h';
                        var scrollbarsVisibilityAuto = scrollbarsVisibility === 'a';

                        var showScrollbarH = COMPATIBILITY.bind(refreshScrollbarAppearance, 0, true, true, canScroll.x);
                        var showScrollbarV = COMPATIBILITY.bind(refreshScrollbarAppearance, 0, false, true, canScroll.y);
                        var hideScrollbarH = COMPATIBILITY.bind(refreshScrollbarAppearance, 0, true, false, canScroll.x);
                        var hideScrollbarV = COMPATIBILITY.bind(refreshScrollbarAppearance, 0, false, false, canScroll.y);

                        //manage class name which indicates scrollable overflow
                        if (hideOverflow.x || hideOverflow.y)
                            addClass(_hostElement, _classNameHostOverflow);
                        else
                            removeClass(_hostElement, _classNameHostOverflow);
                        if (hideOverflow.x)
                            addClass(_hostElement, _classNameHostOverflowX);
                        else
                            removeClass(_hostElement, _classNameHostOverflowX);
                        if (hideOverflow.y)
                            addClass(_hostElement, _classNameHostOverflowY);
                        else
                            removeClass(_hostElement, _classNameHostOverflowY);

                        //add or remove rtl class name for styling purposes
                        if (cssDirectionChanged) {
                            if (_isRTL)
                                addClass(_hostElement, _classNameHostRTL);
                            else
                                removeClass(_hostElement, _classNameHostRTL);
                        }

                        //manage the resize feature (CSS3 resize "polyfill" for this plugin)
                        if (_isBody)
                            addClass(_hostElement, _classNameHostResizeDisabled);
                        if (resizeChanged) {
                            var addCornerEvents = function () { _scrollbarCornerElement.on(_strMouseTouchDownEvent, scrollbarCornerOnMouseDown); };
                            var removeCornerEvents = function () { _scrollbarCornerElement.off(_strMouseTouchDownEvent, scrollbarCornerOnMouseDown); };
                            if (_resizeNone) {
                                addClass(_hostElement, _classNameHostResizeDisabled);
                                removeClass(_scrollbarCornerElement, [
                                    _classNameScrollbarCornerResize,
                                    _classNameScrollbarCornerResizeB,
                                    _classNameScrollbarCornerResizeH,
                                    _classNameScrollbarCornerResizeV].join(_strSpace));
                                removeCornerEvents();
                            }
                            else {
                                removeClass(_hostElement, _classNameHostResizeDisabled);
                                addClass(_scrollbarCornerElement, _classNameScrollbarCornerResize);
                                if (_resizeBoth)
                                    addClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeB);
                                else if (_resizeHorizontal)
                                    addClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeH);
                                else if (_resizeVertical)
                                    addClass(_scrollbarCornerElement, _classNameScrollbarCornerResizeV);

                                removeCornerEvents();
                                addCornerEvents();
                            }
                        }

                        //manage the scrollbars general visibility + the scrollbar interactivity (unusable class name)
                        if (scrollbarsVisibilityChanged || overflowBehaviorChanged || hideOverflow.c || hasOverflow.c || ignoreOverlayScrollbarHidingChanged) {
                            if (ignoreOverlayScrollbarHiding) {
                                if (ignoreOverlayScrollbarHidingChanged) {
                                    removeClass(_hostElement, _classNameHostScrolling);
                                    if (ignoreOverlayScrollbarHiding) {
                                        hideScrollbarH();
                                        hideScrollbarV();
                                    }
                                }
                            }
                            else if (scrollbarsVisibilityAuto) {
                                if (canScroll.x)
                                    showScrollbarH();
                                else
                                    hideScrollbarH();

                                if (canScroll.y)
                                    showScrollbarV();
                                else
                                    hideScrollbarV();
                            }
                            else if (scrollbarsVisibilityVisible) {
                                showScrollbarH();
                                showScrollbarV();
                            }
                            else if (scrollbarsVisibilityHidden) {
                                hideScrollbarH();
                                hideScrollbarV();
                            }
                        }

                        //manage the scrollbars auto hide feature (auto hide them after specific actions)
                        if (scrollbarsAutoHideChanged || ignoreOverlayScrollbarHidingChanged) {
                            var addMouseTouchEvents = function (move) {
                                if (_supportPassiveEvents) {
                                    if(move)
                                        addPassiveEventListener(_hostElement, _strMouseTouchMoveEvent, hostOnMouseMove);
                                    else {
                                        addPassiveEventListener(_hostElement, _strMouseTouchEnter, hostOnMouseEnter);
                                        addPassiveEventListener(_hostElement, _strMouseTouchLeave, hostOnMouseLeave);
                                    }
                                }
                                else {
                                    if(move)
                                        _hostElement.on(_strMouseTouchMoveEvent, hostOnMouseMove);
                                    else {
                                        _hostElement.on(_strMouseTouchEnter, hostOnMouseEnter)
                                            .on(_strMouseTouchLeave, hostOnMouseLeave);
                                    }
                                }

                                //if the plugin is initialized and the mouse is over the host element, make the scrollbars visible
                                if(!_initialized)
                                    _hostElement.one("mouseover", hostOnMouseEnter);
                            };
                            var removeMouseTouchEvents = function () {
                                if (_supportPassiveEvents) {
                                    removePassiveEventListener(_hostElement, _strMouseTouchMoveEvent, hostOnMouseMove);
                                    removePassiveEventListener(_hostElement, _strMouseTouchEnter, hostOnMouseEnter);
                                    removePassiveEventListener(_hostElement, _strMouseTouchLeave, hostOnMouseLeave);
                                }
                                else {
                                    _hostElement.off(_strMouseTouchMoveEvent, hostOnMouseMove)
                                        .off(_strMouseTouchEnter, hostOnMouseEnter)
                                        .off(_strMouseTouchLeave, hostOnMouseLeave);
                                }
                            };
                            if (_scrollbarsAutoHideLeave || _scrollbarsAutoHideMove) {
                                removeMouseTouchEvents();
                                addMouseTouchEvents(_scrollbarsAutoHideMove);
                            }
                            else {
                                removeMouseTouchEvents();
                            }

                            if (_scrollbarsAutoHideNever)
                                refreshScrollbarsAutoHide(true);
                            else
                                refreshScrollbarsAutoHide(false, true);
                        }

                        //manage scrollbars handle length and offset
                        if (hostSizeChanged || overflowAmount.c || heightAutoChanged || widthAutoChanged || resizeChanged || boxSizingChanged || paddingAbsoluteChanged || ignoreOverlayScrollbarHidingChanged || cssDirectionChanged) {
                            refreshScrollbarHandleLength(true);
                            refreshScrollbarHandleOffset(true, currScroll.l);
                            refreshScrollbarHandleLength(false);
                            refreshScrollbarHandleOffset(false, currScroll.t);
                        }

                        //manage interactivity
                        if (scrollbarsClickScrollingChanged)
                            refreshScrollbarsInteractive(true, scrollbarsClickScrolling);
                        if (scrollbarsDragScrollingChanged)
                            refreshScrollbarsInteractive(false, scrollbarsDragScrolling);

                        //handle scroll
                        if (_isTextarea && contentSizeChanged) {
                            var textareaInfo = getTextareaInfo();
                            if (textareaInfo) {
                                var textareaRowsChanged = _textareaInfoCache === undefined ? true : textareaInfo.rows !== _textareaInfoCache.rows;
                                var cursorRow = textareaInfo.cR;
                                var cursorCol = textareaInfo.cC;
                                var widestRow = textareaInfo.w;
                                var lastRow = textareaInfo.r;
                                var lastCol = textareaInfo.c;
                                var cursorPos = textareaInfo.p;
                                var cursorMax = textareaInfo.m;
                                var cursorIsLastPosition = (cursorMax === cursorPos && _textareaHasFocus);
                                var doScroll = {
                                    x: (!textareaAutoWrapping && (cursorCol === lastCol && cursorRow === widestRow)) ? _overflowAmountCache.x : -1,
                                    y: (textareaAutoWrapping ? cursorIsLastPosition || textareaRowsChanged && (previousOverflow !== undefined ? (currScroll.t === previousOverflow.y) : false) : (cursorIsLastPosition || textareaRowsChanged) && cursorRow === lastRow) ? _overflowAmountCache.y : -1
                                };
                                var doScrollX = doScroll.x > -1;
                                var doScrollY = doScroll.y > -1;

                                if (doScrollX || doScrollY) {
                                    if (doScrollY)
                                        _viewportElement[_strScrollTop](doScroll.y);
                                    if (doScrollX) {
                                        if (_isRTL && _normalizeRTLCache && _rtlScrollBehavior.i)
                                            _viewportElement[_strScrollLeft](0); //if inverted, scroll to 0 -> normalized this means to max scroll offset.
                                        else
                                            _viewportElement[_strScrollLeft](doScroll.x);
                                    }
                                }
                            }
                            _textareaInfoCache = textareaInfo;
                        }
                        else if (!_isTextarea) {
                            if (_isRTL && _rtlScrollBehavior.i && _nativeScrollbarIsOverlaid.y && hasOverflow.x && _normalizeRTLCache)
                                currScroll.l += _contentBorderSize.w || 0;
                            _viewportElement[_strScrollLeft](currScroll.l);
                            _viewportElement[_strScrollTop](currScroll.t);
                        }

                        if (cssDirectionChanged) {
                            callCallback("onDirectionChanged", {
                                isRTL: _isRTL,
                                dir: cssDirection
                            });
                        }
                        if (hostSizeChanged) {
                            callCallback("onHostSizeChanged", {
                                width: _hostSizeCache.w,
                                height: _hostSizeCache.h
                            });
                        }
                        if (contentSizeChanged) {
                            callCallback("onContentSizeChanged", {
                                width: _contentScrollSizeCache.w,
                                height: _contentScrollSizeCache.h
                            });
                        }
                        if (hasOverflow.c || hideOverflow.c) {
                            callCallback("onOverflowChanged", {
                                x: hasOverflow.x,
                                y: hasOverflow.y,
                                xScrollable: hideOverflow.xs,
                                yScrollable: hideOverflow.ys,
                                clipped: hideOverflow.x || hideOverflow.y
                            });
                        }
                        if (overflowAmount.c) {
                            callCallback("onOverflowAmountChanged", {
                                x: overflowAmount.x,
                                y: overflowAmount.y
                            });
                        }
                    }

                    //fix body min size
                    if (_isBody && (_hasOverflowCache.c || _bodyMinSizeCache.c)) {
                        //its possible that no min size was measured until now, because the content arrange element was just added now, in this case, measure now the min size.
                        if (!_bodyMinSizeCache.f)
                            bodyMinSizeChanged();
                        if (_nativeScrollbarIsOverlaid.y && _hasOverflowCache.x)
                            _contentElement.css(_strMinMinus + _strWidth, _bodyMinSizeCache.w + _overlayScrollbarDummySize.y);
                        if (_nativeScrollbarIsOverlaid.x && _hasOverflowCache.y)
                            _contentElement.css(_strMinMinus + _strHeight, _bodyMinSizeCache.h + _overlayScrollbarDummySize.x);
                        _bodyMinSizeCache.c = false;
                    }

                    unfreezeResizeObserver(_sizeObserverElement);
                    unfreezeResizeObserver(_sizeAutoObserverElement);

                    callCallback("onUpdated", { forced: force });
                }


                //==== Options ====//

                /**
                 * Sets new options but doesn't call the update method.
                 * @param newOptions The object which contains the new options.
                 */
                function setOptions(newOptions) {
                    _currentOptions = extend(true, {}, _currentOptions, _pluginsOptions.v(newOptions, _pluginsOptions.t, true));
                    _currentPreparedOptions = extend(true, {}, _currentPreparedOptions, _pluginsOptions.v(newOptions, _pluginsOptions.t, false, true));
                }


                //==== Scrollbars ====//

                /**
                 * Builds all scrollbars if they aren't already build.
                 */
                function buildScrollbars() {
                    _scrollbarHorizontalElement = FRAMEWORK(generateDiv(_classNameScrollbar + _strSpace + _classNameScrollbarHorizontal));
                    _scrollbarHorizontalTrackElement = FRAMEWORK(generateDiv(_classNameScrollbarTrack));
                    _scrollbarHorizontalHandleElement = FRAMEWORK(generateDiv(_classNameScrollbarHandle));
                    _scrollbarVerticalElement = FRAMEWORK(generateDiv(_classNameScrollbar + _strSpace + _classNameScrollbarVertical));
                    _scrollbarVerticalTrackElement = FRAMEWORK(generateDiv(_classNameScrollbarTrack));
                    _scrollbarVerticalHandleElement = FRAMEWORK(generateDiv(_classNameScrollbarHandle));

                    _scrollbarHorizontalElement.append(_scrollbarHorizontalTrackElement);
                    _scrollbarHorizontalTrackElement.append(_scrollbarHorizontalHandleElement);
                    _scrollbarVerticalElement.append(_scrollbarVerticalTrackElement);
                    _scrollbarVerticalTrackElement.append(_scrollbarVerticalHandleElement);

                    _paddingElement.after(_scrollbarVerticalElement);
                    _paddingElement.after(_scrollbarHorizontalElement);

                    //scrollbar events
                    if (_supportTransition) {
                        _scrollbarHorizontalElement.on(_strTransitionEndEvent, function (event) {
                            if (event.target !== _scrollbarHorizontalElement[0])
                                return;
                            refreshScrollbarHandleLength(true);
                            refreshScrollbarHandleOffset(true, _viewportElement[_strScrollLeft]());
                        });
                        _scrollbarVerticalElement.on(_strTransitionEndEvent, function (event) {
                            if (event.target !== _scrollbarVerticalElement[0])
                                return;
                            refreshScrollbarHandleLength(false);
                            refreshScrollbarHandleOffset(false, _viewportElement[_strScrollTop]());
                        });
                    }
                    initScrollbarInteractivity(true);
                    initScrollbarInteractivity(false);
                    _scrollbarCornerElement = FRAMEWORK(generateDiv(_classNameScrollbarCorner));
                    _hostElement.append(_scrollbarCornerElement);
                }

                /**
                 * Initializes all scrollbar interactivity. (track and handle dragging, clicking, scrolling)
                 * @param isHorizontal True if the target scrollbar is the horizontal scrollbar, false if the target scrollbar is the vertical scrollbar.
                 */
                function initScrollbarInteractivity(isHorizontal) {
                    var scrollbarVars = getScrollbarVars(isHorizontal);
                    var insideIFrame = _windowElement.top !== _windowElement;
                    var mouseDownScroll;
                    var mouseDownOffset;
                    var xy = scrollbarVars.xy;
                    var XY = scrollbarVars.XY;
                    var scroll = _strScroll + scrollbarVars.LT;
                    var strActive = 'active';
                    var trackTimeout;
                    var scrollDurationFactor = 1;
                    var increaseDecreaseScrollAmountKeyCodes = [ 16, 17 ]; //shift, ctrl
                    function increaseTrackScrollAmount() {
                        scrollDurationFactor = 0.5;
                    };
                    function decreaseTrackScrollAmount() {
                        scrollDurationFactor = 1;
                    };
                    function documentKeyDown(event) {
                        if (FRAMEWORK.inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1)
                            increaseTrackScrollAmount();
                    };
                    function documentKeyUp(event) {
                        if (FRAMEWORK.inArray(event.keyCode, increaseDecreaseScrollAmountKeyCodes) > -1)
                            decreaseTrackScrollAmount();
                    };
                    function onMouseTouchDownContinue(event) {
                        var originalEvent = event.originalEvent || event;
                        var isTouchEvent = originalEvent.touches !== undefined;
                        return _isSleeping || nativeOverlayScrollbarsAreActive() || !_scrollbarsDragScrollingCache || (isTouchEvent && !_scrollbarsTouchSupport) ? false : COMPATIBILITY.mBtn(event) === 1 || isTouchEvent;
                    };
                    function handleDragMove(event) {
                        if(!onMouseTouchDownContinue(event)) {
                            documentMouseTouchUp(event);
                            return;
                        }
                            
                        var trackLength = scrollbarVars.i.tl;
                        var handleLength = scrollbarVars.i.hl;
                        var scrollRange = scrollbarVars.i.ms;
                        var scrollRaw = (_msieVersion && insideIFrame ? event['screen' + XY] : COMPATIBILITY.page(event)[xy]) - mouseDownOffset; //use screen coordinates in EDGE & IE because the page values are incorrect in frames.
                        var scrollDeltaPercent = scrollRaw / (trackLength - handleLength);
                        var scrollDelta = (scrollRange * scrollDeltaPercent);
                        scrollDelta = isFinite(scrollDelta) ? scrollDelta : 0;
                        if (_isRTL && isHorizontal && !_rtlScrollBehavior.i)
                            scrollDelta *= -1;
                        _viewportElement[scroll](mouseDownScroll + scrollDelta);

                        if (!_supportPassiveEvents)
                            COMPATIBILITY.prvD(event);
                    };
                    function documentMouseTouchUp(event) {
                        event = event || event.originalEvent;

                        removeClass(_bodyElement, _classNameDragging);
                        removeClass(scrollbarVars.h, strActive);
                        removeClass(scrollbarVars.t, strActive);
                        removeClass(scrollbarVars.s, strActive);

                        _documentElement.off(_strMouseTouchMoveEvent, handleDragMove)
                            .off(_strMouseTouchUpEvent, documentMouseTouchUp)
                            .off(_strKeyDownEvent, documentKeyDown)
                            .off(_strKeyUpEvent, documentKeyUp)
                            .off(_strSelectStartEvent, documentOnSelectStart);

                        decreaseTrackScrollAmount();
                        mouseDownScroll = undefined;
                        mouseDownOffset = undefined;
                        if (trackTimeout !== undefined) {
                            _base.scrollStop();
                            clearTimeout(trackTimeout);
                            trackTimeout = undefined;
                        }
                        
                        var rect = _hostElement[0].getBoundingClientRect();
                        var mouseInsideHost = event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;

                        //if mouse is outside host element
                        if (!mouseInsideHost)
                            hostOnMouseLeave();
                    
                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove)
                            refreshScrollbarsAutoHide(false);
                    };
                    function onHandleMouseTouchDown(event) {
                        mouseDownScroll = _viewportElement[scroll]();
                        mouseDownScroll = mouseDownScroll === undefined ? 0 : mouseDownScroll;
                        if (_isRTL && isHorizontal && !_rtlScrollBehavior.n || !_isRTL)
                            mouseDownScroll = mouseDownScroll < 0 ? 0 : mouseDownScroll;
                        mouseDownOffset = _msieVersion && insideIFrame ? event['screen' + XY] : COMPATIBILITY.page(event)[xy]; //use screen coordinates in EDGE & IE because the page values are incorrect in frames.

                        addClass(_bodyElement, _classNameDragging);
                        addClass(scrollbarVars.h, strActive);
                        addClass(scrollbarVars.s, strActive);

                        _documentElement.on(_strMouseTouchMoveEvent, handleDragMove)
                            .on(_strMouseTouchUpEvent, documentMouseTouchUp)
                            .on(_strSelectStartEvent, documentOnSelectStart);

                        if(_msieVersion || !_documentMixed)
                            COMPATIBILITY.prvD(event);
                        COMPATIBILITY.stpP(event);
                    };
                    scrollbarVars.h.on(_strMouseTouchDownEvent, function(event) {
                        if (onMouseTouchDownContinue(event))
                            onHandleMouseTouchDown(event);
                    });
                    scrollbarVars.t.on(_strMouseTouchDownEvent, function(event) {
                        if (onMouseTouchDownContinue(event)) {
                            var scrollDistance = _viewportSize[scrollbarVars._wh];
                            var trackOffset = scrollbarVars.t.offset()[scrollbarVars.lt];
                            var ctrlKey = event.ctrlKey;
                            var instantScroll = event.shiftKey;
                            var instantScrollTransition = instantScroll && ctrlKey;
                            var isFirstIteration = true;
                            var easing = 'linear';
                            var decreaseScroll;
                            var finishedCondition;
                            var scrollAction = function () {
                                if(!_destroyed) {
                                    var mouseOffset = mouseDownOffset - trackOffset;
                                    var trackLength = scrollbarVars.i.tl;
                                    var handleOffset = scrollbarVars.i.ho;
                                    var handleLength = scrollbarVars.i.hl;
                                    var scrollRange = scrollbarVars.i.ms;
                                    var scrollDuration = 270 * scrollDurationFactor;
                                    var timeoutDelay = isFirstIteration ? Math.max(400, scrollDuration) : scrollDuration;
                                    var instantScrollPosition = scrollRange * ((mouseOffset - (handleLength / 2)) / (trackLength - handleLength)); // 100% * positionPercent
                                    var rtlIsNormal = _isRTL && isHorizontal && ((!_rtlScrollBehavior.i && !_rtlScrollBehavior.n) || _normalizeRTLCache);
                                    var decreaseScrollCondition = rtlIsNormal ? handleOffset < mouseOffset : handleOffset > mouseOffset;
                                    var scrollObj = { };

                                    if(instantScroll) {
                                        instantScrollPosition = isFinite(instantScrollPosition) ? instantScrollPosition : 0;
                                        if (_isRTL && isHorizontal && !_rtlScrollBehavior.i)
                                            instantScrollPosition = (scrollRange - instantScrollPosition);
                                        if(instantScrollTransition) {
                                            scrollObj.n = false;
                                            scrollObj[xy] = instantScrollPosition;
                                            _base.scroll(scrollObj, 130, easing, function() { onHandleMouseTouchDown(event); });
                                        }
                                        else {
                                            _viewportElement[scroll](instantScrollPosition);
                                            onHandleMouseTouchDown(event);
                                        }
                                    }
                                    else {
                                        decreaseScroll = isFirstIteration ? decreaseScrollCondition : decreaseScroll;
                                        finishedCondition = rtlIsNormal ? decreaseScroll ? handleOffset + handleLength >= mouseOffset : handleOffset <= mouseOffset : decreaseScroll ? handleOffset <= mouseOffset : handleOffset + handleLength >= mouseOffset;

                                        scrollObj[xy] = decreaseScroll ? '-=' + scrollDistance : '+=' + scrollDistance;
                                        _base.scrollStop();
                                        _base.scroll(scrollObj, scrollDuration, easing);

                                        if (finishedCondition) {
                                            clearTimeout(trackTimeout);
                                            _base.scrollStop();
                                            trackTimeout = undefined;
                                        }
                                        else
                                            trackTimeout = setTimeout(scrollAction, timeoutDelay);
                                        isFirstIteration = false;
                                    }
                                }
                            };
                            if (ctrlKey)
                                increaseTrackScrollAmount();
                            mouseDownOffset = COMPATIBILITY.page(event)[xy];

                            addClass(_bodyElement, _classNameDragging);
                            addClass(scrollbarVars.t, strActive);
                            addClass(scrollbarVars.s, strActive);

                            _documentElement.on(_strMouseTouchUpEvent, documentMouseTouchUp)
                                .on(_strKeyDownEvent, documentKeyDown)
                                .on(_strKeyUpEvent, documentKeyUp)
                                .on(_strSelectStartEvent, documentOnSelectStart);

                            scrollAction();
                            COMPATIBILITY.prvD(event);
                            COMPATIBILITY.stpP(event);
                        }
                    }).on(_strMouseTouchEnter, function() { //make sure both scrollbars will stay visible if one scrollbar is hovered if autoHide is "scroll".
                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove) {
                            _scrollbarsAutoHideFlagScrollAndHovered = true;
                            refreshScrollbarsAutoHide(true);
                        }
                    }).on(_strMouseTouchLeave, function() {
                        if (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove) {
                            _scrollbarsAutoHideFlagScrollAndHovered = false;
                            refreshScrollbarsAutoHide(false);
                        }
                    });
                    scrollbarVars.s.on(_strMouseTouchDownEvent, function(event) {
                        COMPATIBILITY.stpP(event);
                    });
                }

                /**
                 * Shows or hides the given scrollbar and applied a class name which indicates if the scrollbar is scrollable or not.
                 * @param isHorizontal True if the horizontal scrollbar is the target, false if the vertical scrollbar is the target.
                 * @param shallBeVisible True if the scrollbar shall be shown, false if hidden.
                 * @param canScroll True if the scrollbar is scrollable, false otherwise.
                 */
                function refreshScrollbarAppearance(isHorizontal, shallBeVisible, canScroll) {
                    var scrollbarClassName = isHorizontal ? _classNameHostScrollbarHorizontalHidden : _classNameHostScrollbarVerticalHidden;
                    var scrollbarElement = isHorizontal ? _scrollbarHorizontalElement : _scrollbarVerticalElement;

                    if (shallBeVisible)
                        removeClass(_hostElement, scrollbarClassName);
                    else
                        addClass(_hostElement, scrollbarClassName);

                    if (canScroll)
                        removeClass(scrollbarElement, _classNameScrollbarUnusable);
                    else
                        addClass(scrollbarElement, _classNameScrollbarUnusable);
                }

                /**
                 * Autoshows / autohides both scrollbars with.
                 * @param shallBeVisible True if the scrollbars shall be autoshown (only the case if they are hidden by a autohide), false if the shall be auto hidden.
                 * @param delayfree True if the scrollbars shall be hidden without a delay, false or undefined otherwise.
                 */
                function refreshScrollbarsAutoHide(shallBeVisible, delayfree) {
                    clearTimeout(_scrollbarsAutoHideTimeoutId);
                    if (shallBeVisible) {
                        //if(_hasOverflowCache.x && _hideOverflowCache.xs)
                        removeClass(_scrollbarHorizontalElement, _classNameScrollbarAutoHidden);
                        //if(_hasOverflowCache.y && _hideOverflowCache.ys)
                        removeClass(_scrollbarVerticalElement, _classNameScrollbarAutoHidden);
                    }
                    else {
                        var strActive = 'active';
                        var hide = function () {
                            if (!_scrollbarsAutoHideFlagScrollAndHovered && !_destroyed) {
                                var anyActive = _scrollbarHorizontalHandleElement.hasClass(strActive) || _scrollbarVerticalHandleElement.hasClass(strActive);
                                if (!anyActive && (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove || _scrollbarsAutoHideLeave))
                                    addClass(_scrollbarHorizontalElement, _classNameScrollbarAutoHidden);
                                if (!anyActive && (_scrollbarsAutoHideScroll || _scrollbarsAutoHideMove || _scrollbarsAutoHideLeave))
                                    addClass(_scrollbarVerticalElement, _classNameScrollbarAutoHidden);
                            }
                        };
                        if (_scrollbarsAutoHideDelay > 0 && delayfree !== true)
                            _scrollbarsAutoHideTimeoutId = setTimeout(hide, _scrollbarsAutoHideDelay);
                        else
                            hide();
                    }
                }

                /**
                 * Refreshes the handle length of the given scrollbar.
                 * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.
                 */
                function refreshScrollbarHandleLength(isHorizontal) {
                    var handleCSS = {};
                    var scrollbarVars = getScrollbarVars(isHorizontal);
                    var digit = 1000000;
                    //get and apply intended handle length
                    var handleRatio = Math.min(1, (_hostSizeCache[scrollbarVars._wh] - (_paddingAbsoluteCache ? (isHorizontal ? _paddingX : _paddingY) : 0)) / _contentScrollSizeCache[scrollbarVars._wh]);
                    handleCSS[scrollbarVars.wh] = (Math.floor(handleRatio * 100 * digit) / digit) + "%"; //the last * digit / digit is for flooring to the 4th digit

                    if (!nativeOverlayScrollbarsAreActive())
                        scrollbarVars.h.css(handleCSS);

                    //measure the handle length to respect min & max length
                    scrollbarVars.i.hl = scrollbarVars.h[0]['offset' + scrollbarVars.WH];   //hl = handle length
                    scrollbarVars.i.hlr = handleRatio;                                      //hr = handle length ratio
                }

                /**
                 * Refreshes the handle offset of the given scrollbar.
                 * @param isHorizontal True if the horizontal scrollbar handle shall be refreshed, false if the vertical one shall be refreshed.
                 * @param currentScroll The current scroll offset of the given scrollbar axis. (if isHorizontal ? scrollLeft : scrollTop)
                 */
                function refreshScrollbarHandleOffset(isHorizontal, currentScroll) {
                    var isRTLisHorizontal = _isRTL && isHorizontal;
                    var handleCSS = {};
                    var scrollbarVars = getScrollbarVars(isHorizontal);
                    var strTranslateBrace = 'translate(';
                    var strTransform = 'transform';
                    var translateValue;

                    //measure the handle length to respect min & max length
                    //DONT use the variable '_contentScrollSizeCache[scrollbarVars._wh]' instead of '_viewportElement[0]['scroll' + scrollbarVars.WH]'
                    // because its a bit behind during the small delay when content size updates
                    //(delay = _mutationObserverContentLag, if its 0 then this var could be used)
                    var maxScroll = _viewportElement[0][_strScroll + scrollbarVars.WH] - _viewportElement[0]['client' + scrollbarVars.WH];
                    var handleLength = scrollbarVars.i.hl;
                    var trackLength = scrollbarVars.t[0]['offset' + scrollbarVars.WH];
                    var handleTrackDiff = trackLength - handleLength;
                    var offset = handleTrackDiff;
                    var transformOffset;
                    var posRatio;

                    //if rtl scroll max is negative
                    if (_rtlScrollBehavior.n && isRTLisHorizontal)
                        maxScroll *= -1;

                    posRatio = currentScroll / maxScroll;
                    posRatio = isNaN(posRatio) ? 0 : Math.min(1, posRatio);

                    scrollbarVars.i.ms = maxScroll;       //ms = max scroll
                    scrollbarVars.i.cs = currentScroll;   //cs = current scroll
                    scrollbarVars.i.csr = posRatio;       //csr = current scroll Ratio

                    offset *= posRatio;
                    offset = isNaN(offset) ? 0 : offset;
                    if (isRTLisHorizontal && !_rtlScrollBehavior.i)
                        offset = trackLength - handleLength - offset;
                    offset = Math.max(0, offset);

                    if (_supportTransform) {
                        transformOffset = isRTLisHorizontal ? -(trackLength - handleLength - offset) : offset; //in px
                        //transformOffset = (transformOffset / trackLength * 100) * (trackLength / handleLength); //in %
                        translateValue = isHorizontal ? strTranslateBrace + transformOffset + 'px, 0)' : strTranslateBrace + '0, ' + transformOffset + 'px)';
                        handleCSS['-webkit-' + strTransform] = translateValue;
                        handleCSS['-moz-' + strTransform] = translateValue;
                        handleCSS['-ms-' + strTransform] = translateValue;
                        handleCSS['-o-' + strTransform] = translateValue;
                        handleCSS[strTransform] = translateValue;
                    }
                    else
                        handleCSS[scrollbarVars.lt] = offset;

                    //only apply css if offset has changed and overflow exists.
                    if (!nativeOverlayScrollbarsAreActive())
                        scrollbarVars.h.css(handleCSS);

                    scrollbarVars.i.ho = offset;                        //ho = handle offset
                    scrollbarVars.i.tl = trackLength;                   //tl = track length
                }

                /**
                 * Refreshes the interactivity of the given scrollbar element.
                 * @param isTrack True if the track element is the target, false if the handle element is the target.
                 * @param value True for interactivity false for no interactivity.
                 */
                function refreshScrollbarsInteractive(isTrack, value) {
                    var action = value ? 'removeClass' : 'addClass';
                    var element1 = isTrack ? _scrollbarHorizontalTrackElement : _scrollbarHorizontalHandleElement;
                    var element2 = isTrack ? _scrollbarVerticalTrackElement : _scrollbarVerticalHandleElement;
                    var className = isTrack ? _classNameScrollbarTrackOff : _classNameScrollbarHandleOff;

                    element1[action](className);
                    element2[action](className);
                }

                /**
                 * Returns a object which is used for fast access for specific variables.
                 * @param isHorizontal True if the horizontal scrollbar vars shall be accessed, false if the vertical scrollbar vars shall be accessed.
                 * @returns {{wh: string, WH: string, lt: string, _wh: string, _lt: string, t: *, h: *, c: {}, s: *}}
                 */
                function getScrollbarVars(isHorizontal) {
                    return {
                        wh: isHorizontal ? _strWidth : _strHeight,
                        WH: isHorizontal ? 'Width' : 'Height',
                        lt: isHorizontal ? _strLeft : _strTop,
                        LT: isHorizontal ? 'Left' : 'Top',
                        xy: isHorizontal ? _strX : _strY,
                        XY: isHorizontal ? 'X' : 'Y',
                        _wh: isHorizontal ? 'w' : 'h',
                        _lt: isHorizontal ? 'l' : 't',
                        t: isHorizontal ? _scrollbarHorizontalTrackElement : _scrollbarVerticalTrackElement,
                        h: isHorizontal ? _scrollbarHorizontalHandleElement : _scrollbarVerticalHandleElement,
                        s: isHorizontal ? _scrollbarHorizontalElement : _scrollbarVerticalElement,
                        i: isHorizontal ? _scrollHorizontalInfo : _scrollVerticalInfo
                    };
                }


                //==== Utils ====//

                /**
                 * Calls the callback with the given name. The Context of this callback is always _base (this).
                 * @param name The name of the target which shall be called.
                 * @param args The args with which the callback shall be called.
                 */
                function callCallback(name, args) {
                    if(_initialized) {
                        var callback = _currentPreparedOptions.callbacks[name];
                        var extensionOnName = name;
                        var ext;

                        if(extensionOnName.substr(0, 2) === "on")
                            extensionOnName = extensionOnName.substr(2, 1).toLowerCase() + extensionOnName.substr(3);

                        if(type(callback) == TYPES.f)
                            callback.call(_base, args);

                        FRAMEWORK.each(_extensions, function() {
                            ext = this;
                            if(type(ext.on) == TYPES.f)
                                ext.on(extensionOnName, args);
                        });
                    }
                }

                /**
                 * Sets the "top, right, bottom, left" properties, with a given prefix, of the given css object.
                 * @param targetCSSObject The css object to which the values shall be applied.
                 * @param prefix The prefix of the "top, right, bottom, left" css properties. (example: 'padding-' is a valid prefix)
                 * @param values A array of values which shall be applied to the "top, right, bottom, left" -properties. The array order is [top, right, bottom, left].
                 * If this argument is undefined the value '' (empty string) will be applied to all properties.
                 */
                function setTopRightBottomLeft(targetCSSObject, prefix, values) {
                    if (values === undefined)
                        values = [_strEmpty, _strEmpty, _strEmpty, _strEmpty];

                    targetCSSObject[prefix + _strTop] = values[0];
                    targetCSSObject[prefix + _strRight] = values[1];
                    targetCSSObject[prefix + _strBottom] = values[2];
                    targetCSSObject[prefix + _strLeft] = values[3];
                }

                /**
                 * Checks whether the given object is a HTMLElement.
                 * @param o The object which shall be checked.
                 * @returns {boolean} True the given object is a HTMLElement, false otherwise.
                 */
                function isHTMLElement(o) {
                    var strOwnerDocument = 'ownerDocument';
                    var strHTMLElement = 'HTMLElement';
                    var wnd = o && o[strOwnerDocument] ? (o[strOwnerDocument].parentWindow || window) : window;
                    return (
                        typeof wnd[strHTMLElement] == TYPES.o ? o instanceof wnd[strHTMLElement] : //DOM2
                        o && typeof o == TYPES.o && o !== null && o.nodeType === 1 && typeof o.nodeName == TYPES.s
                    );
                }

                /**
                 * Compares 2 arrays and returns the differences between them as a array.
                 * @param a1 The first array which shall be compared.
                 * @param a2 The second array which shall be compared.
                 * @returns {Array} The differences between the two arrays.
                 */
                function getArrayDifferences(a1, a2) {
                    var a = [ ];
                    var diff = [ ];
                    var i;
                    var k;
                    for (i = 0; i < a1.length; i++)
                        a[a1[i]] = true;
                    for (i = 0; i < a2.length; i++) {
                        if (a[a2[i]])
                            delete a[a2[i]];
                        else
                            a[a2[i]] = true;
                    }
                    for (k in a)
                        diff.push(k);
                    return diff;
                }

                /**
                 * Returns Zero or the number to which the value can be parsed.
                 * @param value The value which shall be parsed.
                 * @param toFloat Indicates whether the number shall be parsed to a float.
                 */
                function parseToZeroOrNumber(value, toFloat) {
                    var num = toFloat ? window.parseFloat(value) : window.parseInt(value);
                    return isNaN(num) ? 0 : num;
                }
                

                /**
                 * Gets several information of the textarea and returns them as a object or undefined if the browser doesn't support it.
                 * @returns {{cursorRow: Number, cursorCol, rows: Number, cols: number, wRow: number, pos: number, max : number}} or undefined if not supported.
                 */
                function getTextareaInfo() {
                    //read needed values
                    var textareaCursorPosition = _targetElement[0].selectionStart;
                    if (textareaCursorPosition === undefined)
                        return;

                    var strLength = 'length';
                    var textareaValue = _targetElement.val();
                    var textareaLength = textareaValue[strLength];
                    var textareaRowSplit = textareaValue.split("\n");
                    var textareaLastRow = textareaRowSplit[strLength];
                    var textareaCurrentCursorRowSplit = textareaValue.substr(0, textareaCursorPosition).split("\n");
                    var widestRow = 0;
                    var textareaLastCol = 0;
                    var cursorRow = textareaCurrentCursorRowSplit[strLength];
                    var cursorCol = textareaCurrentCursorRowSplit[textareaCurrentCursorRowSplit[strLength] - 1][strLength];
                    var rowCols;
                    var i;

                    //get widest Row and the last column of the textarea
                    for (i = 0; i < textareaRowSplit[strLength]; i++) {
                        rowCols = textareaRowSplit[i][strLength];
                        if (rowCols > textareaLastCol) {
                            widestRow = i + 1;
                            textareaLastCol = rowCols;
                        }
                    }

                    return {
                        cR: cursorRow, //cursorRow
                        cC: cursorCol, //cursorCol
                        r: textareaLastRow, //rows
                        c: textareaLastCol, //cols
                        w: widestRow, //wRow
                        p: textareaCursorPosition, //pos
                        m: textareaLength //max
                    };
                }

                /**
                 * Checks the given key code and returns a boolean which is indicating if the given key code is a restricted one.
                 * @param keyCode The key code which shall be checked.
                 * @returns {boolean} True if the given key code is restricted, false otherwise.
                 */
                function textareaIsRestrictedKeyCode(keyCode) {
                    for (var i = 0; i < _textareaKeyDownRestrictedKeyCodes.length; i++) {
                        if (keyCode === _textareaKeyDownRestrictedKeyCodes[i])
                            return true;
                    }
                    return false;
                }

                /**
                 * Determines whether native overlay scrollbars are active.
                 * @returns {boolean} True if native overlay scrollbars are active, false otherwise.
                 */
                function nativeOverlayScrollbarsAreActive() {
                    return (_ignoreOverlayScrollbarHidingCache && (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y));
                }

                /**
                 * Gets the element which is used to measure the content size.
                 * @returns {*} TextareaCover if target element is textarea else the ContentElement.
                 */
                function getContentMeasureElement() {
                    return _isTextarea ? _textareaCoverElement[0] : _contentElement[0];
                }

                /**
                 * Generates a string which represents a HTML div with the given classes or attributes.
                 * @param classesOrAttrs The class of the div as string or a object which represents the attributes of the div. (The class attribute can also be written as "className".)
                 * @param content The content of the div as string.
                 * @returns {string} The concated string which represents a HTML div and its content.
                 */
                function generateDiv(classesOrAttrs, content) {
                    return '<div ' + (classesOrAttrs ? type(classesOrAttrs) == TYPES.s ?
                        'class="' + classesOrAttrs + '"' :
                            (function() {
                                var key;
                                var attrs = '';
                                if(FRAMEWORK.isPlainObject(classesOrAttrs)) {
                                    for (key in classesOrAttrs)
                                        attrs += (key === 'className' ? 'class' : key) + '="' + classesOrAttrs[key] + '" ';
                                }
                                return attrs;
                            })() :
                            _strEmpty) +
                        '>' +
                        (content ? content : _strEmpty) +
                        '</div>';
                }

                /**
                 * Gets the value of the given property from the given object.
                 * @param obj The object from which the property value shall be got.
                 * @param path The property of which the value shall be got.
                 * @returns {*} Returns the value of the searched property or undefined of the property wasn't found.
                 */
                function getObjectPropVal(obj, path) {
                    var splits = path.split(_strDot);
                    var i = 0;
                    var val;
                    for(; i < splits.length; i++) {
                        if(!obj.hasOwnProperty(splits[i]))
                            return;
                        val = obj[splits[i]];
                        if(i < splits.length && type(val) == TYPES.o)
                            obj = val;
                    }
                    return val;
                }

                /**
                 * Sets the value of the given property from the given object.
                 * @param obj The object from which the property value shall be set.
                 * @param path The property of which the value shall be set.
                 * @param val The value of the property which shall be set.
                 */
                function setObjectPropVal(obj, path, val) {
                    var splits = path.split(_strDot);
                    var splitsLength = splits.length;
                    var i = 0;
                    var extendObj = { };
                    var extendObjRoot = extendObj;
                    for(; i < splitsLength; i++)
                        extendObj = extendObj[splits[i]] = i + 1 < splitsLength ? { } : val;
                    FRAMEWORK.extend(obj, extendObjRoot, true);
                }

                //==== Utils Cache ====//

                /**
                 * Compares two values and returns the result of the comparison as a boolean.
                 * @param current The first value which shall be compared.
                 * @param cache The second value which shall be compared.
                 * @param force If true the returned value is always true.
                 * @returns {boolean} True if both variables aren't equal or some of them is undefined or when the force parameter is true, false otherwise.
                 */
                function checkCacheSingle(current, cache, force) {
                    if (force === true)
                        return force;
                    if (cache === undefined)
                        return true;
                    else if (current !== cache)
                        return true;
                    return false;
                }

                /**
                 * Compares two objects with two properties and returns the result of the comparison as a boolean.
                 * @param current The first object which shall be compared.
                 * @param cache The second object which shall be compared.
                 * @param prop1 The name of the first property of the objects which shall be compared.
                 * @param prop2 The name of the second property of the objects which shall be compared.
                 * @param force If true the returned value is always true.
                 * @returns {boolean} True if both variables aren't equal or some of them is undefined or when the force parameter is true, false otherwise.
                 */
                function checkCacheDouble(current, cache, prop1, prop2, force) {
                    if (force === true)
                        return force;
                    if (prop2 === undefined && force === undefined) {
                        if (prop1 === true)
                            return prop1;
                        else
                            prop1 = undefined;
                    }
                    prop1 = prop1 === undefined ? 'w' : prop1;
                    prop2 = prop2 === undefined ? 'h' : prop2;
                    if (cache === undefined)
                        return true;
                    else if (current[prop1] !== cache[prop1] || current[prop2] !== cache[prop2])
                        return true;
                    return false;
                }

                /**
                 * Compares two objects which have four properties and returns the result of the comparison as a boolean.
                 * @param current The first object with four properties.
                 * @param cache The second object with four properties.
                 * @returns {boolean} True if both objects aren't equal or some of them is undefined, false otherwise.
                 */
                function checkCacheTRBL(current, cache) {
                    if (cache === undefined)
                        return true;
                    else if (current.t !== cache.t ||
                        current.r !== cache.r ||
                        current.b !== cache.b ||
                        current.l !== cache.l)
                        return true;
                    return false;
                }


                //==== Shortcuts ====//

                /**
                 * jQuery type method shortcut.
                 */
                function type(obj) {
                    return COMPATIBILITY.type(obj);
                }

                /**
                 * jQuery extend method shortcut.
                 */
                function extend() {
                    return FRAMEWORK.extend.apply(this, arguments);
                }

                /**
                 * jQuery addClass method shortcut.
                 */
                function addClass(el, classes) {
                    return _frameworkProto.addClass.call(el, classes);
                }

                /**
                 * jQuery removeClass method shortcut.
                 */
                function removeClass(el, classes) {
                    return _frameworkProto.removeClass.call(el, classes);
                }

                /**
                 * jQuery remove method shortcut.
                 */
                function remove(el) {
                    return _frameworkProto.remove.call(el);
                }

                /**
                 * Finds the first child element with the given selector of the given element.
                 * @param el The root element from which the selector shall be valid.
                 * @param selector The selector of the searched element.
                 * @returns {*} The first element which is a child of the given element and matches the givens selector.
                 */
                function findFirst(el, selector) {
                    return _frameworkProto.find.call(el, selector).eq(0);
                }


                //==== API ====//

                /**
                 * Puts the instance to sleep. It wont respond to any changes in the DOM and won't update. Scrollbar Interactivity is also disabled as well as the resize handle.
                 * This behavior can be reset by calling the update method.
                 */
                _base.sleep = function () {
                    _isSleeping = true;
                };

                /**
                 * Updates the plugin and DOM to the current options.
                 * This method should only be called if a update is 100% required.
                 * @param force True if every property shall be updated and the cache shall be ignored.
                 * !INTERNAL USAGE! : force can be a string "auto", "auto+" or "zoom" too
                 * if this is the case then before a real update the content size and host element attributes gets checked, and if they changed only then the update method will be called.
                 */
                _base.update = function (force) {
                    var attrsChanged;
                    var contentSizeC;
                    var isString = type(force) == TYPES.s;
                    var imgElementSelector = 'img';
                    var imgElementLoadEvent = 'load';
                    if(isString) {
                        if (force.indexOf(_strAuto) === 0) {
                            attrsChanged = meaningfulAttrsChanged();
                            contentSizeC = updateAutoContentSizeChanged();
                            if (attrsChanged || contentSizeC)
                                update(false, contentSizeC, false, force.slice(-1) == "+");
                        }
                        else if (force === 'zoom')
                            update(true, true);
                    }
                    else {
                        force = _isSleeping || force;
                        _isSleeping = false;
                        update(false, false, force, true);
                    }
                    if(!_isTextarea) {
                        _contentElement.find(imgElementSelector).each(function(i, el) {
                            var index = COMPATIBILITY.inA(el, _imgs);
                            if (index === -1)
                                FRAMEWORK(el).off(imgElementLoadEvent, imgOnLoad).on(imgElementLoadEvent, imgOnLoad);
                        });
                    }
                };

                /**
                 Gets or sets the current options. The update method will be called automatically if new options were set.
                 * @param newOptions If new options are given, then the new options will be set, if new options aren't given (undefined or a not a plain object) then the current options will be returned.
                 * @param value If new options is a property path string, then this value will be used to set the option to which the property path string leads.
                 * @returns {*}
                 */
                _base.options = function (newOptions, value) {
                    //return current options if newOptions are undefined or empty
                    if (FRAMEWORK.isEmptyObject(newOptions) || !FRAMEWORK.isPlainObject(newOptions)) {
                        if (type(newOptions) == TYPES.s) {
                            if (arguments.length >= 2) {
                                var option = { };
                                setObjectPropVal(option, newOptions, value);
                                setOptions(option);
                                update();
                                return;
                            }
                            else
                                return getObjectPropVal(_currentOptions, newOptions);
                        }
                        else
                            return _currentOptions;
                    }
                    setOptions(newOptions);
                    var isSleepingTmp = _isSleeping || false;
                    _isSleeping = false;
                    update();
                    _isSleeping = isSleepingTmp;
                };

                /**
                 * Restore the DOM, disconnects all observers, remove all resize observers and destroy all methods.
                 */
                _base.destroy = function () {
                    _destroyed = true;

                    autoUpdateLoop.remove(_base);
                    mutationObserversDisconnect();
                    removeResizeObserver(_sizeObserverElement);
                    if (_sizeAutoObserverAdded)
                        removeResizeObserver(_sizeAutoObserverElement);

                    for(var extName in _extensions)
                        _base.removeExt(extName);

                    remove(_sizeObserverElement);
                    if (_contentGlueElement !== undefined)
                        remove(_contentGlueElement);
                    if (_contentArrangeElement !== undefined)
                        remove(_contentArrangeElement);
                    if (_sizeAutoObserverAdded)
                        remove(_sizeAutoObserverElement);

                    if (_supportPassiveEvents) {
                        removePassiveEventListener(_hostElement, _strMouseTouchMoveEvent, hostOnMouseMove);
                        removePassiveEventListener(_hostElement, _strMouseTouchEnter, hostOnMouseEnter);
                        removePassiveEventListener(_hostElement, _strMouseTouchLeave, hostOnMouseLeave);
                    }
                    else {
                        _hostElement.off(_strMouseTouchMoveEvent, hostOnMouseMove)
                            .off(_strMouseTouchEnter, hostOnMouseEnter)
                            .off(_strMouseTouchLeave, hostOnMouseLeave);
                    }

                    remove(_scrollbarHorizontalElement);
                    remove(_scrollbarVerticalElement);
                    if(_scrollbarCornerElement)
                        remove(_scrollbarCornerElement);
                    if (!_resizeNone)
                        scrollbarCornerOnResized();

                    _contentElement.contents()
                        .unwrap()
                        .unwrap()
                        .unwrap();

                    if (_isBody)
                        removeClass(_htmlElement, _classNameHTMLElement);


                    if (_isTextarea) {
                        _targetElement.off(_strScroll, textareaOnScroll)
                            .off('drop', textareaOnDrop)
                            .off('focus', textareaOnFocus)
                            .off('focusout', textareaOnFocusOut);
                        if (_msieVersion > 9 || !_autoUpdateRecommended)
                            _targetElement.off('input', textareaOnInput);
                        else {
                            _targetElement.off(_strKeyDownEvent, textareaOnKeyDown)
                                .off(_strKeyUpEvent, textareaOnKeyUp);
                        }

                        remove(_textareaCoverElement);
                        removeClass(_targetElement, _classNameTextareaElement + _strSpace + _classNameTextInherit)
                            .unwrap()
                            .removeAttr(LEXICON.s);
                        remove(_hostElement);
                    }
                    else {
                        removeClass(_targetElement, _classNameHostElement);
                        removeClass(_hostElement, [
                            _classNameHostElement,
                            _classNameHostResizeDisabled,
                            _classNameHostRTL,
                            _classNameHostScrollbarHorizontalHidden,
                            _classNameHostScrollbarVerticalHidden,
                            _classNameHostTransition,
                            _classNameHostScrolling,
                            _classNameHostOverflow,
                            _classNameHostOverflowX,
                            _classNameHostOverflowY,
                            _classNameThemeNone,
                            _classNameCache].join(_strSpace));
                    }

                    for(var i = 0; i < _imgs.length; i++)
                        FRAMEWORK(_imgs[i]).off('load', imgOnLoad);
                    _imgs = undefined;

                    INSTANCES(pluginTargetElement, 0);
                    callCallback("onDestroyed");

                    for (var property in _base)
                        delete _base[property];
                    _base = undefined;
                };

                /**
                 * Scrolls to a given position or element.
                 * @param coordinates
                 * 1. Can be "coordinates" which looks like:
                 *    { x : ?, y : ? } OR          Object with x and y properties
                 *    { left : ?, top : ? } OR     Object with left and top properties
                 *    { l : ?, t : ? } OR          Object with l and t properties
                 *    [ ?, ? ] OR                  Array where the first two element are the coordinates (first is x, second is y)
                 *    ?                            A single value which stays for both axis
                 *    A value can be a number, a string or a calculation.
                 *
                 *    Operators:
                 *    [NONE]  The current scroll will be overwritten by the value.
                 *    '+='    The value will be added to the current scroll offset
                 *    '-='    The value will be subtracted from the current scroll offset
                 *    '*='    The current scroll wil be multiplicated by the value.
                 *    '/='    The current scroll wil be divided by the value.
                 *
                 *    Units:
                 *    [NONE]  The value is the final scroll amount.                   final = (value * 1)
                 *    'px'    Same as none
                 *    '%'     The value is dependent on the current scroll value.     final = ((currentScrollValue / 100) * value)
                 *    'vw'    The value is multiplicated by the viewport width.       final = (value * viewportWidth)
                 *    'vh'    The value is multiplicated by the viewport height.      final = (value * viewportHeight)
                 *
                 *    example final values:
                 *    200, '200px', '50%', '1vw', '1vh', '+=200', '/=1vw', '*=2px', '-=5vh', '+=33%', '+= 50% - 2px', '-= 1vw - 50%'
                 *
                 * 2. Can be a HTML or jQuery element:
                 *    The final scroll offset is the offset (without margin) of the given HTML / jQuery element.
                 *
                 * 3. Can be a object with a HTML or jQuery element with additional settings:
                 *    {
                 *      el : [HTMLElement, jQuery element],            MUST be specified, else this object isn't valid.
                 *      scroll : [string, array, object],              Default value is 'always'.
                 *      block : [string, array, object],               Default value is 'begin'.
                 *      margin : [number, boolean array, object]       Default value is false.
                 *    }
                 *
                 *    Possible scroll settings are:
                 *    'always'      Scrolls always.
                 *    'ifneeded'    Scrolls only if the element isnt fully in view.
                 *    'never'       Scrolls never.
                 *
                 *    Possible block settings are:
                 *    'begin'   Both axis shall be docked to the "begin" edge. - The element will be docked to the top and left edge of the viewport.
                 *    'end'     Both axis shall be docked to the "end" edge. - The element will be docked to the bottom and right edge of the viewport. (If direction is RTL to the bottom and left edge.)
                 *    'center'  Both axis shall be docked to "center". - The element will be centered in the viewport.
                 *    'nearest' The element will be docked to the nearest edge(s).
                 *
                 *    Possible margin settings are: -- The actual margin of the element wont be affect, this option affects only the final scroll offset.
                 *    [BOOLEAN]                                         If true the css margin of the element will be used, if false no margin will be used.
                 *    [NUMBER]                                          The margin will be used for all edges.
                 *
                 * @param duration The duration of the scroll animation, OR a jQuery animation configuration object.
                 * @param easing The animation easing.
                 * @param complete The animation complete callback.
                 * @returns
                 * {
                 *    x: {position: *, ratio: (number|*), max: (number|*), handleOffset: (number|*), handleLength: *, handleLengthRatio: (number|*), trackLength: *, isRTL: *, isRTLNormalized: *},
                 *    y: {position: *, ratio: (number|*), max: (number|*), handleOffset: (number|*), handleLength: *, handleLengthRatio: (number|*), trackLength: *}
                 * }
                 */
                _base.scroll = function (coordinates, duration, easing, complete) {
                    if (arguments.length === 0 || coordinates === undefined) {
                        var infoX = _scrollHorizontalInfo;
                        var infoY = _scrollVerticalInfo;
                        var normalizeInvert = _normalizeRTLCache && _isRTL && _rtlScrollBehavior.i;
                        var normalizeNegate = _normalizeRTLCache && _isRTL && _rtlScrollBehavior.n;
                        var scrollX = infoX.cs;
                        var scrollXRatio = infoX.csr;
                        var maxScrollX = infoX.ms;
                        scrollXRatio = normalizeInvert ? 1 - scrollXRatio : scrollXRatio;
                        scrollX = normalizeInvert ? maxScrollX - scrollX : scrollX;
                        scrollX *= normalizeNegate ? -1 : 1;
                        maxScrollX *= normalizeNegate ? -1 : 1;
                        return {
                            x: {
                                position: scrollX,
                                ratio: scrollXRatio,
                                max: maxScrollX,
                                handleOffset: infoX.ho,
                                handleLength: infoX.hl,
                                handleLengthRatio: infoX.hlr,
                                trackLength: infoX.tl,
                                isRTL: _isRTL,
                                isRTLNormalized: _normalizeRTLCache
                            },
                            y: {
                                position: infoY.cs,
                                ratio: infoY.csr,
                                max: infoY.ms,
                                handleOffset: infoY.ho,
                                handleLength: infoY.hl,
                                handleLengthRatio: infoY.hlr,
                                trackLength: infoY.tl
                            }
                        };
                    }

                    var normalizeRTL = _normalizeRTLCache;
                    var coordinatesXAxisProps = [_strX, _strLeft, 'l'];
                    var coordinatesYAxisProps = [_strY, _strTop, 't'];
                    var coordinatesOperators = ['+=', '-=', '*=', '/='];
                    var i;
                    var finalScroll = { };
                    var doScrollLeft;
                    var doScrollTop;
                    var animationOptions;
                    var durationIsObject = type(duration) == TYPES.o;
                    var strEnd = 'end';
                    var strBegin = 'begin';
                    var strCenter = 'center';
                    var strNearest = 'nearest';
                    var strAlways = 'always';
                    var strNever = 'never';
                    var strIfNeeded = 'ifneeded';
                    var strLength = LEXICON.l;
                    var settingsAxis;
                    var settingsScroll;
                    var settingsBlock;
                    var settingsMargin;
                    var finalElement;
                    var elementObjSettingsAxisValues = [_strX, _strY, 'xy', 'yx'];
                    var elementObjSettingsBlockValues = [strBegin, strEnd, strCenter, strNearest];
                    var elementObjSettingsScrollValues = [strAlways, strNever, strIfNeeded];
                    var coordinatesIsElementObj = coordinates.hasOwnProperty('el');
                    var possibleElement = coordinatesIsElementObj ? coordinates.el : coordinates;
                    var possibleElementIsJQuery = possibleElement instanceof FRAMEWORK || JQUERY ? possibleElement instanceof JQUERY : false;
                    var possibleElementIsHTMLElement = possibleElementIsJQuery ? false : isHTMLElement(possibleElement);
                    var checkSettingsStringValue = function (currValue, allowedValues) {
                        for (i = 0; i < allowedValues[strLength]; i++) {
                            if (currValue === allowedValues[i])
                                return true;
                        }
                        return false;
                    };
                    var getRawScroll = function (coordinates) {
                        var rawScroll = {};
                        if (type(coordinates) == TYPES.a && coordinates[strLength] > 0) {
                            rawScroll.x = coordinates[0];
                            rawScroll.y = coordinates[1];
                        }
                        else if (type(coordinates) == TYPES.s || type(coordinates) == TYPES.n) {
                            rawScroll.x = coordinates;
                            rawScroll.y = coordinates;
                        }
                        else if (type(coordinates) == TYPES.o) {
                            normalizeRTL = type(coordinates.n) == TYPES.b ? coordinates.n : normalizeRTL;
                            coordinates = extend({}, coordinates);
                            i = 0;
                            for (var key in coordinates) {
                                if (coordinates.hasOwnProperty(key)) {
                                    if (i > 2)
                                        delete coordinates[key];
                                    i++;
                                }
                            }
                            var getRawScrollValue = function (isX) {
                                var coordinateProps = isX ? coordinatesXAxisProps : coordinatesYAxisProps;
                                for (i = 0; i < coordinateProps[strLength]; i++) {
                                    if (coordinateProps[i] in coordinates) {
                                        return coordinates[coordinateProps[i]];
                                    }
                                }
                            };
                            rawScroll.x = getRawScrollValue(true);
                            rawScroll.y = getRawScrollValue(false);
                        }
                        return rawScroll;
                    };
                    var getFinalScroll = function (isX, rawScroll) {
                        var isString = type(rawScroll) == TYPES.s;
                        if(isString)
                            _base.update(_strAuto + "+");
                        var operator;
                        var amount;
                        var scrollInfo = isX ? _scrollHorizontalInfo : _scrollVerticalInfo;
                        var currScroll = scrollInfo.cs;
                        var maxScroll = scrollInfo.ms;
                        var mult = ' * ';
                        var finalValue;
                        var isRTLisX = _isRTL && isX;
                        var normalizeShortcuts = isRTLisX && _rtlScrollBehavior.n && !normalizeRTL;
                        var strReplace = 'replace';
                        if (isString) {
                            //check operator
                            if (rawScroll[strLength] > 2) {
                                var possibleOperator = rawScroll.substr(0, 2);
                                for (i = 0; i < coordinatesOperators[strLength]; i++) {
                                    if (possibleOperator === coordinatesOperators[i]) {
                                        operator = coordinatesOperators[i];
                                        break;
                                    }
                                }
                            }

                            //calculate units and shortcuts
                            rawScroll = operator !== undefined ? rawScroll.substr(2) : rawScroll;
                            rawScroll = rawScroll[strReplace](/min/g, 0); //'min' = 0%
                            rawScroll = rawScroll[strReplace](/</g, 0);   //'<'   = 0%
                            rawScroll = rawScroll[strReplace](/max/g, (normalizeShortcuts ? '-' : _strEmpty) + _strHundredPercent);    //'max' = 100%
                            rawScroll = rawScroll[strReplace](/>/g, (normalizeShortcuts ? '-' : _strEmpty) + _strHundredPercent);      //'>'   = 100%
                            rawScroll = rawScroll[strReplace](/px/g, _strEmpty);
                            rawScroll = rawScroll[strReplace](/%/g, mult + (maxScroll * (isRTLisX && _rtlScrollBehavior.n ? -1 : 1) / 100.0));
                            rawScroll = rawScroll[strReplace](/vw/g, mult + _viewportSize.w);
                            rawScroll = rawScroll[strReplace](/vh/g, mult + _viewportSize.h);
                            amount = parseToZeroOrNumber(parseToZeroOrNumber(window.eval(rawScroll), true).toFixed());
                        }
                        else {
                            amount = rawScroll;
                        }

                        if (amount !== undefined && !isNaN(amount) && type(amount) == TYPES.n) {
                            var normalizeIsRTLisX = normalizeRTL && isRTLisX;
                            var operatorCurrScroll = currScroll * (normalizeIsRTLisX && _rtlScrollBehavior.n ? -1 : 1);
                            var invert = normalizeIsRTLisX && _rtlScrollBehavior.i;
                            var negate = normalizeIsRTLisX && _rtlScrollBehavior.n;
                            operatorCurrScroll = invert ? (maxScroll - operatorCurrScroll) : operatorCurrScroll;
                            switch (operator) {
                                case '+=':
                                    finalValue = operatorCurrScroll + amount;
                                    break;
                                case '-=':
                                    finalValue = operatorCurrScroll - amount;
                                    break;
                                case '*=':
                                    finalValue = operatorCurrScroll * amount;
                                    break;
                                case '/=':
                                    finalValue = operatorCurrScroll / amount;
                                    break;
                                default:
                                    finalValue = amount;
                                    break;
                            }
                            finalValue = invert ? maxScroll - finalValue : finalValue;
                            finalValue *= negate ? -1 : 1;
                            finalValue = isRTLisX && _rtlScrollBehavior.n ? Math.min(0, Math.max(maxScroll, finalValue)) : Math.max(0, Math.min(maxScroll, finalValue));
                        }
                        return finalValue === currScroll ? undefined : finalValue;
                    };
                    var getPerAxisValue = function (value, valueInternalType, defaultValue, allowedValues) {
                        var resultDefault = [ defaultValue, defaultValue ];
                        var valueType = type(value);
                        var valueArrLength;
                        var valueArrItem;

                        //value can be [ string, or array of two strings ]
                        if (valueType == valueInternalType) {
                            value = [value, value];
                        }
                        else if (valueType == TYPES.a) {
                            valueArrLength = value[strLength];
                            if (valueArrLength > 2 || valueArrLength < 1)
                                value = resultDefault;
                            else {
                                if (valueArrLength === 1)
                                    value[1] = defaultValue;
                                for (i = 0; i < valueArrLength; i++) {
                                    valueArrItem = value[i];
                                    if (type(valueArrItem) != valueInternalType || !checkSettingsStringValue(valueArrItem, allowedValues)) {
                                        value = resultDefault;
                                        break;
                                    }
                                }
                            }
                        }
                        else if (valueType == TYPES.o)
                            value = [ value[_strX]|| defaultValue, value[_strY] || defaultValue];
                        else
                            value = resultDefault;
                        return { x : value[0], y : value[1] };
                    };
                    var generateMargin = function (marginTopRightBottomLeftArray) {
                        var result = [ ];
                        var currValue;
                        var currValueType;
                        var valueDirections = [ _strTop, _strRight, _strBottom, _strLeft ];
                        for(i = 0; i < marginTopRightBottomLeftArray[strLength]; i++) {
                            if(i === valueDirections[strLength])
                                break;
                            currValue = marginTopRightBottomLeftArray[i];
                            currValueType = type(currValue);
                            if(currValueType == TYPES.b)
                                result.push(currValue ? parseToZeroOrNumber(finalElement.css(_strMarginMinus + valueDirections[i])) : 0);
                            else
                                result.push(currValueType == TYPES.n ? currValue : 0);
                        }
                        return result;
                    };
    
                    if (possibleElementIsJQuery || possibleElementIsHTMLElement) {
                        //get settings
                        var margin = coordinatesIsElementObj ? coordinates.margin : 0;
                        var axis = coordinatesIsElementObj ? coordinates.axis : 0;
                        var scroll = coordinatesIsElementObj ? coordinates.scroll : 0;
                        var block = coordinatesIsElementObj ? coordinates.block : 0;
                        var marginDefault = [ 0, 0, 0, 0 ];
                        var marginType = type(margin);
                        var marginLength;
                        finalElement = possibleElementIsJQuery ? possibleElement : FRAMEWORK(possibleElement);
                        if (finalElement[strLength] === 0)
                            return;

                        _base.update(_strAuto + "+");
                        
                        //margin can be [ boolean, number, array of 2, array of 4, object ]
                        if (marginType == TYPES.n || marginType == TYPES.b)
                            margin = generateMargin([margin, margin, margin, margin]);
                        else if (marginType == TYPES.a) {
                            marginLength = margin[strLength];
                            if(marginLength === 2)
                                margin = generateMargin([margin[0], margin[1], margin[0], margin[1]]);
                            else if(marginLength >= 4)
                                margin = generateMargin(margin);
                            else
                                margin = marginDefault;
                        }
                        else if (marginType == TYPES.o)
                            margin = generateMargin([margin[_strTop], margin[_strRight], margin[_strBottom], margin[_strLeft]]);
                        else
                            margin = marginDefault;

                        //block = type(block) === TYPES.b ? block ? [ strNearest, strBegin ] : [ strNearest, strEnd ] : block;
                        settingsAxis = checkSettingsStringValue(axis, elementObjSettingsAxisValues) ? axis : 'xy';
                        settingsScroll = getPerAxisValue(scroll, TYPES.s, strAlways, elementObjSettingsScrollValues);
                        settingsBlock = getPerAxisValue(block, TYPES.s, strBegin, elementObjSettingsBlockValues);
                        settingsMargin = margin;

                        var viewportScroll = {
                            l: _scrollHorizontalInfo.cs,
                            t: _scrollVerticalInfo.cs
                        };
                        // use padding element instead of viewport element because padding element has never padding, margin or position applied.
                        var viewportOffset = _paddingElement.offset(); 
                        
                        //get coordinates
                        var elementOffset = finalElement.offset();
                        var doNotScroll = {
                            x : settingsScroll.x == strNever || settingsAxis == _strY,
                            y : settingsScroll.y == strNever || settingsAxis == _strX
                        };
                        elementOffset[_strTop] -= settingsMargin[0];
                        elementOffset[_strLeft] -= settingsMargin[3];
                        var elementScrollCoordinates = {
                            x: Math.round(elementOffset[_strLeft] - viewportOffset[_strLeft] + viewportScroll.l),
                            y: Math.round(elementOffset[_strTop] - viewportOffset[_strTop] + viewportScroll.t)
                        };
                        if (_isRTL) {
                            if (!_rtlScrollBehavior.n && !_rtlScrollBehavior.i)
                                elementScrollCoordinates.x = Math.round(viewportOffset[_strLeft] - elementOffset[_strLeft] + viewportScroll.l);
                            if (_rtlScrollBehavior.n && normalizeRTL)
                                elementScrollCoordinates.x *= -1;
                            if (_rtlScrollBehavior.i && normalizeRTL)
                                elementScrollCoordinates.x = Math.round(viewportOffset[_strLeft] - elementOffset[_strLeft] + (_scrollHorizontalInfo.ms - viewportScroll.l));
                        }

                        //measuring is required
                        if (settingsBlock.x != strBegin || settingsBlock.y != strBegin || settingsScroll.x == strIfNeeded || settingsScroll.y == strIfNeeded || _isRTL) {
                            var measuringElm = finalElement[0];
                            var rawElementSize = _supportTransform ? measuringElm.getBoundingClientRect() : { 
                                width : measuringElm[LEXICON.oW],
                                height : measuringElm[LEXICON.oH]
                            };
                            var elementSize = {
                                w: rawElementSize[_strWidth] + settingsMargin[3] + settingsMargin[1],
                                h: rawElementSize[_strHeight] + settingsMargin[0] + settingsMargin[2]
                            };
                            var finalizeBlock = function(isX) {
                                var vars = getScrollbarVars(isX);
                                var wh = vars._wh;
                                var lt = vars.lt;
                                var xy = vars.xy;
                                var blockIsEnd = settingsBlock[xy] == (isX ? _isRTL ? strBegin : strEnd : strEnd);
                                var blockIsCenter = settingsBlock[xy] == strCenter;
                                var blockIsNearest = settingsBlock[xy] == strNearest;
                                var scrollNever = settingsScroll[xy] == strNever;
                                var scrollIfNeeded = settingsScroll[xy] == strIfNeeded;
                                var vpSize = _viewportSize[wh];
                                var vpOffset = viewportOffset[lt];
                                var elSize = elementSize[wh];
                                var elOffset = elementOffset[lt];
                                var divide = blockIsCenter ? 2 : 1;
                                var elementCenterOffset = elOffset + (elSize / 2);
                                var viewportCenterOffset = vpOffset + (vpSize / 2);
                                var isInView =
                                    elSize <= vpSize
                                    && elOffset >= vpOffset
                                    && elOffset + elSize <= vpOffset + vpSize;

                                if(scrollNever)
                                    doNotScroll[xy] = true;
                                else if(!doNotScroll[xy]) {
                                    if (blockIsNearest || scrollIfNeeded) {
                                        doNotScroll[xy] = scrollIfNeeded ? isInView : false;
                                        blockIsEnd = elSize < vpSize ? elementCenterOffset > viewportCenterOffset : elementCenterOffset < viewportCenterOffset;
                                    }
                                    elementScrollCoordinates[xy] -= blockIsEnd || blockIsCenter ? ((vpSize / divide) - (elSize / divide)) * (isX && _isRTL && normalizeRTL ? -1 : 1) : 0;
                                }
                            };
                            finalizeBlock(true);
                            finalizeBlock(false);
                        }

                        if (doNotScroll.y)
                            delete elementScrollCoordinates.y;
                        if (doNotScroll.x)
                            delete elementScrollCoordinates.x;

                        coordinates = elementScrollCoordinates;
                    }
                    
                    finalScroll[_strScrollLeft] = getFinalScroll(true, getRawScroll(coordinates).x);
                    finalScroll[_strScrollTop] = getFinalScroll(false, getRawScroll(coordinates).y);
                    doScrollLeft = finalScroll[_strScrollLeft] !== undefined;
                    doScrollTop = finalScroll[_strScrollTop] !== undefined;

                    if ((doScrollLeft || doScrollTop) && (duration > 0 || durationIsObject)) {
                        if (durationIsObject)
                            _viewportElement.animate(finalScroll, duration);
                        else {
                            animationOptions = {
                                duration: duration,
                                complete: complete
                            };
                            if (type(easing) == TYPES.a) {
                                var specialEasing = {};
                                specialEasing[_strScrollLeft] = easing[0];
                                specialEasing[_strScrollTop] = easing[1];
                                animationOptions.specialEasing = specialEasing;
                            }
                            else {
                                animationOptions.easing = easing;
                            }
                            _viewportElement.animate(finalScroll, animationOptions);
                        }
                    }
                    else {
                        if (doScrollLeft)
                            _viewportElement[_strScrollLeft](finalScroll[_strScrollLeft]);
                        if (doScrollTop)
                            _viewportElement[_strScrollTop](finalScroll[_strScrollTop]);
                    }
                };

                /**
                 * Stops all scroll animations.
                 * @returns {*} The current OverlayScrollbars instance (for chaining).
                 */
                _base.scrollStop = function (param1, param2, param3) {
                    _viewportElement.stop(param1, param2, param3);
                    return _base;
                };

                /**
                 * Returns all relevant elements.
                 * @param elementName The name of the element which shall be returned.
                 * @returns {{target: *, host: *, padding: *, viewport: *, content: *, scrollbarHorizontal: {scrollbar: *, track: *, handle: *}, scrollbarVertical: {scrollbar: *, track: *, handle: *}, scrollbarCorner: *} | *}
                 */
                _base.getElements = function (elementName) {
                    var obj = {
                        target: _targetElement[0],
                        host: _hostElement[0],
                        padding: _paddingElement[0],
                        viewport: _viewportElement[0],
                        content: _contentElement[0],
                        scrollbarHorizontal: {
                            scrollbar: _scrollbarHorizontalElement[0],
                            track: _scrollbarHorizontalTrackElement[0],
                            handle: _scrollbarHorizontalHandleElement[0]
                        },
                        scrollbarVertical: {
                            scrollbar: _scrollbarVerticalElement[0],
                            track: _scrollbarVerticalTrackElement[0],
                            handle: _scrollbarVerticalHandleElement[0]
                        },
                        scrollbarCorner: _scrollbarCornerElement[0]
                    };
                    return type(elementName) == TYPES.s ? getObjectPropVal(obj, elementName) : obj;
                };

                /**
                 * Returns a object which describes the current state of this instance.
                 * @param stateProperty A specific property from the state object which shall be returned.
                 * @returns {{widthAuto, heightAuto, overflowAmount, hideOverflow, hasOverflow, contentScrollSize, viewportSize, hostSize, autoUpdate} | *}
                 */
                _base.getState = function (stateProperty) {
                    var prepare = function (obj) {
                        if (!FRAMEWORK.isPlainObject(obj))
                            return obj;
                        var extended = extend(true, {}, obj);
                        var changePropertyName = function (from, to) {
                            if (extended.hasOwnProperty(from)) {
                                extended[to] = extended[from];
                                delete extended[from];
                            }
                        };
                        changePropertyName('w', _strWidth); //change w to width
                        changePropertyName('h', _strHeight); //change h to height
                        delete extended.c; //delete c (the 'changed' prop)
                        return extended;
                    };
                    var obj = {
                        sleeping: prepare(_isSleeping) || false,
                        autoUpdate: prepare(!_mutationObserverConnected),
                        widthAuto: prepare(_widthAutoCache),
                        heightAuto: prepare(_heightAutoCache),
                        padding: prepare(_cssPaddingCache),
                        overflowAmount: prepare(_overflowAmountCache),
                        hideOverflow: prepare(_hideOverflowCache),
                        hasOverflow: prepare(_hasOverflowCache),
                        contentScrollSize: prepare(_contentScrollSizeCache),
                        viewportSize: prepare(_viewportSize),
                        hostSize: prepare(_hostSizeCache),
                        documentMixed : prepare(_documentMixed)
                    };
                    return type(stateProperty) == TYPES.s ? getObjectPropVal(obj, stateProperty) : obj;
                };

                /**
                 * Gets all or specific extension instance.
                 * @param extName The name of the extension from which the instance shall be got.
                 * @returns {{}} The instance of the extension with the given name or undefined if the instance couldn't be found.
                 */
                _base.ext = function(extName) {
                    var result;
                    var privateMethods = _extensionsPrivateMethods.split(' ');
                    var i = 0;
                    if(type(extName) == TYPES.s) {
                        if(_extensions.hasOwnProperty(extName)) {
                            result = extend(true, {}, _extensions[extName]);
                            for (; i < privateMethods.length; i++)
                                delete result[privateMethods[i]];
                        }
                    }
                    else {
                        result = { };
                        for(i in _extensions)
                            result[i] = extend(true, { }, _base.ext(i));
                    }
                    return result;
                };

                /**
                 * Adds a extension to this instance.
                 * @param extName The name of the extension which shall be added.
                 * @param extensionOptions The extension options which shall be used.
                 * @returns {{}} The instance of the added extension or undefined if the extension couldn't be added properly.
                 */
                _base.addExt = function(extName, extensionOptions) {
                    var registeredExtensionObj = window[PLUGINNAME].extension(extName);
                    var instance;
                    var instanceAdded;
                    var instanceContract;
                    var contractResult;
                    var contractFulfilled = true;
                    if(registeredExtensionObj) {
                        if(!_extensions.hasOwnProperty(extName)) {
                            instance = registeredExtensionObj.extension.call(_base,
                                extend(true, { }, registeredExtensionObj.defaultOptions),
                                FRAMEWORK,
                                COMPATIBILITY);

                            if (instance) {
                                instanceContract = instance.contract;
                                if (type(instanceContract) == TYPES.f) {
                                    contractResult = instanceContract(window);
                                    contractFulfilled = type(contractResult) == TYPES.b ? contractResult : contractFulfilled;
                                }
                                if(contractFulfilled) {
                                    _extensions[extName] = instance;
                                    instanceAdded = instance.added;
                                    if(type(instanceAdded) == TYPES.f)
                                        instanceAdded(extensionOptions);

                                    return _base.ext(extName);
                                }
                            }
                        }
                        else
                            return _base.ext(extName);
                    }
                    else
                        console.warn("A extension with the name \"" + extName + "\" isn't registered.");
                };

                /**
                 * Removes a extension from this instance.
                 * @param extName The name of the extension which shall be removed.
                 * @returns {boolean} True if the extension was removed, false otherwise e.g. if the extension wasn't added before.
                 */
                _base.removeExt = function(extName) {
                    var instance = _extensions[extName];
                    var instanceRemoved;
                    if(instance) {
                        delete _extensions[extName];

                        instanceRemoved = instance.removed;
                        if(type(instanceRemoved) == TYPES.f)
                            instanceRemoved();

                        return true;
                    }
                    return false;
                };

                /**
                 * Constructs the plugin.
                 * @param targetElement The element to which the plugin shall be applied.
                 * @param options The initial options of the plugin.
                 * @param extensions The extension(s) which shall be added right after the initialization.
                 * @returns {boolean} True if the plugin was successfully initialized, false otherwise.
                 */
                function construct(targetElement, options, extensions) {
                    _defaultOptions = globals.defaultOptions;
                    _nativeScrollbarStyling = globals.nativeScrollbarStyling;
                    _nativeScrollbarSize = extend(true, {}, globals.nativeScrollbarSize);
                    _nativeScrollbarIsOverlaid = extend(true, {}, globals.nativeScrollbarIsOverlaid);
                    _overlayScrollbarDummySize = extend(true, {}, globals.overlayScrollbarDummySize);
                    _rtlScrollBehavior = extend(true, {}, globals.rtlScrollBehavior);

                    //parse & set options but don't update
                    setOptions(extend(true, { }, _defaultOptions, options));

                    //check if the plugin hasn't to be initialized
                    if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.x && !_currentPreparedOptions.nativeScrollbarsOverlaid.initialize) {
                        callCallback("onInitializationWithdrawn");
                        return false;
                    }

                    _cssCalc = globals.cssCalc;
                    _msieVersion = globals.msie;
                    _autoUpdateRecommended = globals.autoUpdateRecommended;
                    _supportTransition = globals.supportTransition;
                    _supportTransform = globals.supportTransform;
                    _supportPassiveEvents = globals.supportPassiveEvents;
                    _supportResizeObserver = globals.supportResizeObserver;
                    _supportMutationObserver = globals.supportMutationObserver;
                    _restrictedMeasuring = globals.restrictedMeasuring;
                    _documentElement = FRAMEWORK(targetElement.ownerDocument);
                    _windowElement = FRAMEWORK(_documentElement[0].defaultView || _documentElement[0].parentWindow);
                    _htmlElement = findFirst(_documentElement, 'html');
                    _bodyElement =  findFirst(_htmlElement, 'body');
                    _targetElement = FRAMEWORK(targetElement);
                    _isTextarea = _targetElement.is('textarea');
                    _isBody = _targetElement.is('body');
                    _documentMixed = _documentElement[0] !== document;

                    var initBodyScroll;
                    if (_isBody) {
                        initBodyScroll = {};
                        initBodyScroll.l = Math.max(_targetElement[_strScrollLeft](), _htmlElement[_strScrollLeft](), _windowElement[_strScrollLeft]());
                        initBodyScroll.t = Math.max(_targetElement[_strScrollTop](), _htmlElement[_strScrollTop](), _windowElement[_strScrollTop]());
                    }

                    //build Hide-scrollbars DOM
                    if (_isTextarea) {
                        _targetElement.wrap(generateDiv(_classNameHostTextareaElement));
                        addClass(_targetElement, _classNameTextareaElement + _strSpace + _classNameTextInherit);
                        _hostElement = _targetElement.parent();
                        var hostElementCSS = {};
                        if (!_currentPreparedOptions.sizeAutoCapable) {
                            hostElementCSS[_strWidth] = _targetElement.css(_strWidth);
                            hostElementCSS[_strHeight] = _targetElement.css(_strHeight);
                        }
                        _hostElement.css(hostElementCSS)
                            .wrapInner(generateDiv(_classNameContentElement + _strSpace + _classNameTextInherit))
                            .wrapInner(generateDiv(_classNameViewportElement + _strSpace + _classNameTextInherit))
                            .wrapInner(generateDiv(_classNamePaddingElement + _strSpace + _classNameTextInherit));
                        _contentElement = findFirst(_hostElement, _strDot + _classNameContentElement);
                        _viewportElement = findFirst(_hostElement, _strDot + _classNameViewportElement);
                        _paddingElement = findFirst(_hostElement, _strDot + _classNamePaddingElement);
                        _textareaCoverElement = FRAMEWORK(generateDiv(_classNameTextareaCoverElement));
                        _contentElement.prepend(_textareaCoverElement);

                        _targetElement.on(_strScroll, textareaOnScroll)
                            .on('drop', textareaOnDrop)
                            .on('focus', textareaOnFocus)
                            .on('focusout', textareaOnFocusOut);
                        if (_msieVersion > 9 || !_autoUpdateRecommended) {
                            _targetElement.on('input', textareaOnInput);
                        }
                        else {
                            _targetElement.on(_strKeyDownEvent, textareaOnKeyDown)
                                .on(_strKeyUpEvent, textareaOnKeyUp);
                        }
                    } else {
                        addClass(_targetElement, _classNameHostElement);
                        _hostElement = _targetElement;
                        _hostElement.wrapInner(generateDiv(_classNameContentElement))
                            .wrapInner(generateDiv(_classNameViewportElement))
                            .wrapInner(generateDiv(_classNamePaddingElement));
                        _contentElement = findFirst(_hostElement, _strDot + _classNameContentElement);
                        _viewportElement = findFirst(_hostElement, _strDot + _classNameViewportElement);
                        _paddingElement = findFirst(_hostElement, _strDot + _classNamePaddingElement);

                        //add transitionend event
                        _contentElement.on(_strTransitionEndEvent, function (event) {
                            if (_autoUpdateCache === true)
                                return;
                            event = event.originalEvent || event;
                            if (isSizeAffectingCSSProperty(event.propertyName))
                                update(_strAuto);
                        });
                    }

                    buildScrollbars();

                    //add scroll event
                    if (_supportPassiveEvents)
                        addPassiveEventListener(_viewportElement, _strScroll, viewportOnScroll);
                    else
                        _viewportElement.on(_strScroll, viewportOnScroll);

                    if (_nativeScrollbarStyling)
                        addClass(_viewportElement, _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y ? _classNameViewportNativeScrollbarsOverlaid : _classNameViewportNativeScrollbarsInvisible);

                    //build mutation observers
                    if (_supportMutationObserver) {
                        var mutationObserver = COMPATIBILITY.mO();
                        var contentLastUpdate = COMPATIBILITY.now();
                        var mutationTarget;
                        var mutationAttrName;
                        var contentTimeout;
                        var now;
                        var sizeAuto;
                        var action;

                        _mutationObserverHost = new mutationObserver(function (mutations) {
                            if (!_initialized || _isSleeping)
                                return;

                            var doUpdate = false;
                            //var doUpdateScrollbars = false;
                            var mutation;
                            FRAMEWORK.each(mutations, function () {
                                mutation = this;
                                mutationTarget = mutation.target;
                                mutationAttrName = mutation.attributeName;

                                if (mutationAttrName === LEXICON.c)
                                    doUpdate = hostClassNamesChanged(mutation.oldValue, mutationTarget.className);
                                else if (mutationAttrName === LEXICON.s)
                                    doUpdate = mutation.oldValue !== mutationTarget[LEXICON.s].cssText;
                                else
                                    doUpdate = true;

                                if (doUpdate)
                                    return false;
                            });

                            if (doUpdate)
                                _base.update(_strAuto);
                            /*
                            if(doUpdateScrollbars) {
                                refreshScrollbarHandleLength(true);
                                refreshScrollbarHandleOffset(true, _scrollHorizontalInfo.cs);
                                refreshScrollbarHandleLength(false);
                                refreshScrollbarHandleOffset(false, _scrollVerticalInfo.cs);
                            }
                            */
                        });
                        _mutationObserverContent = new mutationObserver(function (mutations) {
                            if (!_initialized || _isSleeping)
                                return;

                            var doUpdate = false;
                            var mutation;
                            FRAMEWORK.each(mutations, function () {
                                mutation = this;
                                doUpdate = isUnknownMutation(mutation);
                                return !doUpdate;
                            });

                            if (doUpdate) {
                                now = COMPATIBILITY.now();
                                sizeAuto = (_heightAutoCache || _widthAutoCache);
                                action = function () {
                                    if(!_destroyed) {
                                        contentLastUpdate = now;

                                        //if cols, rows or wrap attr was changed
                                        if (_isTextarea)
                                            textareaUpdate();

                                        if (sizeAuto)
                                            update();
                                        else
                                            _base.update(_strAuto);
                                    }
                                };
                                clearTimeout(contentTimeout);
                                if (_mutationObserverContentLag <= 0 || now - contentLastUpdate > _mutationObserverContentLag || !sizeAuto)
                                    action();
                                else
                                    contentTimeout = setTimeout(action, _mutationObserverContentLag);
                            }
                        });
                    }

                    //build resize observer for the host element
                    if (_isBody) {
                        addClass(_htmlElement, _classNameHTMLElement);

                        //apply the body scroll to handle it right in the update method
                        _viewportElement[_strScrollLeft](initBodyScroll.l);
                        _viewportElement[_strScrollTop](initBodyScroll.t);
                    }
                    _sizeObserverElement = FRAMEWORK(generateDiv('os-resize-observer-host'));
                    _hostElement.prepend(_sizeObserverElement);
                    addResizeObserver(_sizeObserverElement, hostOnResized);

                    //update for the first time
                    hostOnResized(); //initialize cache for host size
                    _base.update(_strAuto); //initialize cache for content

                    //add the transition class for transitions AFTER the first update (for preventing unwanted transitions)
                    setTimeout(function () {
                        if (_supportTransition && !_destroyed)
                            addClass(_hostElement, _classNameHostTransition)
                    }, 333);

                    //the plugin is initialized now!
                    _initialized = true;
                    callCallback("onInitialized");

                    //add extensions
                    if(type(extensions) == TYPES.s)
                        extensions = [ extensions ];
                    if(COMPATIBILITY.isA(extensions))
                        FRAMEWORK.each(extensions, function () {_base.addExt(this); });
                    else if(FRAMEWORK.isPlainObject(extensions))
                        FRAMEWORK.each(extensions, function (key, value) { _base.addExt(key, value); });

                    return _initialized;
                }

                if (construct(pluginTargetElement, options, extensions)) {
                    INSTANCES(pluginTargetElement, _base);
                    return _base;
                }
                _base = undefined;
            }

            /**
             * Initializes a new OverlayScrollbarsInstance object or changes options if already initialized or returns the current instance.
             * @param pluginTargetElements The elements to which the Plugin shall be initialized.
             * @param options The custom options with which the plugin shall be initialized.
             * @param extensions The extension(s) which shall be added right after initialization.
             * @returns {*}
             */
            window[PLUGINNAME] = function(pluginTargetElements, options, extensions) {
                if(arguments.length === 0)
                    return this;

                initOverlayScrollbarsStatics();

                var arr = [ ];
                var inst;
                var result;
                if(FRAMEWORK.isPlainObject(options)) {
                    if (pluginTargetElements && pluginTargetElements.length) {
                        FRAMEWORK.each(pluginTargetElements, function () {
                            inst = this;
                            if(inst !== undefined)
                                arr.push(OverlayScrollbarsInstance(inst, options, extensions, _pluginsGlobals, _pluginsAutoUpdateLoop));
                        });
                        result = arr.length > 1 ? arr : arr[0];
                    }
                    else
                        result = OverlayScrollbarsInstance(pluginTargetElements, options, extensions, _pluginsGlobals, _pluginsAutoUpdateLoop);
                }
                else if(pluginTargetElements) {
                    if(pluginTargetElements.length && pluginTargetElements.length > 0) {
                        FRAMEWORK.each(pluginTargetElements, function() {
                            inst = INSTANCES(this);
                            if(options === '!') {
                                if(inst instanceof window[PLUGINNAME])
                                    arr.push(inst);
                            }
                            else
                                arr.push(inst);
                        });
                        result = arr.length > 1 ? arr : arr[0];
                    }
                    else
                        result = INSTANCES(pluginTargetElements);
                }
                return result;
            };

            /**
             * Returns a object which contains global information about the plugin and each instance of it.
             * The returned object is just a copy, that means that changes to the returned object won't have any effect to the original object.
             */
            window[PLUGINNAME].globals = function () {
                initOverlayScrollbarsStatics();
                var globals = FRAMEWORK.extend(true, { }, _pluginsGlobals);
                delete globals['msie'];
                return globals;
            };

            /**
             * Gets or Sets the default options for each new plugin initialization.
             * @param newDefaultOptions The object with which the default options shall be extended.
             */
            window[PLUGINNAME].defaultOptions = function(newDefaultOptions) {
                initOverlayScrollbarsStatics();
                var currDefaultOptions = _pluginsGlobals.defaultOptions;
                if(newDefaultOptions === undefined)
                    return FRAMEWORK.extend(true, { }, currDefaultOptions);

                //set the new default options
                _pluginsGlobals.defaultOptions = FRAMEWORK.extend(true, { }, currDefaultOptions , _pluginsOptions.v(newDefaultOptions, _pluginsOptions.t, true));
            };

            /**
             * Registers, Unregisters or returns a extension.
             * Register: Pass the name and the extension. (defaultOptions is optional)
             * Unregister: Pass the name and anything except a function as extension parameter.
             * Get extension: Pass the name of the extension which shall be got.
             * Get all extensions: Pass no arguments.
             * @param extensionName The name of the extension which shall be registered, unregistered or returned.
             * @param extension A function which generates the instance of the extension or anything other to remove a already registered extension.
             * @param defaultOptions The default options which shall be used for the registered extension.
             */
            window[PLUGINNAME].extension = function(extensionName, extension, defaultOptions) {
                var extNameTypeString = COMPATIBILITY.type(extensionName) == TYPES.s;
                var argLen = arguments[LEXICON.l];
                var i = 0;
                if(argLen < 1 || !extNameTypeString) {
                    //return a copy of all extension objects
                    return FRAMEWORK.extend(true, { length : _pluginsExtensions[LEXICON.l] }, _pluginsExtensions);
                }
                else if(extNameTypeString) {
                    if(COMPATIBILITY.type(extension) == TYPES.f) {
                        //register extension
                        _pluginsExtensions.push({
                            name : extensionName,
                            extension : extension,
                            defaultOptions : defaultOptions
                        });
                    }
                    else {
                        for(; i < _pluginsExtensions[LEXICON.l]; i++) {
                            if (_pluginsExtensions[i].name === extensionName) {
                                if(argLen > 1)
                                    _pluginsExtensions.splice(i, 1); //remove extension
                                else
                                    return FRAMEWORK.extend(true, { }, _pluginsExtensions[i]); //return extension with the given name
                            }
                        }
                    }
                }
            };

            return window[PLUGINNAME];
        })();

        if(JQUERY && JQUERY.fn) {
            /**
             * The jQuery initialization interface.
             * @param options The initial options for the construction of the plugin. To initialize the plugin, this option has to be a object! If it isn't a object, the instance(s) are returned and the plugin wont be initialized.
             * @param extensions The extension(s) which shall be added right after initialization.
             * @returns {*} After initialization it returns the jQuery element array, else it returns the instance(s) of the elements which are selected.
             */
            JQUERY.fn.overlayScrollbars = function (options, extensions) {
                var _elements = this;
                if(JQUERY.isPlainObject(options)) {
                    JQUERY.each(_elements, function() { PLUGIN(this, options, extensions); });
                    return _elements;
                }
                else
                    return PLUGIN(_elements, options);
            };
        }
        return PLUGIN;
    }
));