| // Copyright 2014 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 | // | 
 | var CLOSURE_NO_DEPS=true; | 
 |  | 
 | var controller; | 
 |  | 
 | /** | 
 |  * Armed callback to be triggered when a keyset changes. | 
 |  * @type {{string:target function:callback}} | 
 |  * @private | 
 |  */ | 
 | var keysetChangeListener_; | 
 |  | 
 | /** | 
 |  * Registers a function, which may override a preexisting implementation. | 
 |  * @param {string} path Full path for the function name. | 
 |  * @param {function=} opt_fn Optional function definition. If not specified, | 
 |  *     the default implementation prettyprints the method call with arguments. | 
 |  * @return {function} Registered function, which may be a mock implementation. | 
 |  */ | 
 | function registerFunction(path, opt_fn) { | 
 |   var parts = path.split('.'); | 
 |   var base = window; | 
 |   var part = null; | 
 |   var fn = opt_fn; | 
 |   if (!fn) { | 
 |     fn = function() { | 
 |       var prettyprint = function(arg) { | 
 |         if (arg instanceof Array) { | 
 |           var terms = []; | 
 |           for (var i = 0; i < arg.length; i++) { | 
 |             terms.push(prettyprint(arg[i])); | 
 |           } | 
 |           return '[' + terms.join(', ') + ']'; | 
 |         } else if (typeof arg == 'object') { | 
 |           var properties = []; | 
 |           for (var key in arg) { | 
 |              properties.push(key + ': ' + prettyprint(arg[key])); | 
 |           } | 
 |           return '{' + properties.join(', ') + '}'; | 
 |         } else { | 
 |           return arg; | 
 |         } | 
 |       }; | 
 |       // The property 'arguments' is an array-like object. Convert to a true | 
 |       // array for prettyprinting. | 
 |       var args = Array.prototype.slice.call(arguments); | 
 |       console.log('Call to ' + path + ': ' + prettyprint(args)); | 
 |     }; | 
 |   } | 
 |   for (var i = 0; i < parts.length - 1; i++) { | 
 |     part = parts[i]; | 
 |     if (!base[part]) { | 
 |       base[part] = {}; | 
 |     } | 
 |     base = base[part]; | 
 |   } | 
 |   base[parts[parts.length - 1]] = fn; | 
 |   return fn; | 
 | } | 
 |  | 
 | /** | 
 |  * The chrome.i18n API is not compatible with component extensions due to the | 
 |  * way component extensions are loaded (crbug/66834). | 
 |  */ | 
 | function overrideGetMessage() { | 
 |   var originalGetMessage = chrome.i18n.getMessage; | 
 |  | 
 |   /** | 
 |    * Localize a string resource. | 
 |    * @param {string} key The message key to localize. | 
 |    * @return {string} Translated resource. | 
 |    */ | 
 |   chrome.i18n.getMessage = function(key) { | 
 |     if (key.startsWith('@@')) | 
 |       return originalGetMessage(key); | 
 |  | 
 |     // TODO(kevers): Add support for other locales. | 
 |     var table = i18n.input.chrome.inputview.TranslationTable; | 
 |     var entry = table[key]; | 
 |     if (!entry) | 
 |       entry = table[key.toLowerCase()]; | 
 |     return entry ? entry.message || '' : ''; | 
 |   }; | 
 | }; | 
 |  | 
 | /** | 
 |  * Overrides call to switch keysets in order to catch when the keyboard | 
 |  * is ready for input. Used to synchronize the start of automated | 
 |  * virtual keyboard tests. | 
 |  */ | 
 | function overrideSwitchToKeyset() { | 
 |   var KeyboardContainer = i18n.input.chrome.inputview.KeyboardContainer; | 
 |   var switcher = KeyboardContainer.prototype.switchToKeyset; | 
 |   KeyboardContainer.prototype.switchToKeyset = function() { | 
 |     var success = switcher.apply(this, arguments); | 
 |     if (success) { | 
 |       // The first resize call forces resizing of the keyboard window. | 
 |       // The second resize call forces a clean layout for chrome://keyboard. | 
 |       controller.resize(false); | 
 |       controller.resize(true); | 
 |       var settings = controller.model_.settings; | 
 |       settings.supportCompact = true; | 
 |       if (keysetChangeListener_ && | 
 |           keysetChangeListener_.target == arguments[0]) { | 
 |         var callback = keysetChangeListener_.callback; | 
 |         keysetChangeListener_ = undefined; | 
 |         // TODO (rsadam): Get rid of this hack. Currently this is needed to | 
 |         // ensure the keyset was fully loaded before carrying on with the test. | 
 |         setTimeout(callback, 0); | 
 |       } | 
 |     } | 
 |     return success; | 
 |   }; | 
 | } | 
 |  | 
 | /** | 
 |  * Arms a one time callback to invoke when the VK switches to the target keyset. | 
 |  * Only one keyset change callback may be armed at any time. Used to synchronize | 
 |  * tests and to track initial load time for the virtual keyboard. | 
 |  * @param {string} keyset The target keyset. | 
 |  * @param {function} callback The callback to invoke when the keyset becomes | 
 |  *     active. | 
 |  */ | 
 | function onSwitchToKeyset(keyset, callback) { | 
 |   if (keysetChangeListener_) { | 
 |     console.error('A keyset change listener is already armed.'); | 
 |     return; | 
 |   } | 
 |   keysetChangeListener_ = { | 
 |     target: keyset, | 
 |     callback: callback | 
 |   }; | 
 | } | 
 |  | 
 | /** | 
 |  * Spatial data is used in conjunction with a language model to offer | 
 |  * corrections for 'fat finger' typing and is not needed for the system VK. | 
 |  */ | 
 | function overrideGetSpatialData() { | 
 |   var Controller = i18n.input.chrome.inputview.Controller; | 
 |   Controller.prototype.getSpatialData_ = function() {}; | 
 | } | 
 |  | 
 | /** | 
 |  * Return the most recently used US layout. By default, this will return the | 
 |  * compact layout. | 
 |  */ | 
 | function getDefaultUsLayout() { | 
 |   return window.localStorage['vkDefaultLayoutIsFull'] | 
 |       ? 'us' : 'us.compact.qwerty'; | 
 | } | 
 |  | 
 | // Plug in for API calls. | 
 | function registerInputviewApi() { | 
 |  | 
 |   // Flag values for ctrl, alt and shift as defined by EventFlags | 
 |   // in "event_constants.h". | 
 |   // @enum {number} | 
 |   var Modifier = { | 
 |     NONE: 0, | 
 |     ALT: 8, | 
 |     CONTROL: 4, | 
 |     SHIFT: 2, | 
 |     CAPSLOCK: 256 | 
 |   }; | 
 |  | 
 |   // Mapping from keyName to keyCode (see ui::KeyEvent). | 
 |   var nonAlphaNumericKeycodes = { | 
 |     Backquote: 0xC0, | 
 |     Backslash: 0xDC, | 
 |     Backspace: 0x08, | 
 |     BracketLeft: 0xDB, | 
 |     BracketRight: 0xDD, | 
 |     Comma: 0xBC, | 
 |     Enter: 0x0D, | 
 |     Period: 0xBE, | 
 |     Quote: 0xBF, | 
 |     Semicolon: 0xBA, | 
 |     Slash: 0xBF, | 
 |     Space: 0x20, | 
 |     Tab: 0x09 | 
 |   }; | 
 |  | 
 |   /** | 
 |    * Displays a console message containing the last runtime error. | 
 |    * @private | 
 |    */ | 
 |   function logIfError_() { | 
 |     if (chrome.runtime.lastError) { | 
 |       console.log(chrome.runtime.lastError); | 
 |     } | 
 |   } | 
 |  | 
 |   function commitText_(text) { | 
 |     chrome.virtualKeyboardPrivate.insertText(text, logIfError_); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Retrieve the preferred keyboard configuration. | 
 |    * @param {function} callback The callback function for processing the | 
 |    *     keyboard configuration. | 
 |    * @private | 
 |    */ | 
 |   function getKeyboardConfig_(callback) { | 
 |     chrome.virtualKeyboardPrivate.getKeyboardConfig(callback); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Retrieve a list of all enabled input methods. | 
 |    * @param {function} callback The callback function for processing the list | 
 |    *     of enabled input methods. | 
 |    * @private | 
 |    */ | 
 |   function getInputMethods_(callback) { | 
 |     if (chrome.inputMethodPrivate) | 
 |       chrome.inputMethodPrivate.getInputMethods(callback); | 
 |     else | 
 |       callback([]); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Retrieve the name of the active input method. | 
 |    * @param {function} callback The callback function for processing the | 
 |    *     name of the active input mehtod. | 
 |    * @private | 
 |    */ | 
 |   function getCurrentInputMethod_(callback) { | 
 |     if (chrome.inputMethodPrivate) | 
 |       chrome.inputMethodPrivate.getCurrentInputMethod(callback); | 
 |     else | 
 |       callback(''); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Retrieve the current display size in inches. | 
 |    * @param {function} callback | 
 |    * @private | 
 |    */ | 
 |   function getDisplayInInches_(callback) { | 
 |     callback(0); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Retrieve the current input method configuration. | 
 |    * @param {function} callback The callback function for processing the | 
 |    *     name of the active input mehtod. | 
 |    * @private | 
 |    */ | 
 |   function getInputMethodConfig_(callback) { | 
 |     if (chrome.inputMethodPrivate) | 
 |       chrome.inputMethodPrivate.getInputMethodConfig(callback); | 
 |     else | 
 |       callback(''); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Changes the active input method. | 
 |    * @param {string} inputMethodId The id of the input method to activate. | 
 |    * @private | 
 |    */ | 
 |   function switchToInputMethod_(inputMethodId) { | 
 |     if (chrome.inputMethodPrivate) | 
 |       chrome.inputMethodPrivate.setCurrentInputMethod(inputMethodId) | 
 |   } | 
 |  | 
 |   /** | 
 |    * Opens the language settings for specifying and configuring input methods. | 
 |    * @private | 
 |    */ | 
 |   function openSettings_() { | 
 |     chrome.virtualKeyboardPrivate.openSettings(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Dispatches a virtual key event. The system VK does not use the IME | 
 |    * API as its primary role is to work in conjunction with a non-VK aware | 
 |    * IME. Some reformatting of the key data is required to work with the | 
 |    * virtualKeyboardPrivate API. | 
 |    * @param {!Object} keyData Description of the key event. | 
 |    */ | 
 |   function sendKeyEvent_(keyData) { | 
 |     keyData.forEach(function(data) { | 
 |       var charValue = data.key.length == 1 ? data.key.charCodeAt(0) : 0; | 
 |       var keyCode = data.keyCode ? data.keyCode : | 
 |           getKeyCode_(data.key, data.code); | 
 |       var event = { | 
 |         type: data.type, | 
 |         charValue: charValue, | 
 |         keyCode: keyCode, | 
 |         keyName: data.code, | 
 |         modifiers: Modifier.NONE | 
 |       }; | 
 |       if (data.altKey) | 
 |         event.modifiers |= Modifier.ALT; | 
 |       if (data.ctrlKey) | 
 |         event.modifiers |= Modifier.CONTROL; | 
 |       if (data.shiftKey) | 
 |         event.modifiers |= Modifier.SHIFT; | 
 |       if (data.capsLock) | 
 |         event.modifiers |= Modifier.CAPSLOCK; | 
 |  | 
 |       chrome.virtualKeyboardPrivate.sendKeyEvent(event, logIfError_); | 
 |     }); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Computes keyCodes for use with ui::KeyEvent. | 
 |    * @param {string} keyChar Character being typed. | 
 |    * @param {string} keyName w3c name of the character. | 
 |    */ | 
 |   function getKeyCode_(keyChar, keyName) { | 
 |     var keyCode = nonAlphaNumericKeycodes[keyName]; | 
 |     if (keyCode) | 
 |       return keyCode; | 
 |  | 
 |     var match = /Key([A-Z])/.exec(keyName); | 
 |     if (match) | 
 |       return match[1].charCodeAt(0); | 
 |  | 
 |     match = /Digit([0-9])/.exec(keyName); | 
 |     if (match) | 
 |       return match[1].charCodeAt(0); | 
 |  | 
 |     if (keyChar.length == 1) { | 
 |       if (keyChar >= 'a' && keyChar <= 'z') | 
 |         return keyChar.charCodeAt(0) - 32; | 
 |       if (keyChar >= 'A' && keyChar <= 'Z') | 
 |         return keyChar.charCodeAt(0); | 
 |       if (keyChar >= '0' && keyChar <= '9') | 
 |         return keyChar.charCodeAt(0); | 
 |     } | 
 |     return 0; | 
 |   } | 
 |  | 
 |   window.inputview = { | 
 |     commitText: commitText_, | 
 |     getKeyboardConfig: getKeyboardConfig_, | 
 |     getInputMethods: getInputMethods_, | 
 |     getCurrentInputMethod: getCurrentInputMethod_, | 
 |     getInputMethodConfig: getInputMethodConfig_, | 
 |     switchToInputMethod: switchToInputMethod_, | 
 |     getDisplayInInches: getDisplayInInches_, | 
 |     openSettings: openSettings_ | 
 |   }; | 
 |  | 
 |   registerFunction('chrome.input.ime.hideInputView', function() { | 
 |     chrome.virtualKeyboardPrivate.hideKeyboard(); | 
 |     chrome.virtualKeyboardPrivate.lockKeyboard(false); | 
 |   }); | 
 |  | 
 |   var defaultSendMessage = registerFunction('chrome.runtime.sendMessage'); | 
 |   registerFunction('chrome.runtime.sendMessage', function(message) { | 
 |     if (message.type == 'send_key_event') | 
 |       sendKeyEvent_(message.keyData); | 
 |     else if (message.type == 'commit_text') | 
 |       commitText_(message.text); | 
 |     else | 
 |       defaultSendMessage(message); | 
 |   }); | 
 | } | 
 |  | 
 | registerFunction('chrome.runtime.getBackgroundPage', function() { | 
 |   var callback = arguments[0]; | 
 |   callback(); | 
 | }); | 
 | registerFunction('chrome.runtime.sendMessage'); | 
 | registerFunction('chrome.runtime.onMessage.addListener'); | 
 |  | 
 | if (!chrome.i18n) { | 
 |   chrome.i18n = {}; | 
 |   chrome.i18n.getMessage = function(name) { | 
 |     return name; | 
 |   } | 
 | } | 
 |  | 
 | /** | 
 |  * Trigger loading the virtual keyboard on completion of page load. | 
 |  */ | 
 | window.onload = function() { | 
 |   var params = {}; | 
 |   var matches = window.location.href.match(/[#?].*$/); | 
 |   if (matches && matches.length > 0) { | 
 |     matches[0].slice(1).split('&').forEach(function(s) { | 
 |       var pair = s.split('='); | 
 |       params[pair[0]] = pair[1]; | 
 |     }); | 
 |   } | 
 |  | 
 |   var keyset = params['id'] || getDefaultUsLayout(); | 
 |   var languageCode = params['language'] || 'en'; | 
 |   var passwordLayout = params['passwordLayout'] || 'us'; | 
 |   var name = params['name'] || 'English'; | 
 |  | 
 |   overrideGetMessage(); | 
 |   overrideSwitchToKeyset(); | 
 |   overrideGetSpatialData(); | 
 |   registerInputviewApi(); | 
 |   i18n.input.chrome.inputview.Controller.DEV = true; | 
 |   i18n.input.chrome.inputview.Adapter.prototype.isSwitching = function() { | 
 |     return false; | 
 |   }; | 
 |  | 
 |   if (keyset != 'none') { | 
 |     window.initializeVirtualKeyboard(keyset, languageCode, passwordLayout, | 
 |         name); | 
 |   } | 
 | }; | 
 |  | 
 | /** | 
 |  * Run cleanup tasks. | 
 |  */ | 
 | window.onbeforeunload = function() { | 
 |   if (controller) | 
 |     goog.dispose(controller); | 
 | }; | 
 |  | 
 | /** | 
 |  * Loads a virtual keyboard. If a keyboard was previously loaded, it is | 
 |  * reinitialized with the new configuration. | 
 |  * @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. | 
 |  * @param {Object=} opt_config Optional configuration settings. | 
 |  */ | 
 | window.initializeVirtualKeyboard = function(keyset, languageCode, | 
 |     passwordLayout, name, opt_config) { | 
 |   var Controller = i18n.input.chrome.inputview.Controller; | 
 |   Controller.DISABLE_HWT = !(opt_config && opt_config.enableHwtForTesting); | 
 |   onSwitchToKeyset(keyset, function() { | 
 |     chrome.virtualKeyboardPrivate.keyboardLoaded(); | 
 |   }); | 
 |   if (controller) | 
 |     controller.initialize(keyset, languageCode, passwordLayout, name); | 
 |   else | 
 |     controller = new Controller(keyset, languageCode, passwordLayout, name); | 
 | }; |