Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
260 lines
8.3 KiB
JavaScript
Executable file
260 lines
8.3 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/* global AppWindow */
|
|
|
|
(function(exports) {
|
|
/**
|
|
* This window inherits the AppWindow, and modifies some properties
|
|
* different from the later.
|
|
*
|
|
* For some flow diagrams related to input management, please refer to
|
|
* https://wiki.mozilla.org/Gaia/System/InputManagement#Flow_Diagrams .
|
|
*
|
|
* @class InputWindow
|
|
* @param {OBject} configs The configuration of the input app
|
|
* @augments AppWindow
|
|
*/
|
|
var InputWindow = function(configs) {
|
|
// note: properties in configs will become this[properties]
|
|
configs.isInputMethod = true;
|
|
configs.name = 'InputMethods';
|
|
configs.url = configs.origin + configs.path;
|
|
|
|
this.splashed = true;
|
|
|
|
// we're waiting for _ready to fire to do something further
|
|
this._pendingReady = false;
|
|
|
|
AppWindow.call(this, configs);
|
|
|
|
// input keyboard transition was not supposed to have a timeout before,
|
|
// so we give this a much higher tolerance
|
|
this.transitionController.OPENING_TRANSITION_TIMEOUT = 5000;
|
|
this.transitionController.CLOSING_TRANSITION_TIMEOUT = 5000;
|
|
|
|
// Set the ID onto the element so Gij tests
|
|
// can probe it with Keyboard#getCurrentKeyboard.
|
|
this.browser.element.dataset.frameName = configs.id;
|
|
};
|
|
|
|
/**
|
|
* @borrows AppWindow.prototype as InputWindow.prototype
|
|
* @memberof InputWindow
|
|
*/
|
|
InputWindow.prototype = Object.create(AppWindow.prototype);
|
|
|
|
InputWindow.prototype.constructor = InputWindow;
|
|
|
|
InputWindow.REGISTERED_EVENTS = ['mozbrowsererror'];
|
|
|
|
// use only the transition controller as the sub component
|
|
InputWindow.SUB_COMPONENTS = {
|
|
'transitionController': 'AppTransitionController'
|
|
};
|
|
|
|
InputWindow.prototype.containerElement = document.getElementById('keyboards');
|
|
|
|
InputWindow.prototype.view = function iw_view() {
|
|
return `<div class="${this.CLASS_LIST}" id="${this.instanceID}"
|
|
transition-state="closed">
|
|
<div class="browser-container"></div>
|
|
</div>`;
|
|
};
|
|
|
|
InputWindow.prototype.eventPrefix = 'input-app';
|
|
|
|
/**
|
|
* Different animation from the original window.
|
|
*
|
|
* @type string
|
|
* @memberof InputWindow
|
|
*/
|
|
InputWindow.prototype.openAnimation = 'slide-from-bottom';
|
|
|
|
/**
|
|
* Different animation from the original window.
|
|
*
|
|
* @type string
|
|
* @memberof InputWindow
|
|
*/
|
|
InputWindow.prototype.closeAnimation = 'slide-to-bottom';
|
|
|
|
InputWindow.prototype._DEBUG = false;
|
|
|
|
InputWindow.prototype.CLASS_LIST = 'inputWindow';
|
|
InputWindow.prototype.CLASS_NAME = 'InputWindow';
|
|
|
|
/**
|
|
* Fired when the input app signals its readiness through window.resizeTo(),
|
|
* which translates to mozbrowserresized;
|
|
* or
|
|
* Fired when the input app wants to resize itself
|
|
*
|
|
* @event InputWindow#mozbrowserresize
|
|
*/
|
|
InputWindow.prototype._handle_mozbrowserresize =
|
|
function iw_handle_mozbrowserresize(evt) {
|
|
|
|
var height = evt.detail.height;
|
|
|
|
this._setHeight(height);
|
|
|
|
this.publish('ready');
|
|
|
|
// we're already opened, so publish heightchanged
|
|
if ('opened' === this.transitionController._transitionState) {
|
|
this.publish('heightchanged');
|
|
}
|
|
|
|
evt.stopPropagation();
|
|
};
|
|
|
|
/**
|
|
* ready is a one-time event and is triggered when the keyboard app
|
|
* signals its readiness through mozbrowserresize event.
|
|
* we only listen to this one-time ready event when we're opening the
|
|
* keyboard app through |setAsActiveInput()|; subsequent mozbrowserresize
|
|
* events (like keyboard app wants to resize itself) would not trigger this
|
|
* ready event.
|
|
*
|
|
* @event InputWindow#_ready
|
|
*/
|
|
InputWindow.prototype._handle__ready =
|
|
function iw_handle__ready(evt) {
|
|
this.element.removeEventListener('_ready', this);
|
|
this._pendingReady = false;
|
|
|
|
this._setHeight(evt.detail.height);
|
|
|
|
AppWindow.prototype.open.call(this,
|
|
this.immediateOpen ? 'immediate' : undefined
|
|
);
|
|
};
|
|
|
|
InputWindow.prototype._setHeight = function iw_setHeight(height) {
|
|
// bug 1059683: when we're on a HiDPI device with non-integer
|
|
// devicePixelRatio the system may calculate (from available screen height
|
|
// and keyboard height) the available height for current window/layout that
|
|
// is a fraction smaller than the ideal value, which can result in a
|
|
// 1-device-px gap between the current window/layout and keyboard, on such
|
|
// devices. to mitigate this, the keyboard tries to report 1 less pixel of
|
|
// height if it sees that the height of the keyboard is a fraction when
|
|
// expressed in device pixel.
|
|
|
|
var dpx = window.devicePixelRatio;
|
|
if ((height * dpx) % 1 !== 0) {
|
|
height = Math.floor(height * dpx) / dpx;
|
|
}
|
|
|
|
this.height = height;
|
|
// Gecko 52 removed mozpasspointerevents. Clip the full-viewport iframe
|
|
// to the actual keyboard area so it receives touches without covering
|
|
// the input field above it.
|
|
if (this.browser && this.browser.element && Number.isFinite(height) && height > 0) {
|
|
this.browser.element.style.clip = 'rect(' +
|
|
Math.max(0, window.innerHeight - height) + 'px, auto, auto, auto)';
|
|
}
|
|
};
|
|
|
|
// Set the input method activeness of this InputWindow:
|
|
// - mozbrowserresize event (for the readiness of the input app)
|
|
// - setVisible & setInputMethodActive
|
|
// - styling classes
|
|
InputWindow.prototype._setAsActiveInput =
|
|
function iw_setAsActiveInput(active) {
|
|
this.debug('setAsActiveInput: ' + this.manifestURL + this.path +
|
|
', active: ' + active);
|
|
|
|
this.setVisible(active);
|
|
|
|
if (this.browser.element.setInputMethodActive) {
|
|
this.browser.element.setInputMethodActive(active);
|
|
} else {
|
|
console.warn('setInputMethodActive is not available');
|
|
}
|
|
|
|
if (active) {
|
|
this.browser.element.addEventListener('mozbrowserresize', this, true);
|
|
this.element.classList.add('top-most');
|
|
} else {
|
|
this.browser.element.removeEventListener('mozbrowserresize', this, true);
|
|
this.element.classList.remove('top-most');
|
|
|
|
this.height = 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Input apps can never change orientation for whatever reasons.
|
|
*
|
|
* @override
|
|
* @memberof InputWindow
|
|
*/
|
|
InputWindow.prototype.lockOrientation = function iw_setOrientation(){
|
|
};
|
|
InputWindow.prototype.setOrientation = function iw_setOrientation(){
|
|
};
|
|
|
|
/**
|
|
* Close the input window.
|
|
*
|
|
* Also, remove the handler of ready event because our caller might be closing
|
|
* this input window immediately after it has just called open() on this, and
|
|
* the ready event (required by the whole opening process) hasn't been
|
|
* triggered -- In that case, since we're closing, we must ignore that ready
|
|
* event too.
|
|
*
|
|
* @override
|
|
* @param {String} immediate If the window has to be closed without animation
|
|
* @memberof InputWindow
|
|
*/
|
|
InputWindow.prototype.close = function iw_close(immediate){
|
|
this.element.removeEventListener('_ready', this);
|
|
|
|
AppWindow.prototype.close.call(this, immediate);
|
|
};
|
|
|
|
/**
|
|
* Open the input window, optionally replacing the layout before doing so.
|
|
*
|
|
* @override
|
|
* @param {Object} configs The configs of the layout
|
|
* @memberof InputWindow
|
|
*/
|
|
InputWindow.prototype.open = function iw_open(configs){
|
|
var hashChanged = false;
|
|
|
|
if (configs.hash !== this.hash) {
|
|
this.browser.element.src = this.origin + this.pathInitial + configs.hash;
|
|
this.debug(this.browser.element.frameName + ' is overwritten: ' +
|
|
this.browser.element.src);
|
|
|
|
this.browser.element.dataset.frameName = configs.id;
|
|
|
|
this.hash = configs.hash;
|
|
|
|
hashChanged = true;
|
|
}
|
|
|
|
this.immediateOpen = configs.immediateOpen;
|
|
|
|
this.element.addEventListener('_ready', this);
|
|
this._pendingReady = true;
|
|
|
|
this._setAsActiveInput(true);
|
|
|
|
// if we're not chaning the hash and we're currently closing, then we are
|
|
// currently still the active input method (because setAsActiveInput(false)
|
|
// is called at |closed| on the manager); as we still want to open the
|
|
// window, and as we won't trigger the hashchange nor the input/visibility
|
|
// events on the app, we need to trigger the readyhandler by ourselves.
|
|
// (on top of that, we want to show the keyboard immediately.)
|
|
if (!hashChanged &&
|
|
'closing' === this.transitionController._transitionState){
|
|
this.immediateOpen = true;
|
|
this.publish('ready');
|
|
}
|
|
};
|
|
|
|
exports.InputWindow = InputWindow;
|
|
})(window);
|