spec/helpers/measurement.js
function getAppliedViewportOverflows ( documentElementProps, bodyProps ) {
    var _window = getAppliedOverflows( documentElementProps, false ),
        body = getAppliedOverflows( bodyProps, false ),
        consolidated = { window: {}, body: {} };

    // Handle the interdependent relation between body and window (documentElement) overflow
    if ( _window.overflowX === "visible" ) {
        // If the window overflow is set to "visible", body props get transferred to the window, body changes to
        // "visible". (Nothing really changes if both are set to "visible".)
        consolidated.body.overflowX = "visible";
        consolidated.window.overflowX = body.overflowX;
    } else {
        // No transfer of properties.
        // - If body overflow is "visible", it remains that way, and the window stays as it is.
        // - If body and window are set to properties other than "visible", they keep their divergent settings.
        consolidated.body.overflowX = body.overflowX;
        consolidated.window.overflowX = _window.overflowX;
    }

    // Repeat for overflowY
    if ( _window.overflowY === "visible" ) {
        consolidated.body.overflowY = "visible";
        consolidated.window.overflowY = body.overflowY;
    } else {
        consolidated.body.overflowY = body.overflowY;
        consolidated.window.overflowY = _window.overflowY;
    }

    // window.overflow(X/Y): "visible" actually means "auto" because scroll bars appear as needed; transform
    if ( consolidated.window.overflowX === "visible" ) consolidated.window.overflowX = "auto";
    if ( consolidated.window.overflowY === "visible" ) consolidated.window.overflowY = "auto";

    // In iOS, window.overflow(X/Y): "hidden" actually means "auto"; transform
    if ( isIOS() ) {
        if ( consolidated.window.overflowX === "hidden" ) consolidated.window.overflowX = "auto";
        if ( consolidated.window.overflowY === "hidden" ) consolidated.window.overflowY = "auto";
    }

    // Add the boolean status properties to the result
    consolidated.window = getAppliedOverflows( consolidated.window, true );
    consolidated.body = getAppliedOverflows( consolidated.body, true );

    return consolidated;
}
function getAppliedViewportOverflows ( documentElementProps, bodyProps ) {
    var _window = getAppliedOverflows( documentElementProps, false ),
        body = getAppliedOverflows( bodyProps, false ),
        consolidated = { window: {}, body: {} };

    // Handle the interdependent relation between body and window (documentElement) overflow
    if ( _window.overflowX === "visible" ) {
        // If the window overflow is set to "visible", body props get transferred to the window, body changes to
        // "visible". (Nothing really changes if both are set to "visible".)
        consolidated.body.overflowX = "visible";
        consolidated.window.overflowX = body.overflowX;
    } else {
        // No transfer of properties.
        // - If body overflow is "visible", it remains that way, and the window stays as it is.
        // - If body and window are set to properties other than "visible", they keep their divergent settings.
        consolidated.body.overflowX = body.overflowX;
        consolidated.window.overflowX = _window.overflowX;
    }

    // Repeat for overflowY
    if ( _window.overflowY === "visible" ) {
        consolidated.body.overflowY = "visible";
        consolidated.window.overflowY = body.overflowY;
    } else {
        consolidated.body.overflowY = body.overflowY;
        consolidated.window.overflowY = _window.overflowY;
    }

    // window.overflow(X/Y): "visible" actually means "auto" because scroll bars appear as needed; transform
    if ( consolidated.window.overflowX === "visible" ) consolidated.window.overflowX = "auto";
    if ( consolidated.window.overflowY === "visible" ) consolidated.window.overflowY = "auto";

    // In iOS, window.overflow(X/Y): "hidden" actually means "auto"; transform
    if ( isIOS() ) {
        if ( consolidated.window.overflowX === "hidden" ) consolidated.window.overflowX = "auto";
        if ( consolidated.window.overflowY === "hidden" ) consolidated.window.overflowY = "auto";
    }

    // Add the boolean status properties to the result
    consolidated.window = getAppliedOverflows( consolidated.window, true );
    consolidated.body = getAppliedOverflows( consolidated.body, true );

    return consolidated;
}
function getAppliedViewportOverflows ( documentElementProps , bodyProps ) { var _window = getAppliedOverflows ( documentElementProps , false ) , body = getAppliedOverflows ( bodyProps , false ) , consolidated = { window : { } , body : { } } ; // Handle the interdependent relation between body and window (documentElement) overflow if ( _window . overflowX === "visible" ) { // If the window overflow is set to "visible", body props get transferred to the window, body changes to // "visible". (Nothing really changes if both are set to "visible".) consolidated . body . overflowX = "visible" ; consolidated . window . overflowX = body . overflowX ; } else { // No transfer of properties. // - If body overflow is "visible", it remains that way, and the window stays as it is. // - If body and window are set to properties other than "visible", they keep their divergent settings. consolidated . body . overflowX = body . overflowX ; consolidated . window . overflowX = _window . overflowX ; } // Repeat for overflowY if ( _window . overflowY === "visible" ) { consolidated . body . overflowY = "visible" ; consolidated . window . overflowY = body . overflowY ; } else { consolidated . body . overflowY = body . overflowY ; consolidated . window . overflowY = _window . overflowY ; } // window.overflow(X/Y): "visible" actually means "auto" because scroll bars appear as needed; transform if ( consolidated . window . overflowX === "visible" ) consolidated . window . overflowX = "auto" ; if ( consolidated . window . overflowY === "visible" ) consolidated . window . overflowY = "auto" ; // In iOS, window.overflow(X/Y): "hidden" actually means "auto"; transform if ( isIOS ( ) ) { if ( consolidated . window . overflowX === "hidden" ) consolidated . window . overflowX = "auto" ; if ( consolidated . window . overflowY === "hidden" ) consolidated . window . overflowY = "auto" ; } // Add the boolean status properties to the result consolidated . window = getAppliedOverflows ( consolidated . window , true ) ; consolidated . body = getAppliedOverflows ( consolidated . body , true ) ; return consolidated ; }
Determines the effective overflow setting of the viewport and body, separately for each axis, based on the
`overflow`, `overflowX` and `overflowY` properties of the documentElement and body which must be passed in as a
hash.

Returns the results for viewport and body in an aggregated `{ window: ..., body: ...}` hash.

For the basic resolution mechanism, see getAppliedOverflows(). When determining the effective overflow, the
peculiarities of viewport and body are taken into account:

- Viewport and body overflows are interdependent. If the nominal viewport overflow for a given axis is "visible",
the viewport inherits the body overflow for that axis, and the body overflow is set to "visible". Curiously,
that transfer is _not_ reflected in the computed values, it just manifests in behaviour.

- Once that is done, if the viewport overflow is still "visible" for an axis, it is effectively turned into
"auto". Scroll bars appear when the content overflows the viewport (ie, "auto" behaviour). Hence, this function
will indeed report "auto". Again, the transformation is only manifest in behaviour, not in the computed values.

- In iOS, if the effective overflow setting of the viewport is "hidden", it is ignored and treated as "auto".
Content can still overflow the viewport, and scroll bars appear as needed.

Now, the catch. This behaviour is impossible to feature-detect. The computed values are not at all affected by
it, and the results reported eg. for clientHeight, offsetHeight, scrollHeight of body and documentElement do
not differ between Safari on iOS and, say, Chrome. The numbers don't give the behaviour away.

So we have to resort to browser sniffing here. It sucks, but there is literally no other option.

NB Additional status properties (see getAppliedOverflows) are always generated here.

@param {Object} documentElementProps            hash of documentElement properties (computed values)
@param {string} documentElementProps.overflow
@param {string} documentElementProps.overflowX
@param {string} documentElementProps.overflowY

@param {Object} bodyProps                       hash of body properties (computed values)
@param {string} bodyProps.overflow
@param {string} bodyProps.overflowX
@param {string} bodyProps.overflowY

@returns {{window: AppliedOverflow, body: AppliedOverflow}}
Determines the effective overflow setting of the viewport and body separately for each axis based on the overflow overflowX and overflowY properties of the documentElement and body which must be passed in as a hash .