summaryrefslogtreecommitdiffstats
path: root/ui/keyboard
diff options
context:
space:
mode:
authorbryeung@chromium.org <bryeung@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-18 11:24:01 +0000
committerbryeung@chromium.org <bryeung@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-18 11:24:01 +0000
commit726002268c55e3597a6965fe69e3d488a9d64586 (patch)
tree89d21cbb6125a6518bb7128a48f3c8afa5964a7a /ui/keyboard
parent0cacba977cc16dd6cbd991a75ec7e1e991b0b1dc (diff)
downloadchromium_src-726002268c55e3597a6965fe69e3d488a9d64586.zip
chromium_src-726002268c55e3597a6965fe69e3d488a9d64586.tar.gz
chromium_src-726002268c55e3597a6965fe69e3d488a9d64586.tar.bz2
Add a virtual keyboard webui at chrome://keyboard/
This is just the old virtual keyboard that was removed a couple of years ago, being used as a temporary stand-in. BUG=222801 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=194579 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=194635 Review URL: https://codereview.chromium.org/13652010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194881 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/keyboard')
-rw-r--r--ui/keyboard/DEPS4
-rw-r--r--ui/keyboard/keyboard.gyp37
-rw-r--r--ui/keyboard/keyboard_constants.cc12
-rw-r--r--ui/keyboard/keyboard_constants.h20
-rw-r--r--ui/keyboard/keyboard_resources.grd33
-rw-r--r--ui/keyboard/keyboard_ui_controller.cc55
-rw-r--r--ui/keyboard/keyboard_ui_controller.h29
-rw-r--r--ui/keyboard/resources/common.js716
-rw-r--r--ui/keyboard/resources/images/chevron.svg9
-rw-r--r--ui/keyboard/resources/images/del.svg17
-rw-r--r--ui/keyboard/resources/images/keyboard.svg25
-rw-r--r--ui/keyboard/resources/images/mic.svg23
-rw-r--r--ui/keyboard/resources/images/ret.svg17
-rw-r--r--ui/keyboard/resources/images/shift-down.svg15
-rw-r--r--ui/keyboard/resources/images/shift.svg15
-rw-r--r--ui/keyboard/resources/images/tab.svg14
-rw-r--r--ui/keyboard/resources/index.html10
-rw-r--r--ui/keyboard/resources/layout_us.js89
-rw-r--r--ui/keyboard/resources/main.css193
-rw-r--r--ui/keyboard/resources/main.js192
-rw-r--r--ui/keyboard/resources/manifest.json14
21 files changed, 1538 insertions, 1 deletions
diff --git a/ui/keyboard/DEPS b/ui/keyboard/DEPS
new file mode 100644
index 0000000..07406d8
--- /dev/null
+++ b/ui/keyboard/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public",
+ "+grit/keyboard_resources.h",
+]
diff --git a/ui/keyboard/keyboard.gyp b/ui/keyboard/keyboard.gyp
index add01e5..6562daa 100644
--- a/ui/keyboard/keyboard.gyp
+++ b/ui/keyboard/keyboard.gyp
@@ -8,35 +8,59 @@
},
'targets': [
{
+ 'target_name': 'keyboard_resources',
+ 'type': 'none',
+ 'variables': {
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/ui/keyboard',
+ },
+ 'actions': [
+ {
+ 'action_name': 'keyboard_resources',
+ 'variables': {
+ 'grit_grd_file': 'keyboard_resources.grd',
+ },
+ 'includes': [ '../../build/grit_action.gypi' ],
+ },
+ ],
+ 'includes': [ '../../build/grit_target.gypi' ],
+ },
+ {
'target_name': 'keyboard',
'type': '<(component)',
'dependencies': [
'../../base/base.gyp:base',
+ '../../content/content.gyp:content_browser',
'../../skia/skia.gyp:skia',
'../aura/aura.gyp:aura',
'../compositor/compositor.gyp:compositor',
'../ui.gyp:ui',
+ 'keyboard_resources',
],
'defines': [
'KEYBOARD_IMPLEMENTATION',
],
'sources': [
+ 'keyboard_constants.cc',
+ 'keyboard_constants.h',
'keyboard_controller.cc',
'keyboard_controller.h',
'keyboard_controller_proxy.h',
'keyboard_export.h',
'keyboard_switches.cc',
'keyboard_switches.h',
+ 'keyboard_ui_controller.cc',
+ 'keyboard_ui_controller.h',
'keyboard_util.cc',
'keyboard_util.h',
]
},
{
'target_name': 'keyboard_unittests',
- 'type': 'executable',
+ 'type': '<(gtest_target_type)',
'dependencies': [
'../../base/base.gyp:base',
'../../base/base.gyp:test_support_base',
+ '../../content/content.gyp:content_browser',
'../../skia/skia.gyp:skia',
'../../testing/gtest.gyp:gtest',
'../aura/aura.gyp:aura',
@@ -51,6 +75,17 @@
'keyboard_controller_unittest.cc',
'keyboard_test_suite.cc',
],
+ 'conditions': [
+ [ 'os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', {
+ 'conditions': [
+ ['linux_use_tcmalloc==1', {
+ 'dependencies': [
+ '../../base/allocator/allocator.gyp:allocator',
+ ],
+ }],
+ ],
+ }],
+ ],
},
],
}
diff --git a/ui/keyboard/keyboard_constants.cc b/ui/keyboard/keyboard_constants.cc
new file mode 100644
index 0000000..35f8130
--- /dev/null
+++ b/ui/keyboard/keyboard_constants.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/keyboard/keyboard_constants.h"
+
+namespace keyboard {
+
+const char kKeyboardWebUIURL[] = "chrome://keyboard";
+const char kKeyboardWebUIHost[] = "keyboard";
+
+} // namespace keyboard
diff --git a/ui/keyboard/keyboard_constants.h b/ui/keyboard/keyboard_constants.h
new file mode 100644
index 0000000..eff96ea
--- /dev/null
+++ b/ui/keyboard/keyboard_constants.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_KEYBOARD_KEYBOARD_CONSTANTS_H_
+#define UI_KEYBOARD_KEYBOARD_CONSTANTS_H_
+
+#include "ui/keyboard/keyboard_export.h"
+
+namespace keyboard {
+
+// The URL of the keyboard WebUI.
+KEYBOARD_EXPORT extern const char kKeyboardWebUIURL[];
+
+// The host of the keyboard WebUI URL.
+KEYBOARD_EXPORT extern const char kKeyboardWebUIHost[];
+
+} // namespace keyboard
+
+#endif // UI_KEYBOARD_KEYBOARD_CONSTANTS_H_
diff --git a/ui/keyboard/keyboard_resources.grd b/ui/keyboard/keyboard_resources.grd
new file mode 100644
index 0000000..58b93df
--- /dev/null
+++ b/ui/keyboard/keyboard_resources.grd
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ NOTE: if you are adding resources here, you should probably also edit:
+ chrome/browser/ui/webui/virtual_keyboard_ui.cc // FIXME: this will move
+ chrome/browser/extensions/image_loader.cc
+-->
+<grit latest_public_release="0" current_release="1">
+ <outputs>
+ <output filename="grit/keyboard_resources.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="keyboard_resources.pak" type="data_package" />
+ <output filename="keyboard_resources.rc" type="rc_all" />
+ </outputs>
+ <release seq="1">
+ <includes>
+ <include name="IDR_KEYBOARD_MANIFEST" file="resources/manifest.json" type="BINDATA" />
+ <include name="IDR_KEYBOARD_INDEX" file="resources/index.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+ <include name="IDR_KEYBOARD_MAIN_CSS" file="resources/main.css" type="BINDATA" />
+ <include name="IDR_KEYBOARD_MAIN_JS" file="resources/main.js" type="BINDATA" />
+ <include name="IDR_KEYBOARD_COMMON_JS" file="resources/common.js" type="BINDATA" />
+ <include name="IDR_KEYBOARD_LAYOUT_US_JS" file="resources/layout_us.js" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_CHEVRON" file="resources/images/chevron.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_DEL" file="resources/images/del.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_KEYBOARD" file="resources/images/keyboard.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_MIC" file="resources/images/mic.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_RET" file="resources/images/ret.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_SHIFT_DOWN" file="resources/images/shift-down.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_SHIFT" file="resources/images/shift.svg" type="BINDATA" />
+ <include name="IDR_KEYBOARD_IMAGES_TAB" file="resources/images/tab.svg" type="BINDATA" />
+ </includes>
+ </release>
+</grit>
diff --git a/ui/keyboard/keyboard_ui_controller.cc b/ui/keyboard/keyboard_ui_controller.cc
new file mode 100644
index 0000000..d0bc5ff
--- /dev/null
+++ b/ui/keyboard/keyboard_ui_controller.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/keyboard/keyboard_ui_controller.h"
+
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "grit/keyboard_resources.h"
+#include "ui/keyboard/keyboard_constants.h"
+
+namespace {
+
+content::WebUIDataSource* CreateKeyboardUIDataSource() {
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(keyboard::kKeyboardWebUIHost);
+
+ source->SetDefaultResource(IDR_KEYBOARD_INDEX);
+
+ source->AddResourcePath("main.css", IDR_KEYBOARD_MAIN_CSS);
+
+ source->AddResourcePath("main.js", IDR_KEYBOARD_MAIN_JS);
+ source->AddResourcePath("common.js", IDR_KEYBOARD_COMMON_JS);
+ source->AddResourcePath("layout_us.js", IDR_KEYBOARD_LAYOUT_US_JS);
+ source->AddResourcePath("images/chevron.svg", IDR_KEYBOARD_IMAGES_CHEVRON);
+ source->AddResourcePath("images/del.svg", IDR_KEYBOARD_IMAGES_DEL);
+ source->AddResourcePath("images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD);
+ source->AddResourcePath("images/mic.svg", IDR_KEYBOARD_IMAGES_MIC);
+ source->AddResourcePath("images/ret.svg", IDR_KEYBOARD_IMAGES_RET);
+ source->AddResourcePath("images/shift_down.svg",
+ IDR_KEYBOARD_IMAGES_SHIFT_DOWN);
+ source->AddResourcePath("images/shift.svg", IDR_KEYBOARD_IMAGES_SHIFT);
+ source->AddResourcePath("images/tab.svg", IDR_KEYBOARD_IMAGES_TAB);
+
+ return source;
+}
+
+} // namespace
+
+namespace keyboard {
+
+KeyboardUIController::KeyboardUIController(content::WebUI* web_ui)
+ : WebUIController(web_ui) {
+ content::BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ content::WebUIDataSource::Add(
+ browser_context,
+ CreateKeyboardUIDataSource());
+}
+
+KeyboardUIController::~KeyboardUIController() {}
+
+} // namespace keyboard
diff --git a/ui/keyboard/keyboard_ui_controller.h b/ui/keyboard/keyboard_ui_controller.h
new file mode 100644
index 0000000..b06ec26
--- /dev/null
+++ b/ui/keyboard/keyboard_ui_controller.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_KEYBOARD_KEYBOARD_UI_CONTROLLER_H_
+#define UI_KEYBOARD_KEYBOARD_UI_CONTROLLER_H_
+
+#include "content/public/browser/web_ui_controller.h"
+#include "ui/keyboard/keyboard_export.h"
+
+namespace content {
+class WebUI;
+};
+
+namespace keyboard {
+
+// WebUIController for chrome://keyboard/.
+class KEYBOARD_EXPORT KeyboardUIController : public content::WebUIController {
+ public:
+ explicit KeyboardUIController(content::WebUI* web_ui);
+ virtual ~KeyboardUIController();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeyboardUIController);
+};
+
+} // namespace keyboard
+
+#endif // UI_KEYBOARD_KEYBOARD_UI_CONTROLLER_H_
diff --git a/ui/keyboard/resources/common.js b/ui/keyboard/resources/common.js
new file mode 100644
index 0000000..03aa5509
--- /dev/null
+++ b/ui/keyboard/resources/common.js
@@ -0,0 +1,716 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A simple virtual keyboard implementation.
+ */
+
+var KEY_MODE = 'key';
+var SHIFT_MODE = 'shift';
+var NUMBER_MODE = 'number';
+var SYMBOL_MODE = 'symbol';
+// TODO(bryeung): tear out all of this mode switching code
+var MODES = [KEY_MODE, SHIFT_MODE, NUMBER_MODE, SYMBOL_MODE];
+var currentMode = KEY_MODE;
+var enterShiftModeOnSpace = false;
+var MODE_CODES = {};
+var MODE_TRANSITIONS = {};
+
+MODE_CODES[KEY_MODE] = 0;
+MODE_CODES[SHIFT_MODE] = 1;
+MODE_CODES[NUMBER_MODE] = 2;
+MODE_CODES[SYMBOL_MODE] = 3;
+
+MODE_TRANSITIONS[KEY_MODE + SHIFT_MODE] = SHIFT_MODE;
+MODE_TRANSITIONS[KEY_MODE + NUMBER_MODE] = NUMBER_MODE;
+MODE_TRANSITIONS[SHIFT_MODE + SHIFT_MODE] = KEY_MODE;
+MODE_TRANSITIONS[SHIFT_MODE + NUMBER_MODE] = NUMBER_MODE;
+MODE_TRANSITIONS[NUMBER_MODE + SHIFT_MODE] = SYMBOL_MODE;
+MODE_TRANSITIONS[NUMBER_MODE + NUMBER_MODE] = KEY_MODE;
+MODE_TRANSITIONS[SYMBOL_MODE + SHIFT_MODE] = NUMBER_MODE;
+MODE_TRANSITIONS[SYMBOL_MODE + NUMBER_MODE] = KEY_MODE;
+
+var KEYBOARDS = {};
+
+/**
+ * The long-press delay in milliseconds before long-press handler is invoked.
+ * @type {number}
+ */
+var LONGPRESS_DELAY_MSEC = 500;
+
+/**
+ * The repeat delay in milliseconds before a key starts repeating. Use the same
+ * rate as Chromebook. (See chrome/browser/chromeos/language_preferences.cc)
+ * @type {number}
+ */
+var REPEAT_DELAY_MSEC = 500;
+
+/**
+ * The repeat interval or number of milliseconds between subsequent keypresses.
+ * Use the same rate as Chromebook.
+ * @type {number}
+ */
+var REPEAT_INTERVAL_MSEC = 50;
+
+/**
+ * The keyboard layout name currently in use.
+ * @type {string}
+ */
+var currentKeyboardLayout = 'us';
+
+/**
+ * A structure to track the currently repeating key on the keyboard.
+ */
+var repeatKey = {
+ /**
+ * The timer for the delay before repeating behaviour begins.
+ * @type {number|undefined}
+ */
+ timer: undefined,
+
+ /**
+ * The interval timer for issuing keypresses of a repeating key.
+ * @type {number|undefined}
+ */
+ interval: undefined,
+
+ /**
+ * The key which is currently repeating.
+ * @type {BaseKey|undefined}
+ */
+ key: undefined,
+
+ /**
+ * Cancel the repeat timers of the currently active key.
+ */
+ cancel: function() {
+ clearTimeout(this.timer);
+ clearInterval(this.interval);
+ this.timer = undefined;
+ this.interval = undefined;
+ this.key = undefined;
+ }
+};
+
+/**
+ * Set the keyboard mode.
+ * @param {string} mode The new mode.
+ */
+function setMode(mode) {
+ currentMode = mode;
+
+ var rows = KEYBOARDS[currentKeyboardLayout]['rows'];
+ for (var i = 0; i < rows.length; ++i) {
+ rows[i].showMode(currentMode);
+ }
+}
+
+/**
+ * Transition the mode according to the given transition.
+ * @param {string} transition The transition to take.
+ */
+function transitionMode(transition) {
+ setMode(MODE_TRANSITIONS[currentMode + transition]);
+}
+
+function logIfError() {
+ if (chrome.runtime.lastError) {
+ console.log(chrome.runtime.lastError);
+ }
+}
+
+/**
+ * Send the given key to chrome, via the experimental extension API.
+ * @param {string} keyIdentifier The key to send.
+ */
+function sendKey(keyIdentifier) {
+ // FIXME(bryeung)
+ console.log('Typed: ' + keyIdentifier);
+ var keyEvent = {
+ type: 'keydown',
+ keyIdentifier: keyIdentifier
+ };
+ chrome.experimental.input.virtualKeyboard.sendKeyboardEvent(keyEvent,
+ logIfError);
+ keyEvent.type = 'keyup';
+ chrome.experimental.input.virtualKeyboard.sendKeyboardEvent(keyEvent,
+ logIfError);
+
+ // Exit shift mode after pressing any key but space.
+ if (currentMode == SHIFT_MODE && keyIdentifier != 'Spacebar') {
+ transitionMode(SHIFT_MODE);
+ }
+ // Enter shift mode after typing a closing punctuation and then a space for a
+ // new sentence.
+ if (enterShiftModeOnSpace) {
+ enterShiftModeOnSpace = false;
+ if (currentMode != SHIFT_MODE && keyIdentifier == 'Spacebar') {
+ setMode(SHIFT_MODE);
+ }
+ }
+ if (currentMode != SHIFT_MODE &&
+ (keyIdentifier == '.' || keyIdentifier == '?' || keyIdentifier == '!')) {
+ enterShiftModeOnSpace = true;
+ }
+}
+
+/**
+ * Add a child div element that represents the content of the given element.
+ * A child div element that represents a text content is added if
+ * opt_textContent is given. Otherwise a child element that represents an image
+ * content is added. If the given element already has a child, the child element
+ * is modified.
+ * @param {Element} element The DOM Element to which the content is added.
+ * @param {string} opt_textContent The text to be inserted.
+ */
+function addContent(element, opt_textContent) {
+ if (element.childNodes.length > 0) {
+ var content = element.childNodes[0];
+ if (opt_textContent) {
+ content.textContent = opt_textContent;
+ }
+ return;
+ }
+
+ var content = document.createElement('div');
+ if (opt_textContent) {
+ content.textContent = opt_textContent;
+ content.className = 'text-key';
+ } else {
+ content.className = 'image-key';
+ }
+ element.appendChild(content);
+}
+
+/**
+ * Set up the event handlers necessary to respond to mouse and touch events on
+ * the virtual keyboard.
+ * @param {BaseKey} key The BaseKey object corresponding to this key.
+ * @param {Element} element The top-level DOM Element to set event handlers on.
+ * @param {Object.<string, function()>} handlers The object that contains key
+ * event handlers in the following form.
+ *
+ * { 'up': keyUpHandler,
+ * 'down': keyDownHandler,
+ * 'long': keyLongHandler }
+ *
+ * keyDownHandler: Called when the key is pressed. This will be called
+ * repeatedly when holding a repeating key.
+ * keyUpHandler: Called when the key is released. This is only called
+ * once per actual key press.
+ * keyLongHandler: Called when the key is long-pressed for
+ * |LONGPRESS_DELAY_MSEC| milliseconds.
+ *
+ * The object does not necessarily contain all the handlers above, but
+ * needs to contain at least one of them.
+ */
+function setupKeyEventHandlers(key, element, handlers) {
+ var keyDownHandler = handlers['down'];
+ var keyUpHandler = handlers['up'];
+ var keyLongHandler = handlers['long'];
+ if (!(keyDownHandler || keyUpHandler || keyLongPressHandler)) {
+ throw new Error('Invalid handlers passed to setupKeyEventHandlers');
+ }
+
+ /**
+ * Handle a key down event on the virtual key.
+ * @param {UIEvent} evt The UI event which triggered the key down.
+ */
+ var downHandler = function(evt) {
+ // Prevent any of the system gestures from happening.
+ evt.preventDefault();
+
+ // Don't process a key down if the key is already down.
+ if (key.pressed) {
+ return;
+ }
+ key.pressed = true;
+ if (keyDownHandler) {
+ keyDownHandler();
+ }
+ repeatKey.cancel();
+
+ // Start a repeating timer if there is a repeat interval and a function to
+ // process key down events.
+ if (key.repeat && keyDownHandler) {
+ repeatKey.key = key;
+ // The timeout for the repeating timer occurs at
+ // REPEAT_DELAY_MSEC - REPEAT_INTERVAL_MSEC so that the interval
+ // function can handle all repeat keypresses and will get the first one
+ // at the correct time.
+ repeatKey.timer = setTimeout(function() {
+ repeatKey.timer = undefined;
+ repeatKey.interval = setInterval(function() {
+ keyDownHandler();
+ }, REPEAT_INTERVAL_MSEC);
+ }, Math.max(0, REPEAT_DELAY_MSEC - REPEAT_INTERVAL_MSEC));
+ }
+
+ if (keyLongHandler) {
+ // Copy the currentTarget of event, which is neccessary because |evt| can
+ // be modified before |keyLongHandler| is called.
+ var evtCopy = {};
+ evtCopy.currentTarget = evt.currentTarget;
+ key.longPressTimer = setTimeout(function() {
+ keyLongHandler(evtCopy),
+ clearTimeout(key.longPressTimer);
+ delete key.longPressTimer;
+ key.pressed = false;
+ }, LONGPRESS_DELAY_MSEC);
+ }
+ };
+
+ /**
+ * Handle a key up event on the virtual key.
+ * @param {UIEvent} evt The UI event which triggered the key up.
+ */
+ var upHandler = function(evt) {
+ // Prevent any of the system gestures from happening.
+ evt.preventDefault();
+
+ // Reset long-press timer.
+ if (key.longPressTimer) {
+ clearTimeout(key.longPressTimer);
+ delete key.longPressTimer;
+ }
+
+ // If they key was not actually pressed do not send a key up event.
+ if (!key.pressed) {
+ return;
+ }
+ key.pressed = false;
+
+ // Cancel running repeat timer for the released key only.
+ if (repeatKey.key == key) {
+ repeatKey.cancel();
+ }
+
+ if (keyUpHandler) {
+ keyUpHandler();
+ }
+ };
+
+ // Setup mouse event handlers.
+ element.addEventListener('mousedown', downHandler);
+ element.addEventListener('mouseup', upHandler);
+
+ // Setup touch handlers.
+ element.addEventListener('touchstart', downHandler);
+ element.addEventListener('touchend', upHandler);
+}
+
+/**
+ * Create closure for the sendKey function.
+ * @param {string} key The key paramater to sendKey.
+ * @return {function()} A function which calls sendKey(key).
+ */
+function sendKeyFunction(key) {
+ return function() {
+ sendKey(key);
+ };
+}
+
+/**
+ * Plain-old-data class to represent a character.
+ * @param {string} display The HTML to be displayed.
+ * @param {string} id The key identifier for this Character.
+ * @constructor
+ */
+function Character(display, id) {
+ this.display = display;
+ this.keyIdentifier = id;
+}
+
+/**
+ * Convenience function to make the keyboard data more readable.
+ * @param {string} display The display for the created Character.
+ * @param {string} opt_id The id for the created Character.
+ * @return {Character} A character that contains display and opt_id. If
+ * opt_id is omitted, display is used as the id.
+ */
+function C(display, opt_id) {
+ var id = opt_id || display;
+ return new Character(display, id);
+}
+
+/**
+ * An abstract base-class for all keys on the keyboard.
+ * @constructor
+ */
+function BaseKey() {}
+
+BaseKey.prototype = {
+ /**
+ * The cell type of this key. Determines the background colour.
+ * @type {string}
+ */
+ cellType_: '',
+
+ /**
+ * If true, holding this key will issue repeat keypresses.
+ * @type {boolean}
+ */
+ repeat_: false,
+
+ /**
+ * Track the pressed state of the key. This is true if currently pressed.
+ * @type {boolean}
+ */
+ pressed_: false,
+
+ /**
+ * Get the repeat behaviour of the key.
+ * @return {boolean} True if the key will repeat.
+ */
+ get repeat() {
+ return this.repeat_;
+ },
+
+ /**
+ * Set the repeat behaviour of the key
+ * @param {boolean} repeat True if the key should repeat.
+ */
+ set repeat(repeat) {
+ this.repeat_ = repeat;
+ },
+
+ /**
+ * Get the pressed state of the key.
+ * @return {boolean} True if the key is currently pressed.
+ */
+ get pressed() {
+ return this.pressed_;
+ },
+
+ /**
+ * Set the pressed state of the key.
+ * @param {boolean} pressed True if the key is currently pressed.
+ */
+ set pressed(pressed) {
+ this.pressed_ = pressed;
+ },
+
+ /**
+ * Create the DOM elements for the given keyboard mode. Must be overridden.
+ * @param {string} mode The keyboard mode to create elements for.
+ * @return {Element} The top-level DOM Element for the key.
+ */
+ makeDOM: function(mode) {
+ throw new Error('makeDOM not implemented in BaseKey');
+ },
+};
+
+/**
+ * A simple key which displays Characters.
+ * @param {Object} key The Character for KEY_MODE.
+ * @param {Object} shift The Character for SHIFT_MODE.
+ * @param {string} className An optional class name for the key.
+ * @constructor
+ * @extends {BaseKey}
+ */
+function Key(key, shift, className) {
+ this.modeElements_ = {};
+ this.cellType_ = '';
+ this.className_ = (className) ? 'key ' + className : 'key';
+
+ this.modes_ = {};
+ this.modes_[KEY_MODE] = key;
+ this.modes_[SHIFT_MODE] = shift;
+}
+
+Key.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode) {
+ if (!this.modes_[mode]) {
+ return null;
+ }
+
+ this.modeElements_[mode] = document.createElement('div');
+ var element = this.modeElements_[mode];
+ element.className = this.className_;
+
+ addContent(element, this.modes_[mode].display);
+
+ setupKeyEventHandlers(this, element,
+ { 'up': sendKeyFunction(this.modes_[mode].keyIdentifier) });
+ return element;
+ }
+};
+
+/**
+ * A key which displays an SVG image.
+ * @param {string} className The class that provides the image.
+ * @param {string} keyId The key identifier for the key.
+ * @param {boolean} opt_repeat True if the key should repeat.
+ * @constructor
+ * @extends {BaseKey}
+ */
+function SvgKey(className, keyId, opt_repeat) {
+ this.modeElements_ = {};
+ this.cellType_ = 'nc';
+ this.className_ = className;
+ this.keyId_ = keyId;
+ this.repeat_ = opt_repeat || false;
+}
+
+SvgKey.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode) {
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].className = 'key';
+ this.modeElements_[mode].classList.add(this.className_);
+ addContent(this.modeElements_[mode]);
+
+ // send the key event on key down if key repeat is enabled
+ var handler = this.repeat_ ? { 'down' : sendKeyFunction(this.keyId_) } :
+ { 'up' : sendKeyFunction(this.keyId_) };
+ setupKeyEventHandlers(this, this.modeElements_[mode], handler);
+
+ return this.modeElements_[mode];
+ }
+};
+
+/**
+ * A Key that remains the same through all modes.
+ * @param {string} className The class name for the key.
+ * @param {string} content The display text for the key.
+ * @param {string} keyId The key identifier for the key.
+ * @constructor
+ * @extends {BaseKey}
+ */
+function SpecialKey(className, content, keyId) {
+ this.modeElements_ = {};
+ this.cellType_ = 'nc';
+ this.content_ = content;
+ this.keyId_ = keyId;
+ this.className_ = className;
+}
+
+SpecialKey.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode) {
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].className = 'key';
+ this.modeElements_[mode].classList.add(this.className_);
+ addContent(this.modeElements_[mode], this.content_);
+
+ setupKeyEventHandlers(this, this.modeElements_[mode],
+ { 'up': sendKeyFunction(this.keyId_) });
+
+ return this.modeElements_[mode];
+ }
+};
+
+/**
+ * A shift key.
+ * @constructor
+ * @param {string} className The class name for the key.
+ * @extends {BaseKey}
+ */
+function ShiftKey(className) {
+ this.modeElements_ = {};
+ this.cellType_ = 'nc';
+ this.className_ = className;
+}
+
+ShiftKey.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode) {
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].className = 'key shift';
+ this.modeElements_[mode].classList.add(this.className_);
+
+ if (mode == KEY_MODE || mode == SHIFT_MODE) {
+ addContent(this.modeElements_[mode]);
+ } else if (mode == NUMBER_MODE) {
+ addContent(this.modeElements_[mode], 'more');
+ } else if (mode == SYMBOL_MODE) {
+ addContent(this.modeElements_[mode], '#123');
+ }
+
+ if (mode == SHIFT_MODE || mode == SYMBOL_MODE) {
+ this.modeElements_[mode].classList.add('moddown');
+ } else {
+ this.modeElements_[mode].classList.remove('moddown');
+ }
+
+ setupKeyEventHandlers(this, this.modeElements_[mode],
+ { 'down': function() {
+ transitionMode(SHIFT_MODE);
+ }});
+
+ return this.modeElements_[mode];
+ },
+};
+
+/**
+ * The symbol key: switches the keyboard into symbol mode.
+ * @constructor
+ * @extends {BaseKey}
+ */
+function SymbolKey() {
+ this.modeElements_ = {};
+ this.cellType_ = 'nc';
+}
+
+SymbolKey.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode, height) {
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].className = 'key symbol';
+
+ if (mode == KEY_MODE || mode == SHIFT_MODE) {
+ addContent(this.modeElements_[mode], '#123');
+ } else if (mode == NUMBER_MODE || mode == SYMBOL_MODE) {
+ addContent(this.modeElements_[mode], 'abc');
+ }
+
+ if (mode == NUMBER_MODE || mode == SYMBOL_MODE) {
+ this.modeElements_[mode].classList.add('moddown');
+ } else {
+ this.modeElements_[mode].classList.remove('moddown');
+ }
+
+ setupKeyEventHandlers(this, this.modeElements_[mode],
+ { 'down': function() {
+ transitionMode(NUMBER_MODE);
+ }});
+
+ return this.modeElements_[mode];
+ }
+};
+
+/**
+ * The ".com" key.
+ * @constructor
+ * @extends {BaseKey}
+ */
+function DotComKey() {
+ this.modeElements_ = {};
+ this.cellType_ = 'nc';
+}
+
+DotComKey.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode) {
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].className = 'key com';
+ addContent(this.modeElements_[mode], '.com');
+
+ setupKeyEventHandlers(this, this.modeElements_[mode],
+ { 'up': function() {
+ sendKey('.');
+ sendKey('c');
+ sendKey('o');
+ sendKey('m');
+ }});
+
+ return this.modeElements_[mode];
+ }
+};
+
+/**
+ * The key that hides the keyboard.
+ * @constructor
+ * @extends {BaseKey}
+ */
+function HideKeyboardKey() {
+ this.modeElements_ = {};
+ this.cellType_ = 'nc';
+}
+
+HideKeyboardKey.prototype = {
+ __proto__: BaseKey.prototype,
+
+ /** @override */
+ makeDOM: function(mode) {
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].className = 'key hide';
+ addContent(this.modeElements_[mode]);
+
+ setupKeyEventHandlers(this, this.modeElements_[mode],
+ { 'down': function() { console.log('Hide the keyboard!'); } });
+
+ return this.modeElements_[mode];
+ }
+};
+
+/**
+ * A container for keys.
+ * @param {number} position The position of the row (0-3).
+ * @param {Array.<BaseKey>} keys The keys in the row.
+ * @constructor
+ */
+function Row(position, keys) {
+ this.position_ = position;
+ this.keys_ = keys;
+ this.element_ = null;
+ this.modeElements_ = {};
+}
+
+Row.prototype = {
+ /**
+ * Create the DOM elements for the row.
+ * @return {Element} The top-level DOM Element for the row.
+ */
+ makeDOM: function() {
+ this.element_ = document.createElement('div');
+ this.element_.className = 'row';
+ for (var i = 0; i < MODES.length; ++i) {
+ var mode = MODES[i];
+ this.modeElements_[mode] = document.createElement('div');
+ this.modeElements_[mode].style.display = 'none';
+ this.element_.appendChild(this.modeElements_[mode]);
+ }
+
+ for (var j = 0; j < this.keys_.length; ++j) {
+ var key = this.keys_[j];
+ for (var i = 0; i < MODES.length; ++i) {
+ var keyDom = key.makeDOM(MODES[i]);
+ if (keyDom) {
+ this.modeElements_[MODES[i]].appendChild(keyDom);
+ }
+ }
+ }
+
+ for (var i = 0; i < MODES.length; ++i) {
+ var clearingDiv = document.createElement('div');
+ clearingDiv.style.clear = 'both';
+ this.modeElements_[MODES[i]].appendChild(clearingDiv);
+ }
+
+ return this.element_;
+ },
+
+ /**
+ * Shows the given mode.
+ * @param {string} mode The mode to show.
+ */
+ showMode: function(mode) {
+ for (var i = 0; i < MODES.length; ++i) {
+ this.modeElements_[MODES[i]].style.display = 'none';
+ }
+ this.modeElements_[mode].style.display = '-webkit-box';
+ },
+
+ /**
+ * Returns the size of keys this row contains.
+ * @return {number} The size of keys.
+ */
+ get length() {
+ return this.keys_.length;
+ }
+};
diff --git a/ui/keyboard/resources/images/chevron.svg b/ui/keyboard/resources/images/chevron.svg
new file mode 100644
index 0000000..d75deb0
--- /dev/null
+++ b/ui/keyboard/resources/images/chevron.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="50px" height="50px" viewBox="-0.042 -8.542 50 50" xml:space="preserve">
+<g>
+ <path fill="#bababb" d="M24.958,12.318l11.482-6.646v3.801l-11.482,7.904L13.479,9.473V5.672L24.958,12.318z M24.958,22.06l11.482-6.57v3.726
+ l-11.482,8.031l-11.479-8.031v-3.701L24.958,22.06z"/>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/del.svg b/ui/keyboard/resources/images/del.svg
new file mode 100644
index 0000000..1f257d5
--- /dev/null
+++ b/ui/keyboard/resources/images/del.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="5 5 40 40" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#bababb" d="M20.8,28.75c-0.043,0-0.087-0.012-0.126-0.034l-6-3.5C14.598,25.17,14.55,25.088,14.55,25
+ s0.047-0.171,0.124-0.216l6-3.5c0.039-0.023,0.083-0.034,0.126-0.034c0.043,0,0.086,0.011,0.125,0.033
+ c0.078,0.044,0.125,0.127,0.125,0.217v7c0,0.09-0.048,0.172-0.125,0.217C20.886,28.739,20.843,28.75,20.8,28.75L20.8,28.75z"/>
+ <path fill="#bababb" d="M20.8,21.5v7l-6-3.5L20.8,21.5 M20.8,21c-0.087,0-0.174,0.022-0.252,0.068l-6,3.5
+ C14.395,24.657,14.3,24.822,14.3,25s0.094,0.342,0.248,0.432l6,3.5C20.626,28.977,20.713,29,20.8,29
+ c0.086,0,0.172-0.022,0.249-0.066c0.155-0.089,0.251-0.255,0.251-0.434v-7c0-0.179-0.096-0.344-0.251-0.434
+ C20.972,21.021,20.886,21,20.8,21L20.8,21z"/>
+ </g>
+ <polygon fill="#bababb" points="33.934,23.5 18.3,23.5 18.3,26.5 35.2,26.5 "/>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/keyboard.svg b/ui/keyboard/resources/images/keyboard.svg
new file mode 100644
index 0000000..b8c7135
--- /dev/null
+++ b/ui/keyboard/resources/images/keyboard.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="50px" height="50px" viewBox="0 0 50 50" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#bababb" fill-rule="evenodd" clip-rule="evenodd" d="M41,12H9c-2.209,0-4,1.791-4,4v18c0,2.209,1.791,4,4,4h32c2.209,0,4-1.791,4-4
+ V16C45,13.791,43.209,12,41,12z M33,18c0-0.552,0.447-1,1-1h2c0.552,0,1,0.448,1,1v2c0,0.552-0.448,1-1,1h-2c-0.553,0-1-0.448-1-1
+ V18z M33,24c0-0.553,0.447-1,1-1h2c0.552,0,1,0.447,1,1v2c0,0.553-0.448,1-1,1h-2c-0.553,0-1-0.447-1-1V24z M28,18
+ c0-0.552,0.447-1,1-1h2c0.553,0,1,0.448,1,1v2c0,0.552-0.447,1-1,1h-2c-0.553,0-1-0.448-1-1V18z M28,24c0-0.553,0.447-1,1-1h2
+ c0.553,0,1,0.447,1,1v2c0,0.553-0.447,1-1,1h-2c-0.553,0-1-0.447-1-1V24z M23,18c0-0.552,0.447-1,1-1h2c0.553,0,1,0.448,1,1v2
+ c0,0.552-0.447,1-1,1h-2c-0.553,0-1-0.448-1-1V18z M23,24c0-0.553,0.447-1,1-1h2c0.553,0,1,0.447,1,1v2c0,0.553-0.447,1-1,1h-2
+ c-0.553,0-1-0.447-1-1V24z M18,18c0-0.552,0.447-1,1-1h2c0.553,0,1,0.448,1,1v2c0,0.552-0.447,1-1,1h-2c-0.553,0-1-0.448-1-1V18z
+ M18,24c0-0.553,0.447-1,1-1h2c0.553,0,1,0.447,1,1v2c0,0.553-0.447,1-1,1h-2c-0.553,0-1-0.447-1-1V24z M13,18
+ c0-0.552,0.447-1,1-1h2c0.553,0,1,0.448,1,1v2c0,0.552-0.447,1-1,1h-2c-0.553,0-1-0.448-1-1V18z M8,18c0-0.552,0.447-1,1-1h2
+ c0.552,0,1,0.448,1,1v2c0,0.552-0.448,1-1,1H9c-0.553,0-1-0.448-1-1V18z M8,24c0-0.553,0.447-1,1-1h2c0.552,0,1,0.447,1,1v2
+ c0,0.553-0.448,1-1,1H9c-0.553,0-1-0.447-1-1V24z M14,32c0,0.553-0.447,1-1,1H9c-0.553,0-1-0.447-1-1v-2c0-0.553,0.447-1,1-1h4
+ c0.553,0,1,0.447,1,1V32z M13,26v-2c0-0.553,0.447-1,1-1h2c0.553,0,1,0.447,1,1v2c0,0.553-0.447,1-1,1h-2
+ C13.447,27,13,26.553,13,26z M35,32c0,0.553-0.447,1-1,1H16c-0.553,0-1-0.447-1-1v-2c0-0.553,0.447-1,1-1h18c0.553,0,1,0.447,1,1
+ V32z M42,32c0,0.553-0.447,1-1,1h-4c-0.553,0-1-0.447-1-1v-2c0-0.553,0.447-1,1-1h4c0.553,0,1,0.447,1,1V32z M42,26
+ c0,0.553-0.447,1-1,1h-2c-0.553,0-1-0.447-1-1v-2c0-0.553,0.447-1,1-1h2c0.553,0,1,0.447,1,1V26z M42,20c0,0.552-0.447,1-1,1h-2
+ c-0.553,0-1-0.448-1-1v-2c0-0.552,0.447-1,1-1h2c0.553,0,1,0.448,1,1V20z"/>
+ </g>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/mic.svg b/ui/keyboard/resources/images/mic.svg
new file mode 100644
index 0000000..2b5711c
--- /dev/null
+++ b/ui/keyboard/resources/images/mic.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<g>
+ <line fill="#bababb" stroke="#bababb" stroke-width="10" stroke-linecap="round" x1="25" y1="24.5" x2="25" y2="15.5"/>
+ <g>
+ <defs>
+ <rect id="SVGID_1_" x="-295" y="-214.5" width="640" height="480"/>
+ </defs>
+ <clipPath id="SVGID_2_">
+ <use xlink:href="#SVGID_1_" overflow="visible"/>
+ </clipPath>
+ <path clip-path="url(#SVGID_2_)" fill="none" stroke="#bababb" stroke-width="3" stroke-linecap="round" d="M34,24.5
+ c0,4.971-4.029,9-9,9c-4.971,0-9-4.029-9-9"/>
+
+ <line clip-path="url(#SVGID_2_)" fill="#bababb" stroke="#bababb" stroke-width="3" stroke-linecap="round" x1="25" y1="33.5" x2="25" y2="39.5"/>
+
+ <line clip-path="url(#SVGID_2_)" fill="#bababb" stroke="#bababb" stroke-width="3" stroke-linecap="round" x1="29" y1="39.5" x2="21" y2="39.5"/>
+ </g>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/ret.svg b/ui/keyboard/resources/images/ret.svg
new file mode 100644
index 0000000..9f327f95
--- /dev/null
+++ b/ui/keyboard/resources/images/ret.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#bababb" d="M22.75,31.432c-0.043,0-0.087-0.012-0.126-0.034l-6-3.5c-0.077-0.045-0.124-0.127-0.124-0.216
+ s0.047-0.171,0.124-0.216l6-3.5c0.039-0.023,0.083-0.034,0.126-0.034c0.043,0,0.086,0.011,0.125,0.033
+ C22.952,24.01,23,24.093,23,24.182v7c0,0.09-0.048,0.172-0.125,0.217C22.835,31.421,22.792,31.432,22.75,31.432L22.75,31.432z"/>
+ <path fill="#bababb" d="M22.75,24.182v7l-6-3.5L22.75,24.182 M22.75,23.682c-0.087,0-0.174,0.022-0.252,0.068l-6,3.5
+ c-0.154,0.089-0.248,0.254-0.248,0.432s0.094,0.342,0.248,0.432l6,3.5c0.078,0.046,0.165,0.068,0.252,0.068
+ c0.086,0,0.172-0.022,0.249-0.066c0.155-0.089,0.251-0.255,0.251-0.434v-7c0-0.179-0.096-0.344-0.251-0.434
+ C22.921,23.704,22.835,23.682,22.75,23.682L22.75,23.682z"/>
+ </g>
+ <path fill="#bababb" d="M30.25,20.083v6.1h-10v3h11.5c0.828,0,1.5-0.672,1.5-1.5v-8.865L30.25,20.083z"/>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/shift-down.svg b/ui/keyboard/resources/images/shift-down.svg
new file mode 100644
index 0000000..958aa29
--- /dev/null
+++ b/ui/keyboard/resources/images/shift-down.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="50px" height="50px" viewBox="10 10 30 30">
+<!-- TODO(fsamuel): remove the width and height workaround above after fixing SVG bug -->
+<g>
+ <path fill="#e0e0e0" d="M23,29.25c-0.138,0-0.25-0.112-0.25-0.25v-2.75H20c-0.101,0-0.192-0.061-0.231-0.154
+ s-0.017-0.201,0.054-0.272l5-5c0.049-0.049,0.113-0.073,0.177-0.073s0.128,0.024,0.177,0.073l5,5
+ c0.071,0.071,0.093,0.179,0.055,0.272c-0.039,0.093-0.13,0.154-0.231,0.154h-2.75V29c0,0.138-0.112,0.25-0.25,0.25H23z"/>
+ <path fill="#e0e0e0" d="M25,21l5,5h-3v3h-4v-3h-3L25,21 M25,20.5c-0.128,0-0.256,0.049-0.354,0.146l-5,5
+ c-0.143,0.143-0.186,0.358-0.108,0.545C19.615,26.377,19.797,26.5,20,26.5h2.5V29c0,0.276,0.224,0.5,0.5,0.5h4
+ c0.276,0,0.5-0.224,0.5-0.5v-2.5H30c0.202,0,0.385-0.122,0.462-0.309c0.077-0.187,0.034-0.402-0.108-0.545l-5-5
+ C25.255,20.548,25.127,20.5,25,20.5L25,20.5z"/>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/shift.svg b/ui/keyboard/resources/images/shift.svg
new file mode 100644
index 0000000..08d8c8a
--- /dev/null
+++ b/ui/keyboard/resources/images/shift.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="50px" height="50px" viewBox="10 10 30 30">
+<!-- TODO(fsamuel): remove the width and height workaround above after fixing SVG bug -->
+<g>
+ <path fill="#bababb" d="M23,29.25c-0.138,0-0.25-0.112-0.25-0.25v-2.75H20c-0.101,0-0.192-0.061-0.231-0.154
+ s-0.017-0.201,0.054-0.272l5-5c0.049-0.049,0.113-0.073,0.177-0.073s0.128,0.024,0.177,0.073l5,5
+ c0.071,0.071,0.093,0.179,0.055,0.272c-0.039,0.093-0.13,0.154-0.231,0.154h-2.75V29c0,0.138-0.112,0.25-0.25,0.25H23z"/>
+ <path fill="#bababb" d="M25,21l5,5h-3v3h-4v-3h-3L25,21 M25,20.5c-0.128,0-0.256,0.049-0.354,0.146l-5,5
+ c-0.143,0.143-0.186,0.358-0.108,0.545C19.615,26.377,19.797,26.5,20,26.5h2.5V29c0,0.276,0.224,0.5,0.5,0.5h4
+ c0.276,0,0.5-0.224,0.5-0.5v-2.5H30c0.202,0,0.385-0.122,0.462-0.309c0.077-0.187,0.034-0.402-0.108-0.545l-5-5
+ C25.255,20.548,25.127,20.5,25,20.5L25,20.5z"/>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/images/tab.svg b/ui/keyboard/resources/images/tab.svg
new file mode 100644
index 0000000..774e306
--- /dev/null
+++ b/ui/keyboard/resources/images/tab.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100%" height="100%" viewBox="5 5 40 40" enable-background="new" xml:space="preserve">
+<g>
+ <line fill="none" stroke="#bababb" stroke-width="1.7" stroke-linecap="round" x1="36.377" y1="20.5" x2="36.377" y2="29.5"/>
+ <g>
+ <path fill="#bababb" d="M27.377,28.75c-0.043,0-0.086-0.011-0.125-0.033c-0.077-0.045-0.125-0.127-0.125-0.217v-7
+ c0-0.089,0.048-0.172,0.125-0.217c0.039-0.022,0.082-0.033,0.125-0.033c0.044,0,0.087,0.011,0.126,0.034l6,3.5
+ c0.077,0.045,0.124,0.127,0.124,0.216s-0.047,0.171-0.124,0.216l-6,3.5C27.464,28.738,27.421,28.75,27.377,28.75L27.377,28.75z"/>
+ </g>
+ <polygon fill="#bababb" points="14.888,23.5 13.623,26.5 29.877,26.5 29.877,23.5 "/>
+</g>
+</svg>
diff --git a/ui/keyboard/resources/index.html b/ui/keyboard/resources/index.html
new file mode 100644
index 0000000..8cd8b0e
--- /dev/null
+++ b/ui/keyboard/resources/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <link rel="stylesheet" href="main.css">
+ <script src="common.js"></script>
+ <script src="layout_us.js"></script>
+ <script src="main.js"></script>
+ </head>
+ <body id="b"></body>
+</html>
diff --git a/ui/keyboard/resources/layout_us.js b/ui/keyboard/resources/layout_us.js
new file mode 100644
index 0000000..bea4e57
--- /dev/null
+++ b/ui/keyboard/resources/layout_us.js
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A simple English virtual keyboard implementation.
+ */
+
+/**
+ * All keys for the rows of the keyboard.
+ * NOTE: every row below should have an aspect of 12.6.
+ * @type {Array.<Array.<BaseKey>>}
+ */
+var KEYS_US = [
+ [
+ new Key(C('\`'), C('~')),
+ new Key(C('1'), C('!')),
+ new Key(C('2'), C('@')),
+ new Key(C('3'), C('#')),
+ new Key(C('4'), C('$')),
+ new Key(C('5'), C('%')),
+ new Key(C('6'), C('^')),
+ new Key(C('7'), C('&')),
+ new Key(C('8'), C('*')),
+ new Key(C('9'), C('(')),
+ new Key(C('0'), C(')')),
+ new Key(C('-'), C('_')),
+ new Key(C('='), C('+')),
+ new SvgKey('backspace', 'Backspace', true /* repeat */)
+ ],
+ [
+ new SvgKey('tab', 'Tab'),
+ new Key(C('q'), C('Q')),
+ new Key(C('w'), C('W')),
+ new Key(C('e'), C('E')),
+ new Key(C('r'), C('R')),
+ new Key(C('t'), C('T')),
+ new Key(C('y'), C('Y')),
+ new Key(C('u'), C('U')),
+ new Key(C('i'), C('I')),
+ new Key(C('o'), C('O')),
+ new Key(C('p'), C('P')),
+ new Key(C('['), C('{')),
+ new Key(C(']'), C('}')),
+ new Key(C('\\'), C('|'), 'bar'),
+ ],
+ [
+ new SymbolKey(),
+ new Key(C('a'), C('A')),
+ new Key(C('s'), C('S')),
+ new Key(C('d'), C('D')),
+ new Key(C('f'), C('F')),
+ new Key(C('g'), C('G')),
+ new Key(C('h'), C('H')),
+ new Key(C('j'), C('J')),
+ new Key(C('k'), C('K')),
+ new Key(C('l'), C('L')),
+ new Key(C(';'), C(':')),
+ new Key(C('\''), C('"')),
+ new SvgKey('return', 'Enter')
+ ],
+ [
+ new ShiftKey('left-shift'),
+ new Key(C('z'), C('Z')),
+ new Key(C('x'), C('X')),
+ new Key(C('c'), C('C')),
+ new Key(C('v'), C('V')),
+ new Key(C('b'), C('B')),
+ new Key(C('n'), C('N')),
+ new Key(C('m'), C('M')),
+ new Key(C(','), C('<')),
+ new Key(C('.'), C('>')),
+ new Key(C('/'), C('?')),
+ new ShiftKey('right-shift')
+ ],
+ [
+ new DotComKey(),
+ new SpecialKey('at', '@', '@'),
+ new SpecialKey('space', ' ', 'Spacebar'),
+ new SpecialKey('comma', ',', ','),
+ new SpecialKey('period', '.', '.')
+ ]
+];
+
+// Add layout to KEYBOARDS, which is defined in common.js
+KEYBOARDS['us'] = {
+ 'definition': KEYS_US,
+ 'aspect': 3.15,
+};
diff --git a/ui/keyboard/resources/main.css b/ui/keyboard/resources/main.css
new file mode 100644
index 0000000..6dbe38a
--- /dev/null
+++ b/ui/keyboard/resources/main.css
@@ -0,0 +1,193 @@
+/*
+ Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+*/
+
+body {
+ -webkit-user-select: none;
+ color: white;
+ margin: 0;
+ overflow: hidden;
+ padding: 0;
+}
+
+div.main {
+ -webkit-box-orient: vertical;
+ background: -webkit-linear-gradient(#bababa, #868686) no-repeat;
+ display: -webkit-box;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: -10px;
+}
+
+div.keyboard {
+ -webkit-box-flex: 1;
+ display: -webkit-box;
+ margin: 0 auto;
+ text-align: center;
+}
+
+div.rows {
+ -webkit-box-flex: 1;
+ -webkit-box-orient: vertical;
+ display: -webkit-box;
+ margin-bottom: 5px;
+ text-align: center;
+}
+
+div.row {
+ -webkit-box-flex: 1;
+ display: -webkit-box;
+ margin-right: 5px;
+ margin-top: 5px;
+}
+
+div.row > div {
+ -webkit-box-flex: 1;
+ display: -webkit-box;
+}
+
+.panel {
+ border: 0;
+ clear: both;
+ margin-left: 5px;
+ text-align: left;
+}
+
+.backspace > div {
+ background-image: url('images/del.svg');
+}
+
+.tab > div {
+ background-image: url('images/tab.svg');
+}
+
+.return > div {
+ background-image: url('images/ret.svg');
+}
+
+.mic > div {
+ background-image: url('images/mic.svg');
+}
+
+.button {
+ background: -webkit-linear-gradient(rgb(90, 97, 111), rgb(80, 86, 98));
+}
+.button:active {
+ background: -webkit-linear-gradient(rgb(80, 86, 98), rgb(90, 97, 111));
+}
+
+.auxiliary:active {
+ background: -webkit-linear-gradient(rgb(90, 97, 111), rgb(80, 86, 98));
+}
+
+.key {
+ -webkit-box-flex: 1;
+ background: -webkit-linear-gradient(#fff, #cacaca);
+ border: 1px solid transparent;
+ border-radius: 6px;
+ /* Reserving equivalent space to .key:active so
+ keys don't shift when selected. */
+ /* Do not use box shadow until performance improves
+ * http://code.google.com/p/chromium/issues/detail?id=99045
+ box-shadow: 0px 1px 1px #000;
+ */
+ color: #535353;
+ display: -webkit-box;
+ font-family: sans-serif;
+ font-weight: 100;
+ margin-left: 5px;
+ position: relative;
+}
+
+.key > div {
+ bottom: 0;
+ left: 0;
+ margin: auto;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.key:active {
+ background: -webkit-linear-gradient(#d6d6d6, #acacac);
+ border: 1px solid rgba(125,125,125,0.5);
+ /* Do not use box shadow until performance improves
+ * http://code.google.com/p/chromium/issues/detail?id=99045
+ box-shadow: 0px 0px 15px #fff;
+ */
+}
+
+div.moddown {
+ background: -webkit-linear-gradient(#d6d6d6, #acacac);
+ border-color: rgb(48, 74, 155);
+}
+
+.image-key {
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ height: 100%;
+ width: 100%;
+}
+
+.text-key {
+ height: 1.2em;
+}
+
+
+.shift > div.image-key {
+ background-image: url('images/shift.svg');
+}
+
+.moddown.shift > div.image-key {
+ background-image: url('images/shift-down.svg');
+}
+
+.hide > div {
+ background-image: url('images/keyboard.svg');
+}
+
+.at,
+.tab,
+.com,
+.comma,
+.hide,
+.mic,
+.period {
+ -webkit-box-flex: 1.3;
+}
+
+.symbol {
+ -webkit-box-flex: 1.4;
+}
+
+.return {
+ -webkit-box-flex: 1.5;
+}
+
+.backspace {
+ -webkit-box-flex: 1.6;
+}
+
+.left-shift {
+ -webkit-box-flex: 1.8;
+}
+
+.right-shift {
+ -webkit-box-flex: 2.0;
+}
+
+.space {
+ -webkit-box-flex: 4.8;
+}
+
+.bar {
+ -webkit-box-flex: 0.6;
+}
+
+.nodisplay {
+ display: none;
+}
diff --git a/ui/keyboard/resources/main.js b/ui/keyboard/resources/main.js
new file mode 100644
index 0000000..a628cad
--- /dev/null
+++ b/ui/keyboard/resources/main.js
@@ -0,0 +1,192 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A simple virtual keyboard implementation.
+ */
+
+/**
+ * The ratio of the row height to the font size.
+ * @type {number}
+ */
+/** @const */ var kFontSizeRatio = 3.5;
+
+/**
+ * Alias for document.getElementById.
+ * @param {string} id The ID of the element to find.
+ * @return {HTMLElement} The found element or null if not found.
+ */
+function $(id) {
+ return document.getElementById(id);
+}
+
+/**
+ * Return the id attribute of the keyboard element for the given layout.
+ * @param {string} layout The keyboard layout.
+ * @return {string} The id attribute of the keyboard element.
+ */
+function getKeyboardId(layout) {
+ return 'keyboard_' + layout;
+}
+
+/**
+ * Return the aspect ratio of the current keyboard.
+ * @return {number} The aspect ratio of the current keyboard.
+ */
+function getKeyboardAspect() {
+ return KEYBOARDS[currentKeyboardLayout]['aspect'];
+}
+
+/**
+ * Calculate the height of the keyboard based on the size of the page.
+ * @return {number} The height of the keyboard in pixels.
+ */
+function getKeyboardHeight() {
+ var x = window.innerWidth;
+ var y = window.innerHeight;
+ return (x > getKeyboardAspect() * y) ?
+ y : Math.floor(x / getKeyboardAspect());
+}
+
+/**
+ * Create a DOM of the keyboard rows for the given keyboard layout.
+ * Do nothing if the DOM is already created.
+ * @param {string} layout The keyboard layout for which rows are created.
+ * @param {Element} element The DOM Element to which rows are appended.
+ * @param {boolean} autoPadding True if padding needs to be added to both side
+ * of the rows that have less keys.
+ */
+function initRows(layout, element, autoPadding) {
+ var keyboard = KEYBOARDS[layout];
+ if ('rows' in keyboard) {
+ return;
+ }
+ var def = keyboard['definition'];
+ var rows = [];
+ for (var i = 0; i < def.length; ++i) {
+ rows.push(new Row(i, def[i]));
+ }
+ keyboard['rows'] = rows;
+
+ var maxRowLength = -1;
+ for (var i = 0; i < rows.length; ++i) {
+ if (rows[i].length > maxRowLength) {
+ maxRowLength = rows[i].length;
+ }
+ }
+
+ // A div element which holds rows for the layout.
+ var rowsDiv = document.createElement('div');
+ rowsDiv.className = 'rows';
+ for (var i = 0; i < rows.length; ++i) {
+ var rowDiv = rows[i].makeDOM();
+ if (autoPadding && rows[i].length < maxRowLength) {
+ var padding = 50 * (maxRowLength - rows[i].length) / maxRowLength;
+ rowDiv.style.paddingLeft = padding + '%';
+ rowDiv.style.paddingRight = padding + '%';
+ }
+ rowsDiv.appendChild(rowDiv);
+ rows[i].showMode(currentMode);
+ }
+ keyboard['rowsDiv'] = rowsDiv;
+ element.appendChild(rowsDiv);
+}
+
+/**
+ * Create a DOM of the keyboard for the given keyboard layout.
+ * Do nothing if the DOM is already created.
+ * @param {string} layout The keyboard layout for which keyboard is created.
+ * @param {Element} element The DOM Element to which keyboard is appended.
+ */
+function initKeyboard(layout, element) {
+ var keyboard = KEYBOARDS[layout];
+ if (!keyboard || keyboard['keyboardDiv']) {
+ return;
+ }
+ var keyboardDiv = document.createElement('div');
+ keyboardDiv.id = getKeyboardId(layout);
+ keyboardDiv.className = 'keyboard';
+ initRows(layout, keyboardDiv);
+ keyboard['keyboardDiv'] = keyboardDiv;
+ window.onresize();
+ element.appendChild(keyboardDiv);
+}
+
+/**
+ * Resize the keyboard according to the new window size.
+ */
+window.onresize = function() {
+ var keyboardDiv = KEYBOARDS[currentKeyboardLayout]['keyboardDiv'];
+ var height = getKeyboardHeight();
+ keyboardDiv.style.height = height + 'px';
+ var mainDiv = $('main');
+ mainDiv.style.width = Math.floor(getKeyboardAspect() * height) + 'px';
+ var rowsLength = KEYBOARDS[currentKeyboardLayout]['rows'].length;
+ keyboardDiv.style.fontSize = (height / kFontSizeRatio / rowsLength) + 'px';
+};
+
+/**
+ * Init the keyboard.
+ */
+var mainDiv = null;
+
+/**
+ * Initialize keyboard.
+ */
+window.onload = function() {
+ var body = $('b');
+
+ // Catch all unhandled touch events and prevent default, to prevent the
+ // keyboard from responding to gestures like double tap.
+ function disableGestures(evt) {
+ evt.preventDefault();
+ }
+ body.addEventListener('touchstart', disableGestures);
+ body.addEventListener('touchmove', disableGestures);
+ body.addEventListener('touchend', disableGestures);
+
+ mainDiv = document.createElement('div');
+ mainDiv.className = 'main';
+ mainDiv.id = 'main';
+ body.appendChild(mainDiv);
+
+ initKeyboard(currentKeyboardLayout, mainDiv);
+
+ window.onhashchange();
+};
+
+/**
+ * Switch the keyboard layout based on the current URL hash.
+ */
+window.onhashchange = function() {
+ var oldLayout = currentKeyboardLayout;
+ var newLayout = location.hash.replace(/^#/, '');
+ if (oldLayout == newLayout) {
+ return;
+ }
+
+ if (KEYBOARDS[newLayout] === undefined) {
+ // Unsupported layout.
+ newLayout = 'us';
+ }
+ currentKeyboardLayout = newLayout;
+
+ var mainDiv = $('main');
+ initKeyboard(currentKeyboardLayout, mainDiv);
+
+ [newLayout, oldLayout].forEach(function(layout) {
+ var visible = (layout == newLayout);
+ var keyboardDiv = KEYBOARDS[layout]['keyboardDiv'];
+ keyboardDiv.className = visible ? 'keyboard' : 'nodisplay';
+ var canvas = KEYBOARDS[layout]['canvas'];
+ if (canvas !== undefined) {
+ if (!visible) {
+ canvas.clear();
+ }
+ }
+ if (visible) {
+ window.onresize();
+ }
+ });
+};
diff --git a/ui/keyboard/resources/manifest.json b/ui/keyboard/resources/manifest.json
new file mode 100644
index 0000000..9076308
--- /dev/null
+++ b/ui/keyboard/resources/manifest.json
@@ -0,0 +1,14 @@
+{
+ "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtEsRXms+bpXbcmBFljmWOKVkVPteGqY376YUY6CRcUNsYft98M41KO+oPURWfbauCErLyJb4Y1xcb4ZrRnwGoNSvLaTY/ij4bdUn8eNPtqviLLrHZCEBaNwHYFNPCOEQqvtmjZ+J0NGCgUbx2bUouD+BDDG30GeMfu7ArzB5RLQIDAQAB",
+ "name": "Virtual Keyboard",
+ "version": "0.0.1",
+ "manifest_version": 2,
+ "description": "Virtual Keyboard",
+ "incognito" : "split",
+ "chrome_url_overrides": {
+ "keyboard": "index.html"
+ },
+ "permissions": [
+ "experimental"
+ ]
+}