// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information.
///
import _BaseCoreUtils = require('../Core/_BaseCoreUtils');
import _Global = require('../Core/_Global');
import _WinRT = require('../Core/_WinRT');
"use strict";
var _Constants = {
visualViewportClass: "win-visualviewport-space",
scrollTimeout: 150,
}
// Definiton of *Visible Document*:
// Some portions of this file refer to the *visible document* or *visibleDoc*. Generally speaking,
// this is the portion of the app that is visible to the user (factoring in optical zoom and input pane occlusion).
// Technically speaking, in most cases, this is equivalent to the *visual viewport*. The exception is
// when the input pane has shown without resizing the *visual viewport*. In this case, the *visible document*
// is the *visual viewport* height minus the input pane occlusion.
// This private module provides accurate metrics for the Visual Viewport and WWA's IHM offsets in Win10 WWA
// where "-ms-device-fixed" CSS positioning is supported. WinJS controls will also use this module for
// positoning themselves relative to the viewport in a web browser outside of WWA. Their preference is still
// to rely on "-ms-device-fixed" positioning, but currently fallback to "fixed" positioning in enviornments where
// "-ms-device-fixed" is not supported.
export var _KeyboardInfo: {
_visible: boolean;
_extraOccluded: number;
_isResized: boolean;
_visibleDocBottom: number;
_visibleDocHeight: number;
_visibleDocTop: number;
_visibleDocBottomOffset: number;
_visualViewportHeight: number;
_visualViewportWidth: number;
_visualViewportSpace: ClientRect;
_animationShowLength: number;
_scrollTimeout: number;
_layoutViewportCoords: { visibleDocTop: number; visibleDocBottom: number };
}
// WWA Soft Keyboard offsets
_KeyboardInfo = {
// Determine if the keyboard is visible or not.
get _visible(): boolean {
try {
return (
_WinRT.Windows.UI.ViewManagement.InputPane &&
_WinRT.Windows.UI.ViewManagement.InputPane.getForCurrentView().occludedRect.height > 0
);
} catch (e) {
return false;
}
},
// See if we have to reserve extra space for the IHM
get _extraOccluded(): number {
var occluded = 0;
// Controls using -ms-device-fixed positioning only need to reposition themselves to remain visible
// If the IHM has not resized the viewport.
if (!_KeyboardInfo._isResized && _WinRT.Windows.UI.ViewManagement.InputPane) {
occluded = _WinRT.Windows.UI.ViewManagement.InputPane.getForCurrentView().occludedRect.height;
}
return occluded;
},
// See if the view has been resized to fit a keyboard
get _isResized(): boolean {
// Compare ratios. Very different includes IHM space.
var heightRatio = _Global.document.documentElement.clientHeight / _Global.innerHeight,
widthRatio = _Global.document.documentElement.clientWidth / _Global.innerWidth;
// If they're nearly identical, then the view hasn't been resized for the IHM
// Only check one bound because we know the IHM will make it shorter, not skinnier.
return (widthRatio / heightRatio < 0.99);
},
// Get the bottom of the visible area, relative to the top edge of the visible area.
get _visibleDocBottom(): number {
return _KeyboardInfo._visibleDocTop + _KeyboardInfo._visibleDocHeight;
},
// Get the height of the visible area, e.g. the height of the visual viewport minus any IHM occlusion.
get _visibleDocHeight(): number {
return _KeyboardInfo._visualViewportHeight - _KeyboardInfo._extraOccluded;
},
// Get the top offset of our visible area, aka the top of the visual viewport.
// This is always 0 when elements use -ms-device-fixed positioning.
get _visibleDocTop(): number {
return 0;
},
// Get the offset for, and relative to, the bottom edge of the visual viewport plus any IHM occlusion.
get _visibleDocBottomOffset(): number {
// For -ms-device-fixed positioned elements, the bottom is just 0 when there's no IHM.
// When the IHM appears, the text input that invoked it may be in a position on the page that is occluded by the IHM.
// In that instance, the default browser behavior is to resize the visual viewport and scroll the input back into view.
// However, if the viewport resize is prevented by an IHM event listener, the keyboard will still occlude
// -ms-device-fixed elements, so we adjust the bottom offset of the appbar by the height of the occluded rect of the IHM.
return _KeyboardInfo._extraOccluded;
},
// Get the visual viewport height. window.innerHeight doesn't return floating point values which are present with high DPI.
get _visualViewportHeight(): number {
var boundingRect = _KeyboardInfo._visualViewportSpace;
return boundingRect.height;
},
// Get the visual viewport width. window.innerWidth doesn't return floating point values which are present with high DPI.
get _visualViewportWidth(): number {
var boundingRect = _KeyboardInfo._visualViewportSpace;
return boundingRect.width;
},
// The visual viewport space element is hidden given -ms-device-fixed positioning and used to calculate
// the 4 edges of the visual viewport with floating point precision.
get _visualViewportSpace(): ClientRect {
var visualViewportSpace: HTMLElement = _Global.document.body.querySelector("." + _Constants.visualViewportClass);
if (!visualViewportSpace) {
visualViewportSpace = _Global.document.createElement("DIV");
visualViewportSpace.className = _Constants.visualViewportClass;
_Global.document.body.appendChild(visualViewportSpace);
}
return visualViewportSpace.getBoundingClientRect();
},
// Get total length of the IHM showPanel animation
get _animationShowLength(): number {
if (_BaseCoreUtils.hasWinRT) {
if (_WinRT.Windows.UI.Core.AnimationMetrics) {
// Desktop exposes the AnimationMetrics API that allows us to look up the relevant IHM animation metrics.
var a = _WinRT.Windows.UI.Core.AnimationMetrics, animationDescription = new a.AnimationDescription(a.AnimationEffect.showPanel, a.AnimationEffectTarget.primary);
var animations = animationDescription.animations;
var max = 0;
for (var i = 0; i < animations.size; i++) {
var animation = animations[i];
max = Math.max(max, animation.delay + animation.duration);
}
return max;
} else {
// Phone platform does not yet expose the Animation Metrics API.
// Hard code the correct values for the time being.
// https://github.com/winjs/winjs/issues/1060
var animationDuration = 300;
var animationDelay = 50;
return animationDelay + animationDuration;
}
}
else {
return 0;
}
},
// Padding for IHM timer to allow for first scroll event. Tpyically used in conjunction with the
// _animationShowLength to determine the length of time in which a showing IHM would have triggered
// a window resize to occur.
get _scrollTimeout(): number {
return _Constants.scrollTimeout;
},
// _layoutViewportCoords gives the top and bottom offset of the visible document for elements using
// position:fixed. Comparison with position:-ms-device-fixed helper:
// - Like -ms-device-fixed helper, takes into account input pane occlusion.
// - Unlike -ms-device-fixed helper, doesn't account for optical zoom.
get _layoutViewportCoords(): { visibleDocTop: number; visibleDocBottom: number } {
var topOffset = _Global.window.pageYOffset - _Global.document.documentElement.scrollTop;
var bottomOffset = _Global.document.documentElement.clientHeight - (topOffset + this._visibleDocHeight);
return {
visibleDocTop: topOffset,
visibleDocBottom: bottomOffset
};
}
};