// 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( '' ); 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 $; } ));