Update to google-input-tools version 1.0.5.0
[chromium-blink-merge.git] / third_party / google_input_tools / src / chrome / os / inputview / adapter.js
blob959499543c417459615e50adc033699af2806bfc
1 // Copyright 2014 The ChromeOS IME Authors. All Rights Reserved.
2 // limitations under the License.
3 // See the License for the specific language governing permissions and
4 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5 // distributed under the License is distributed on an "AS-IS" BASIS,
6 // Unless required by applicable law or agreed to in writing, software
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // You may obtain a copy of the License at
11 // you may not use this file except in compliance with the License.
12 // Licensed under the Apache License, Version 2.0 (the "License");
14 goog.provide('i18n.input.chrome.inputview.Adapter');
16 goog.require('goog.events.Event');
17 goog.require('goog.events.EventHandler');
18 goog.require('goog.events.EventTarget');
19 goog.require('goog.events.EventType');
20 goog.require('goog.object');
21 goog.require('i18n.input.chrome.DataSource');
22 goog.require('i18n.input.chrome.inputview.ReadyState');
23 goog.require('i18n.input.chrome.inputview.StateType');
24 goog.require('i18n.input.chrome.inputview.events.EventType');
25 goog.require('i18n.input.chrome.inputview.events.SurroundingTextChangedEvent');
26 goog.require('i18n.input.chrome.message.ContextType');
27 goog.require('i18n.input.chrome.message.Event');
28 goog.require('i18n.input.chrome.message.Name');
29 goog.require('i18n.input.chrome.message.Type');
31 goog.scope(function() {
32 var CandidatesBackEvent = i18n.input.chrome.DataSource.CandidatesBackEvent;
33 var ContextType = i18n.input.chrome.message.ContextType;
34 var Type = i18n.input.chrome.message.Type;
35 var Name = i18n.input.chrome.message.Name;
39 /**
40  * The adapter for interview.
41  *
42  * @param {!i18n.input.chrome.inputview.ReadyState} readyState .
43  * @extends {goog.events.EventTarget}
44  * @constructor
45  */
46 i18n.input.chrome.inputview.Adapter = function(readyState) {
47   goog.base(this);
49   /**
50    * Whether the keyboard is visible.
51    *
52    * @type {boolean}
53    */
54   this.isVisible = !document.webkitHidden;
56   /**
57    * The modifier state map.
58    *
59    * @type {!Object.<i18n.input.chrome.inputview.StateType, boolean>}
60    * @private
61    */
62   this.modifierState_ = {};
64   /**
65    * The system ready state.
66    *
67    * @private {!i18n.input.chrome.inputview.ReadyState}
68    */
69   this.readyState_ = readyState;
71   chrome.runtime.onMessage.addListener(this.onMessage_.bind(this));
73   /** @private {!goog.events.EventHandler} */
74   this.handler_ = new goog.events.EventHandler(this);
75   this.handler_.listen(document, 'webkitvisibilitychange',
76       this.onVisibilityChange_);
78 goog.inherits(i18n.input.chrome.inputview.Adapter,
79     goog.events.EventTarget);
80 var Adapter = i18n.input.chrome.inputview.Adapter;
83 /** @type {boolean} */
84 Adapter.prototype.isA11yMode = false;
87 /** @type {boolean} */
88 Adapter.prototype.isExperimental = false;
91 /** @type {boolean} */
92 Adapter.prototype.showGlobeKey = false;
95 /** @protected {string} */
96 Adapter.prototype.contextType = ContextType.DEFAULT;
99 /** @type {string} */
100 Adapter.prototype.screen = '';
103 /** @type {boolean} */
104 Adapter.prototype.isChromeVoxOn = false;
107 /** @type {string} */
108 Adapter.prototype.textBeforeCursor = '';
112  * Callback for updating settings.
114  * @param {!Object} message .
115  * @private
116  */
117 Adapter.prototype.onUpdateSettings_ = function(message) {
118   this.contextType = message['contextType'];
119   this.screen = message['screen'];
120   this.dispatchEvent(new i18n.input.chrome.message.Event(Type.UPDATE_SETTINGS,
121       message));
126  * Sets the modifier states.
128  * @param {i18n.input.chrome.inputview.StateType} stateType .
129  * @param {boolean} enable True to enable the state, false otherwise.
130  */
131 Adapter.prototype.setModifierState = function(stateType, enable) {
132   this.modifierState_[stateType] = enable;
137  * Clears the modifier states.
138  */
139 Adapter.prototype.clearModifierStates = function() {
140   this.modifierState_ = {};
145  * Simulates to send 'keydown' and 'keyup' event.
147  * @param {string} key
148  * @param {string} code
149  * @param {number=} opt_keyCode The key code.
150  * @param {!Object=} opt_spatialData .
151  */
152 Adapter.prototype.sendKeyDownAndUpEvent = function(key, code, opt_keyCode,
153     opt_spatialData) {
154   this.sendKeyEvent_([
155     this.generateKeyboardEvent_(
156         goog.events.EventType.KEYDOWN, key, code, opt_keyCode, opt_spatialData),
157     this.generateKeyboardEvent_(
158         goog.events.EventType.KEYUP, key, code, opt_keyCode, opt_spatialData)
159   ]);
164  * Simulates to send 'keydown' event.
166  * @param {string} key
167  * @param {string} code
168  * @param {number=} opt_keyCode The key code.
169  * @param {!Object=} opt_spatialData .
170  */
171 Adapter.prototype.sendKeyDownEvent = function(key, code, opt_keyCode,
172     opt_spatialData) {
173   this.sendKeyEvent_([this.generateKeyboardEvent_(
174       goog.events.EventType.KEYDOWN, key, code, opt_keyCode,
175       opt_spatialData)]);
180  * Simulates to send 'keyup' event.
182  * @param {string} key
183  * @param {string} code
184  * @param {number=} opt_keyCode The key code.
185  * @param {!Object=} opt_spatialData .
186  */
187 Adapter.prototype.sendKeyUpEvent = function(key, code, opt_keyCode,
188     opt_spatialData) {
189   this.sendKeyEvent_([this.generateKeyboardEvent_(
190       goog.events.EventType.KEYUP, key, code, opt_keyCode, opt_spatialData)]);
195  * Use {@code chrome.input.ime.sendKeyEvents} to simulate key events.
197  * @param {!Array.<!Object.<string, string|boolean>>} keyData .
198  * @private
199  */
200 Adapter.prototype.sendKeyEvent_ = function(keyData) {
201   chrome.runtime.sendMessage(
202       goog.object.create(Name.TYPE, Type.SEND_KEY_EVENT, Name.KEY_DATA,
203           keyData));
208  * Generates a {@code ChromeKeyboardEvent} by given values.
210  * @param {string} type .
211  * @param {string} key The key.
212  * @param {string} code The code.
213  * @param {number=} opt_keyCode The key code.
214  * @param {!Object=} opt_spatialData .
215  * @return {!Object.<string, string|boolean>}
216  * @private
217  */
218 Adapter.prototype.generateKeyboardEvent_ = function(
219     type, key, code, opt_keyCode, opt_spatialData) {
220   var StateType = i18n.input.chrome.inputview.StateType;
221   var ctrl = !!this.modifierState_[StateType.CTRL];
222   var alt = !!this.modifierState_[StateType.ALT];
224   if (ctrl || alt) {
225     key = '';
226   }
227   var result = {
228     'type': type,
229     'key': key,
230     'code': code,
231     'keyCode': opt_keyCode || 0,
232     'spatialData': opt_spatialData
233   };
235   result['altKey'] = alt;
236   result['ctrlKey'] = ctrl;
237   result['shiftKey'] = !!this.modifierState_[StateType.SHIFT];
238   result['capsLock'] = !!this.modifierState_[StateType.CAPSLOCK];
240   return result;
245  * Callback when surrounding text is changed.
247  * @param {string} text .
248  * @private
249  */
250 Adapter.prototype.onSurroundingTextChanged_ = function(text) {
251   this.textBeforeCursor = text;
252   this.dispatchEvent(new i18n.input.chrome.inputview.events.
253       SurroundingTextChangedEvent(this.textBeforeCursor));
258  * Gets the context.
260  * @return {string} .
261  */
262 Adapter.prototype.getContext = function() {
263   var matches = this.textBeforeCursor.match(/([a-zA-Z'-Ḁ-ỹÀ-ȳ]+)\s+$/);
264   var text = matches ? matches[1] : '';
265   return text;
270  * Gets the context type.
272  * @return {string} .
273  */
274 Adapter.prototype.getContextType = function() {
275   return this.contextType || ContextType.DEFAULT;
280  * Sends request for handwriting.
282  * @param {!Object} payload .
283  */
284 Adapter.prototype.sendHwtRequest = function(payload) {
285   chrome.runtime.sendMessage(goog.object.create(
286       Name.TYPE, Type.HWT_REQUEST, Name.MSG, payload
287       ));
292  * True if it is a password box.
294  * @return {boolean} .
295  */
296 Adapter.prototype.isPasswordBox = function() {
297   return this.contextType == 'password';
302  * Callback when blurs in the context.
304  * @private
305  */
306 Adapter.prototype.onContextBlur_ = function() {
307   this.contextType = '';
308   this.dispatchEvent(new goog.events.Event(i18n.input.chrome.inputview.events.
309       EventType.CONTEXT_BLUR));
314  * Callback when focus on a context.
316  * @param {string} contextType .
317  * @private
318  */
319 Adapter.prototype.onContextFocus_ = function(contextType) {
320   this.contextType = contextType;
321   this.dispatchEvent(new goog.events.Event(i18n.input.chrome.inputview.events.
322       EventType.CONTEXT_FOCUS));
327  * Intializes the communication to background page.
329  * @param {string} languageCode The language code.
330  * @private
331  */
332 Adapter.prototype.initBackground_ = function(languageCode) {
333   chrome.runtime.getBackgroundPage((function() {
334     chrome.runtime.sendMessage(
335         goog.object.create(Name.TYPE, Type.CONNECT));
336     chrome.runtime.sendMessage(goog.object.create(Name.TYPE,
337         Type.VISIBILITY_CHANGE, Name.VISIBILITY, !document.webkitHidden));
338     if (languageCode) {
339       this.setLanguage(languageCode);
340     }
341   }).bind(this));
346  * Loads the keyboard settings.
348  * @param {string} languageCode The language code.
349  */
350 Adapter.prototype.initialize = function(languageCode) {
351   if (chrome.accessibilityFeatures &&
352       chrome.accessibilityFeatures.spokenFeedback) {
353     chrome.accessibilityFeatures.spokenFeedback.get({}, (function(details) {
354       this.isChromeVoxOn = details['value'];
355     }).bind(this));
356     chrome.accessibilityFeatures.spokenFeedback.onChange.addListener((function(
357         details) {
358           this.isChromeVoxOn = details['value'];
359         }).bind(this));
360   }
362   this.initBackground_(languageCode);
364   var StateType = i18n.input.chrome.inputview.ReadyState.StateType;
365   if (window.inputview) {
366     if (inputview.getKeyboardConfig) {
367       inputview.getKeyboardConfig((function(config) {
368         this.isA11yMode = !!config['a11ymode'];
369         this.isExperimental = !!config['experimental'];
370         this.readyState_.markStateReady(StateType.KEYBOARD_CONFIG_READY);
371         if (this.readyState_.isReady(StateType.IME_LIST_READY)) {
372           this.dispatchEvent(new goog.events.Event(
373               i18n.input.chrome.inputview.events.EventType.SETTINGS_READY));
374         }
375       }).bind(this));
376     } else {
377       this.readyState_.markStateReady(StateType.KEYBOARD_CONFIG_READY);
378     }
379     if (inputview.getInputMethods) {
380       inputview.getInputMethods((function(inputMethods) {
381         // Only show globe key to switching between IMEs when there are more
382         // than one IME.
383         this.showGlobeKey = inputMethods.length > 1;
384         this.readyState_.markStateReady(StateType.IME_LIST_READY);
385         if (this.readyState_.isReady(StateType.KEYBOARD_CONFIG_READY)) {
386           this.dispatchEvent(new goog.events.Event(
387               i18n.input.chrome.inputview.events.EventType.SETTINGS_READY));
388         }
389       }).bind(this));
390     } else {
391       this.readyState_.markStateReady(StateType.IME_LIST_READY);
392     }
393   } else {
394     this.readyState_.markStateReady(StateType.IME_LIST_READY);
395     this.readyState_.markStateReady(StateType.KEYBOARD_CONFIG_READY);
396   }
398   if (this.readyState_.isReady(StateType.KEYBOARD_CONFIG_READY) &&
399       this.readyState_.isReady(StateType.IME_LIST_READY)) {
400     window.setTimeout((function() {
401       this.dispatchEvent(new goog.events.Event(
402           i18n.input.chrome.inputview.events.EventType.SETTINGS_READY));
403     }).bind(this), 0);
404   }
409  * Gets the currently activated input method.
411  * @param {function(string)} callback .
412  */
413 Adapter.prototype.getCurrentInputMethod = function(callback) {
414   if (window.inputview && inputview.getCurrentInputMethod) {
415     inputview.getCurrentInputMethod(callback);
416   } else {
417     callback('DU');
418   }
423  * Gets the list of all activated input methods.
425  * @param {function(Array.<Object>)} callback .
426  */
427 Adapter.prototype.getInputMethods = function(callback) {
428   if (window.inputview && inputview.getInputMethods) {
429     inputview.getInputMethods(callback);
430   } else {
431     // Provides a dummy IME item to enable IME switcher UI.
432     callback([
433       {'indicator': 'DU', 'id': 'DU', 'name': 'Dummy IME', 'command': 1}]);
434   }
439  * Switches to the input method with id equals |inputMethodId|.
441  * @param {!string} inputMethodId .
442  */
443 Adapter.prototype.switchToInputMethod = function(inputMethodId) {
444   if (window.inputview && inputview.switchToInputMethod) {
445     inputview.switchToInputMethod(inputMethodId);
446   }
451  * Callback for visibility change on the input view window.
453  * @private
454  */
455 Adapter.prototype.onVisibilityChange_ = function() {
456   this.isVisible = !document.webkitHidden;
457   this.dispatchEvent(new goog.events.Event(i18n.input.chrome.inputview.
458       events.EventType.VISIBILITY_CHANGE));
459   chrome.runtime.sendMessage(goog.object.create(Name.TYPE,
460       Type.VISIBILITY_CHANGE, Name.VISIBILITY, !document.webkitHidden));
465  * Sends request for completion.
467  * @param {string} query .
468  * @param {!Object=} opt_spatialData .
469  */
470 Adapter.prototype.sendCompletionRequest = function(query, opt_spatialData) {
471   var spatialData = {};
472   if (opt_spatialData) {
473     spatialData[Name.SOURCES] = opt_spatialData.sources;
474     spatialData[Name.POSSIBILITIES] = opt_spatialData.possibilities;
475   }
476   chrome.runtime.sendMessage(goog.object.create(Name.TYPE,
477       Type.COMPLETION, Name.TEXT, query, Name.SPATIAL_DATA, spatialData));
482  * Selects the candidate.
484  * @param {!Object} candidate .
485  */
486 Adapter.prototype.selectCandidate = function(candidate) {
487   chrome.runtime.sendMessage(goog.object.create(
488       Name.TYPE, Type.SELECT_CANDIDATE, Name.CANDIDATE, candidate));
493  * Commits the text.
495  * @param {string} text .
496  */
497 Adapter.prototype.commitText = function(text) {
498   chrome.runtime.sendMessage(goog.object.create(
499       Name.TYPE, Type.COMMIT_TEXT, Name.TEXT, text));
504  * Sets the language.
506  * @param {string} language .
507  */
508 Adapter.prototype.setLanguage = function(language) {
509   chrome.runtime.sendMessage(goog.object.create(
510       Name.TYPE, Type.SET_LANGUAGE, Name.LANGUAGE, language));
515  * Callbck when completion is back.
517  * @param {!Object} message .
518  * @private
519  */
520 Adapter.prototype.onCandidatesBack_ = function(message) {
521   var source = message['source'] || '';
522   var candidates = message['candidates'] || [];
523   this.dispatchEvent(new CandidatesBackEvent(source, candidates));
528  * Hides the keyboard.
529  */
530 Adapter.prototype.hideKeyboard = function() {
531   chrome.input.ime.hideInputView();
536  * Sends Input Tool code to background.
538  * @param {string} inputToolCode .
539  */
540 Adapter.prototype.setInputToolCode = function(inputToolCode) {
541   chrome.runtime.sendMessage(
542       goog.object.create(
543           Name.TYPE,
544           Type.HWT_SET_INPUTTOOL,
545           Name.MSG,
546           inputToolCode));
551  * Sends DOUBLE_CLICK_ON_SPACE_KEY message.
552  */
553 Adapter.prototype.doubleClickOnSpaceKey = function() {
554   chrome.runtime.sendMessage(
555       goog.object.create(
556           Name.TYPE,
557           Type.DOUBLE_CLICK_ON_SPACE_KEY));
562  * Sends message to the background when switch to emoji.
564  */
565 Adapter.prototype.setEmojiInputToolCode = function() {
566   chrome.runtime.sendMessage(
567       goog.object.create(
568           Name.TYPE,
569           Type.EMOJI_SET_INPUTTOOL));
574  * Sends message to the background when do internal inputtool switch.
576  * @param {boolean} inputToolValue The value of the language flag.
577  */
578 Adapter.prototype.toggleLanguageState = function(inputToolValue) {
579   chrome.runtime.sendMessage(
580       goog.object.create(
581           Name.TYPE,
582           Type.TOGGLE_LANGUAGE_STATE,
583           Name.MSG,
584           inputToolValue));
589  * Sends unset Input Tool code to background.
590  */
591 Adapter.prototype.unsetInputToolCode = function() {
592   chrome.runtime.sendMessage(
593       goog.object.create(
594           Name.TYPE,
595           Type.HWT_UNSET_INPUTTOOL));
600  * Sends message to the background when switch to other mode from emoji.
602  */
603 Adapter.prototype.unsetEmojiInputToolCode = function() {
604   chrome.runtime.sendMessage(
605       goog.object.create(
606           Name.TYPE,
607           Type.EMOJI_UNSET_INPUTTOOL));
612  * Processes incoming message from option page or inputview window.
614  * @param {*} request Message from option page or inputview window.
615  * @param {*} sender Information about the script
616  *     context that sent the message.
617  * @param {function(*): void} sendResponse Function to call to send a response.
618  * @return {boolean|undefined} {@code true} to keep the message channel open in
619  *     order to send a response asynchronously.
620  * @private
621  */
622 Adapter.prototype.onMessage_ = function(request, sender, sendResponse) {
623   var type = request[Name.TYPE];
624   var msg = request[Name.MSG];
625   switch (type) {
626     case Type.CANDIDATES_BACK:
627       this.onCandidatesBack_(msg);
628       break;
629     case Type.CONTEXT_FOCUS:
630       this.onContextFocus_(request[Name.CONTEXT_TYPE]);
631       break;
632     case Type.CONTEXT_BLUR:
633       this.onContextBlur_();
634       break;
635     case Type.SURROUNDING_TEXT_CHANGED:
636       this.onSurroundingTextChanged_(request[Name.TEXT]);
637       break;
638     case Type.UPDATE_SETTINGS:
639       this.onUpdateSettings_(msg);
640       break;
641     case Type.HWT_NETWORK_ERROR:
642     case Type.HWT_PRIVACY_INFO:
643       this.dispatchEvent(new i18n.input.chrome.message.Event(type, msg));
644       break;
645   }
650  * Sends the privacy confirmed message to background and broadcasts it.
651  */
652 Adapter.prototype.sendHwtPrivacyConfirmMessage = function() {
653   chrome.runtime.sendMessage(
654       goog.object.create(Name.TYPE, Type.HWT_PRIVACY_GOT_IT));
655   this.dispatchEvent(
656       new goog.events.Event(Type.HWT_PRIVACY_GOT_IT));
660 /** @override */
661 Adapter.prototype.disposeInternal = function() {
662   goog.dispose(this.handler_);
664   goog.base(this, 'disposeInternal');
666 });  // goog.scope