1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use of this source code is governed by a BSD-style license that can be
3ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// found in the LICENSE file.
4ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @fileoverview A simple, English virtual keyboard implementation.
7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar KEY_MODE = 'key';
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar SHIFT_MODE = 'shift';
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar NUMBER_MODE = 'number';
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar SYMBOL_MODE = 'symbol';
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar MODES = [ KEY_MODE, SHIFT_MODE, NUMBER_MODE, SYMBOL_MODE ];
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar currentMode = KEY_MODE;
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar MODE_TRANSITIONS = {};
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[KEY_MODE + SHIFT_MODE] = SHIFT_MODE;
18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[KEY_MODE + NUMBER_MODE] = NUMBER_MODE;
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[SHIFT_MODE + SHIFT_MODE] = KEY_MODE;
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[SHIFT_MODE + NUMBER_MODE] = NUMBER_MODE;
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[NUMBER_MODE + SHIFT_MODE] = SYMBOL_MODE;
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[NUMBER_MODE + NUMBER_MODE] = KEY_MODE;
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[SYMBOL_MODE + SHIFT_MODE] = NUMBER_MODE;
24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenMODE_TRANSITIONS[SYMBOL_MODE + NUMBER_MODE] = KEY_MODE;
25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Transition the mode according to the given transition.
28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} transition The transition to take.
29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {void}
30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction transitionMode(transition) {
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  currentMode = MODE_TRANSITIONS[currentMode + transition];
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Plain-old-data class to represent a character.
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} display The HTML to be displayed.
38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} id The key identifier for this Character.
39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction Character(display, id) {
42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.display = display;
43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.keyIdentifier = id;
44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Convenience function to make the keyboard data more readable.
48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} display Both the display and id for the created Character.
49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction C(display) {
51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return new Character(display, display);
52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * An abstract base-class for all keys on the keyboard.
56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction BaseKey() {}
59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenBaseKey.prototype = {
61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * The aspect ratio of this key.
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @type {number}
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  aspect_: 1,
66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * The cell type of this key.  Determines the background colour.
69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @type {string}
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  cellType_: '',
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {number} The aspect ratio of this key.
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  get aspect() {
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.aspect_;
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Set the position, a.k.a. row, of this key.
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {string} position The position.
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {void}
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  set position(position) {
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i in this.modeElements_) {
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[i].classList.add(this.cellType_ + 'r' + position);
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Returns the amount of padding for the top of the key.
93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {string} mode The mode for the key.
94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {number} height The height of the key.
95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {number} Padding in pixels.
96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  getPadding: function(mode, height) {
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return Math.floor(height / 3.5);
99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Size the DOM elements of this key.
103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {string} mode The mode to be sized.
104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {number} height The height of the key.
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {void}
106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  sizeElement: function(mode, height) {
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var padding = this.getPadding(mode, height);
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var border = 1;
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var margin = 5;
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var width = Math.floor(height * this.aspect_);
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var extraHeight = margin + padding + 2 * border;
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var extraWidth = margin + 2 * border;
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].style.width = (width - extraWidth) + 'px';
117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].style.height = (height - extraHeight) + 'px';
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].style.marginLeft = margin + 'px';
119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].style.fontSize = (height / 3.5) + 'px';
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].style.paddingTop = padding + 'px';
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Resize all modes of this key based on the given height.
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {number} height The height of the key.
126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {void}
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  resize: function(height) {
129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i in this.modeElements_) {
130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.sizeElement(i, height);
131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Create the DOM elements for the given keyboard mode.  Must be overridden.
136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {string} mode The keyboard mode to create elements for.
137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {number} height The height of the key.
138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {Element} The top-level DOM Element for the key.
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    throw new Error('makeDOM not implemented in BaseKey');
142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A simple key which displays Characters.
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Character} key The Character for KEY_MODE.
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Character} shift The Character for SHIFT_MODE.
149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Character} num The Character for NUMBER_MODE.
150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Character} symbol The Character for SYMBOL_MODE.
151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction Key(key, shift, num, symbol) {
155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {};
156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = 1;  // ratio width:height
157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = '';
158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modes_ = {};
160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modes_[KEY_MODE] = key;
161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modes_[SHIFT_MODE] = shift;
162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modes_[NUMBER_MODE] = num;
163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modes_[SYMBOL_MODE] = symbol;
164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenKey.prototype = {
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].textContent = this.modes_[mode].display;
173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].className = 'key';
174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick =
178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        sendKeyFunction(this.modes_[mode].keyIdentifier);
179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A key which displays an SVG image.
186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} aspect The aspect ratio of the key.
187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} className The class that provides the image.
188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} keyId The key identifier for the key.
189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction SvgKey(aspect, className, keyId) {
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {};
194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = aspect;
195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = 'nc';
196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.className_ = className;
197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.keyId_ = keyId;
198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenSvgKey.prototype = {
201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  getPadding: function(mode, height) { return 0; },
205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].className = 'key';
210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var img = document.createElement('div');
212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    img.className = 'image-key ' + this.className_;
213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].appendChild(img);
214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick = sendKeyFunction(this.keyId_);
216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A Key that remains the same through all modes.
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} aspect The aspect ratio of the key.
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} content The display text for the key.
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} keyId The key identifier for the key.
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction SpecialKey(aspect, content, keyId) {
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {};
233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = aspect;
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = 'nc';
235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.content_ = content;
236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.keyId_ = keyId;
237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenSpecialKey.prototype = {
240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].textContent = this.content_;
246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].className = 'key';
247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick = sendKeyFunction(this.keyId_);
249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A shift key.
258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} aspect The aspect ratio of the key.
259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction ShiftKey(aspect) {
263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {};
264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = aspect;
265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = 'nc';
266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenShiftKey.prototype = {
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  getPadding: function(mode, height) {
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (mode == NUMBER_MODE || mode == SYMBOL_MODE) {
274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return BaseKey.prototype.getPadding.call(this, mode, height);
275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return 0;
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (mode == KEY_MODE || mode == SHIFT_MODE) {
284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      var shift = document.createElement('div');
285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      shift.className = 'image-key shift';
286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].appendChild(shift);
287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (mode == NUMBER_MODE) {
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].textContent = 'more';
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (mode == SYMBOL_MODE) {
290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].textContent = '#123';
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (mode == SHIFT_MODE || mode == SYMBOL_MODE) {
294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].className = 'moddown key';
295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].className = 'key';
297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick = function() {
302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      transitionMode(SHIFT_MODE);
303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      setMode(currentMode);
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    };
305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The symbol key: switches the keyboard into symbol mode.
311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction SymbolKey() {
315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {}
316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = 1.3;
317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = 'nc';
318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenSymbolKey.prototype = {
321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
324ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (mode == KEY_MODE || mode == SHIFT_MODE) {
328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].textContent = '#123';
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (mode == NUMBER_MODE || mode == SYMBOL_MODE) {
330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].textContent = 'abc';
331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (mode == NUMBER_MODE || mode == SYMBOL_MODE) {
334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].className = 'moddown key';
335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].className = 'key';
337ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
339ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
340ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
341ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick = function() {
342ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      transitionMode(NUMBER_MODE);
343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      setMode(currentMode);
344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    };
345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The ".com" key.
352ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction DotComKey() {
356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {}
357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = 1.3;
358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = 'nc';
359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenDotComKey.prototype = {
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].textContent = '.com';
368ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].className = 'key';
369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick = function() {
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      sendKey('.');
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      sendKey('c');
375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      sendKey('o');
376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      sendKey('m');
377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    };
378ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
384ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The key that hides the keyboard.
385ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
386ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @extends {BaseKey}
387ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
388ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction HideKeyboardKey() {
389ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {}
390ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.aspect_ = 1.3;
391ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.cellType_ = 'nc';
392ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
393ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
394ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenHideKeyboardKey.prototype = {
395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  __proto__: BaseKey.prototype,
396ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
397ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
398ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  getPadding: function(mode, height) { return 0; },
399ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
400ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /** @inheritDoc */
401ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(mode, height) {
402ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode] = document.createElement('div');
403ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].className = 'key';
404ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
405ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var hide = document.createElement('div');
406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    hide.className = 'image-key hide';
407ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].appendChild(hide);
408ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
409ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.sizeElement(mode, height);
410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].onclick = function() {
412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // TODO(bryeung): need a way to cancel the keyboard
413ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    };
414ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
415ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.modeElements_[mode];
416ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
417ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
418ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
419ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
420ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * A container for keys.
421ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} position The position of the row (0-3).
422ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {Array.<BaseKey>} keys The keys in the row.
423ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor
424ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
425ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction Row(position, keys) {
426ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.position_ = position;
427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.keys_ = keys;
428ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.element_ = null;
429ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  this.modeElements_ = {};
430ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenRow.prototype = {
433ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
434ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Get the total aspect ratio of the row.
435ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {number} The aspect ratio relative to a height of 1 unit.
436ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
437ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  get aspect() {
438ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    var total = 0;
439ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < this.keys_.length; ++i) {
440ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      total += this.keys_[i].aspect;
441ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
442ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return total;
443ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
444ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
445ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
446ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Create the DOM elements for the row.
447ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {Element} The top-level DOM Element for the row.
448ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
449ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  makeDOM: function(height) {
450ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.element_ = document.createElement('div');
451ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.element_.className = 'row';
452ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < MODES.length; ++i) {
453ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      var mode = MODES[i];
454ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode] = document.createElement('div');
455ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[mode].style.display = 'none';
456ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.element_.appendChild(this.modeElements_[mode]);
457ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
458ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
459ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var j = 0; j < this.keys_.length; ++j) {
460ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      var key = this.keys_[j];
461ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      for (var i = 0; i < MODES.length; ++i) {
462ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        this.modeElements_[MODES[i]].appendChild(key.makeDOM(MODES[i]), height);
463ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
464ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
465ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
466ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < MODES.length; ++i) {
467ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      var clearingDiv = document.createElement('div');
468ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      clearingDiv.style.clear = 'both';
469ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[MODES[i]].appendChild(clearingDiv);
470ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
471ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < this.keys_.length; ++i) {
473ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.keys_[i].position = this.position_;
474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
475ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return this.element_;
477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Shows the given mode.
481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {string} mode The mode to show.
482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {void}
483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  showMode: function(mode) {
485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < MODES.length; ++i) {
486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.modeElements_[MODES[i]].style.display = 'none';
487ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
488ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    this.modeElements_[mode].style.display = 'block';
489ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
491ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  /**
492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * Resizes all keys in the row according to the global size.
493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @param {number} height The height of the key.
494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   * @return {void}
495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen   */
496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  resize: function(height) {
497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (var i = 0; i < this.keys_.length; ++i) {
498ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      this.keys_[i].resize(height);
499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
500ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  },
501ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
502ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
503ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * All keys for the rows of the keyboard.
505ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * NOTE: every row below should have an aspect of 12.6.
506ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {Array.<Array.<BaseKey>>}
507ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar KEYS = [
509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  [
510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SvgKey(1, 'tab', 'Tab'),
511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('q'), C('Q'), C('1'), C('`')),
512ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('w'), C('W'), C('2'), C('~')),
513ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('e'), C('E'), C('3'), new Character('<', 'LessThan')),
514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('r'), C('R'), C('4'), new Character('>', 'GreaterThan')),
515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('t'), C('T'), C('5'), C('[')),
516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('y'), C('Y'), C('6'), C(']')),
517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('u'), C('U'), C('7'), C('{')),
518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('i'), C('I'), C('8'), C('}')),
519ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('o'), C('O'), C('9'), C('\'')),
520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('p'), C('P'), C('0'), C('|')),
521ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SvgKey(1.6, 'backspace', 'Backspace')
522ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ],
523ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  [
524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SymbolKey(),
525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('a'), C('A'), C('!'), C('+')),
526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('s'), C('S'), C('@'), C('=')),
527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('d'), C('D'), C('#'), C(' ')),
528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('f'), C('F'), C('$'), C(' ')),
529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('g'), C('G'), C('%'), C(' ')),
530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('h'), C('H'), C('^'), C(' ')),
531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('j'), C('J'), new Character('&', 'Ampersand'), C(' ')),
532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('k'), C('K'), C('*'), C('#')),
533ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('l'), C('L'), C('('), C(' ')),
534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('\''), C('\''), C(')'), C(' ')),
535ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SvgKey(1.3, 'return', 'Enter')
536ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ],
537ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  [
538ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new ShiftKey(1.6),
539ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('z'), C('Z'), C('/'), C(' ')),
540ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('x'), C('X'), C('-'), C(' ')),
541ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('c'), C('C'), C('\''), C(' ')),
542ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('v'), C('V'), C('"'), C(' ')),
543ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('b'), C('B'), C(':'), C('.')),
544ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('n'), C('N'), C(';'), C(' ')),
545ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('m'), C('M'), C('_'), C(' ')),
546ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('!'), C('!'), C('{'), C(' ')),
547ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('?'), C('?'), C('}'), C(' ')),
548ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new Key(C('/'), C('/'), C('\\'), C(' ')),
549ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new ShiftKey(1)
550ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ],
551ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  [
552ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SvgKey(1.3, 'mic', ''),
553ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new DotComKey(),
554ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SpecialKey(1.3, '@', '@'),
555ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // TODO(bryeung): the spacebar needs to be a little bit more stretchy,
556ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // since this row has only 7 keys (as opposed to 12), the truncation
557ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // can cause it to not be wide enough.
558ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SpecialKey(4.8, ' ', 'Spacebar'),
559ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SpecialKey(1.3, ',', ','),
560ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new SpecialKey(1.3, '.', '.'),
561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new HideKeyboardKey()
562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ]
563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen];
564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
565ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
566ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * All of the rows in the keyboard.
567ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {Array.<Row>}
568ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
569ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar allRows = [];  // Populated during start()
570ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
571ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
572ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Calculate the height of the row based on the size of the page.
573ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {number} The height of each row, in pixels.
574ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
575ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction getRowHeight() {
576ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var x = window.innerWidth;
577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var y = window.innerHeight;
578ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return (x > kKeyboardAspect * y) ?
579ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      (height = Math.floor(y / 4)) :
580ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      (height = Math.floor(x / (kKeyboardAspect * 4)));
581ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
582ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
583ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
584ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Set the keyboard mode.
585ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} mode The new mode.
586ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {void}
587ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
588ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction setMode(mode) {
589ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (var i = 0; i < allRows.length; ++i) {
590ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    allRows[i].showMode(mode);
591ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
592ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
593ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
594ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
595ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The keyboard's aspect ratio.
596ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number}
597ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
598ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar kKeyboardAspect = 3.3;
599ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
600ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
601ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Send the given key to chrome, via the experimental extension API.
602ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} key The key to send.
603ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {void}
604ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
605ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction sendKey(key) {
606ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!chrome.experimental) {
607ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    console.log(key);
608ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return;
609ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
610ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
611ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var keyEvent = {'type': 'keydown', 'keyIdentifier': key};
612ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (currentMode == SHIFT_MODE)
613ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    keyEvent['shiftKey'] = true;
614ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
615ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  chrome.experimental.input.sendKeyboardEvent(keyEvent);
616ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  keyEvent['type'] = 'keyup';
617ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  chrome.experimental.input.sendKeyboardEvent(keyEvent);
618ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
619ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // TODO(bryeung): deactivate shift after a successful keypress
620ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
621ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
622ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
623ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Create a closure for the sendKey function.
624ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {string} key The parameter to sendKey.
625ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {void}
626ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
627ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenfunction sendKeyFunction(key) {
628ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return function() { sendKey(key); }
629ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
630ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
631ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
632ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Resize the keyboard according to the new window size.
633ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {void}
634ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
635ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenwindow.onresize = function() {
636ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var height = getRowHeight();
637ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var newX = document.documentElement.clientWidth;
638ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
639ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // All rows should have the same aspect, so just use the first one
640ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var totalWidth = Math.floor(height * allRows[0].aspect);
641ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var leftPadding = Math.floor((newX - totalWidth) / 2);
642ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  document.getElementById('b').style.paddingLeft = leftPadding + 'px';
643ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
644ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (var i = 0; i < allRows.length; ++i) {
645ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    allRows[i].resize(height);
646ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
647ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
648ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
649ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/**
650ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Init the keyboard.
651ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {void}
652ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */
653ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenwindow.onload = function() {
654ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  var body = document.getElementById('b');
655ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (var i = 0; i < KEYS.length; ++i) {
656ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    allRows.push(new Row(i, KEYS[i]));
657ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
658ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
659ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (var i = 0; i < allRows.length; ++i) {
660ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    body.appendChild(allRows[i].makeDOM(getRowHeight()));
661ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    allRows[i].showMode(KEY_MODE);
662ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
663ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
664ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  window.onresize();
665ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
666ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
667ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TODO(bryeung): would be nice to leave less gutter (without causing
668ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// rendering issues with floated divs wrapping at some sizes).
669