| Index: third_party/google_input_tools/src/chrome/os/inputview/controller.js
|
| diff --git a/third_party/google_input_tools/src/chrome/os/inputview/controller.js b/third_party/google_input_tools/src/chrome/os/inputview/controller.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..27f305bdb0a1d4021ebf4d8ec6b4ca19fe702b80
|
| --- /dev/null
|
| +++ b/third_party/google_input_tools/src/chrome/os/inputview/controller.js
|
| @@ -0,0 +1,1658 @@
|
| +// Copyright 2014 The ChromeOS IME Authors. All Rights Reserved.
|
| +// limitations under the License.
|
| +// See the License for the specific language governing permissions and
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// distributed under the License is distributed on an "AS-IS" BASIS,
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// You may obtain a copy of the License at
|
| +// you may not use this file except in compliance with the License.
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +//
|
| +goog.provide('i18n.input.chrome.inputview.Controller');
|
| +
|
| +goog.require('goog.Disposable');
|
| +goog.require('goog.array');
|
| +goog.require('goog.async.Delay');
|
| +goog.require('goog.dom');
|
| +goog.require('goog.dom.classlist');
|
| +goog.require('goog.events.EventHandler');
|
| +goog.require('goog.events.EventType');
|
| +goog.require('goog.i18n.bidi');
|
| +goog.require('goog.object');
|
| +goog.require('i18n.input.chrome.DataSource');
|
| +goog.require('i18n.input.chrome.inputview.Adapter');
|
| +goog.require('i18n.input.chrome.inputview.CandidatesInfo');
|
| +goog.require('i18n.input.chrome.inputview.ConditionName');
|
| +goog.require('i18n.input.chrome.inputview.Css');
|
| +goog.require('i18n.input.chrome.inputview.KeyboardContainer');
|
| +goog.require('i18n.input.chrome.inputview.M17nModel');
|
| +goog.require('i18n.input.chrome.inputview.Model');
|
| +goog.require('i18n.input.chrome.inputview.PerfTracker');
|
| +goog.require('i18n.input.chrome.inputview.ReadyState');
|
| +goog.require('i18n.input.chrome.inputview.Settings');
|
| +goog.require('i18n.input.chrome.inputview.SizeSpec');
|
| +goog.require('i18n.input.chrome.inputview.SoundController');
|
| +goog.require('i18n.input.chrome.inputview.SpecNodeName');
|
| +goog.require('i18n.input.chrome.inputview.StateType');
|
| +goog.require('i18n.input.chrome.inputview.Statistics');
|
| +goog.require('i18n.input.chrome.inputview.SwipeDirection');
|
| +goog.require('i18n.input.chrome.inputview.elements.ElementType');
|
| +goog.require('i18n.input.chrome.inputview.elements.content.Candidate');
|
| +goog.require('i18n.input.chrome.inputview.elements.content.CandidateView');
|
| +goog.require('i18n.input.chrome.inputview.elements.content.ExpandedCandidateView');
|
| +goog.require('i18n.input.chrome.inputview.elements.content.MenuView');
|
| +goog.require('i18n.input.chrome.inputview.events.EventType');
|
| +goog.require('i18n.input.chrome.inputview.events.KeyCodes');
|
| +goog.require('i18n.input.chrome.inputview.handler.PointerHandler');
|
| +goog.require('i18n.input.chrome.inputview.util');
|
| +goog.require('i18n.input.chrome.message.ContextType');
|
| +goog.require('i18n.input.chrome.message.Name');
|
| +goog.require('i18n.input.chrome.message.Type');
|
| +goog.require('i18n.input.lang.InputToolCode');
|
| +
|
| +
|
| +
|
| +goog.scope(function() {
|
| +var CandidateType = i18n.input.chrome.inputview.elements.content.Candidate.Type;
|
| +var Candidate = i18n.input.chrome.inputview.elements.content.Candidate;
|
| +var CandidateView = i18n.input.chrome.inputview.elements.content.CandidateView;
|
| +var ConditionName = i18n.input.chrome.inputview.ConditionName;
|
| +var ContextType = i18n.input.chrome.message.ContextType;
|
| +var Css = i18n.input.chrome.inputview.Css;
|
| +var ElementType = i18n.input.chrome.inputview.elements.ElementType;
|
| +var EventType = i18n.input.chrome.inputview.events.EventType;
|
| +var ExpandedCandidateView = i18n.input.chrome.inputview.elements.content.
|
| + ExpandedCandidateView;
|
| +var InputToolCode = i18n.input.lang.InputToolCode;
|
| +var KeyCodes = i18n.input.chrome.inputview.events.KeyCodes;
|
| +var MenuView = i18n.input.chrome.inputview.elements.content.MenuView;
|
| +var Name = i18n.input.chrome.message.Name;
|
| +var PerfTracker = i18n.input.chrome.inputview.PerfTracker;
|
| +var SizeSpec = i18n.input.chrome.inputview.SizeSpec;
|
| +var SpecNodeName = i18n.input.chrome.inputview.SpecNodeName;
|
| +var StateType = i18n.input.chrome.inputview.StateType;
|
| +var content = i18n.input.chrome.inputview.elements.content;
|
| +var SoundController = i18n.input.chrome.inputview.SoundController;
|
| +var Sounds = i18n.input.chrome.inputview.Sounds;
|
| +var util = i18n.input.chrome.inputview.util;
|
| +
|
| +
|
| +
|
| +/**
|
| + * The controller of the input view keyboard.
|
| + *
|
| + * @param {string} keyset The keyboard keyset.
|
| + * @param {string} languageCode The language code for this keyboard.
|
| + * @param {string} passwordLayout The layout for password box.
|
| + * @param {string} name The input tool name.
|
| + * @constructor
|
| + * @extends {goog.Disposable}
|
| + */
|
| +i18n.input.chrome.inputview.Controller = function(keyset, languageCode,
|
| + passwordLayout, name) {
|
| + /**
|
| + * The model.
|
| + *
|
| + * @type {!i18n.input.chrome.inputview.Model}
|
| + * @private
|
| + */
|
| + this.model_ = new i18n.input.chrome.inputview.Model();
|
| +
|
| + /** @private {!i18n.input.chrome.inputview.PerfTracker} */
|
| + this.perfTracker_ = new i18n.input.chrome.inputview.PerfTracker();
|
| +
|
| + /**
|
| + * The layout map.
|
| + *
|
| + * @type {!Object.<string, !Object>}
|
| + * @private
|
| + */
|
| + this.layoutDataMap_ = {};
|
| +
|
| + /**
|
| + * The keyset data map.
|
| + *
|
| + * @type {!Object.<string, !Object>}
|
| + * @private
|
| + */
|
| + this.keysetDataMap_ = {};
|
| +
|
| + /**
|
| + * The event handler.
|
| + *
|
| + * @type {!goog.events.EventHandler}
|
| + * @private
|
| + */
|
| + this.handler_ = new goog.events.EventHandler(this);
|
| +
|
| + /**
|
| + * The m17n model.
|
| + *
|
| + * @type {!i18n.input.chrome.inputview.M17nModel}
|
| + * @private
|
| + */
|
| + this.m17nModel_ = new i18n.input.chrome.inputview.M17nModel();
|
| +
|
| + /**
|
| + * The pointer handler.
|
| + *
|
| + * @type {!i18n.input.chrome.inputview.handler.PointerHandler}
|
| + * @private
|
| + */
|
| + this.pointerHandler_ = new i18n.input.chrome.inputview.handler.
|
| + PointerHandler();
|
| +
|
| + /**
|
| + * The statistics object for recording metrics values.
|
| + *
|
| + * @type {!i18n.input.chrome.inputview.Statistics}
|
| + * @private
|
| + */
|
| + this.statistics_ = i18n.input.chrome.inputview.Statistics.getInstance();
|
| +
|
| + /** @private {!i18n.input.chrome.inputview.ReadyState} */
|
| + this.readyState_ = new i18n.input.chrome.inputview.ReadyState();
|
| +
|
| + /** @private {!i18n.input.chrome.inputview.Adapter} */
|
| + this.adapter_ = new i18n.input.chrome.inputview.Adapter(this.readyState_);
|
| +
|
| + /** @private {!i18n.input.chrome.inputview.KeyboardContainer} */
|
| + this.container_ = new i18n.input.chrome.inputview.KeyboardContainer(
|
| + this.adapter_);
|
| + this.container_.render();
|
| +
|
| + /** @private {!i18n.input.chrome.inputview.SoundController} */
|
| + this.soundController_ = new SoundController(false);
|
| +
|
| + /**
|
| + * The context type and keyset mapping group by input method id.
|
| + * key: input method id.
|
| + * value: Object
|
| + * key: context type string.
|
| + * value: keyset string.
|
| + *
|
| + * @private {!Object.<string, !Object.<string, string>>}
|
| + */
|
| + this.contextTypeToKeysetMap_ = {};
|
| +
|
| + this.initialize(keyset, languageCode, passwordLayout, name);
|
| + /**
|
| + * The suggestions.
|
| + * Note: sets a default empty result to avoid null check.
|
| + *
|
| + * @private {!i18n.input.chrome.inputview.CandidatesInfo}
|
| + */
|
| + this.candidatesInfo_ = i18n.input.chrome.inputview.CandidatesInfo.getEmpty();
|
| +
|
| + this.registerEventHandler_();
|
| +};
|
| +goog.inherits(i18n.input.chrome.inputview.Controller,
|
| + goog.Disposable);
|
| +var Controller = i18n.input.chrome.inputview.Controller;
|
| +
|
| +
|
| +/**
|
| + * @define {boolean} Flag to disable handwriting.
|
| + */
|
| +Controller.DISABLE_HWT = false;
|
| +
|
| +
|
| +/**
|
| + * A flag to indicate whether the shift is for auto capital.
|
| + *
|
| + * @private {boolean}
|
| + */
|
| +Controller.prototype.shiftForAutoCapital_ = false;
|
| +
|
| +
|
| +/**
|
| + * @define {boolean} Flag to indicate whether it is debugging.
|
| + */
|
| +Controller.DEV = false;
|
| +
|
| +
|
| +/**
|
| + * The handwriting view code, use the code can switch handwriting panel view.
|
| + *
|
| + * @const {string}
|
| + * @private
|
| + */
|
| +Controller.HANDWRITING_VIEW_CODE_ = 'hwt';
|
| +
|
| +
|
| +/**
|
| + * The emoji view code, use the code can switch to emoji.
|
| + *
|
| + * @const {string}
|
| + * @private
|
| + */
|
| +Controller.EMOJI_VIEW_CODE_ = 'emoji';
|
| +
|
| +
|
| +/**
|
| + * The limitation for backspace repeat time to avoid unexpected
|
| + * problem that backspace is held all the time.
|
| + *
|
| + * @private {number}
|
| + */
|
| +Controller.BACKSPACE_REPEAT_LIMIT_ = 255;
|
| +
|
| +
|
| +/**
|
| + * The repeated times of the backspace.
|
| + *
|
| + * @private {number}
|
| + */
|
| +Controller.prototype.backspaceRepeated_ = 0;
|
| +
|
| +
|
| +/**
|
| + * The handwriting input tool code suffix.
|
| + *
|
| + * @const {string}
|
| + * @private
|
| + */
|
| +Controller.HANDWRITING_CODE_SUFFIX_ = '-t-i0-handwrit';
|
| +
|
| +
|
| +/**
|
| + * True if the settings is loaded.
|
| + *
|
| + * @type {boolean}
|
| + */
|
| +Controller.prototype.isSettingReady = false;
|
| +
|
| +
|
| +/**
|
| + * True if the keyboard is set up.
|
| + * Note: This flag is only used for automation testing.
|
| + *
|
| + * @type {boolean}
|
| + */
|
| +Controller.prototype.isKeyboardReady = false;
|
| +
|
| +
|
| +/**
|
| + * The auto repeat timer for backspace hold.
|
| + *
|
| + * @type {goog.async.Delay}
|
| + * @private
|
| + */
|
| +Controller.prototype.backspaceAutoRepeat_;
|
| +
|
| +
|
| +/**
|
| + * The active keyboard code.
|
| + *
|
| + * @type {string}
|
| + * @private
|
| + */
|
| +Controller.prototype.currentKeyset_ = '';
|
| +
|
| +
|
| +/**
|
| + * The current input method id.
|
| + *
|
| + * @private {string}
|
| + */
|
| +Controller.prototype.currentInputmethod_ = '';
|
| +
|
| +
|
| +/**
|
| + * The operations on candidates.
|
| + *
|
| + * @enum {number}
|
| + */
|
| +Controller.CandidatesOperation = {
|
| + NONE: 0,
|
| + EXPAND: 1,
|
| + SHRINK: 2
|
| +};
|
| +
|
| +
|
| +/**
|
| + * The active language code.
|
| + *
|
| + * @type {string}
|
| + * @private
|
| + */
|
| +Controller.prototype.lang_;
|
| +
|
| +
|
| +/**
|
| + * The password keyset.
|
| + *
|
| + * @private {string}
|
| + */
|
| +Controller.prototype.passwordKeyset_ = '';
|
| +
|
| +
|
| +/**
|
| + * The soft key map, because key configuration is loaded before layout,
|
| + * controller needs this varaible to save it and hook into keyboard view.
|
| + *
|
| + * @type {!Array.<!content.SoftKey>}
|
| + * @private
|
| + */
|
| +Controller.prototype.softKeyList_;
|
| +
|
| +
|
| +/**
|
| + * The mapping from soft key id to soft key view id.
|
| + *
|
| + * @type {!Object.<string, string>}
|
| + * @private
|
| + */
|
| +Controller.prototype.mapping_;
|
| +
|
| +
|
| +/**
|
| + * The dead key.
|
| + *
|
| + * @type {string}
|
| + * @private
|
| + */
|
| +Controller.prototype.deadKey_ = '';
|
| +
|
| +
|
| +/**
|
| + * The input tool name.
|
| + *
|
| + * @type {string}
|
| + * @private
|
| + */
|
| +Controller.prototype.title_;
|
| +
|
| +
|
| +/**
|
| + * Registers event handlers.
|
| + * @private
|
| + */
|
| +Controller.prototype.registerEventHandler_ = function() {
|
| + this.handler_.
|
| + listen(this.model_,
|
| + EventType.LAYOUT_LOADED,
|
| + this.onLayoutLoaded_).
|
| + listen(this.model_,
|
| + EventType.CONFIG_LOADED,
|
| + this.onConfigLoaded_).
|
| + listen(this.m17nModel_,
|
| + EventType.CONFIG_LOADED,
|
| + this.onConfigLoaded_).
|
| + listen(this.pointerHandler_, [
|
| + EventType.LONG_PRESS,
|
| + EventType.CLICK,
|
| + EventType.DOUBLE_CLICK,
|
| + EventType.DOUBLE_CLICK_END,
|
| + EventType.POINTER_UP,
|
| + EventType.POINTER_DOWN,
|
| + EventType.POINTER_OVER,
|
| + EventType.POINTER_OUT,
|
| + EventType.SWIPE
|
| + ], this.onPointerEvent_).
|
| + listen(window, goog.events.EventType.RESIZE, this.resize).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.inputview.events.EventType.
|
| + SURROUNDING_TEXT_CHANGED,
|
| + this.onSurroundingTextChanged_).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.DataSource.EventType.CANDIDATES_BACK,
|
| + this.onCandidatesBack_).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.inputview.events.EventType.CONTEXT_FOCUS,
|
| + this.onContextFocus_).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.inputview.events.EventType.CONTEXT_BLUR,
|
| + this.onContextBlur_).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.inputview.events.EventType.VISIBILITY_CHANGE,
|
| + this.onVisibilityChange_).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.inputview.events.EventType.SETTINGS_READY,
|
| + this.onSettingsReady_).
|
| + listen(this.adapter_,
|
| + i18n.input.chrome.message.Type.UPDATE_SETTINGS,
|
| + this.onUpdateSettings_);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for updating settings.
|
| + *
|
| + * @param {!i18n.input.chrome.message.Event} e .
|
| + * @private
|
| + */
|
| +Controller.prototype.onUpdateSettings_ = function(e) {
|
| + var settings = this.model_.settings;
|
| + if (goog.isDef(e.msg['autoSpace'])) {
|
| + settings.autoSpace = e.msg['autoSpace'];
|
| + }
|
| + if (goog.isDef(e.msg['autoCapital'])) {
|
| + settings.autoCapital = e.msg['autoCapital'];
|
| + }
|
| + if (goog.isDef(e.msg['candidatesNavigation'])) {
|
| + settings.candidatesNavigation = e.msg['candidatesNavigation'];
|
| + }
|
| + if (goog.isDef(e.msg['supportCompact'])) {
|
| + settings.supportCompact = e.msg['supportCompact'];
|
| + }
|
| + if (goog.isDef(e.msg[Name.KEYSET])) {
|
| + this.contextTypeToKeysetMap_[this.currentInputMethod_][
|
| + ContextType.DEFAULT] = e.msg[Name.KEYSET];
|
| + }
|
| + if (goog.isDef(e.msg['enableLongPress'])) {
|
| + settings.enableLongPress = e.msg['enableLongPress'];
|
| + }
|
| + if (goog.isDef(e.msg['doubleSpacePeriod'])) {
|
| + settings.doubleSpacePeriod = e.msg['doubleSpacePeriod'];
|
| + }
|
| + if (goog.isDef(e.msg['soundOnKeypress'])) {
|
| + settings.soundOnKeypress = e.msg['soundOnKeypress'];
|
| + this.soundController_.setEnabled(settings.soundOnKeypress);
|
| + }
|
| + this.perfTracker_.tick(PerfTracker.TickName.BACKGROUND_SETTINGS_FETCHED);
|
| + var isPassword = this.adapter_.isPasswordBox();
|
| + this.switchToKeySet(this.getActiveKeyset_());
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for setting ready.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.onSettingsReady_ = function() {
|
| + if (this.isSettingReady) {
|
| + return;
|
| + }
|
| +
|
| + this.isSettingReady = true;
|
| + var keysetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
|
| + if (this.adapter_.isA11yMode) {
|
| + keysetMap[ContextType.PASSWORD] = keysetMap[ContextType.DEFAULT] =
|
| + util.getConfigName(keysetMap[ContextType.DEFAULT]);
|
| + } else {
|
| + var preferredKeyset = /** @type {string} */ (this.model_.settings.
|
| + getPreference(util.getConfigName(
|
| + keysetMap[ContextType.DEFAULT])));
|
| + if (preferredKeyset) {
|
| + keysetMap[ContextType.PASSWORD] = keysetMap[ContextType.DEFAULT] =
|
| + preferredKeyset;
|
| + }
|
| + }
|
| + this.maybeCreateViews_();
|
| + this.switchToKeySet(this.getActiveKeyset_());
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the data for spatial module.
|
| + *
|
| + * @param {!content.SoftKey} key .
|
| + * @param {number} x The x-offset of the touch point.
|
| + * @param {number} y The y-offset of the touch point.
|
| + * @return {!Object} .
|
| + * @private
|
| + */
|
| +Controller.prototype.getSpatialData_ = function(key, x, y) {
|
| + var items = [];
|
| + items.push([this.getKeyContent_(key), key.estimator.estimateInLogSpace(x, y)
|
| + ]);
|
| + for (var i = 0; i < key.nearbyKeys.length; i++) {
|
| + var nearByKey = key.nearbyKeys[i];
|
| + var content = this.getKeyContent_(nearByKey);
|
| + if (content && util.REGEX_LANGUAGE_MODEL_CHARACTERS.test(content)) {
|
| + items.push([content, nearByKey.estimator.estimateInLogSpace(x, y)]);
|
| + }
|
| + }
|
| + goog.array.sort(items, function(item1, item2) {
|
| + return item1[1] - item2[1];
|
| + });
|
| + var sources = items.map(function(item) {
|
| + return item[0].toLowerCase();
|
| + });
|
| + var possibilities = items.map(function(item) {
|
| + return item[1];
|
| + });
|
| + return {
|
| + 'sources': sources,
|
| + 'possibilities': possibilities
|
| + };
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the key content.
|
| + *
|
| + * @param {!content.SoftKey} key .
|
| + * @return {string} .
|
| + * @private
|
| + */
|
| +Controller.prototype.getKeyContent_ = function(key) {
|
| + if (key.type == i18n.input.chrome.inputview.elements.ElementType.
|
| + CHARACTER_KEY) {
|
| + key = /** @type {!content.CharacterKey} */ (key);
|
| + return key.getActiveCharacter();
|
| + }
|
| + if (key.type == i18n.input.chrome.inputview.elements.ElementType.
|
| + COMPACT_KEY) {
|
| + key = /** @type {!content.FunctionalKey} */ (key);
|
| + return key.text;
|
| + }
|
| + return '';
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for pointer event.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.events.PointerEvent} e .
|
| + * @private
|
| + */
|
| +Controller.prototype.onPointerEvent_ = function(e) {
|
| + if ((this.adapter_.isChromeVoxOn || !this.model_.settings.enableLongPress) &&
|
| + e.type == EventType.LONG_PRESS) {
|
| + return;
|
| + }
|
| +
|
| + if (e.view) {
|
| + this.handlePointerAction_(e.view, e);
|
| + } else {
|
| + var tabbableKeysets = [
|
| + Controller.HANDWRITING_VIEW_CODE_,
|
| + Controller.EMOJI_VIEW_CODE_];
|
| + if (goog.array.contains(tabbableKeysets, this.currentKeyset_)) {
|
| + this.resetAll_();
|
| + this.switchToKeySet(this.container_.currentKeysetView.fromKeyset);
|
| + }
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Handles the swipe action.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.elements.Element} view The view, for
|
| + * swipe event, the view would be the soft key which starts the swipe.
|
| + * @param {!i18n.input.chrome.inputview.events.SwipeEvent} e The swipe event.
|
| + * @private
|
| + */
|
| +Controller.prototype.handleSwipeAction_ = function(view, e) {
|
| + var direction = e.direction;
|
| + if (this.container_.altDataView.isVisible()) {
|
| + this.container_.altDataView.highlightItem(e.x, e.y);
|
| + return;
|
| + }
|
| +
|
| + if (view.type == ElementType.CHARACTER_KEY) {
|
| + view = /** @type {!content.CharacterKey} */ (view);
|
| + if (direction & i18n.input.chrome.inputview.SwipeDirection.UP ||
|
| + direction & i18n.input.chrome.inputview.SwipeDirection.DOWN) {
|
| + var ch = view.getCharacterByGesture(!!(direction &
|
| + i18n.input.chrome.inputview.SwipeDirection.UP));
|
| + if (ch) {
|
| + view.flickerredCharacter = ch;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (view.type == ElementType.COMPACT_KEY) {
|
| + view = /** @type {!content.CompactKey} */ (view);
|
| + if ((direction & i18n.input.chrome.inputview.SwipeDirection.UP) &&
|
| + view.hintText) {
|
| + view.flickerredCharacter = view.hintText;
|
| + }
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Execute a command.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.elements.content.MenuView.Command}
|
| + * command The command that about to be executed.
|
| + * @param {string=} opt_arg The optional command argument.
|
| + * @private
|
| + */
|
| +Controller.prototype.executeCommand_ = function(command, opt_arg) {
|
| + var CommandEnum = MenuView.Command;
|
| + switch (command) {
|
| + case CommandEnum.SWITCH_IME:
|
| + var inputMethodId = opt_arg;
|
| + if (inputMethodId) {
|
| + this.adapter_.switchToInputMethod(inputMethodId);
|
| + }
|
| + break;
|
| +
|
| + case CommandEnum.SWITCH_KEYSET:
|
| + var keyset = opt_arg;
|
| + if (keyset) {
|
| + this.statistics_.recordSwitch(keyset);
|
| + this.switchToKeySet(keyset);
|
| + }
|
| + break;
|
| + case CommandEnum.OPEN_EMOJI:
|
| + this.switchToKeySet(Controller.EMOJI_VIEW_CODE_);
|
| + break;
|
| +
|
| + case CommandEnum.OPEN_HANDWRITING:
|
| + // TODO: remember handwriting keyset.
|
| + this.switchToKeySet(Controller.HANDWRITING_VIEW_CODE_);
|
| + break;
|
| +
|
| + case CommandEnum.OPEN_SETTING:
|
| + if (window.inputview) {
|
| + inputview.openSettings();
|
| + }
|
| + break;
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Handles the pointer action.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.elements.Element} view The view.
|
| + * @param {!i18n.input.chrome.inputview.events.PointerEvent} e .
|
| + * @private
|
| + */
|
| +Controller.prototype.handlePointerAction_ = function(view, e) {
|
| + if (e.type == i18n.input.chrome.inputview.events.EventType.SWIPE) {
|
| + e = /** @type {!i18n.input.chrome.inputview.events.SwipeEvent} */ (e);
|
| + this.handleSwipeAction_(view, e);
|
| + }
|
| + switch (view.type) {
|
| + case ElementType.BACK_BUTTON:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.switchToKeySet(this.container_.currentKeysetView.fromKeyset);
|
| + }
|
| + return;
|
| + case ElementType.EXPAND_CANDIDATES:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.showCandidates_(this.candidatesInfo_.source,
|
| + this.candidatesInfo_.candidates,
|
| + Controller.CandidatesOperation.EXPAND);
|
| + }
|
| + return;
|
| + case ElementType.SHRINK_CANDIDATES:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.showCandidates_(this.candidatesInfo_.source,
|
| + this.candidatesInfo_.candidates,
|
| + Controller.CandidatesOperation.SHRINK);
|
| + }
|
| + return;
|
| + case ElementType.CANDIDATE:
|
| + view = /** @type {!Candidate} */ (view);
|
| + if (e.type == EventType.POINTER_UP) {
|
| + if (view.candidateType == CandidateType.CANDIDATE) {
|
| + this.adapter_.selectCandidate(view.candidate);
|
| + } else if (view.candidateType == CandidateType.NUMBER) {
|
| + this.adapter_.commitText(view.candidate[Name.CANDIDATE]);
|
| + }
|
| + this.container_.cleanStroke();
|
| + }
|
| + if (e.type == EventType.POINTER_OUT || e.type == EventType.POINTER_UP) {
|
| + view.setHighlighted(false);
|
| + } else if (e.type == EventType.POINTER_DOWN ||
|
| + e.type == EventType.POINTER_OVER) {
|
| + view.setHighlighted(true);
|
| + }
|
| + return;
|
| +
|
| + case ElementType.ALTDATA_VIEW:
|
| + view = /** @type {!content.AltDataView} */ (view);
|
| + if (e.type == EventType.POINTER_DOWN &&
|
| + e.target == view.getCoverElement()) {
|
| + view.hide();
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + var ch = view.getHighlightedCharacter();
|
| + this.adapter_.sendKeyDownAndUpEvent(ch, view.triggeredBy.id,
|
| + view.triggeredBy.keyCode,
|
| + {'sources': [ch.toLowerCase()], 'possibilities': [1]});
|
| + view.hide();
|
| + this.clearUnstickyState_();
|
| + }
|
| + return;
|
| +
|
| + case ElementType.MENU_ITEM:
|
| + view = /** @type {!content.MenuItem} */ (view);
|
| + if (e.type == EventType.CLICK) {
|
| + this.resetAll_();
|
| + this.executeCommand_.apply(this, view.getCommand());
|
| + this.container_.menuView.hide();
|
| + }
|
| + view.setHighlighted(e.type == EventType.POINTER_DOWN ||
|
| + e.type == EventType.POINTER_OVER);
|
| + // TODO: Add chrome vox support.
|
| + return;
|
| +
|
| + case ElementType.MENU_VIEW:
|
| + view = /** @type {!MenuView} */ (view);
|
| +
|
| + if (e.type == EventType.POINTER_DOWN &&
|
| + e.target == view.getCoverElement()) {
|
| + view.hide();
|
| + }
|
| + return;
|
| +
|
| + case ElementType.EMOJI_KEY:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + if (!this.container_.currentKeysetView.isDragging && view.text != '') {
|
| + this.adapter_.commitText(view.text);
|
| + }
|
| + }
|
| + return;
|
| +
|
| + case ElementType.HWT_PRIVACY_GOT_IT:
|
| + this.adapter_.sendHwtPrivacyConfirmMessage();
|
| + return;
|
| +
|
| + case ElementType.SOFT_KEY_VIEW:
|
| + // Delegates the events on the soft key view to its soft key.
|
| + view = /** @type {!i18n.input.chrome.inputview.elements.layout.
|
| + SoftKeyView} */ (view);
|
| + if (!view.softKey) {
|
| + return;
|
| + }
|
| + view = view.softKey;
|
| + }
|
| +
|
| + if (view.type != ElementType.MODIFIER_KEY &&
|
| + !this.container_.altDataView.isVisible() &&
|
| + !this.container_.menuView.isVisible()) {
|
| + // The highlight of the modifier key is depending on the state instead
|
| + // of the key down or up.
|
| + if (e.type == EventType.POINTER_OVER || e.type == EventType.POINTER_DOWN ||
|
| + e.type == EventType.DOUBLE_CLICK) {
|
| + view.setHighlighted(true);
|
| + } else if (e.type == EventType.POINTER_OUT ||
|
| + e.type == EventType.POINTER_UP ||
|
| + e.type == EventType.DOUBLE_CLICK_END) {
|
| + view.setHighlighted(false);
|
| + }
|
| + }
|
| + this.handlePointerEventForSoftKey_(
|
| + /** @type {!content.SoftKey} */ (view), e);
|
| + this.updateContextModifierState_();
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Handles softkey of the pointer action.
|
| + *
|
| + * @param {!content.SoftKey} softKey .
|
| + * @param {!i18n.input.chrome.inputview.events.PointerEvent} e .
|
| + * @private
|
| + */
|
| +Controller.prototype.handlePointerEventForSoftKey_ = function(softKey, e) {
|
| + var key;
|
| + switch (softKey.type) {
|
| + case ElementType.CANDIDATES_PAGE_UP:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.container_.expandedCandidateView.pageUp();
|
| + }
|
| + break;
|
| + case ElementType.CANDIDATES_PAGE_DOWN:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.container_.expandedCandidateView.pageDown();
|
| + }
|
| + break;
|
| + case ElementType.CHARACTER_KEY:
|
| + key = /** @type {!content.CharacterKey} */ (softKey);
|
| + if (e.type == EventType.LONG_PRESS) {
|
| + this.container_.altDataView.show(
|
| + key, goog.i18n.bidi.isRtlLanguage(this.languageCode_));
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.model_.stateManager.triggerChording();
|
| + var ch = key.getActiveCharacter();
|
| + this.adapter_.sendKeyDownAndUpEvent(ch, key.id, key.keyCode,
|
| + this.getSpatialData_(key, e.x, e.y));
|
| + this.clearUnstickyState_();
|
| + key.flickerredCharacter = '';
|
| + }
|
| + break;
|
| +
|
| + case ElementType.MODIFIER_KEY:
|
| + key = /** @type {!content.ModifierKey} */ (softKey);
|
| + var isStateEnabled = this.model_.stateManager.hasState(key.toState);
|
| + var isChording = this.model_.stateManager.isChording(key.toState);
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.changeState_(key.toState, !isStateEnabled, true);
|
| + this.model_.stateManager.setKeyDown(key.toState, true);
|
| + } else if (e.type == EventType.POINTER_UP || e.type == EventType.
|
| + POINTER_OUT) {
|
| + if (isChording) {
|
| + this.changeState_(key.toState, false, false);
|
| + } else if (key.toState != StateType.CAPSLOCK &&
|
| + this.model_.stateManager.isKeyDown(key.toState)) {
|
| + this.changeState_(key.toState, isStateEnabled, false);
|
| + }
|
| + this.model_.stateManager.setKeyDown(key.toState, false);
|
| + } else if (e.type == EventType.DOUBLE_CLICK) {
|
| + this.changeState_(key.toState, isStateEnabled, true);
|
| + } else if (e.type == EventType.LONG_PRESS) {
|
| + if (!isChording) {
|
| + this.changeState_(key.toState, true, true);
|
| + this.model_.stateManager.setKeyDown(key.toState, false);
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case ElementType.BACKSPACE_KEY:
|
| + key = /** @type {!content.FunctionalKey} */ (softKey);
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.backspaceTick_();
|
| + } else if (e.type == EventType.POINTER_UP || e.type == EventType.
|
| + POINTER_OUT) {
|
| + this.stopBackspaceAutoRepeat_();
|
| + this.adapter_.sendKeyUpEvent('\u0008', KeyCodes.BACKSPACE);
|
| + }
|
| + break;
|
| +
|
| + case ElementType.TAB_KEY:
|
| + key = /** @type {!content.FunctionalKey} */ (softKey);
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.adapter_.sendKeyDownEvent('\u0009', KeyCodes.TAB);
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.sendKeyUpEvent('\u0009', KeyCodes.TAB);
|
| + }
|
| + break;
|
| +
|
| + case ElementType.ENTER_KEY:
|
| + key = /** @type {!content.FunctionalKey} */ (softKey);
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.adapter_.sendKeyDownEvent('\u000D', KeyCodes.ENTER);
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.sendKeyUpEvent('\u000D', KeyCodes.ENTER);
|
| + }
|
| + break;
|
| +
|
| + case ElementType.ARROW_UP:
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_UP);
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_UP);
|
| + }
|
| + break;
|
| +
|
| + case ElementType.ARROW_DOWN:
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_DOWN);
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_DOWN);
|
| + }
|
| + break;
|
| +
|
| + case ElementType.ARROW_LEFT:
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_LEFT);
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_LEFT);
|
| + }
|
| + break;
|
| +
|
| + case ElementType.ARROW_RIGHT:
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + this.adapter_.sendKeyDownEvent('', KeyCodes.ARROW_RIGHT);
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.sendKeyUpEvent('', KeyCodes.ARROW_RIGHT);
|
| + }
|
| + break;
|
| + case ElementType.EN_SWITCHER:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + key = /** @type {!content.EnSwitcherKey} */ (softKey);
|
| + this.adapter_.toggleLanguageState(this.model_.stateManager.isEnMode);
|
| + this.model_.stateManager.isEnMode = !this.model_.stateManager.isEnMode;
|
| + key.update();
|
| + }
|
| + break;
|
| + case ElementType.SPACE_KEY:
|
| + key = /** @type {!content.SpaceKey} */ (softKey);
|
| + var doubleSpacePeriod = this.model_.settings.doubleSpacePeriod;
|
| + if (e.type == EventType.POINTER_UP || (!doubleSpacePeriod && e.type ==
|
| + EventType.DOUBLE_CLICK_END)) {
|
| + this.adapter_.sendKeyDownAndUpEvent(key.getCharacter(),
|
| + KeyCodes.SPACE);
|
| + this.clearUnstickyState_();
|
| + } else if (e.type == EventType.DOUBLE_CLICK && doubleSpacePeriod) {
|
| + this.adapter_.doubleClickOnSpaceKey();
|
| + }
|
| + break;
|
| +
|
| + case ElementType.SWITCHER_KEY:
|
| + key = /** @type {!content.SwitcherKey} */ (softKey);
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.statistics_.recordSwitch(key.toKeyset);
|
| + if (this.isSubKeyset_(key.toKeyset, this.currentKeyset_)) {
|
| + this.model_.stateManager.reset();
|
| + this.container_.update();
|
| + this.updateContextModifierState_();
|
| + this.container_.menuView.hide();
|
| + } else {
|
| + this.resetAll_();
|
| + }
|
| + // Switch to the specific keyboard.
|
| + this.switchToKeySet(key.toKeyset);
|
| + if (key.record) {
|
| + this.model_.settings.savePreference(
|
| + util.getConfigName(key.toKeyset),
|
| + key.toKeyset);
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case ElementType.COMPACT_KEY:
|
| + key = /** @type {!content.CompactKey} */ (softKey);
|
| + if (e.type == EventType.LONG_PRESS) {
|
| + this.container_.altDataView.show(
|
| + key, goog.i18n.bidi.isRtlLanguage(this.languageCode_));
|
| + } else if (e.type == EventType.POINTER_UP) {
|
| + this.model_.stateManager.triggerChording();
|
| + this.adapter_.sendKeyDownAndUpEvent(key.getActiveCharacter(), '', 0,
|
| + this.getSpatialData_(key, e.x, e.y));
|
| + this.clearUnstickyState_();
|
| + key.flickerredCharacter = '';
|
| + }
|
| + break;
|
| +
|
| + case ElementType.HIDE_KEYBOARD_KEY:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.hideKeyboard();
|
| + }
|
| + break;
|
| +
|
| + case ElementType.MENU_KEY:
|
| + key = /** @type {!content.MenuKey} */ (softKey);
|
| + if (e.type == EventType.POINTER_DOWN) {
|
| + var isCompact = this.currentKeyset_.indexOf('compact') != -1;
|
| + var remappedKeyset = this.getRemappedKeyset_(this.currentKeyset_);
|
| + var keysetData = this.keysetDataMap_[remappedKeyset];
|
| + var enableCompact = !this.adapter_.isA11yMode &&
|
| + !!keysetData[SpecNodeName.HAS_COMPACT_KEYBOARD] &&
|
| + this.model_.settings.supportCompact;
|
| + var self = this;
|
| + var currentKeyset = this.currentKeyset_;
|
| + var hasHwt = !this.adapter_.isPasswordBox() &&
|
| + !Controller.DISABLE_HWT && goog.object.contains(
|
| + InputToolCode, this.getHwtInputToolCode_());
|
| + var enableSettings = this.shouldEnableSettings() &&
|
| + window.inputview && inputview.openSettings;
|
| + this.adapter_.getInputMethods(function(inputMethods) {
|
| + this.container_.menuView.show(key, currentKeyset, isCompact,
|
| + enableCompact, this.currentInputMethod_, inputMethods, hasHwt,
|
| + enableSettings, this.adapter_.isExperimental);
|
| + }.bind(this));
|
| + }
|
| + break;
|
| +
|
| + case ElementType.GLOBE_KEY:
|
| + if (e.type == EventType.POINTER_UP) {
|
| + this.adapter_.clearModifierStates();
|
| + this.adapter_.setModifierState(
|
| + i18n.input.chrome.inputview.StateType.CTRL, true);
|
| + this.adapter_.sendKeyDownAndUpEvent(' ', KeyCodes.SPACE, 0x20);
|
| + this.adapter_.setModifierState(
|
| + i18n.input.chrome.inputview.StateType.CTRL, false);
|
| + }
|
| + break;
|
| + case ElementType.IME_SWITCH:
|
| + key = /** @type {!content.FunctionalKey} */ (softKey);
|
| + this.adapter_.sendKeyDownAndUpEvent('', key.id);
|
| + break;
|
| + }
|
| + // Play key sound on pointer up.
|
| + if (e.type == EventType.POINTER_UP)
|
| + this.soundController_.onKeyUp(softKey.type);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Clears unsticky state.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.clearUnstickyState_ = function() {
|
| + if (this.model_.stateManager.hasUnStickyState()) {
|
| + for (var key in StateType) {
|
| + var value = StateType[key];
|
| + if (this.model_.stateManager.hasState(value) &&
|
| + !this.model_.stateManager.isSticky(value)) {
|
| + this.changeState_(value, false, false);
|
| + }
|
| + }
|
| + }
|
| + this.container_.update();
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Stops the auto-repeat for backspace.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.stopBackspaceAutoRepeat_ = function() {
|
| + goog.dispose(this.backspaceAutoRepeat_);
|
| + this.backspaceAutoRepeat_ = null;
|
| + this.adapter_.sendKeyUpEvent('\u0008', KeyCodes.BACKSPACE);
|
| + this.backspaceRepeated_ = 0;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * The tick for the backspace key.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.backspaceTick_ = function() {
|
| + if (this.backspaceRepeated_ >= Controller.BACKSPACE_REPEAT_LIMIT_) {
|
| + this.stopBackspaceAutoRepeat_();
|
| + return;
|
| + }
|
| + this.backspaceRepeated_++;
|
| + this.backspaceDown_();
|
| + this.soundController_.onKeyRepeat(ElementType.BACKSPACE_KEY);
|
| +
|
| + if (this.backspaceAutoRepeat_) {
|
| + this.backspaceAutoRepeat_.start(75);
|
| + } else {
|
| + this.backspaceAutoRepeat_ = new goog.async.Delay(
|
| + goog.bind(this.backspaceTick_, this), 300);
|
| + this.backspaceAutoRepeat_.start();
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for VISIBILITY_CHANGE.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.onVisibilityChange_ = function() {
|
| + if (!this.adapter_.isVisible) {
|
| + this.resetAll_();
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Resets the whole keyboard include clearing candidates,
|
| + * reset modifier state, etc.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.resetAll_ = function() {
|
| + this.clearCandidates_();
|
| + this.container_.candidateView.hideNumberRow();
|
| + this.model_.stateManager.reset();
|
| + this.container_.update();
|
| + this.updateContextModifierState_();
|
| + this.deadKey_ = '';
|
| + this.resize();
|
| + this.container_.expandedCandidateView.close();
|
| + this.container_.menuView.hide();
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback when the context is changed.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.onContextFocus_ = function() {
|
| + this.resetAll_();
|
| + this.switchToKeySet(this.getActiveKeyset_());
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback when surrounding text is changed.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.events.SurroundingTextChangedEvent} e .
|
| + * @private
|
| + */
|
| +Controller.prototype.onSurroundingTextChanged_ = function(e) {
|
| + if (!this.model_.settings.autoCapital || !e.text) {
|
| + return;
|
| + }
|
| +
|
| + var isShiftEnabled = this.model_.stateManager.hasState(StateType.SHIFT);
|
| + var needAutoCap = this.model_.settings.autoCapital &&
|
| + util.needAutoCap(e.text);
|
| + if (needAutoCap && !isShiftEnabled) {
|
| + this.changeState_(StateType.SHIFT, true, false);
|
| + this.shiftForAutoCapital_ = true;
|
| + } else if (!needAutoCap && this.shiftForAutoCapital_) {
|
| + this.changeState_(StateType.SHIFT, false, false);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for Context blurs.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.onContextBlur_ = function() {
|
| + this.clearCandidates_();
|
| + this.deadKey_ = '';
|
| + this.container_.menuView.hide();
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Backspace key is down.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.backspaceDown_ = function() {
|
| + if (this.container_.hasStrokesOnCanvas()) {
|
| + this.clearCandidates_();
|
| + this.container_.cleanStroke();
|
| + } else {
|
| + this.adapter_.sendKeyDownEvent('\u0008', KeyCodes.BACKSPACE);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for state change.
|
| + *
|
| + * @param {StateType} stateType The state type.
|
| + * @param {boolean} enable True to enable the state.
|
| + * @param {boolean} isSticky True to make the state sticky.
|
| + * @private
|
| + */
|
| +Controller.prototype.changeState_ = function(stateType, enable, isSticky) {
|
| + if (stateType == StateType.ALTGR) {
|
| + var code = KeyCodes.ALT_RIGHT;
|
| + if (enable) {
|
| + this.adapter_.sendKeyDownEvent('', code);
|
| + } else {
|
| + this.adapter_.sendKeyUpEvent('', code);
|
| + }
|
| + }
|
| + if (stateType == StateType.SHIFT) {
|
| + this.shiftForAutoCapital_ = false;
|
| + }
|
| + var isEnabledBefore = this.model_.stateManager.hasState(stateType);
|
| + var isStickyBefore = this.model_.stateManager.isSticky(stateType);
|
| + this.model_.stateManager.setState(stateType, enable);
|
| + this.model_.stateManager.setSticky(stateType, isSticky);
|
| + if (isEnabledBefore != enable || isStickyBefore != isSticky) {
|
| + this.container_.update();
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Updates the modifier state for context.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.updateContextModifierState_ = function() {
|
| + var stateManager = this.model_.stateManager;
|
| + this.adapter_.setModifierState(StateType.ALT,
|
| + stateManager.hasState(StateType.ALT));
|
| + this.adapter_.setModifierState(StateType.CTRL,
|
| + stateManager.hasState(StateType.CTRL));
|
| + this.adapter_.setModifierState(StateType.CAPSLOCK,
|
| + stateManager.hasState(StateType.CAPSLOCK));
|
| + if (!this.shiftForAutoCapital_) {
|
| + // If shift key is automatically on because of feature - autoCapital,
|
| + // Don't set modifier state to adapter.
|
| + this.adapter_.setModifierState(StateType.SHIFT,
|
| + stateManager.hasState(StateType.SHIFT));
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback for AUTO-COMPLETE event.
|
| + *
|
| + * @param {!i18n.input.chrome.DataSource.CandidatesBackEvent} e .
|
| + * @private
|
| + */
|
| +Controller.prototype.onCandidatesBack_ = function(e) {
|
| + this.candidatesInfo_ = new i18n.input.chrome.inputview.CandidatesInfo(
|
| + e.source, e.candidates);
|
| + this.showCandidates_(e.source, e.candidates, Controller.CandidatesOperation.
|
| + NONE);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Shows the candidates to the candidate view.
|
| + *
|
| + * @param {string} source The source text.
|
| + * @param {!Array.<!Object>} candidates The candidate text list.
|
| + * @param {Controller.CandidatesOperation} operation .
|
| + * @private
|
| + */
|
| +Controller.prototype.showCandidates_ = function(source, candidates,
|
| + operation) {
|
| + var state = !!source ? ExpandedCandidateView.State.COMPLETION_CORRECTION :
|
| + ExpandedCandidateView.State.PREDICTION;
|
| + var expandView = this.container_.expandedCandidateView;
|
| + var expand = false;
|
| + if (operation == Controller.CandidatesOperation.NONE) {
|
| + expand = expandView.state == state;
|
| + } else {
|
| + expand = operation == Controller.CandidatesOperation.EXPAND;
|
| + }
|
| +
|
| + if (candidates.length == 0) {
|
| + this.clearCandidates_();
|
| + expandView.state = ExpandedCandidateView.State.NONE;
|
| + return;
|
| + }
|
| +
|
| + // The compact pinyin needs full candidates instead of three candidates.
|
| + var isThreeCandidates = this.currentKeyset_.indexOf('compact') != -1 &&
|
| + this.currentKeyset_.indexOf('pinyin-zh-CN') == -1;
|
| + if (isThreeCandidates) {
|
| + if (candidates.length > 1) {
|
| + // Swap the first candidate and the second candidate.
|
| + var tmp = candidates[0];
|
| + candidates[0] = candidates[1];
|
| + candidates[1] = tmp;
|
| + }
|
| + }
|
| + var isHwt = Controller.HANDWRITING_VIEW_CODE_ == this.currentKeyset_;
|
| + this.container_.candidateView.showCandidates(candidates, isThreeCandidates,
|
| + this.model_.settings.candidatesNavigation && !isHwt);
|
| +
|
| + if (expand) {
|
| + expandView.state = state;
|
| + this.container_.currentKeysetView.setVisible(false);
|
| + expandView.showCandidates(candidates,
|
| + this.container_.candidateView.candidateCount);
|
| + this.container_.candidateView.switchToIcon(CandidateView.IconType.
|
| + SHRINK_CANDIDATES, true);
|
| + } else {
|
| + expandView.state = ExpandedCandidateView.State.NONE;
|
| + expandView.setVisible(false);
|
| + this.container_.currentKeysetView.setVisible(true);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Clears candidates.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.clearCandidates_ = function() {
|
| + this.candidatesInfo_ = i18n.input.chrome.inputview.CandidatesInfo.getEmpty();
|
| + this.container_.candidateView.clearCandidates();
|
| + this.container_.expandedCandidateView.close();
|
| + this.container_.expandedCandidateView.state = ExpandedCandidateView.State.
|
| + NONE;
|
| + if (this.container_.currentKeysetView) {
|
| + this.container_.currentKeysetView.setVisible(true);
|
| + }
|
| + this.container_.candidateView.switchToIcon(CandidateView.IconType.BACK,
|
| + Controller.HANDWRITING_VIEW_CODE_ == this.currentKeyset_);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback when the layout is loaded.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.events.LayoutLoadedEvent} e The event.
|
| + * @private
|
| + */
|
| +Controller.prototype.onLayoutLoaded_ = function(e) {
|
| + var layoutID = e.data['layoutID'];
|
| + this.layoutDataMap_[layoutID] = e.data;
|
| + this.perfTracker_.tick(PerfTracker.TickName.LAYOUT_LOADED);
|
| + this.maybeCreateViews_();
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Creates the whole view.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.maybeCreateViews_ = function() {
|
| + if (!this.isSettingReady) {
|
| + return;
|
| + }
|
| +
|
| + for (var keyset in this.keysetDataMap_) {
|
| + var keysetData = this.keysetDataMap_[keyset];
|
| + var layoutId = keysetData[SpecNodeName.LAYOUT];
|
| + var layoutData = this.layoutDataMap_[layoutId];
|
| + if (!this.container_.keysetViewMap[keyset] && layoutData) {
|
| + var conditions = {};
|
| + conditions[ConditionName.SHOW_ALTGR] =
|
| + keysetData[SpecNodeName.HAS_ALTGR_KEY];
|
| +
|
| + conditions[ConditionName.SHOW_MENU] =
|
| + keysetData[SpecNodeName.SHOW_MENU_KEY];
|
| + // In symbol and more keysets, we want to show a symbol key in the globe
|
| + // SoftKeyView. So this view should alway visible in the two keysets.
|
| + // Currently, SHOW_MENU_KEY is false for the two keysets, so we use
|
| + // !keysetData[SpecNodeName.SHOW_MENU_KEY] here.
|
| + conditions[ConditionName.SHOW_GLOBE_OR_SYMBOL] =
|
| + !keysetData[SpecNodeName.SHOW_MENU_KEY] ||
|
| + this.adapter_.showGlobeKey;
|
| + conditions[ConditionName.SHOW_EN_SWITCHER_KEY] = false;
|
| +
|
| + // If the view for the keyboard code doesn't exist, and the layout
|
| + // data is ready, then creates the view.
|
| + this.container_.addKeysetView(keysetData, layoutData, keyset,
|
| + this.languageCode_, this.model_, this.title_, conditions);
|
| + this.perfTracker_.tick(PerfTracker.TickName.KEYBOARD_CREATED);
|
| + }
|
| + }
|
| + this.switchToKeySet(this.getActiveKeyset_());
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Switch to a specific keyboard.
|
| + *
|
| + * @param {string} keyset The keyset name.
|
| + */
|
| +Controller.prototype.switchToKeySet = function(keyset) {
|
| + if (!this.isSettingReady) {
|
| + return;
|
| + }
|
| +
|
| + var lastKeysetView = this.container_.currentKeysetView;
|
| + var ret = this.container_.switchToKeyset(this.getRemappedKeyset_(keyset),
|
| + this.title_, this.adapter_.isPasswordBox(), this.adapter_.isA11yMode,
|
| + keyset, this.currentKeyset_, this.languageCode_);
|
| +
|
| + // Update the keyset of current context type.
|
| + this.contextTypeToKeysetMap_[this.currentInputMethod_][
|
| + this.adapter_.getContextType()] = keyset;
|
| +
|
| + if (ret) {
|
| + this.updateLanguageState_(this.currentKeyset_, keyset);
|
| + // Deactivate the last keyset view instance.
|
| + if (lastKeysetView) {
|
| + lastKeysetView.deactivate(this.currentKeyset_);
|
| + }
|
| + this.currentKeyset_ = keyset;
|
| +
|
| + this.resize(Controller.DEV);
|
| + this.statistics_.setCurrentLayout(keyset);
|
| + // Activate the current key set view instance.
|
| + this.container_.currentKeysetView.activate(keyset);
|
| + this.perfTracker_.tick(PerfTracker.TickName.KEYBOARD_SHOWN);
|
| + this.perfTracker_.stop();
|
| + } else {
|
| + this.loadResource_(keyset);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Callback when the configuration is loaded.
|
| + *
|
| + * @param {!i18n.input.chrome.inputview.events.ConfigLoadedEvent} e The event.
|
| + * @private
|
| + */
|
| +Controller.prototype.onConfigLoaded_ = function(e) {
|
| + var data = e.data;
|
| + var keyboardCode = data[i18n.input.chrome.inputview.SpecNodeName.ID];
|
| + this.keysetDataMap_[keyboardCode] = data;
|
| + this.perfTracker_.tick(PerfTracker.TickName.KEYSET_LOADED);
|
| + var context = data[i18n.input.chrome.inputview.SpecNodeName.ON_CONTEXT];
|
| + if (context && !this.adapter_.isA11yMode) {
|
| + var keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
|
| + if (!keySetMap) {
|
| + keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_] = {};
|
| + }
|
| + keySetMap[context] = keyboardCode;
|
| + }
|
| +
|
| + var layoutId = data[i18n.input.chrome.inputview.SpecNodeName.LAYOUT];
|
| + var layoutData = this.layoutDataMap_[layoutId];
|
| + if (layoutData) {
|
| + this.maybeCreateViews_();
|
| + } else {
|
| + this.model_.loadLayout(data[i18n.input.chrome.inputview.SpecNodeName.
|
| + LAYOUT]);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Resizes the whole UI.
|
| + *
|
| + * @param {boolean=} opt_ignoreWindowResize .
|
| + */
|
| +Controller.prototype.resize = function(opt_ignoreWindowResize) {
|
| + var height;
|
| + var widthPercent;
|
| + var candidateViewHeight;
|
| + var isHorizontal = screen.width > screen.height;
|
| + var isWideScreen = (Math.min(screen.width, screen.height) / Math.max(
|
| + screen.width, screen.height)) < 0.6;
|
| + this.model_.stateManager.covariance.update(isWideScreen, isHorizontal,
|
| + this.adapter_.isA11yMode);
|
| + if (this.adapter_.isA11yMode) {
|
| + height = SizeSpec.A11Y_HEIGHT;
|
| + widthPercent = screen.width > screen.height ? SizeSpec.A11Y_WIDTH_PERCENT.
|
| + HORIZONTAL : SizeSpec.A11Y_WIDTH_PERCENT.VERTICAL;
|
| + candidateViewHeight = SizeSpec.A11Y_CANDIDATE_VIEW_HEIGHT;
|
| + } else {
|
| + var keyset = this.keysetDataMap_[this.currentKeyset_];
|
| + var layout = keyset && keyset[SpecNodeName.LAYOUT];
|
| + var data = layout && this.layoutDataMap_[layout];
|
| + var spec = data && data[SpecNodeName.WIDTH_PERCENT] ||
|
| + SizeSpec.NON_A11Y_WIDTH_PERCENT;
|
| + height = SizeSpec.NON_A11Y_HEIGHT;
|
| + if (isHorizontal) {
|
| + if (isWideScreen) {
|
| + widthPercent = spec.HORIZONTAL_WIDE_SCREEN;
|
| + } else {
|
| + widthPercent = spec.HORIZONTAL;
|
| + }
|
| + } else {
|
| + widthPercent = spec.VERTICAL;
|
| + }
|
| + candidateViewHeight = SizeSpec.NON_A11Y_CANDIDATE_VIEW_HEIGHT;
|
| + }
|
| +
|
| + var viewportSize = goog.dom.getViewportSize();
|
| + if (viewportSize.height != height && !opt_ignoreWindowResize) {
|
| + window.resizeTo(screen.width, height);
|
| + return;
|
| + }
|
| +
|
| + this.container_.resize(screen.width, height, widthPercent,
|
| + candidateViewHeight);
|
| + if (this.container_.currentKeysetView) {
|
| + this.isKeyboardReady = true;
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Loads the resources, for currentKeyset, passwdKeyset, handwriting,
|
| + * emoji, etc.
|
| + *
|
| + * @private
|
| + */
|
| +Controller.prototype.loadAllResources_ = function() {
|
| + var keysetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
|
| + goog.array.forEach([keysetMap[ContextType.DEFAULT],
|
| + Controller.HANDWRITING_VIEW_CODE_,
|
| + Controller.EMOJI_VIEW_CODE_,
|
| + keysetMap[ContextType.PASSWORD]], function(keyset) {
|
| + this.loadResource_(keyset);
|
| + }, this);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the remapped keyset.
|
| + *
|
| + * @param {string} keyset .
|
| + * @return {string} The remapped keyset.
|
| + * @private
|
| + */
|
| +Controller.prototype.getRemappedKeyset_ = function(keyset) {
|
| + if (goog.array.contains(util.KEYSETS_USE_US, keyset)) {
|
| + return 'us';
|
| + }
|
| + return keyset;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Loads a single resource.
|
| + *
|
| + * @param {string} keyset .
|
| + * loaded.
|
| + * @private
|
| + */
|
| +Controller.prototype.loadResource_ = function(keyset) {
|
| + var remapped = this.getRemappedKeyset_(keyset);
|
| + if (!this.keysetDataMap_[remapped]) {
|
| + if (/^m17n:/.test(remapped)) {
|
| + this.m17nModel_.loadConfig(remapped);
|
| + } else {
|
| + this.model_.loadConfig(remapped);
|
| + }
|
| + return;
|
| + }
|
| +
|
| + var layoutId = this.keysetDataMap_[remapped][SpecNodeName.LAYOUT];
|
| + if (!this.layoutDataMap_[layoutId]) {
|
| + this.model_.loadLayout(layoutId);
|
| + return;
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Sets the keyboard.
|
| + *
|
| + * @param {string} keyset The keyboard keyset.
|
| + * @param {string} languageCode The language code for this keyboard.
|
| + * @param {string} passwordLayout The layout for password box.
|
| + * @param {string} title The title for this keyboard.
|
| + */
|
| +Controller.prototype.initialize = function(keyset, languageCode, passwordLayout,
|
| + title) {
|
| + this.perfTracker_.restart();
|
| + this.adapter_.getCurrentInputMethod(function(currentInputMethod) {
|
| + this.languageCode_ = languageCode;
|
| + this.currentInputMethod_ = currentInputMethod;
|
| + var keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
|
| + if (!keySetMap) {
|
| + keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_] = {};
|
| + }
|
| + keySetMap[ContextType.PASSWORD] = passwordLayout;
|
| + keySetMap[ContextType.DEFAULT] = keyset;
|
| +
|
| + this.title_ = title;
|
| + this.isSettingReady = false;
|
| + this.model_.settings = new i18n.input.chrome.inputview.Settings();
|
| + this.adapter_.initialize(languageCode ? languageCode.split('-')[0] : '');
|
| + this.loadAllResources_();
|
| + this.switchToKeySet(this.getActiveKeyset_());
|
| +
|
| + // Set language attribute and font of body.
|
| + document.body.setAttribute('lang', this.languageCode_);
|
| + goog.dom.classlist.add(document.body, Css.FONT);
|
| + }.bind(this));
|
| +};
|
| +
|
| +
|
| +/** @override */
|
| +Controller.prototype.disposeInternal = function() {
|
| + goog.dispose(this.container_);
|
| + goog.dispose(this.adapter_);
|
| + goog.dispose(this.handler_);
|
| + goog.dispose(this.soundController_);
|
| +
|
| + goog.base(this, 'disposeInternal');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the handwriting Input Tool code of current language code.
|
| + *
|
| + * @return {string} The handwriting Input Tool code.
|
| + * @private
|
| + */
|
| +Controller.prototype.getHwtInputToolCode_ = function() {
|
| + return this.languageCode_.split(/_|-/)[0] +
|
| + Controller.HANDWRITING_CODE_SUFFIX_;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * True to enable settings link.
|
| + *
|
| + * @return {boolean} .
|
| + */
|
| +Controller.prototype.shouldEnableSettings = function() {
|
| + return !this.adapter_.screen || this.adapter_.screen == 'normal';
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the active keyset, if there is a keyset to switch, return it.
|
| + * otherwise if it's a password box, return the password keyset,
|
| + * otherwise return the current keyset.
|
| + *
|
| + * @return {string} .
|
| + * @private
|
| + */
|
| +Controller.prototype.getActiveKeyset_ = function() {
|
| + var keySetMap = this.contextTypeToKeysetMap_[this.currentInputMethod_];
|
| + return keySetMap[this.adapter_.getContextType()] ||
|
| + keySetMap[ContextType.DEFAULT];
|
| +};
|
| +
|
| +
|
| +/**
|
| + * True if keysetB is the sub keyset of keysetA.
|
| + *
|
| + * @param {string} keysetA .
|
| + * @param {string} keysetB .
|
| + * @return {boolean} .
|
| + * @private
|
| + */
|
| +Controller.prototype.isSubKeyset_ = function(keysetA, keysetB) {
|
| + var segmentsA = keysetA.split('.');
|
| + var segmentsB = keysetB.split('.');
|
| + return segmentsA.length >= 2 && segmentsB.length >= 2 &&
|
| + segmentsA[0] == segmentsB[0] && segmentsA[1] == segmentsB[1];
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Updates the compact pinyin to set the inputcode for english and pinyin.
|
| + *
|
| + * @param {string} fromRawKeyset .
|
| + * @param {string} toRawKeyset .
|
| + * @private
|
| + */
|
| +Controller.prototype.updateLanguageState_ =
|
| + function(fromRawKeyset, toRawKeyset) {
|
| + if (fromRawKeyset == 'pinyin-zh-CN.en.compact.qwerty' &&
|
| + toRawKeyset.indexOf('en.compact') == -1) {
|
| + this.adapter_.toggleLanguageState(true);
|
| + } else if (fromRawKeyset.indexOf('en.compact') == -1 &&
|
| + toRawKeyset == 'pinyin-zh-CN.en.compact.qwerty') {
|
| + this.adapter_.toggleLanguageState(false);
|
| + } else if (goog.array.contains(
|
| + i18n.input.chrome.inputview.util.KEYSETS_HAVE_EN_SWTICHER,
|
| + toRawKeyset)) {
|
| + this.adapter_.toggleLanguageState(true);
|
| + this.model_.stateManager.isEnMode = false;
|
| + this.container_.currentKeysetView.update();
|
| + }
|
| +};
|
| +}); // goog.scope
|
|
|