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