Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2012 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 <include src="keyboard_overlay_data.js"/>
      6 <include src="keyboard_overlay_accessibility_helper.js"/>
      7 
      8 var BASE_KEYBOARD = {
      9   top: 0,
     10   left: 0,
     11   width: 1237,
     12   height: 514
     13 };
     14 
     15 var BASE_INSTRUCTIONS = {
     16   top: 194,
     17   left: 370,
     18   width: 498,
     19   height: 142
     20 };
     21 
     22 var MODIFIER_TO_CLASS = {
     23   'SHIFT': 'modifier-shift',
     24   'CTRL': 'modifier-ctrl',
     25   'ALT': 'modifier-alt',
     26   'SEARCH': 'modifier-search'
     27 };
     28 
     29 var IDENTIFIER_TO_CLASS = {
     30   '2A': 'is-shift',
     31   '1D': 'is-ctrl',
     32   '38': 'is-alt',
     33   'E0 5B': 'is-search'
     34 };
     35 
     36 var LABEL_TO_IDENTIFIER = {
     37   'search': 'E0 5B',
     38   'ctrl': '1D',
     39   'alt': '38',
     40   'caps lock': '3A',
     41   'esc': '01',
     42   'disabled': 'DISABLED'
     43 };
     44 
     45 var KEYCODE_TO_LABEL = {
     46   8: 'backspace',
     47   9: 'tab',
     48   13: 'enter',
     49   27: 'esc',
     50   32: 'space',
     51   33: 'pageup',
     52   34: 'pagedown',
     53   35: 'end',
     54   36: 'home',
     55   37: 'left',
     56   38: 'up',
     57   39: 'right',
     58   40: 'down',
     59   46: 'delete',
     60   91: 'search',
     61   92: 'search',
     62   96: '0',
     63   97: '1',
     64   98: '2',
     65   99: '3',
     66   100: '4',
     67   101: '5',
     68   102: '6',
     69   103: '7',
     70   104: '8',
     71   105: '9',
     72   106: '*',
     73   107: '+',
     74   109: '-',
     75   110: '.',
     76   111: '/',
     77   112: 'back',
     78   113: 'forward',
     79   114: 'reload',
     80   115: 'full screen',
     81   116: 'switch window',
     82   117: 'bright down',
     83   118: 'bright up',
     84   119: 'mute',
     85   120: 'vol. down',
     86   121: 'vol. up',
     87   186: ';',
     88   187: '+',
     89   188: ',',
     90   189: '-',
     91   190: '.',
     92   191: '/',
     93   192: '`',
     94   219: '[',
     95   220: '\\',
     96   221: ']',
     97   222: '\'',
     98 };
     99 
    100 var IME_ID_PREFIX = '_comp_ime_';
    101 var EXTENSION_ID_LEN = 32;
    102 
    103 var keyboardOverlayId = 'en_US';
    104 var identifierMap = {};
    105 
    106 /**
    107  * True after at least one keydown event has been received.
    108  */
    109 var gotKeyDown = false;
    110 
    111 /**
    112  * Returns the layout name.
    113  * @return {string} layout name.
    114  */
    115 function getLayoutName() {
    116   return getKeyboardGlyphData().layoutName;
    117 }
    118 
    119 /**
    120  * Returns layout data.
    121  * @return {Array} Keyboard layout data.
    122  */
    123 function getLayout() {
    124   return keyboardOverlayData['layouts'][getLayoutName()];
    125 }
    126 
    127 // Cache the shortcut data after it is constructed.
    128 var shortcutDataCache;
    129 
    130 /**
    131  * Returns shortcut data.
    132  * @return {Object} Keyboard shortcut data.
    133  */
    134 function getShortcutData() {
    135   if (shortcutDataCache)
    136     return shortcutDataCache;
    137 
    138   shortcutDataCache = keyboardOverlayData['shortcut'];
    139 
    140   if (!isDisplayUIScalingEnabled()) {
    141     // Zoom screen in
    142     delete shortcutDataCache['+<>CTRL<>SHIFT'];
    143     // Zoom screen out
    144     delete shortcutDataCache['-<>CTRL<>SHIFT'];
    145     // Reset screen zoom
    146     delete shortcutDataCache['0<>CTRL<>SHIFT'];
    147   }
    148 
    149   return shortcutDataCache;
    150 }
    151 
    152 /**
    153  * Returns the keyboard overlay ID.
    154  * @return {string} Keyboard overlay ID.
    155  */
    156 function getKeyboardOverlayId() {
    157   return keyboardOverlayId;
    158 }
    159 
    160 /**
    161  * Returns keyboard glyph data.
    162  * @return {Object} Keyboard glyph data.
    163  */
    164 function getKeyboardGlyphData() {
    165   return keyboardOverlayData['keyboardGlyph'][getKeyboardOverlayId()];
    166 }
    167 
    168 /**
    169  * Converts a single hex number to a character.
    170  * @param {string} hex Hexadecimal string.
    171  * @return {string} Unicode values of hexadecimal string.
    172  */
    173 function hex2char(hex) {
    174   if (!hex) {
    175     return '';
    176   }
    177   var result = '';
    178   var n = parseInt(hex, 16);
    179   if (n <= 0xFFFF) {
    180     result += String.fromCharCode(n);
    181   } else if (n <= 0x10FFFF) {
    182     n -= 0x10000;
    183     result += (String.fromCharCode(0xD800 | (n >> 10)) +
    184                String.fromCharCode(0xDC00 | (n & 0x3FF)));
    185   } else {
    186     console.error('hex2Char error: Code point out of range :' + hex);
    187   }
    188   return result;
    189 }
    190 
    191 var searchIsPressed = false;
    192 
    193 /**
    194  * Returns a list of modifiers from the key event.
    195  * @param {Event} e The key event.
    196  * @return {Array} List of modifiers based on key event.
    197  */
    198 function getModifiers(e) {
    199   if (!e)
    200     return [];
    201 
    202   var isKeyDown = (e.type == 'keydown');
    203   var keyCodeToModifier = {
    204     16: 'SHIFT',
    205     17: 'CTRL',
    206     18: 'ALT',
    207     91: 'SEARCH',
    208   };
    209   var modifierWithKeyCode = keyCodeToModifier[e.keyCode];
    210   var isPressed = {
    211       'SHIFT': e.shiftKey,
    212       'CTRL': e.ctrlKey,
    213       'ALT': e.altKey,
    214       'SEARCH': searchIsPressed
    215   };
    216   if (modifierWithKeyCode)
    217     isPressed[modifierWithKeyCode] = isKeyDown;
    218 
    219   searchIsPressed = isPressed['SEARCH'];
    220 
    221   // make the result array
    222   return ['SHIFT', 'CTRL', 'ALT', 'SEARCH'].filter(
    223       function(modifier) {
    224         return isPressed[modifier];
    225       }).sort();
    226 }
    227 
    228 /**
    229  * Returns an ID of the key.
    230  * @param {string} identifier Key identifier.
    231  * @param {number} i Key number.
    232  * @return {string} Key ID.
    233  */
    234 function keyId(identifier, i) {
    235   return identifier + '-key-' + i;
    236 }
    237 
    238 /**
    239  * Returns an ID of the text on the key.
    240  * @param {string} identifier Key identifier.
    241  * @param {number} i Key number.
    242  * @return {string} Key text ID.
    243  */
    244 function keyTextId(identifier, i) {
    245   return identifier + '-key-text-' + i;
    246 }
    247 
    248 /**
    249  * Returns an ID of the shortcut text.
    250  * @param {string} identifier Key identifier.
    251  * @param {number} i Key number.
    252  * @return {string} Key shortcut text ID.
    253  */
    254 function shortcutTextId(identifier, i) {
    255   return identifier + '-shortcut-text-' + i;
    256 }
    257 
    258 /**
    259  * Returns true if |list| contains |e|.
    260  * @param {Array} list Container list.
    261  * @param {string} e Element string.
    262  * @return {boolean} Returns true if the list contains the element.
    263  */
    264 function contains(list, e) {
    265   return list.indexOf(e) != -1;
    266 }
    267 
    268 /**
    269  * Returns a list of the class names corresponding to the identifier and
    270  * modifiers.
    271  * @param {string} identifier Key identifier.
    272  * @param {Array} modifiers List of key modifiers.
    273  * @return {Array} List of class names corresponding to specified params.
    274  */
    275 function getKeyClasses(identifier, modifiers) {
    276   var classes = ['keyboard-overlay-key'];
    277   for (var i = 0; i < modifiers.length; ++i) {
    278     classes.push(MODIFIER_TO_CLASS[modifiers[i]]);
    279   }
    280 
    281   if ((identifier == '2A' && contains(modifiers, 'SHIFT')) ||
    282       (identifier == '1D' && contains(modifiers, 'CTRL')) ||
    283       (identifier == '38' && contains(modifiers, 'ALT')) ||
    284       (identifier == 'E0 5B' && contains(modifiers, 'SEARCH'))) {
    285     classes.push('pressed');
    286     classes.push(IDENTIFIER_TO_CLASS[identifier]);
    287   }
    288   return classes;
    289 }
    290 
    291 /**
    292  * Returns true if a character is a ASCII character.
    293  * @param {string} c A character to be checked.
    294  * @return {boolean} True if the character is an ASCII character.
    295  */
    296 function isAscii(c) {
    297   var charCode = c.charCodeAt(0);
    298   return 0x00 <= charCode && charCode <= 0x7F;
    299 }
    300 
    301 /**
    302  * Returns a remapped identiifer based on the preference.
    303  * @param {string} identifier Key identifier.
    304  * @return {string} Remapped identifier.
    305  */
    306 function remapIdentifier(identifier) {
    307   return identifierMap[identifier] || identifier;
    308 }
    309 
    310 /**
    311  * Returns a label of the key.
    312  * @param {string} keyData Key glyph data.
    313  * @param {Array} modifiers Key Modifier list.
    314  * @return {string} Label of the key.
    315  */
    316 function getKeyLabel(keyData, modifiers) {
    317   if (!keyData) {
    318     return '';
    319   }
    320   if (keyData.label) {
    321     return keyData.label;
    322   }
    323   var keyLabel = '';
    324   for (var j = 1; j <= 9; j++) {
    325     var pos = keyData['p' + j];
    326     if (!pos) {
    327       continue;
    328     }
    329     keyLabel = hex2char(pos);
    330     if (!keyLabel) {
    331       continue;
    332      }
    333     if (isAscii(keyLabel) &&
    334         getShortcutData()[getAction(keyLabel, modifiers)]) {
    335       break;
    336     }
    337   }
    338   return keyLabel;
    339 }
    340 
    341 /**
    342  * Returns a normalized string used for a key of shortcutData.
    343  *
    344  * Examples:
    345  *   keyCode: 'd', modifiers: ['CTRL', 'SHIFT'] => 'd<>CTRL<>SHIFT'
    346  *   keyCode: 'alt', modifiers: ['ALT', 'SHIFT'] => 'ALT<>SHIFT'
    347  *
    348  * @param {string} keyCode Key code.
    349  * @param {Array} modifiers Key Modifier list.
    350  * @return {string} Normalized key shortcut data string.
    351  */
    352 function getAction(keyCode, modifiers) {
    353   /** @const */ var separatorStr = '<>';
    354   if (keyCode.toUpperCase() in MODIFIER_TO_CLASS) {
    355     keyCode = keyCode.toUpperCase();
    356     if (keyCode in modifiers) {
    357       return modifiers.join(separatorStr);
    358     } else {
    359       var action = [keyCode].concat(modifiers);
    360       action.sort();
    361       return action.join(separatorStr);
    362     }
    363   }
    364   return [keyCode].concat(modifiers).join(separatorStr);
    365 }
    366 
    367 /**
    368  * Returns a text which displayed on a key.
    369  * @param {string} keyData Key glyph data.
    370  * @return {string} Key text value.
    371  */
    372 function getKeyTextValue(keyData) {
    373   if (keyData.label) {
    374     // Do not show text on the space key.
    375     if (keyData.label == 'space') {
    376       return '';
    377     }
    378     return keyData.label;
    379   }
    380 
    381   var chars = [];
    382   for (var j = 1; j <= 9; ++j) {
    383     var pos = keyData['p' + j];
    384     if (pos && pos.length > 0) {
    385       chars.push(hex2char(pos));
    386     }
    387   }
    388   return chars.join(' ');
    389 }
    390 
    391 /**
    392  * Updates the whole keyboard.
    393  * @param {Array} modifiers Key Modifier list.
    394  */
    395 function update(modifiers) {
    396   var instructions = $('instructions');
    397   if (modifiers.length == 0) {
    398     instructions.style.visibility = 'visible';
    399   } else {
    400     instructions.style.visibility = 'hidden';
    401   }
    402 
    403   var keyboardGlyphData = getKeyboardGlyphData();
    404   var shortcutData = getShortcutData();
    405   var layout = getLayout();
    406   for (var i = 0; i < layout.length; ++i) {
    407     var identifier = remapIdentifier(layout[i][0]);
    408     var keyData = keyboardGlyphData.keys[identifier];
    409     var classes = getKeyClasses(identifier, modifiers, keyData);
    410     var keyLabel = getKeyLabel(keyData, modifiers);
    411     var shortcutId = shortcutData[getAction(keyLabel, modifiers)];
    412     if (modifiers.length == 1 && modifiers[0] == 'SHIFT' &&
    413         identifier == '2A') {
    414       // Currently there is no way to identify whether the left shift or the
    415       // right shift is preesed from the key event, so I assume the left shift
    416       // key is pressed here and do not show keyboard shortcut description for
    417       // 'Shift - Shift' (Toggle caps lock) on the left shift key, the
    418       // identifier of which is '2A'.
    419       // TODO(mazda): Remove this workaround (http://crosbug.com/18047)
    420       shortcutId = null;
    421     }
    422     if (shortcutId) {
    423       classes.push('is-shortcut');
    424     }
    425 
    426     var key = $(keyId(identifier, i));
    427     key.className = classes.join(' ');
    428 
    429     if (!keyData) {
    430       continue;
    431     }
    432 
    433     var keyText = $(keyTextId(identifier, i));
    434     var keyTextValue = getKeyTextValue(keyData);
    435     if (keyTextValue) {
    436        keyText.style.visibility = 'visible';
    437     } else {
    438        keyText.style.visibility = 'hidden';
    439     }
    440     keyText.textContent = keyTextValue;
    441 
    442     var shortcutText = $(shortcutTextId(identifier, i));
    443     if (shortcutId) {
    444       shortcutText.style.visibility = 'visible';
    445       shortcutText.textContent = loadTimeData.getString(shortcutId);
    446     } else {
    447       shortcutText.style.visibility = 'hidden';
    448     }
    449 
    450     var format = keyboardGlyphData.keys[layout[i][0]].format;
    451     if (format) {
    452       if (format == 'left' || format == 'right') {
    453         shortcutText.style.textAlign = format;
    454         keyText.style.textAlign = format;
    455       }
    456     }
    457   }
    458 }
    459 
    460 /**
    461  * A callback function for onkeydown and onkeyup events.
    462  * @param {Event} e Key event.
    463  */
    464 function handleKeyEvent(e) {
    465   if (!getKeyboardOverlayId()) {
    466     return;
    467   }
    468 
    469   // To avoid flickering as the user releases the modifier keys that were held
    470   // to trigger the overlay, avoid updating in response to keyup events until at
    471   // least one keydown event has been received.
    472   if (!gotKeyDown) {
    473     if (e.type == 'keyup') {
    474       return;
    475     } else if (e.type == 'keydown') {
    476       gotKeyDown = true;
    477     }
    478   }
    479 
    480   var modifiers = getModifiers(e);
    481   update(modifiers);
    482   KeyboardOverlayAccessibilityHelper.maybeSpeakAllShortcuts(modifiers);
    483   e.preventDefault();
    484 }
    485 
    486 /**
    487  * Initializes the layout of the keys.
    488  */
    489 function initLayout() {
    490   // Add data for the caps lock key
    491   var keys = getKeyboardGlyphData().keys;
    492   if (!('3A' in keys)) {
    493     keys['3A'] = {label: 'caps lock', format: 'left'};
    494   }
    495   // Add data for the special key representing a disabled key
    496   keys['DISABLED'] = {label: 'disabled', format: 'left'};
    497 
    498   var layout = getLayout();
    499   var keyboard = document.body;
    500   var minX = window.innerWidth;
    501   var maxX = 0;
    502   var minY = window.innerHeight;
    503   var maxY = 0;
    504   var multiplier = 1.38 * window.innerWidth / BASE_KEYBOARD.width;
    505   var keyMargin = 7;
    506   var offsetX = 10;
    507   var offsetY = 7;
    508   for (var i = 0; i < layout.length; i++) {
    509     var array = layout[i];
    510     var identifier = remapIdentifier(array[0]);
    511     var x = Math.round((array[1] + offsetX) * multiplier);
    512     var y = Math.round((array[2] + offsetY) * multiplier);
    513     var w = Math.round((array[3] - keyMargin) * multiplier);
    514     var h = Math.round((array[4] - keyMargin) * multiplier);
    515 
    516     var key = document.createElement('div');
    517     key.id = keyId(identifier, i);
    518     key.className = 'keyboard-overlay-key';
    519     key.style.left = x + 'px';
    520     key.style.top = y + 'px';
    521     key.style.width = w + 'px';
    522     key.style.height = h + 'px';
    523 
    524     var keyText = document.createElement('div');
    525     keyText.id = keyTextId(identifier, i);
    526     keyText.className = 'keyboard-overlay-key-text';
    527     keyText.style.visibility = 'hidden';
    528     key.appendChild(keyText);
    529 
    530     var shortcutText = document.createElement('div');
    531     shortcutText.id = shortcutTextId(identifier, i);
    532     shortcutText.className = 'keyboard-overlay-shortcut-text';
    533     shortcutText.style.visilibity = 'hidden';
    534     key.appendChild(shortcutText);
    535     keyboard.appendChild(key);
    536 
    537     minX = Math.min(minX, x);
    538     maxX = Math.max(maxX, x + w);
    539     minY = Math.min(minY, y);
    540     maxY = Math.max(maxY, y + h);
    541   }
    542 
    543   var width = maxX - minX + 1;
    544   var height = maxY - minY + 1;
    545   keyboard.style.width = (width + 2 * (minX + 1)) + 'px';
    546   keyboard.style.height = (height + 2 * (minY + 1)) + 'px';
    547 
    548   var instructions = document.createElement('div');
    549   instructions.id = 'instructions';
    550   instructions.className = 'keyboard-overlay-instructions';
    551   instructions.style.left = ((BASE_INSTRUCTIONS.left - BASE_KEYBOARD.left) *
    552                              width / BASE_KEYBOARD.width + minX) + 'px';
    553   instructions.style.top = ((BASE_INSTRUCTIONS.top - BASE_KEYBOARD.top) *
    554                             height / BASE_KEYBOARD.height + minY) + 'px';
    555   instructions.style.width = (width * BASE_INSTRUCTIONS.width /
    556                               BASE_KEYBOARD.width) + 'px';
    557   instructions.style.height = (height * BASE_INSTRUCTIONS.height /
    558                                BASE_KEYBOARD.height) + 'px';
    559 
    560   var instructionsText = document.createElement('div');
    561   instructionsText.id = 'instructions-text';
    562   instructionsText.className = 'keyboard-overlay-instructions-text';
    563   instructionsText.innerHTML =
    564       loadTimeData.getString('keyboardOverlayInstructions');
    565   instructions.appendChild(instructionsText);
    566   var instructionsHideText = document.createElement('div');
    567   instructionsHideText.id = 'instructions-hide-text';
    568   instructionsHideText.className = 'keyboard-overlay-instructions-hide-text';
    569   instructionsHideText.innerHTML =
    570       loadTimeData.getString('keyboardOverlayInstructionsHide');
    571   instructions.appendChild(instructionsHideText);
    572   var learnMoreLinkText = document.createElement('div');
    573   learnMoreLinkText.id = 'learn-more-text';
    574   learnMoreLinkText.className = 'keyboard-overlay-learn-more-text';
    575   learnMoreLinkText.addEventListener('click', learnMoreClicked);
    576   var learnMoreLinkAnchor = document.createElement('a');
    577   learnMoreLinkAnchor.href =
    578       loadTimeData.getString('keyboardOverlayLearnMoreURL');
    579   learnMoreLinkAnchor.textContent =
    580       loadTimeData.getString('keyboardOverlayLearnMore');
    581   learnMoreLinkText.appendChild(learnMoreLinkAnchor);
    582   instructions.appendChild(learnMoreLinkText);
    583   keyboard.appendChild(instructions);
    584 }
    585 
    586 /**
    587  * Returns true if the device has a diamond key.
    588  * @return {boolean} Returns true if the device has a diamond key.
    589  */
    590 function hasDiamondKey() {
    591   return loadTimeData.getBoolean('keyboardOverlayHasChromeOSDiamondKey');
    592 }
    593 
    594 /**
    595  * Returns true if display scaling feature is enabled.
    596  * @return {boolean} True if display scaling feature is enabled.
    597  */
    598 function isDisplayUIScalingEnabled() {
    599   return loadTimeData.getBoolean('keyboardOverlayIsDisplayUIScalingEnabled');
    600 }
    601 
    602 /**
    603  * Initializes the layout and the key labels for the keyboard that has a diamond
    604  * key.
    605  */
    606 function initDiamondKey() {
    607   var newLayoutData = {
    608     '1D': [65.0, 287.0, 60.0, 60.0],  // left Ctrl
    609     '38': [185.0, 287.0, 60.0, 60.0],  // left Alt
    610     'E0 5B': [125.0, 287.0, 60.0, 60.0],  // search
    611     '3A': [5.0, 167.0, 105.0, 60.0],  // caps lock
    612     '5B': [803.0, 6.0, 72.0, 35.0],  // lock key
    613     '5D': [5.0, 287.0, 60.0, 60.0]  // diamond key
    614   };
    615 
    616   var layout = getLayout();
    617   var powerKeyIndex = -1;
    618   var powerKeyId = '00';
    619   for (var i = 0; i < layout.length; i++) {
    620     var keyId = layout[i][0];
    621     if (keyId in newLayoutData) {
    622       layout[i] = [keyId].concat(newLayoutData[keyId]);
    623       delete newLayoutData[keyId];
    624     }
    625     if (keyId == powerKeyId)
    626       powerKeyIndex = i;
    627   }
    628   for (var keyId in newLayoutData)
    629     layout.push([keyId].concat(newLayoutData[keyId]));
    630 
    631   // Remove the power key.
    632   if (powerKeyIndex != -1)
    633     layout.splice(powerKeyIndex, 1);
    634 
    635   var keyData = getKeyboardGlyphData()['keys'];
    636   var newKeyData = {
    637     '3A': {'label': 'caps lock', 'format': 'left'},
    638     '5B': {'label': 'lock'},
    639     '5D': {'label': 'diamond', 'format': 'left'}
    640   };
    641   for (var keyId in newKeyData)
    642     keyData[keyId] = newKeyData[keyId];
    643 }
    644 
    645 /**
    646  * A callback function for the onload event of the body element.
    647  */
    648 function init() {
    649   document.addEventListener('keydown', handleKeyEvent);
    650   document.addEventListener('keyup', handleKeyEvent);
    651   chrome.send('getLabelMap');
    652 }
    653 
    654 /**
    655  * Initializes the global map for remapping identifiers of modifier keys based
    656  * on the preference.
    657  * Called after sending the 'getLabelMap' message.
    658  * @param {Object} remap Identifier map.
    659  */
    660 function initIdentifierMap(remap) {
    661   for (var key in remap) {
    662     var val = remap[key];
    663     if ((key in LABEL_TO_IDENTIFIER) &&
    664         (val in LABEL_TO_IDENTIFIER)) {
    665       identifierMap[LABEL_TO_IDENTIFIER[key]] =
    666           LABEL_TO_IDENTIFIER[val];
    667     } else {
    668       console.error('Invalid label map element: ' + key + ', ' + val);
    669     }
    670   }
    671   chrome.send('getInputMethodId');
    672 }
    673 
    674 /**
    675  * Initializes the global keyboad overlay ID and the layout of keys.
    676  * Called after sending the 'getInputMethodId' message.
    677  * @param {inputMethodId} inputMethodId Input Method Identifier.
    678  */
    679 function initKeyboardOverlayId(inputMethodId) {
    680   // Libcros returns an empty string when it cannot find the keyboard overlay ID
    681   // corresponding to the current input method.
    682   // In such a case, fallback to the default ID (en_US).
    683   var inputMethodIdToOverlayId =
    684       keyboardOverlayData['inputMethodIdToOverlayId'];
    685   if (inputMethodId) {
    686     if (inputMethodId.indexOf(IME_ID_PREFIX) == 0) {
    687       // If the input method is a component extension IME, remove the prefix:
    688       //   _comp_ime_<ext_id>
    689       // The extension id is a hash value with 32 characters.
    690       inputMethodId = inputMethodId.slice(
    691           IME_ID_PREFIX.length + EXTENSION_ID_LEN);
    692     }
    693     keyboardOverlayId = inputMethodIdToOverlayId[inputMethodId];
    694   }
    695   if (!keyboardOverlayId) {
    696     console.error('No keyboard overlay ID for ' + inputMethodId);
    697     keyboardOverlayId = 'en_US';
    698   }
    699   while (document.body.firstChild) {
    700     document.body.removeChild(document.body.firstChild);
    701   }
    702   // We show Japanese layout as-is because the user has chosen the layout
    703   // that is quite diffrent from the physical layout that has a diamond key.
    704   if (hasDiamondKey() && getLayoutName() != 'J')
    705     initDiamondKey();
    706   initLayout();
    707   update([]);
    708   window.webkitRequestAnimationFrame(function() {
    709     chrome.send('didPaint');
    710   });
    711 }
    712 
    713 /**
    714  * Handles click events of the learn more link.
    715  * @param {Event} e Mouse click event.
    716  */
    717 function learnMoreClicked(e) {
    718   chrome.send('openLearnMorePage');
    719   chrome.send('dialogClose');
    720   e.preventDefault();
    721 }
    722 
    723 document.addEventListener('DOMContentLoaded', init);
    724