Commit a8126e8c authored by bryeung@chromium.org's avatar bryeung@chromium.org

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

Review URL: https://codereview.chromium.org/13652010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194579 0039d316-1c4b-4281-b951-d872f2087c98
parent 5b5495c7
......@@ -26,6 +26,7 @@
#include "content/public/browser/notification_source.h"
#include "extensions/common/id_util.h"
#include "grit/browser_resources.h"
#include "grit/keyboard_resources.h"
#include "ui/base/resource/resource_bundle.h"
#if defined(OFFICIAL_BUILD)
......@@ -333,6 +334,12 @@ void ComponentLoader::AddChromeApp() {
#endif
}
void ComponentLoader::AddKeyboardApp() {
#if defined(USE_ASH)
Add(IDR_KEYBOARD_MANIFEST, base::FilePath(FILE_PATH_LITERAL("keyboard")));
#endif
}
// static
void ComponentLoader::EnableBackgroundExtensionsForTesting() {
enable_background_extensions_during_testing = true;
......@@ -387,6 +394,10 @@ void ComponentLoader::AddDefaultComponentExtensions(
AddChromeApp();
}
#if defined(USE_AURA)
AddKeyboardApp();
#endif
AddDefaultComponentExtensionsWithBackgroundPages(skip_session_components);
}
......
......@@ -127,6 +127,7 @@ class ComponentLoader {
void AddOrReloadEnterpriseWebStore();
void AddChromeApp();
void AddKeyboardApp();
// Unloads |component| from the memory.
void UnloadComponent(ComponentExtensionInfo* component);
......
......@@ -16,6 +16,7 @@
#include "content/public/browser/browser_thread.h"
#include "grit/chrome_unscaled_resources.h"
#include "grit/component_extension_resources_map.h"
#include "grit/keyboard_resources.h"
#include "grit/theme_resources.h"
#include "skia/ext/image_operations.h"
#include "ui/base/resource/resource_bundle.h"
......@@ -169,6 +170,21 @@ bool ImageLoader::IsComponentExtensionResource(
{"settings_app/settings_app_icon_16.png", IDR_SETTINGS_APP_ICON_16},
{"settings_app/settings_app_icon_32.png", IDR_SETTINGS_APP_ICON_32},
{"settings_app/settings_app_icon_48.png", IDR_SETTINGS_APP_ICON_48},
#endif
#if defined(USE_AURA)
{"keyboard/index.html", IDR_KEYBOARD_INDEX},
{"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS},
{"keyboard/main.js", IDR_KEYBOARD_MAIN_JS},
{"keyboard/common.js", IDR_KEYBOARD_COMMON_JS},
{"keyboard/layout_us.js", IDR_KEYBOARD_LAYOUT_US_JS},
{"keyboard/images/chevron.svg", IDR_KEYBOARD_IMAGES_CHEVRON},
{"keyboard/images/del.svg", IDR_KEYBOARD_IMAGES_DEL},
{"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD},
{"keyboard/images/mic.svg", IDR_KEYBOARD_IMAGES_MIC},
{"keyboard/images/ret.svg", IDR_KEYBOARD_IMAGES_RET},
{"keyboard/images/shift_down.svg", IDR_KEYBOARD_IMAGES_SHIFT_DOWN},
{"keyboard/images/shift.svg", IDR_KEYBOARD_IMAGES_SHIFT},
{"keyboard/images/tab.svg", IDR_KEYBOARD_IMAGES_TAB},
#endif
};
static const size_t kExtraComponentExtensionResourcesSize =
......
......@@ -98,6 +98,8 @@
#if defined(USE_AURA)
#include "chrome/browser/ui/webui/gesture_config_ui.h"
#include "ui/keyboard/keyboard_constants.h"
#include "ui/keyboard/keyboard_ui_controller.h"
#endif
#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
......@@ -357,6 +359,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
#if defined(USE_AURA)
if (url.host() == chrome::kChromeUIGestureConfigHost)
return &NewWebUI<GestureConfigUI>;
if (url.host() == keyboard::kKeyboardWebUIHost)
return &NewWebUI<keyboard::KeyboardUIController>;
#endif
#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
......
......@@ -40,6 +40,7 @@
'../third_party/libusb/libusb.gyp:libusb',
'../third_party/re2/re2.gyp:re2',
'../ui/base/strings/ui_strings.gyp:ui_strings',
'../ui/keyboard/keyboard.gyp:keyboard_resources',
'../ui/ui.gyp:ui',
'../ui/ui.gyp:ui_resources',
'../webkit/support/webkit_support.gyp:glue',
......
......@@ -2498,6 +2498,8 @@
# aura uses some of ash resources.
'../ash/ash.gyp:ash_resources',
'../ui/aura/aura.gyp:aura',
'../ui/keyboard/keyboard.gyp:keyboard',
'../ui/keyboard/keyboard.gyp:keyboard_resources',
],
'conditions': [
['OS=="win"', {
......
......@@ -15,6 +15,7 @@
['OS != "ios" and OS != "android"', {
# New paks should be added here by default.
'pak_inputs': [
'<(SHARED_INTERMEDIATE_DIR)/ui/keyboard/keyboard_resources.pak',
'<(SHARED_INTERMEDIATE_DIR)/webkit/devtools_resources.pak',
'<(grit_out_dir)/component_extension_resources.pak',
'<(grit_out_dir)/options_resources.pak',
......
......@@ -22,6 +22,10 @@
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(USE_AURA)
#include "ui/keyboard/keyboard_constants.h"
#endif
namespace keys = extension_manifest_keys;
namespace errors = extension_manifest_errors;
......@@ -276,6 +280,9 @@ bool URLOverridesHandler::Parse(Extension* extension, string16* error) {
!(extension->location() == Manifest::COMPONENT &&
page == chrome::kChromeUIFileManagerHost));
#endif
#if defined(USE_AURA)
is_override = (is_override && page != keyboard::kKeyboardWebUIHost);
#endif
if (is_override || !iter.value().GetAsString(&val)) {
*error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
......
......@@ -47,7 +47,6 @@ const char kChromeUIHistoryFrameURL[] = "chrome://history-frame/";
const char kChromeUIInspectURL[] = "chrome://inspect/";
const char kChromeUIInstantURL[] = "chrome://instant/";
const char kChromeUIIPCURL[] = "chrome://ipc/";
const char kChromeUIKeyboardURL[] = "chrome://keyboard/";
const char kChromeUIManagedUserPassphrasePageURL[] =
"chrome://managed-user-passphrase/";
const char kChromeUIMemoryRedirectURL[] = "chrome://memory-redirect/";
......@@ -167,7 +166,6 @@ const char kChromeUIHistoryFrameHost[] = "history-frame";
const char kChromeUIInspectHost[] = "inspect";
const char kChromeUIInstantHost[] = "instant";
const char kChromeUIIPCHost[] = "ipc";
const char kChromeUIKeyboardHost[] = "keyboard";
const char kChromeUIKillHost[] = "kill";
const char kChromeUIManagedUserPassphrasePageHost[] = "managed-user-passphrase";
const char kChromeUIMemoryHost[] = "memory";
......
......@@ -46,7 +46,6 @@ extern const char kChromeUIHistoryFrameURL[];
extern const char kChromeUIInspectURL[];
extern const char kChromeUIInstantURL[];
extern const char kChromeUIIPCURL[];
extern const char kChromeUIKeyboardURL[];
extern const char kChromeUIManagedUserPassphrasePageURL[];
extern const char kChromeUIMemoryRedirectURL[];
extern const char kChromeUIMemoryURL[];
......@@ -160,7 +159,6 @@ extern const char kChromeUIHistoryFrameHost[];
extern const char kChromeUIInspectHost[];
extern const char kChromeUIInstantHost[];
extern const char kChromeUIIPCHost[];
extern const char kChromeUIKeyboardHost[];
extern const char kChromeUIKillHost[];
extern const char kChromeUIManagedUserPassphrasePageHost[];
extern const char kChromeUIMemoryHost[];
......
......@@ -205,10 +205,11 @@
"chrome/browser/resources/memory_internals_resources.grd": {
"includes": [27500],
},
"device/device_bluetooth_strings.grd": {
"messages": [28000],
},
"ui/keyboard/keyboard_resources.grd": {
"includes": [28050],
},
# Resource ids starting at 31000 are reserved for projects built on Chromium.
}
include_rules = [
"+content/public",
"+grit/keyboard_resources.h",
]
......@@ -7,36 +7,60 @@
'chromium_code': 1,
},
'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',
],
}],
],
}],
],
},
],
}
// 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
// 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_
<?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>
// 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
// 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_
// 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;
}
};
<?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>
<?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>
<?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>
<?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>
<?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>
<?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>
<?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>
<?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>
<!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>
// 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,
};
/*
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;
}
// 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();
}
});
};
{
"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"
]
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment