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