Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / keyboard / resources / inputview_adapter.js
blob1e0d459f9742204bed7fd3cbc29ed01700a9a9c3
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 var CLOSURE_NO_DEPS=true;
7 var controller;
9 /**
10  * Armed callback to be triggered when a keyset changes.
11  * @type {{string:target function:callback}}
12  * @private
13  */
14 var keysetChangeListener_;
16 /**
17  * Registers a function, which may override a preexisting implementation.
18  * @param {string} path Full path for the function name.
19  * @param {function=} opt_fn Optional function definition. If not specified,
20  *     the default implementation prettyprints the method call with arguments.
21  * @return {function} Registered function, which may be a mock implementation.
22  */
23 function registerFunction(path, opt_fn) {
24   var parts = path.split('.');
25   var base = window;
26   var part = null;
27   var fn = opt_fn;
28   if (!fn) {
29     fn = function() {
30       var prettyprint = function(arg) {
31         if (arg instanceof Array) {
32           var terms = [];
33           for (var i = 0; i < arg.length; i++) {
34             terms.push(prettyprint(arg[i]));
35           }
36           return '[' + terms.join(', ') + ']';
37         } else if (typeof arg == 'object') {
38           var properties = [];
39           for (var key in arg) {
40              properties.push(key + ': ' + prettyprint(arg[key]));
41           }
42           return '{' + properties.join(', ') + '}';
43         } else {
44           return arg;
45         }
46       };
47       // The property 'arguments' is an array-like object. Convert to a true
48       // array for prettyprinting.
49       var args = Array.prototype.slice.call(arguments);
50       console.log('Call to ' + path + ': ' + prettyprint(args));
51     };
52   }
53   for (var i = 0; i < parts.length - 1; i++) {
54     part = parts[i];
55     if (!base[part]) {
56       base[part] = {};
57     }
58     base = base[part];
59   }
60   base[parts[parts.length - 1]] = fn;
61   return fn;
64 /**
65  * The chrome.i18n API is not compatible with component extensions due to the
66  * way component extensions are loaded (crbug/66834).
67  */
68 function overrideGetMessage() {
69   var originalGetMessage = chrome.i18n.getMessage;
71   /**
72    * Localize a string resource.
73    * @param {string} key The message key to localize.
74    * @return {string} Translated resource.
75    */
76   chrome.i18n.getMessage = function(key) {
77     if (key.indexOf('@@') == 0)
78       return originalGetMessage(key);
80     // TODO(kevers): Add support for other locales.
81     var table = i18n.input.chrome.inputview.TranslationTable;
82     var entry = table[key];
83     if (!entry)
84       entry = table[key.toLowerCase()];
85     return entry ? entry.message || '' : '';
86   };
89 /**
90  * Overrides call to switch keysets in order to catch when the keyboard
91  * is ready for input. Used to synchronize the start of automated
92  * virtual keyboard tests.
93  */
94 function overrideSwitchToKeyset() {
95   var KeyboardContainer = i18n.input.chrome.inputview.KeyboardContainer;
96   var switcher = KeyboardContainer.prototype.switchToKeyset;
97   KeyboardContainer.prototype.switchToKeyset = function() {
98     var success = switcher.apply(this, arguments);
99     if (success) {
100       // The first resize call forces resizing of the keyboard window.
101       // The second resize call forces a clean layout for chrome://keyboard.
102       controller.resize(false);
103       controller.resize(true);
104       var settings = controller.model_.settings;
105       settings.supportCompact = true;
106       if (keysetChangeListener_ &&
107           keysetChangeListener_.target == arguments[0]) {
108         var callback = keysetChangeListener_.callback;
109         keysetChangeListener_ = undefined;
110         // TODO (rsadam): Get rid of this hack. Currently this is needed to
111         // ensure the keyset was fully loaded before carrying on with the test.
112         setTimeout(callback, 0);
113       }
114     }
115     return success;
116   };
120  * Arms a one time callback to invoke when the VK switches to the target keyset.
121  * Only one keyset change callback may be armed at any time. Used to synchronize
122  * tests and to track initial load time for the virtual keyboard.
123  * @param {string} keyset The target keyset.
124  * @param {function} callback The callback to invoke when the keyset becomes
125  *     active.
126  */
127 function onSwitchToKeyset(keyset, callback) {
128   if (keysetChangeListener_) {
129     console.error('A keyset change listener is already armed.');
130     return;
131   }
132   keysetChangeListener_ = {
133     target: keyset,
134     callback: callback
135   };
139  * Spatial data is used in conjunction with a language model to offer
140  * corrections for 'fat finger' typing and is not needed for the system VK.
141  */
142 function overrideGetSpatialData() {
143   var Controller = i18n.input.chrome.inputview.Controller;
144   Controller.prototype.getSpatialData_ = function() {};
147 // Plug in for API calls.
148 function registerInputviewApi() {
150   // Flag values for ctrl, alt and shift as defined by EventFlags
151   // in "event_constants.h".
152   // @enum {number}
153   var Modifier = {
154     NONE: 0,
155     ALT: 8,
156     CONTROL: 4,
157     SHIFT: 2
158   };
160   // Mapping from keyName to keyCode (see ui::KeyEvent).
161   var nonAlphaNumericKeycodes = {
162     Backquote: 0xC0,
163     Backslash: 0xDC,
164     Backspace: 0x08,
165     BracketLeft: 0xDB,
166     BracketRight: 0xDD,
167     Comma: 0xBC,
168     Enter: 0x0D,
169     Period: 0xBE,
170     Quote: 0xBF,
171     Semicolon: 0xBA,
172     Slash: 0xBF,
173     Tab: 0x09
174   };
176   /**
177    * Displays a console message containing the last runtime error.
178    * @private
179    */
180   function logIfError_() {
181     if (chrome.runtime.lastError) {
182       console.log(chrome.runtime.lastError);
183     }
184   }
186   function commitText_(text) {
187     chrome.virtualKeyboardPrivate.insertText(text, logIfError_);
188   }
190   /**
191    * Retrieve the preferred keyboard configuration.
192    * @param {function} callback The callback function for processing the
193    *     keyboard configuration.
194    * @private
195    */
196   function getKeyboardConfig_(callback) {
197     chrome.virtualKeyboardPrivate.getKeyboardConfig(callback);
198   }
200   /**
201    * Retrieve a list of all enabled input methods.
202    * @param {function} callback The callback function for processing the list
203    *     of enabled input methods.
204    * @private
205    */
206   function getInputMethods_(callback) {
207     if (chrome.inputMethodPrivate)
208       chrome.inputMethodPrivate.getInputMethods(callback);
209     else
210       callback([]);
211   }
213   /**
214    * Retrieve the name of the active input method.
215    * @param {function} callback The callback function for processing the
216    *     name of the active input mehtod.
217    * @private
218    */
219   function getCurrentInputMethod_(callback) {
220     if (chrome.inputMethodPrivate)
221       chrome.inputMethodPrivate.getCurrentInputMethod(callback);
222     else
223       callback('');
224   }
226   /**
227    * Retrieve the current input method configuration.
228    * @param {function} callback The callback function for processing the
229    *     name of the active input mehtod.
230    * @private
231    */
232   function getInputMethodConfig_(callback) {
233     if (chrome.inputMethodPrivate)
234       chrome.inputMethodPrivate.getInputMethodConfig(callback);
235     else
236       callback('');
237   }
239   /**
240    * Changes the active input method.
241    * @param {string} inputMethodId The id of the input method to activate.
242    * @private
243    */
244   function switchToInputMethod_(inputMethodId) {
245     if (chrome.inputMethodPrivate)
246       chrome.inputMethodPrivate.setCurrentInputMethod(inputMethodId)
247   }
249   /**
250    * Opens the language settings for specifying and configuring input methods.
251    * @private
252    */
253   function openSettings_() {
254     chrome.virtualKeyboardPrivate.openSettings();
255   }
257   /**
258    * Dispatches a virtual key event. The system VK does not use the IME
259    * API as its primary role is to work in conjunction with a non-VK aware
260    * IME. Some reformatting of the key data is required to work with the
261    * virtualKeyboardPrivate API.
262    * @param {!Object} keyData Description of the key event.
263    */
264   function sendKeyEvent_(keyData) {
265     keyData.forEach(function(data) {
266       var charValue = data.key.length == 1 ? data.key.charCodeAt(0) : 0;
267       var keyCode = data.keyCode ? data.keyCode :
268           getKeyCode_(data.key, data.code);
269       var event = {
270         type: data.type,
271         charValue: charValue,
272         keyCode: keyCode,
273         keyName: data.code,
274         modifiers: Modifier.NONE
275       };
276       if (data.altKey)
277         event.modifiers |= Modifier.ALT;
278       if (data.ctrlKey)
279         event.modifiers |= Modifier.CONTROL;
280       if (data.shiftKey || data.capsLock)
281         event.modifiers |= Modifier.SHIFT;
283       chrome.virtualKeyboardPrivate.sendKeyEvent(event, logIfError_);
284     });
285   }
287   /**
288    * Computes keyCodes for use with ui::KeyEvent.
289    * @param {string} keyChar Character being typed.
290    * @param {string} keyName w3c name of the character.
291    */
292   function getKeyCode_(keyChar, keyName) {
293     var keyCode = nonAlphaNumericKeycodes[keyName];
294     if (keyCode)
295       return keyCode;
297     var match = /Key([A-Z])/.exec(keyName);
298     if (match)
299       return match[1].charCodeAt(0);
301     match = /Digit([0-9])/.exec(keyName);
302     if (match)
303       return match[1].charCodeAt(0);
305     if (keyChar.length == 1) {
306       if (keyChar >= 'a' && keyChar <= 'z')
307         return keyChar.charCodeAt(0) - 32;
308       if (keyChar >= 'A' && keyChar <= 'Z')
309         return keyChar.charCodeAt(0);
310       if (keyChar >= '0' && keyChar <= '9')
311         return keyChar.charCodeAt(0);
312     }
313     return 0;
314   }
316   window.inputview = {
317     commitText: commitText_,
318     getKeyboardConfig: getKeyboardConfig_,
319     getInputMethods: getInputMethods_,
320     getCurrentInputMethod: getCurrentInputMethod_,
321     getInputMethodConfig: getInputMethodConfig_,
322     switchToInputMethod: switchToInputMethod_,
323     openSettings: openSettings_
324   };
326   registerFunction('chrome.input.ime.hideInputView', function() {
327     chrome.virtualKeyboardPrivate.hideKeyboard();
328     chrome.virtualKeyboardPrivate.lockKeyboard(false);
329   });
331   var defaultSendMessage = registerFunction('chrome.runtime.sendMessage');
332   registerFunction('chrome.runtime.sendMessage', function(message) {
333     if (message.type == 'send_key_event')
334       sendKeyEvent_(message.keyData);
335     else if (message.type == 'commit_text')
336       commitText_(message.text);
337     else
338       defaultSendMessage(message);
339   });
344 registerFunction('chrome.runtime.getBackgroundPage', function() {
345   var callback = arguments[0];
346   callback();
348 registerFunction('chrome.runtime.sendMessage');
349 registerFunction('chrome.runtime.onMessage.addListener');
351 if (!chrome.i18n) {
352   chrome.i18n = {};
353   chrome.i18n.getMessage = function(name) {
354     return name;
355   }
360  * Trigger loading the virtual keyboard on completion of page load.
361  */
362 window.onload = function() {
363   var params = {};
364   var matches = window.location.href.match(/[#?].*$/);
365   if (matches && matches.length > 0) {
366     matches[0].slice(1).split('&').forEach(function(s) {
367       var pair = s.split('=');
368       params[pair[0]] = pair[1];
369     });
370   }
372   var keyset = params['id'] || 'us.compact.qwerty';
373   var languageCode = params['language'] || 'en';
374   var passwordLayout = params['passwordLayout'] || 'us';
375   var name = params['name'] || 'English';
377   overrideGetMessage();
378   overrideSwitchToKeyset();
379   overrideGetSpatialData();
380   registerInputviewApi();
381   i18n.input.chrome.inputview.Controller.DEV = true;
382   i18n.input.chrome.inputview.Adapter.prototype.isSwitching = function() {
383     return false;
384   };
386   if (keyset != 'none') {
387     window.initializeVirtualKeyboard(keyset, languageCode, passwordLayout,
388         name);
389   }
393  * Run cleanup tasks.
394  */
395 window.onbeforeunload = function() {
396   if (controller)
397     goog.dispose(controller);
401  * Loads a virtual keyboard. If a keyboard was previously loaded, it is
402  * reinitialized with the new configuration.
403  * @param {string} keyset The keyboard keyset.
404  * @param {string} languageCode The language code for this keyboard.
405  * @param {string} passwordLayout The layout for password box.
406  * @param {string} name The input tool name.
407  * @param {Object=} opt_config Optional configuration settings.
408  */
409 window.initializeVirtualKeyboard = function(keyset, languageCode,
410     passwordLayout, name, opt_config) {
411   var Controller = i18n.input.chrome.inputview.Controller;
412   Controller.DISABLE_HWT = !(opt_config && opt_config.enableHwtForTesting);
413   onSwitchToKeyset(keyset, function() {
414     chrome.virtualKeyboardPrivate.keyboardLoaded();
415   });
416   if (controller)
417     controller.initialize(keyset, languageCode, passwordLayout, name);
418   else
419     controller = new Controller(keyset, languageCode, passwordLayout, name);