Home | History | Annotate | Download | only in resources
      1 // Copyright (c) 2011 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 BASE_KEYBOARD = {
      6   top: 0,
      7   left: 0,
      8   width: 1237,
      9   height: 514
     10 };
     11 
     12 var BASE_INSTRUCTIONS = {
     13   top: 194,
     14   left: 370,
     15   width: 498,
     16   height: 112
     17 };
     18 
     19 var LABEL_TO_KEY_TEXT = {
     20   alt: 'alt',
     21   backspace: 'backspace',
     22   ctrl: 'ctrl',
     23   enter: 'enter',
     24   esc: 'esc',
     25   glyph_arrow_down: 'down',
     26   glyph_arrow_left: 'left',
     27   glyph_arrow_right: 'right',
     28   glyph_arrow_up: 'up',
     29   glyph_back: 'back',
     30   glyph_backspace: 'backspace',
     31   glyph_brightness_down: 'bright down',
     32   glyph_brightness_up: 'bright up',
     33   glyph_enter: 'enter',
     34   glyph_forward: 'forward',
     35   glyph_fullscreen: 'full screen',
     36   glyph_ime: 'ime',
     37   glyph_lock: 'lock',
     38   glyph_overview: 'switch window',
     39   glyph_power: 'power',
     40   glyph_right: 'right',
     41   glyph_reload: 'reload',
     42   glyph_search: 'search',
     43   glyph_shift: 'shift',
     44   glyph_tab: 'tab',
     45   glyph_tools: 'tools',
     46   glyph_volume_down: 'vol. down',
     47   glyph_volume_mute: 'mute',
     48   glyph_volume_up: 'vol. up',
     49   shift: 'shift',
     50   tab: 'tab'
     51 };
     52 
     53 var MODIFIER_TO_CLASS = {
     54   'SHIFT': 'modifier-shift',
     55   'CTRL': 'modifier-ctrl',
     56   'ALT': 'modifier-alt'
     57 };
     58 
     59 var IDENTIFIER_TO_CLASS = {
     60   '2A': 'is-shift',
     61   '1D': 'is-ctrl',
     62   '38': 'is-alt'
     63 };
     64 
     65 var keyboardOverlayId = 'en_US';
     66 
     67 /**
     68  * Returns layouts data.
     69  */
     70 function getLayouts() {
     71   return keyboardOverlayData['layouts'];
     72 }
     73 
     74 /**
     75  * Returns shortcut data.
     76  */
     77 function getShortcutData() {
     78   return keyboardOverlayData['shortcut'];
     79 }
     80 
     81 /**
     82  * Returns the keyboard overlay ID.
     83  */
     84 function getKeyboardOverlayId() {
     85   return keyboardOverlayId
     86 }
     87 
     88 /**
     89  * Returns keyboard glyph data.
     90  */
     91 function getKeyboardGlyphData() {
     92   return keyboardOverlayData['keyboardGlyph'][getKeyboardOverlayId()];
     93 }
     94 
     95 /**
     96  * Converts a single hex number to a character.
     97  */
     98 function hex2char(hex) {
     99   if (!hex) {
    100     return '';
    101   }
    102   var result = '';
    103   var n = parseInt(hex, 16);
    104   if (n <= 0xFFFF) {
    105     result += String.fromCharCode(n);
    106   } else if (n <= 0x10FFFF) {
    107     n -= 0x10000;
    108     result += (String.fromCharCode(0xD800 | (n >> 10)) +
    109                String.fromCharCode(0xDC00 | (n & 0x3FF)));
    110   } else {
    111     console.error('hex2Char error: Code point out of range :' + hex);
    112   }
    113   return result;
    114 }
    115 
    116 /**
    117  * Returns a list of modifiers from the key event.
    118  */
    119 function getModifiers(e) {
    120   if (!e) {
    121     return [];
    122   }
    123   var isKeyDown = (e.type == 'keydown');
    124   var keyCodeToModifier = {
    125     16: 'SHIFT',
    126     17: 'CTRL',
    127     18: 'ALT',
    128     91: 'ALT', // left ALT pressed with SHIFT
    129     92: 'ALT', // right ALT pressed with SHIFT
    130   };
    131   var modifierWithKeyCode = keyCodeToModifier[e.keyCode];
    132   var isPressed = {'SHIFT': e.shiftKey, 'CTRL': e.ctrlKey, 'ALT': e.altKey};
    133   // if e.keyCode is one of Shift, Ctrl and Alt, isPressed should
    134   // be changed because the key currently pressed
    135   // does not affect the values of e.shiftKey, e.ctrlKey and e.altKey
    136   if(modifierWithKeyCode){
    137     isPressed[modifierWithKeyCode] = isKeyDown;
    138   }
    139   // make the result array
    140   return ['SHIFT', 'CTRL', 'ALT'].filter(
    141       function(modifier) {
    142         return isPressed[modifier];
    143       }).sort();
    144 }
    145 
    146 /**
    147  * Returns an ID of the key.
    148  */
    149 function keyId(identifier, i) {
    150   return identifier + '-key-' + i;
    151 }
    152 
    153 /**
    154  * Returns an ID of the text on the key.
    155  */
    156 function keyTextId(identifier, i) {
    157   return identifier + '-key-text-' + i;
    158 }
    159 
    160 /**
    161  * Returns an ID of the shortcut text.
    162  */
    163 function shortcutTextId(identifier, i) {
    164   return identifier + '-shortcut-text-' + i;
    165 }
    166 
    167 /**
    168  * Returns true if |list| contains |e|.
    169  */
    170 function contains(list, e) {
    171   return list.indexOf(e) != -1;
    172 }
    173 
    174 /**
    175  * Returns a list of the class names corresponding to the identifier and
    176  * modifiers.
    177  */
    178 function getKeyClasses(identifier, modifiers) {
    179   var classes = ['keyboard-overlay-key'];
    180   for (var i = 0; i < modifiers.length; ++i) {
    181     classes.push(MODIFIER_TO_CLASS[modifiers[i]]);
    182   }
    183 
    184   if ((identifier == '2A' && contains(modifiers, 'SHIFT')) ||
    185       (identifier == '1D' && contains(modifiers, 'CTRL')) ||
    186       (identifier == '38' && contains(modifiers, 'ALT'))) {
    187     classes.push('pressed');
    188     classes.push(IDENTIFIER_TO_CLASS[identifier]);
    189   }
    190   return classes;
    191 }
    192 
    193 /**
    194  * Returns true if a character is a ASCII character.
    195  */
    196 function isAscii(c) {
    197   var charCode = c.charCodeAt(0);
    198   return 0x00 <= charCode && charCode <= 0x7F;
    199 }
    200 
    201 /**
    202  * Returns a label of the key.
    203  */
    204 function getKeyLabel(keyData, modifiers) {
    205   if (!keyData) {
    206     return '';
    207   }
    208   if (keyData.label in LABEL_TO_KEY_TEXT) {
    209     return LABEL_TO_KEY_TEXT[keyData.label];
    210   }
    211   var keyLabel = '';
    212   for (var j = 1; j <= 9; j++) {
    213     var pos =  keyData['p' + j];
    214     if (!pos) {
    215       continue;
    216     }
    217     if (LABEL_TO_KEY_TEXT[pos]) {
    218       return LABEL_TO_KEY_TEXT[pos];
    219     }
    220     keyLabel = hex2char(pos);
    221     if (!keyLabel) {
    222       continue;
    223      }
    224     if (isAscii(keyLabel) &&
    225         getShortcutData()[getAction(keyLabel, modifiers)]) {
    226       break;
    227     }
    228   }
    229   return keyLabel;
    230 }
    231 
    232 /**
    233  * Returns a normalized string used for a key of shortcutData.
    234  *
    235  * Examples:
    236  *   keycode: 'd', modifiers: ['CTRL', 'SHIFT'] => 'd<>CTRL<>SHIFT'
    237  *   keycode: 'alt', modifiers: ['ALT', 'SHIFT'] => 'ALT<>SHIFT'
    238  */
    239 function getAction(keycode, modifiers) {
    240   const SEPARATOR = '<>';
    241   if (keycode.toUpperCase() in MODIFIER_TO_CLASS) {
    242     keycode = keycode.toUpperCase();
    243     if (keycode in modifiers) {
    244       return modifiers.join(SEPARATOR);
    245     } else {
    246       var action = [keycode].concat(modifiers)
    247       action.sort();
    248       return action.join(SEPARATOR);
    249     }
    250   }
    251   return [keycode].concat(modifiers).join(SEPARATOR);
    252 }
    253 
    254 /**
    255  * Returns a text which displayed on a key.
    256  */
    257 function getKeyTextValue(keyData) {
    258   if (LABEL_TO_KEY_TEXT[keyData.label]) {
    259     return LABEL_TO_KEY_TEXT[keyData.label];
    260   }
    261 
    262   var chars = [];
    263   for (var j = 1; j <= 9; ++j) {
    264     var pos = keyData['p' + j];
    265     if (LABEL_TO_KEY_TEXT[pos]) {
    266       return LABEL_TO_KEY_TEXT[pos];
    267     }
    268     if (pos && pos.length > 0) {
    269       chars.push(hex2char(pos));
    270     }
    271   }
    272   return chars.join(' ');
    273 }
    274 
    275 /**
    276  * Updates the whole keyboard.
    277  */
    278 function update(modifiers) {
    279   var instructions = document.getElementById('instructions');
    280   if (modifiers.length == 0) {
    281     instructions.style.visibility = 'visible';
    282   } else {
    283     instructions.style.visibility = 'hidden';
    284   }
    285 
    286   var keyboardGlyphData = getKeyboardGlyphData();
    287   var shortcutData = getShortcutData();
    288   var layout = getLayouts()[keyboardGlyphData.layoutName];
    289   for (var i = 0; i < layout.length; ++i) {
    290     var identifier = layout[i][0];
    291     var keyData = keyboardGlyphData.keys[identifier];
    292     var classes = getKeyClasses(identifier, modifiers, keyData);
    293     var keyLabel = getKeyLabel(keyData, modifiers);
    294     var shortcutId = shortcutData[getAction(keyLabel, modifiers)];
    295     if (shortcutId) {
    296       classes.push('is-shortcut');
    297     }
    298 
    299     var key = document.getElementById(keyId(identifier, i));
    300     key.className = classes.join(' ');
    301 
    302     if (!keyData) {
    303       continue;
    304     }
    305 
    306     var keyText = document.getElementById(keyTextId(identifier, i));
    307     var keyTextValue = getKeyTextValue(keyData);
    308     if (keyTextValue) {
    309        keyText.style.visibility = 'visible';
    310     } else {
    311        keyText.style.visibility = 'hidden';
    312     }
    313     keyText.textContent = keyTextValue;
    314 
    315     var shortcutText = document.getElementById(shortcutTextId(identifier, i));
    316     if (shortcutId) {
    317       shortcutText.style.visibility = 'visible';
    318       shortcutText.textContent = templateData[shortcutId];
    319     } else {
    320       shortcutText.style.visibility = 'hidden';
    321     }
    322 
    323     if (keyData.format) {
    324       var format = keyData.format;
    325       if (format == 'left' || format == 'right') {
    326         shortcutText.style.textAlign = format;
    327         keyText.style.textAlign = format;
    328       }
    329     }
    330   }
    331 }
    332 
    333 /**
    334  * A callback function for onkeydown and onkeyup events.
    335  */
    336 function handleKeyEvent(e){
    337   var modifiers = getModifiers(e);
    338   if (!getKeyboardOverlayId()) {
    339     return;
    340   }
    341   update(modifiers);
    342 }
    343 
    344 /**
    345  * Initializes the layout of the keys.
    346  */
    347 function initLayout() {
    348   var layout = getLayouts()[getKeyboardGlyphData().layoutName];
    349   var keyboard = document.body;
    350   var minX = window.innerWidth;
    351   var maxX = 0;
    352   var minY = window.innerHeight;
    353   var maxY = 0;
    354   var multiplier = 1.38 * window.innerWidth / BASE_KEYBOARD.width;
    355   var keyMargin = 7;
    356   var offsetX = 10;
    357   var offsetY = 7;
    358   for (var i = 0; i < layout.length; i++) {
    359     var array = layout[i];
    360     var identifier = array[0];
    361     var x = Math.round((array[1] + offsetX) * multiplier);
    362     var y = Math.round((array[2] + offsetY) * multiplier);
    363     var w = Math.round((array[3] - keyMargin) * multiplier);
    364     var h = Math.round((array[4] - keyMargin) * multiplier);
    365 
    366     var key = document.createElement('div');
    367     key.id = keyId(identifier, i);
    368     key.className = 'keyboard-overlay-key';
    369     key.style.left = x + 'px';
    370     key.style.top = y + 'px';
    371     key.style.width = w + 'px';
    372     key.style.height = h + 'px';
    373 
    374     var keyText = document.createElement('div');
    375     keyText.id = keyTextId(identifier, i);
    376     keyText.className = 'keyboard-overlay-key-text';
    377     keyText.style.visibility = 'hidden';
    378     key.appendChild(keyText);
    379 
    380     var shortcutText = document.createElement('div');
    381     shortcutText.id = shortcutTextId(identifier, i);
    382     shortcutText.className = 'keyboard-overlay-shortcut-text';
    383     shortcutText.style.visilibity = 'hidden';
    384     key.appendChild(shortcutText);
    385     keyboard.appendChild(key);
    386 
    387     minX = Math.min(minX, x);
    388     maxX = Math.max(maxX, x + w);
    389     minY = Math.min(minY, y);
    390     maxY = Math.max(maxY, y + h);
    391   }
    392 
    393   var width = maxX - minX + 1;
    394   var height = maxY - minY + 1;
    395   keyboard.style.width = (width + 2 * (minX + 1)) + 'px';
    396   keyboard.style.height = (height + 2 * (minY + 1)) + 'px';
    397 
    398   var instructions = document.createElement('div');
    399   instructions.id = 'instructions';
    400   instructions.className = 'keyboard-overlay-instructions';
    401   instructions.style.left = ((BASE_INSTRUCTIONS.left - BASE_KEYBOARD.left) *
    402                              width / BASE_KEYBOARD.width + minX) + 'px';
    403   instructions.style.top = ((BASE_INSTRUCTIONS.top - BASE_KEYBOARD.top) *
    404                             height / BASE_KEYBOARD.height + minY) + 'px';
    405   instructions.style.width = (width * BASE_INSTRUCTIONS.width /
    406                               BASE_KEYBOARD.width) + 'px';
    407   instructions.style.height = (height * BASE_INSTRUCTIONS.height /
    408                                BASE_KEYBOARD.height) + 'px';
    409 
    410   var instructionsText = document.createElement('div');
    411   instructionsText.id = 'instructions-text';
    412   instructionsText.className = 'keyboard-overlay-instructions-text';
    413   instructionsText.innerHTML = templateData.keyboardOverlayInstructions;
    414   instructions.appendChild(instructionsText);
    415   var instructionsHideText = document.createElement('div');
    416   instructionsHideText.id = 'instructions-hide-text';
    417   instructionsHideText.className = 'keyboard-overlay-instructions-hide-text';
    418   instructionsHideText.innerHTML = templateData.keyboardOverlayInstructionsHide;
    419   instructions.appendChild(instructionsHideText);
    420   keyboard.appendChild(instructions);
    421 }
    422 
    423 /**
    424  * A callback function for the onload event of the body element.
    425  */
    426 function init() {
    427   document.addEventListener('keydown', handleKeyEvent);
    428   document.addEventListener('keyup', handleKeyEvent);
    429   chrome.send('getKeyboardOverlayId');
    430 }
    431 
    432 /**
    433  * Initializes the global keyboad overlay ID and the layout of keys.
    434  * Called after sending the 'getKeyboardOverlayId' message.
    435  */
    436 function initKeyboardOverlayId(overlayId) {
    437   // Libcros returns an empty string when it cannot find the keyboard overlay ID
    438   // corresponding to the current input method.
    439   // In such a case, fallback to the default ID (en_US).
    440   if (overlayId) {
    441     keyboardOverlayId = overlayId;
    442   }
    443   while(document.body.firstChild) {
    444     document.body.removeChild(document.body.firstChild);
    445   }
    446   initLayout();
    447   update();
    448 }
    449 
    450 document.addEventListener('DOMContentLoaded', init);
    451