// jQuery.documentSize, v1.2.5
// Copyright (c) 2015-2018 Michael Heim, Zeilenwechsel.de
// Distributed under MIT license
// http://github.com/hashchange/jquery.documentsize

;( function ( root, factory ) {
    "use strict";

    if ( typeof exports === 'object' ) {

        module.exports = factory(
            require( 'jquery' )
        );

    } else if ( typeof define === 'function' && define.amd ) {

        define( [
            'jquery'
        ], factory );

    }
}( this, function ( $ ) {
    "use strict";

    ;( function ( $ ) {
        "use strict";
    
        // IIFE generating the functions $.documentWidth, $.documentHeight, $.windowWidth, $.windowHeight, and
        // $.scrollbarWidth.
        //
        // These functions need to run feature detections which requires insertion of an iframe ($.documentWidth/Height) and
        // a div ($.scrollbarWidth). The body element in the main document must be available when that happens (ie, the
        // opening body tag must have been parsed).
        //
        // For that reason, the detection does not run instantly - after all, the code might be loaded and run while parsing
        // the head. Instead, detection happens on DOM-ready, or when any of the functions is invoked for the first time.
        // Given the purpose of the functions, they won't be called until after the opening body tag has been parsed.
    
        var _scrollbarWidth,
            _supportsWindowInnerWidth,
            _supportsSubpixelAccuracy,
            elementNameForDocSizeQuery,
            ieVersion,
            useGetComputedStyle = !! window.getComputedStyle;
    
        /**
         * @param   {Document} [_document=document]
         * @returns {number}
         */
        $.documentWidth = function ( _document ) {
            var width;
    
            _document || ( _document = document );
    
            try {
    
                if ( elementNameForDocSizeQuery === undefined ) testDocumentScroll();
                width = _document[elementNameForDocSizeQuery].scrollWidth;
    
            } catch ( e ) {
    
                // Fallback for unsupported, broken browsers which can't run the behaviour test successfully
                width = guessDocumentSize( "Width", _document );
    
            }
    
            return width;
        };
    
        /**
         * @param   {Document} [_document=document]
         * @returns {number}
         */
        $.documentHeight = function ( _document ) {
            var height;
    
            _document || ( _document = document );
    
            try {
    
                if ( elementNameForDocSizeQuery === undefined ) testDocumentScroll();
                height = _document[elementNameForDocSizeQuery].scrollHeight;
    
            } catch ( e ) {
    
                // Fallback for unsupported, broken browsers which can't run the behaviour test successfully
                height = guessDocumentSize( "Height", _document );
    
            }
    
            return height;
        };
    
        /**
         * @param   {string|Object|Window} [options="visual"]  (a) viewport option: strings "visual", "layout" (case-
         *                                                         insensitive), or a hash with a `viewport` property
         *                                                     (b) the window argument
         * @param   {string|Object|Window} [_window=window]    same as first argument; positions for window and viewport
         *                                                     argument are interchangeable
         * @returns {number}
         */
        $.windowWidth = function ( options, _window ) {
            var config = getWindowQueryConfig( arguments );
            return getWindowSize( "Width", config );
        };
    
        /**
         * @param   {string|Object|Window} [options="visual"]  (a) viewport option: strings "visual", "layout" (case-
         *                                                         insensitive), or a hash with a `viewport` property
         *                                                     (b) the window argument
         * @param   {string|Object|Window} [_window=window]    same as first argument; positions for window and viewport
         *                                                     argument are interchangeable
         * @returns {number}
         */
        $.windowHeight = function ( options, _window ) {
            var config = getWindowQueryConfig( arguments );
            return getWindowSize( "Height", config );
        };
    
        /**
         * @param   {Window} [_window=window]
         * @returns {number}
         */
        $.pinchZoomFactor = function ( _window ) {
    
            // Calculate the zoom factor based on the width, not the height. getPinchZoomFactor() does just that.
            //
            // It would be more accurate to use the longest side for the calculation, keeping the effect of rounding errors
            // low (unless the browser supports sub-pixel accuracy anyway).
            //
            // Unfortunately, iOS does not allow that approach. Switching from normal to minimal UI is not reflected in the
            // clientHeight, so the zoom factor would seem to change when the UI disappears (even though in reality, it
            // doesn't). We have to use the width, irrespective of orientation.
    
            return getPinchZoomFactor( _window );
        };
    
        /**
         * @returns {number}
         */
        $.scrollbarWidth = browserScrollbarWidth;
    
    
        /**
         * Returns the window width or height. Does the actual work of $.windowWidth() and $.windowHeight().
         *
         * Handles minimal UI in iOS properly. Returns the size of either the visual viewport (default) or the layout
         * viewport.
         *
         * Precision:
         *
         * - The maximum rounding error for the visual viewport is +/- 1px.
         * - Layout viewport width is precise.
         * - Layout viewport height would potentially be prone to larger rounding errors (though in practice they rarely
         *   seem to exceed +/- 2px). Additional logic is employed to detect and correct these errors. As a result, the
         *   layout viewport height is precise as well.
         *
         * @param   {string}            dimension  must be "Width" or "Height" (upper case!)
         * @param   {WindowQueryConfig} config
         * @returns {number}
         */
        function getWindowSize ( dimension, config ) {
            var ddeClientHeight, visualSize, zoomFactor, snapToKnownHeight, lBound, uBound,
                _window = config.window,
    
                scrollbarsConsumeSpace = browserScrollbarWidth() !== 0,
                getLayoutViewportWidth = config.useLayoutViewport && dimension === "Width",
    
                useClientSize = scrollbarsConsumeSpace || !supportsWindowInnerWidth() || getLayoutViewportWidth,
                size = useClientSize ? _window.document.documentElement[ "client" + dimension ] : getWindowInnerSize( dimension, _window );
    
            if ( config.useLayoutViewport && ! useClientSize ) {
    
                // Looking for the layout viewport height on mobile. Calculate it from window.innerHeight and the zoom
                // factor, so as to capture the real height when the browser is in minimal UI on iOS.
                //
                // NB The layout viewport has a size in full CSS pixels (unaffected by zooming, hence no sub pixels).
    
                visualSize = size;
                zoomFactor = getPinchZoomFactor( _window, { asRange: true } );
    
                size = Math.round( visualSize * zoomFactor.calculated );
    
                // Fix rounding errors, caused by the visual height ignoring sub-pixel fractions. If we know we are near a
                // known layout viewport height, use that.
                if ( !supportsSubpixelAccuracy() ) {
    
                    ddeClientHeight =  _window.document.documentElement.clientHeight;
    
                    // In Android, the height we get from ddE.clientHeight and the one calculated from window.innerHeight
                    // should be the same, except for rounding errors in the calculation. So basically, we could just set
                    // any calculated value to the clientHeight, no matter how large the difference is. (That is, if we even
                    // end up here - in some versions of Chrome on Android, for instance, we have sub-pixel accuracy.)
                    //
                    // Not so in iOS. In minimal UI, the real layout viewport may be larger than the one reported by
                    // clientHeight, by about 60px. So we need to impose reasonable limits on what is considered to be near
                    // the clientHeight.
                    //
                    // - If the calculated value is within 3px of the clientHeight, we consider it to be a clear case of a
                    //   rounding error.
                    // - Alternatively, the clientHeight must be between the theoretical maximum and minimum values of the
                    //   computation, assuming maximum rounding errors at every stage.
                    // - If we use that range, its upper bound is capped at 30px above the clientHeight - keeping a safe
                    //   distance from current minimal UI heights, or possible future ones.
    
                    lBound = ( visualSize - 1 ) * zoomFactor.min;
                    uBound = ( visualSize + 1 ) * zoomFactor.max;
    
                    snapToKnownHeight = ( size <= ddeClientHeight + 3 && size >= ddeClientHeight - 3 ) ||
                                        ( lBound <= ddeClientHeight && uBound >= ddeClientHeight && uBound < ddeClientHeight + 30 );
    
                    if ( snapToKnownHeight ) size = ddeClientHeight;
    
                }
            }
    
            return size;
        }
    
        /**
         * Calculates the zoom factor for pinch zooming on mobile. A factor > 1 means that the page is zoomed in (content
         * enlarged).
         *
         * The zoom factor returned here measures the size of the visual viewport with respect to the size of the layout
         * viewport.
         *
         * Ignores page zoom on the desktop (returning a zoom factor of 1). For the distinction between pinch and page zoom,
         * again see Chapter 3 in PPK's book.
         *
         * NB visualViewport.scale, part of the new visualViewport API, is not used here even if available. Browsers usually
         * calculate their zoom level with respect to the ideal layout viewport, not the layout viewport actually in use
         * (see Peter-Paul Koch, The Mobile Web Handbook, Chapter 3: Viewports, Section "Minimum and Maximum Zoom"). So, in
         * Chrome for Android, visualViewport.scale returns that ratio, rather than the isolated effect of zooming into a
         * part of the page.
         *
         * The distinction doesn't matter if the meta viewport tag makes the browser use its ideal viewport anyway
         * (width=device-width, initial-scale=1.0). But it leads to confusing results if, for example, initial-scale is set
         * to values < 1, or if the meta viewport tag is left out altogether. Then, the page shrinks visually _by default_,
         * without the user zooming. The scale reported by the visualViewport API captures the effect and returns a value
         * < 1. However, the pinch zoom factor should remain at 1 because the visual viewport is still identical to the
         * layout viewport - never mind the diminutive size of its content.
         *
         * @param   {Window}  [_window=window]
         * @param   {Object}  [options]
         * @param   {boolean} [options.asRange=false]
         * @returns {number|ZoomAccuracyRange}
         */
        function getPinchZoomFactor ( _window, options ) {
            var ddeClientWidth, windowInnerWidth,
                asRange = options && options.asRange,
                factors = {
                    calculated: 1,
                    min: 1,
                    max: 1
                },
                skip = browserScrollbarWidth() !== 0 || !supportsWindowInnerWidth();
    
            if ( !skip ) {
    
                _window || ( _window = window );
                ddeClientWidth = _window.document.documentElement.clientWidth;
                windowInnerWidth = getWindowInnerWidth( _window );
    
                // Calculate the zoom factor, assuming window.innerWidth is precise (no rounding errors).
                factors.calculated = ddeClientWidth / windowInnerWidth;
    
                // If requested, determine the minimum and maximum value of the zoom factor in the presence of rounding errors.
                if ( asRange ) {
                    if ( supportsSubpixelAccuracy() ) {
                        // No need to take rounding errors into account
                        factors.min = factors.max = factors.calculated;
                    } else {
                        factors.min = ddeClientWidth / ( windowInnerWidth + 1 );
                        factors.max = ddeClientWidth / ( windowInnerWidth - 1 );
                    }
                }
    
            }
    
            return asRange ? factors : factors.calculated;
        }
    
        /**
         * Handles the argument juggling for $.windowWidth() and $.windowHeight(). Extracts the window and viewport settings
         * from the arguments, applying the defaults (use global window, use visual viewport) where necessary.
         *
         * Examines the first two arguments. The window and the viewport setting can appear in either position,
         * interchangeably. The viewport setting can be passed in as a string, or as part of an options hash,
         * `{ viewport: ... }`.
         *
         * Recognized viewport names are "visual" and "layout" (case-insensitive).
         *
         * @param   {Array|Arguments} args
         * @returns {WindowQueryConfig}
         */
        function getWindowQueryConfig( args ) {
            var isWindowArg0, isOptionArg0, isOptionArg1,
    
                // Defaults
                _window = window,
                _useVisualViewport = true;
    
            if ( args && args.length ) {
    
                // Coerce to a real array
                args = Array.prototype.slice.call( args );
    
                // Examine the first argument. Cast strings to an options hash with a `viewport` property.
                isWindowArg0 = isWindow( args[0] );
                if( !isWindowArg0 ) args[0] = castStringToViewportOption( args[0] );
                isOptionArg0 = !isWindowArg0 && args[0];
    
                // Examine the second argument. Again, cast strings to an options hash with a `viewport` property.
                if ( !isOptionArg0 ) args[1] = castStringToViewportOption( args[1] );
                isOptionArg1 = !isOptionArg0 && args[1];
    
                // Extract window and viewport option, if available.
                if ( isWindowArg0 ) {
    
                    _window = args[0];
                    if( isOptionArg1 && args[1].viewport ) _useVisualViewport = isVisualViewport( args[1].viewport );
    
                } else if ( isOptionArg0 ) {
    
                    if( args[0].viewport ) _useVisualViewport = isVisualViewport( args[0].viewport );
                    if ( isWindow( args[1] ) ) _window = args[1];
    
                } else if ( !args[0] && args[1] ) {
    
                    // First argument was falsy, e.g. undefined, null. Ignore it. But process the second, non-falsy argument.
                    if( isOptionArg1 && args[1].viewport ) {
                        _useVisualViewport = isVisualViewport( args[1].viewport );
                    } else if ( isWindow( args[1] ) ) {
                        _window = args[1];
                    }
    
                }
            }
    
            return {
                window: _window,
                useVisualViewport: _useVisualViewport,
                useLayoutViewport: !_useVisualViewport
            };
        }
    
        /**
         * Checks if the argument is the name of the visual viewport. The check is case-insensitive.
         *
         * Expects a string. Tolerates falsy values, returning false then (argument is not naming the visual viewport).
         * Throws an error for everything else. Also throws an error if the viewport name is a string but not recognized
         * (typo alert).
         *
         * Helper for getWindowQueryConfig().
         *
         * @param   {string} [name]  strings "visual", "layout" (case-insensitive)
         * @returns {boolean}
         */
        function isVisualViewport ( name ) {
            var viewport = isString( name ) && name.toLowerCase();
    
            if ( name && !viewport ) throw new Error( "Invalid viewport option: " + name );
            if ( viewport && viewport !== "visual" && viewport !== "layout" ) throw new Error( "Invalid viewport name: " + name );
    
            return viewport === "visual";
        }
    
        /**
         * Examines a value and casts a string to a hash with a `viewport` property (the string being its value). If the
         * value is not a string, or if the string is empty, it is returned as-is. Helper for getWindowQueryConfig().
         *
         * @param   {*} arg
         * @returns {*}
         */
        function castStringToViewportOption ( arg ) {
            return ( isString( arg ) && arg !== "" ) ? { viewport: arg } : arg;
        }
    
        /**
         * Checks if the browser supports window.visualViewport.width and window.visualViewport.height, or window.innerWidth
         * and window.innerHeight otherwise.
         *
         * See getWindowInnerSize() for the distinction between the visualViewport API and window.innerWidth/Height.
         *
         * The check is run on demand, rather than up front while loading the component, because the window properties can
         * behave strangely in the early stages of opening a window. The component might be loaded in the document head,
         * which could potentially be early enough to run into these oddities. So we wait until the first call.
         *
         * IE 8 does not support window.innerWidth and .innerHeight. FF has a bug which as only been fixed in FF25 (released
         * 29 Oct 2013). It manifests itself in a fictional return value of 10 for these properties. See
         *
         * - https://developer.mozilla.org/en-US/docs/Web/API/Window.innerWidth
         * - https://developer.mozilla.org/en-US/docs/Web/API/Window.innerHeight
         * - https://bugzilla.mozilla.org/show_bug.cgi?id=641188
         *
         * @returns {boolean}
         */
        function supportsWindowInnerWidth () {
            if ( _supportsWindowInnerWidth === undefined ) _supportsWindowInnerWidth = getWindowInnerWidth() > 10;
            return _supportsWindowInnerWidth;
        }
    
        /**
         * Does the actual work of $.scrollbarWidth. Protected from external modification. See $.scrollbarWidth for details.
         *
         * Adapted from Ben Alman's scrollbarWidth plugin. See
         * - http://benalman.com/projects/jquery-misc-plugins/#scrollbarwidth
         * - http://jsbin.com/zeliy/1
         *
         * @returns {number}
         */
        function browserScrollbarWidth () {
            var testEl;
    
            if ( _scrollbarWidth === undefined ) {
    
                testEl = document.createElement( "div" );
                testEl.style.cssText = "width: 100px; height: 100px; overflow: scroll; position: absolute; top: -500px; left: -500px; margin: 0px; padding: 0px; border: none;";
    
                document.body.appendChild( testEl );
                _scrollbarWidth = testEl.offsetWidth - testEl.clientWidth;
                document.body.removeChild( testEl );
    
            }
    
            return _scrollbarWidth;
        }
    
        /**
         * Detects which element to use for a document size query (body or documentElement).
         *
         * Sandbox
         * -------
         *
         * The detection is sandboxed in an iframe element created for the purpose. If the iframe window can't be
         * accessed because of some obscure policy restriction or browser bug, the main window and document is used
         * as a fallback.
         *
         * The test is designed to minimize the visual and rendering impact in the test window, in case the fallback
         * should ever be used.
         *
         * Test method
         * -----------
         *
         * We can't test directly which call to use (at least not with an even worse amount of intervention than is
         * already the case, which matters if the iframe is not accessible). But we can work by exclusion.
         *
         * In Chrome (desktop and mobile), Safari (also iOS), and Opera, body.scrollWidth returns the true document
         * width. In Firefox and IE, body.scrollWidth responds to the body content size instead. In those browsers,
         * true document width is returned by document.documentElement.scrollWidth.
         *
         * So we test the behaviour of body.scrollWidth by manipulating the body size, while keeping the document size
         * constant.
         *
         * - We prepare for the test by making sure the body does not display its overflow.
         * - Then we inject a small test element into the body and give it a relative position far outside the viewport.
         *
         * The body size is expanded, but the document size remains unaffected because the body hides the overflowing
         * test element (either outright, or by putting it in a hidden part of the scroll pane). Then we check if
         * body.scrollWidth has responded to the change. From that, we infer the right element to use for a document
         * width query.
         *
         * The function does not return anything. It sets the elementNameForDocSizeQuery in the closure instead.
         */
        function testDocumentScroll () {
    
            var initialDocumentState, _testEl, initialScrollWidth, responds,
    
                iframe = createTestIframe(),
                _document = iframe && iframe.contentDocument || document,
                _body = _document.body,
                inIframe = _document !== document;
    
            // Create a test element which will be used to to expand the body content way to the right.
            _testEl = _document.createElement( "div" );
            _testEl.style.cssText = "width: 1px; height: 1px; position: relative; top: 0px; left: 32000px;";
    
            // Make sure that the body (but not the window) hides its overflow. Only applies if the iframe is not
            // accessible. The iframe document already contains the required styles.
            if ( ! inIframe ) initialDocumentState = prepareGlobalDocument();
    
            // Inject the test element, then test if the body.scrollWidth property responds
            initialScrollWidth = _body.scrollWidth;
            _body.appendChild( _testEl );
            responds = initialScrollWidth !== _body.scrollWidth;
            _body.removeChild( _testEl );
    
            // Restore the overflow settings for window and body
            if ( ! inIframe ) restoreGlobalDocument( initialDocumentState );
    
            // If body.scrollWidth responded, it reacts to body content size, not document size. Default to
            // ddE.scrollWidth. If it did not react, however, it is linked to the (unchanged) document size.
            elementNameForDocSizeQuery = responds ? "documentElement" : "body";
    
            if ( iframe ) document.body.removeChild( iframe );
    
        }
    
        /**
         * Creates an iframe document with an HTML5 doctype and UTF-8 encoding and positions it off screen. Window size
         * is 500px x 500px. Body and window (document element) are set to overflow: hidden.
         *
         * In case the content document of the iframe can't be accessed for some reason, the function returns undefined.
         * This is unlikely to ever happen, though.
         *
         * @returns {HTMLIFrameElement|undefined}
         */
        function createTestIframe () {
            var iframe = document.createElement( "iframe" ),
                body = document.body;
    
            iframe.style.cssText = "position: absolute; top: -600px; left: -600px; width: 500px; height: 500px; margin: 0px; padding: 0px; border: none; display: block;";
            iframe.frameborder = "0";
    
            body.appendChild( iframe );
            iframe.src = 'about:blank';
    
            if ( !iframe.contentDocument ) return;
    
            iframe.contentDocument.write( '<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title><style type="text/css">html, body { overflow: hidden; }</style></head><body></body></html>' );
    
            return iframe;
        }
    
        /**
         * Makes sure the body (but not the window) hides its overflow. Works with the global document, returns the initial
         * state before manipulation (including properties indicating what has been modified).
         *
         * Used only if iframe creation or access has failed for some reason.
         */
        function prepareGlobalDocument () {
            var ddEStyle, bodyStyle,
    
                ddE = document.documentElement,
                body = document.body,
    
                ddEComputedStyles = useGetComputedStyle ? window.getComputedStyle( ddE, null ) : ddE.currentStyle,
                bodyComputedStyles = useGetComputedStyle ? window.getComputedStyle( body, null ) : body.currentStyle,
    
                ddEOverflowX = ( ddEComputedStyles.overflowX || ddEComputedStyles.overflow || "visible" ).toLowerCase(),
                bodyOverflowX = ( bodyComputedStyles.overflowX || bodyComputedStyles.overflow || "visible" ).toLowerCase(),
    
                modifyBody = bodyOverflowX !== "hidden",
                modifyDocumentElement = ddEOverflowX === "visible",
    
                initialState = {
                    documentElement: {
                        modified: modifyDocumentElement
                    },
                    body: {
                        modified: modifyBody
                    }
                };
    
            if ( modifyDocumentElement ) {
                ddEStyle = ddE.style;
                initialState.documentElement.styleOverflowX = ddEStyle.overflowX;
                ddEStyle.overflowX = "auto";
            }
    
            if ( modifyBody ) {
                bodyStyle = body.style;
                initialState.body.styleOverflowX = bodyStyle.overflowX;
                bodyStyle.overflowX = "hidden";
            }
    
            return initialState;
    
        }
    
        /**
         * Restores the body and documentElement styles to their initial state, which is passed in as an argument. Works
         * with the global document.
         *
         * Used only if iframe creation or access has failed for some reason.
         *
         * @param {Object} previousState  the initial state, as returned by prepareGlobalDocument()
         */
        function restoreGlobalDocument ( previousState ) {
    
            if ( previousState.documentElement.modified ) document.documentElement.style.overflowX = previousState.documentElement.styleOverflowX;
            if ( previousState.body.modified ) document.body.style.overflowX = previousState.body.styleOverflowX;
    
        }
    
        /**
         * Returns a best guess for the window width or height. Used as a fallback for unsupported browsers which are too
         * broken to even run the feature test.
         *
         * The conventional jQuery method of guessing the document size is used here: every conceivable value is queried and
         * the largest one is picked.
         *
         * @param {string}   dimension    accepted values are "Width" or "Height" (capitalized first letter!)
         * @param {Document} [_document]
         */
        function guessDocumentSize( dimension, _document ) {
            var ddE = _document.documentElement;
    
            return Math.max(
                ddE.body[ "scroll" + dimension ], _document[ "scroll" + dimension ],
                ddE.body[ "offset" + dimension ], _document[ "offset" + dimension ],
                _document[ "client" + dimension ]
            );
        }
    
        /**
         * Returns window.visualViewport.width if available, or window.innerWidth otherwise.
         *
         * Along the way, the return value is examined to see if the browser supports sub-pixel accuracy (floating-point
         * values).
         *
         * @param   {Window} [_window=window]
         * @returns {number}
         */
        function getWindowInnerWidth ( _window ) {
            return getWindowInnerSize( "Width", _window );
        }
    
        /**
         * Returns window.visualViewport.height if available, or window.innerHeight otherwise.
         *
         * Along the way, the return value is examined to see if the browser supports sub-pixel accuracy (floating-point
         * values).
         *
         * @param   {Window} [_window=window]
         * @returns {number}
         */
        function getWindowInnerHeight ( _window ) {
            return getWindowInnerSize( "Height", _window );
        }
    
        /**
         * Returns window.visualViewport.width if available, or window.innerWidth otherwise, for width. Likewise, it
         * returns window.visualViewport.height or window.innerHeight for height. The dimension argument determines whether
         * width or height is returned.
         *
         * Along the way, the return value is examined to see if the browser supports sub-pixel accuracy (floating-point
         * values).
         *
         * If the visualViewport API is available, it is preferred over window.innerWidth/Height. That's because Chrome,
         * from version 62, has changed the behaviour of window.innerWidth/Height, which used to return the size of the
         * visual viewport. In Chrome, it now returns the size of the layout viewport, breaking compatibility with all other
         * browsers and its own past behaviour. Other browsers may follow suit, though. See
         *
         * - https://www.quirksmode.org/blog/archives/2017/09/chrome_breaks_v.html
         * - https://developers.google.com/web/updates/2017/09/visual-viewport-api
         * - https://bugs.chromium.org/p/chromium/issues/detail?id=767388#c8
         *
         * @param   {string} dimension  must be "Width" or "Height" (upper case!)
         * @param   {Window} [_window=window]
         * @returns {number}
         */
        function getWindowInnerSize ( dimension, _window ) {
            var size =  ( _window || window ).visualViewport ?
                        ( _window || window ).visualViewport[ dimension.toLowerCase() ] :
                        ( _window || window )[ "inner" + dimension];
    
            // Check for fractions. Exclude undefined return values in browsers which don't support window.innerWidth/Height.
            if ( size ) checkForFractions( size );
            return size;
        }
    
        /**
         * Accepts an observed value of window.innerWidth or window.innerHeight and examines it for fractional values.
         * Caches the finding if there is one. Helper for getWindowInnerWidth() and friends.
         *
         * See supportsSubpixelAccuracy() for more.
         *
         * @param {number} sizeMeasurement
         */
        function checkForFractions ( sizeMeasurement ) {
    
            // When checking the cached value, we can't accept false as a definitive answer. We have to continue checking
            // unless the cached value is `true`.
            //
            // See supportsSubpixelAccuracy().
            if ( !_supportsSubpixelAccuracy && isFloat( sizeMeasurement ) ) _supportsSubpixelAccuracy = true;
    
        }
    
        /**
         * Returns whether or not the browser supports sub-pixel accuracy (floating-point values) for window.innerWidth and
         * window.innerHeight - as far as we know yet.
         *
         * The browser behaviour can't be tested directly, and must be figured out by continuous observation. We have to
         * monitor the return values of window.innerWidth/Height for fractions. That is done with getWindowInnerWidth etc.
         * As soon as we find a fraction, we know that the browser supports floating-point accuracy.
         *
         * However, we can never be sure of the opposite. If we haven't encountered a fraction yet, it could simply mean
         * that we are at 100% zoom, and integers have indeed been the correct return value so far.
         *
         * So when the method here answers with true, it is definitive. When it returns false, that just means "maybe not,
         * but we don't know". And the the return value might flip to true in a future query.
         *
         * @returns {boolean}
         */
        function supportsSubpixelAccuracy () {
            return !!_supportsSubpixelAccuracy;
        }
    
        function isWindow ( value ) {
            // Identical to jQuery.isWindow()
            return value != null && value.window == value;         // jshint ignore:line
        }
    
        function isString ( value ) {
            // Done as in the Lodash compatibility build
            return typeof value === 'string' || value && typeof value === 'object' && Object.prototype.toString.call(value) === '[object String]' || false;
        }
    
        /**
         * Returns whether or not a value is of type number. Also rejects NaN as a number.
         *
         * NB This is not the same as $.isNumeric because $.isNumeric( "3" ) is true while isNumber( "3" ) is false.
         *
         * @param   {*} value
         * @returns {boolean}
         */
        function isNumber ( value ) {
            // Done as in the Lodash compatibility build, but rejecting NaN as a number.
            var isNumeric = typeof value === 'number' || value && typeof value === 'object' && Object.prototype.toString.call( value ) === '[object Number]' || false;
    
            // Reject NaN before returning
            return isNumeric && value === +value;
        }
    
        /**
         * Returns whether or not a number is a float, ie has decimals.
         *
         * Requires the argument to be a number. If unsure, guard against false detections with
         * `isNumber( value ) && isFloat ( value )`.
         *
         * For the technique, see http://stackoverflow.com/a/3885844/508355
         *
         * @param   {number} num
         * @returns {boolean}
         */
        function isFloat ( num ) {
            return num === +num && num !== ( num | 0 );         // jshint ignore:line
        }
    
        /**
         * Returns the IE version, or false if the browser is not IE.
         *
         * The result is determined by browser sniffing, rather than a test tailored to the use case. The function must only
         * be called as a last resort, for scenarios where there is no alternative to browser sniffing.
         *
         * These scenarios include:
         *
         * - Preventing IE6 and IE7 from crashing
         * - Preventing IE9 from blocking or delaying the load event
         *
         * The test follows the MSDN recommendation at https://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx#parsingua
         * The result is cached.
         *
         * @returns {number|boolean}
         */
        function getIEVersion () {
            var userAgent, userAgentTestRx;
    
            if ( ieVersion === undefined ) {
    
                ieVersion = false;
                userAgent = navigator && navigator.userAgent;
    
                if ( navigator && navigator.appName === "Microsoft Internet Explorer" && userAgent ) {
                    userAgentTestRx = new RegExp( "MSIE ([0-9]{1,}[\.0-9]{0,})" );                          // jshint ignore:line
                    if ( userAgentTestRx.exec( userAgent ) != null ) ieVersion = parseFloat( RegExp.$1 );
                }
    
            }
    
            return ieVersion;
        }
    
        /**
         * Checks if we are dealing with a truly ancient version of IE (< IE8).
         *
         * This is done by browser sniffing, rather than a test tailored to the use case. Use it only if there is no
         * alternative.
         *
         * @returns {boolean}
         */
        function isAncientIE () {
            var ieVersion = getIEVersion();
            return ieVersion && ieVersion < 8;
        }
    
        /**
         * Checks if the browser is IE9.
         *
         * This is done by browser sniffing, rather than a test tailored to the use case. Use it only if there is no
         * alternative.
         *
         * @returns {boolean}
         */
        function isIE9 () {
            return getIEVersion() === 9;
        }
    
    
        // Let's prime $.documentWidth(), $.documentHeight() and $.scrollbarWidth() immediately after the DOM is ready. It
        // is best to do it up front because the test touches the DOM, so let's get it over with before people set up
        // handlers for mutation events and such.
        //
        // This step has to be skipped for the following browsers:
        //
        // - ancient versions of IE (IE6, IE7).
        //   IE6 and IE7 can't handle the feature tests on DOM ready - they crash right away. Later on, the tests are ok.
        //
        // - IE9.
        //   If the feature tests were run on DOM ready, the window load event would become unreliable. The event might not
        //   fire until the user moves the mouse over the document. This bug is rare and not triggered by jQuery.documentSize
        //   alone; third-party code likely plays a role. The exact circumstances are not clear - see issue #3.
        //
        // For these browsers, we don't run the feature tests preemptively. Instead, we do it on demand, when the first
        // document size query is made.
        if ( typeof $ === "function" && !isAncientIE() && !isIE9() ) {
    
            // Try-catch acts as a safety net for unsupported, broken browsers
            try {
    
                $( function () {
                    if ( elementNameForDocSizeQuery === undefined ) testDocumentScroll();
                    browserScrollbarWidth();
                } );
    
            } catch ( e ) {}
    
        }
    
    
        /**
         * Custom types.
         *
         * For easier documentation and type inference.
         */
    
        /**
         * @name ZoomAccuracyRange
         * @type {Object}
         *
         * @property {number} calculated
         * @property {number} min
         * @property {number} max
         */
    
        /**
         * @name WindowQueryConfig
         * @type {Object}
         *
         * @property {Window}  window
         * @property {boolean} useVisualViewport
         * @property {boolean} useLayoutViewport
         */
    
    }(
        typeof jQuery !== "undefined" ? jQuery :
        typeof Zepto !== "undefined" ? Zepto :
        $
    ));
    return $;

} ));