1effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)(function(exports) {
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Alignment options for a keyset.
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {Object=} opt_keyset The keyset to calculate the dimensions for.
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   *    Defaults to the current active keyset.
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  var AlignmentOptions = function(opt_keyset) {
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keyboard = document.getElementById('keyboard');
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keyset = opt_keyset || keyboard.activeKeyset;
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    this.calculate(keyset);
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AlignmentOptions.prototype = {
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The width of a regular key in logical pixels.
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    keyWidth: 0,
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The horizontal space between two keys in logical pixels.
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    pitchX: 0,
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The vertical space between two keys in logical pixels.
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    pitchY: 0,
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The width in logical pixels the row should expand within.
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availableWidth: 0,
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The x-coordinate in logical pixels of the left most edge of the keyset.
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    offsetLeft: 0,
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The x-coordinate of the right most edge in logical pixels of the keyset.
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    offsetRight: 0,
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The height in logical pixels of all keys.
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    keyHeight: 0,
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The height in logical pixels the keyset should stretch to fit.
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    availableHeight: 0,
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The y-coordinate in logical pixels of the top most edge of the keyset.
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    offsetTop: 0,
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * The y-coordinate in logical pixels of the bottom most edge of the keyset.
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @type {number}
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    offsetBottom: 0,
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * The ideal width of the keyboard container.
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @type {number}
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    width: 0,
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * The ideal height of the keyboard container.
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     * @type {number}
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)     */
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    height: 0,
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /**
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Recalculates the alignment options for a specific keyset.
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @param {Object} keyset The keyset to align.
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    calculate: function (keyset) {
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var rows = keyset.querySelectorAll('kb-row').array();
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Pick candidate row. This is the row with the most keys.
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var row = rows[0];
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var candidateLength = rows[0].childElementCount;
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for (var i = 1; i < rows.length; i++) {
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (rows[i].childElementCount > candidateLength &&
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            rows[i].align == RowAlignment.STRETCH) {
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          row = rows[i];
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          candidateLength = rows[i].childElementCount;
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var allKeys = row.children;
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Calculates widths first.
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Weight of a single interspace.
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var pitches = keyset.pitch.split();
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var pitchWeightX;
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var pitchWeightY;
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      pitchWeightX = parseFloat(pitches[0]);
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      pitchWeightY = pitches.length < 2 ? pitchWeightX : parseFloat(pitch[1]);
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Sum of all keys in the current row.
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var keyWeightSumX = 0;
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for (var i = 0; i < allKeys.length; i++) {
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        keyWeightSumX += allKeys[i].weight;
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var interspaceWeightSumX = (allKeys.length -1) * pitchWeightX;
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Total weight of the row in X.
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var totalWeightX = keyWeightSumX + interspaceWeightSumX +
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          keyset.weightLeft + keyset.weightRight;
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var keyAspectRatio = getKeyAspectRatio();
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var totalWeightY = (pitchWeightY * (rows.length - 1)) +
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         keyset.weightTop +
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         keyset.weightBottom;
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for (var i = 0; i < rows.length; i++) {
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        totalWeightY += rows[i].weight / keyAspectRatio;
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Calculate width and height of the window.
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var bounds = exports.getKeyboardBounds();
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this.width = bounds.width;
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this.height = bounds.height;
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var pixelPerWeightX = bounds.width/totalWeightX;
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var pixelPerWeightY = bounds.height/totalWeightY;
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (keyset.align == LayoutAlignment.CENTER) {
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (totalWeightX/bounds.width < totalWeightY/bounds.height) {
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          pixelPerWeightY = bounds.height/totalWeightY;
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          pixelPerWeightX = pixelPerWeightY;
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          this.width = Math.floor(pixelPerWeightX * totalWeightX)
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } else {
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          pixelPerWeightX = bounds.width/totalWeightX;
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          pixelPerWeightY = pixelPerWeightX;
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          this.height = Math.floor(pixelPerWeightY * totalWeightY);
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Calculate pitch.
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.pitchX = Math.floor(pitchWeightX * pixelPerWeightX);
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.pitchY = Math.floor(pitchWeightY * pixelPerWeightY);
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Convert weight to pixels on x axis.
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this.keyWidth = Math.floor(DEFAULT_KEY_WEIGHT * pixelPerWeightX);
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var offsetLeft = Math.floor(keyset.weightLeft * pixelPerWeightX);
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var offsetRight = Math.floor(keyset.weightRight * pixelPerWeightX);
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this.availableWidth = this.width - offsetLeft - offsetRight;
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Calculates weight to pixels on the y axis.
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var weightY = Math.floor(DEFAULT_KEY_WEIGHT / keyAspectRatio);
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this.keyHeight = Math.floor(weightY * pixelPerWeightY);
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var offsetTop = Math.floor(keyset.weightTop * pixelPerWeightY);
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var offsetBottom = Math.floor(keyset.weightBottom * pixelPerWeightY);
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      this.availableHeight = this.height - offsetTop - offsetBottom;
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var dX = bounds.width - this.width;
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.offsetLeft = offsetLeft + Math.floor(dX/2);
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.offsetRight = offsetRight + Math.ceil(dX/2)
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var dY = bounds.height - this.height;
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.offsetBottom = offsetBottom + dY;
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      this.offsetTop = offsetTop;
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    },
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  };
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * A simple binary search.
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @param {Array} array The array to search.
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @param {number} start The start index.
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @param {number} end The end index.
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @param {Function<Object>:number} The test function used for searching.
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @private
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @return {number} The index of the search, or -1 if it was not found.
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   */
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  function binarySearch_(array, start, end, testFn) {
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (start > end) {
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // No match found.
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return -1;
191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var mid = Math.floor((start+end)/2);
193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var result = testFn(mid);
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (result == 0)
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return mid;
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (result < 0)
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return binarySearch_(array, start, mid - 1, testFn);
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      else
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return binarySearch_(array, mid + 1, end, testFn);
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  /**
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Calculate width and height of the window.
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @private
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @return {Array.<String, number>} The bounds of the keyboard container.
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function getKeyboardBounds_() {
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return {
2090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      "width": screen.width,
2105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      "height": screen.height * DEFAULT_KEYBOARD_ASPECT_RATIO
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    };
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  /**
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * Calculates the desired key aspect ratio based on screen size.
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @return {number} The aspect ratio to use.
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   */
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  function getKeyAspectRatio() {
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return (screen.width > screen.height) ?
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        KEY_ASPECT_RATIO_LANDSCAPE : KEY_ASPECT_RATIO_PORTRAIT;
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Callback function for when the window is resized.
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  var onResize = function() {
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keyboard = $('keyboard');
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    keyboard.stale = true;
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keyset = keyboard.activeKeyset;
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (keyset)
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      realignAll();
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  };
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Updates a specific key to the position specified.
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {Object} key The key to update.
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} width The new width of the key.
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} height The new height of the key.
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} left The left corner of the key.
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} top The top corner of the key.
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function updateKey(key, width, height, left, top) {
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    key.style.position = 'absolute';
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    key.style.width = width + 'px';
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    key.style.height = (height - KEY_PADDING_TOP - KEY_PADDING_BOTTOM) + 'px';
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    key.style.left = left + 'px';
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    key.style.top = (top + KEY_PADDING_TOP) + 'px';
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Returns the key closest to given x-coordinate
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {Array.<kb-key>} allKeys Sorted array of all possible key
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   *     candidates.
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} x The x-coordinate.
2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} pitch The pitch of the row.
2565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {boolean} alignLeft whether to search with respect to the left or
2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   *   or right edge.
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   * @return {?kb-key}
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function findClosestKey(allKeys, x, pitch, alignLeft) {
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Test function.
2625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var testFn = function(i) {
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var ERROR_THRESH = 1;
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var key = allKeys[i];
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var left = parseFloat(key.style.left);
2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (!alignLeft)
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        left += parseFloat(key.style.width);
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var deltaRight = 0.5*(parseFloat(key.style.width) + pitch)
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      deltaLeft = 0.5 * pitch;
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (i > 0)
2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        deltaLeft += 0.5*parseFloat(allKeys[i-1].style.width);
2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var high = Math.ceil(left + deltaRight) + ERROR_THRESH;
2735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var low = Math.floor(left - deltaLeft) - ERROR_THRESH;
2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (x <= high && x >= low)
2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return 0;
2765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return x >= high? 1 : -1;
2775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var index = exports.binarySearch(allKeys, 0, allKeys.length -1, testFn);
279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return index > 0 ? allKeys[index] : null;
2805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
2835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Redistributes the total width amongst the keys in the range provided.
2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {Array.<kb-key>} allKeys Ordered list of keys to stretch.
2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {AlignmentOptions} params Options for aligning the keyset.
2865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} xOffset The x-coordinate of the key who's index is start.
2875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} width The total extraneous width to distribute.
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} keyHeight The height of each key.
2895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} yOffset The y-coordinate of the top edge of the row.
2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function redistribute(allKeys, params, xOffset, width, keyHeight, yOffset) {
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var availableWidth = width - (allKeys.length - 1) * params.pitchX;
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var stretchWeight = 0;
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var nStretch = 0;
2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i = 0; i < allKeys.length; i++) {
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var key = allKeys[i];
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (key.stretch) {
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        stretchWeight += key.weight;
2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        nStretch++;
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      } else if (key.weight == DEFAULT_KEY_WEIGHT) {
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        availableWidth -= params.keyWidth;
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      } else {
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        availableWidth -=
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            Math.floor(key.weight/DEFAULT_KEY_WEIGHT * params.keyWidth);
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (stretchWeight <= 0)
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      console.error("Cannot stretch row without a stretchable key");
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Rounding error to distribute.
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var pixelsPerWeight = availableWidth / stretchWeight;
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i = 0; i < allKeys.length; i++) {
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var key = allKeys[i];
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var keyWidth = params.keyWidth;
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (key.weight != DEFAULT_KEY_WEIGHT) {
3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        keyWidth =
316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            Math.floor(key.weight/DEFAULT_KEY_WEIGHT * params.keyWidth);
3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (key.stretch) {
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        nStretch--;
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (nStretch > 0) {
3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          keyWidth = Math.floor(key.weight * pixelsPerWeight);
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          availableWidth -= keyWidth;
3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } else {
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          keyWidth = availableWidth;
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      updateKey(key, keyWidth, keyHeight, xOffset, yOffset)
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      xOffset += keyWidth + params.pitchX;
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Aligns a row such that the spacebar is perfectly aligned with the row above
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * it. A precondition is that all keys in this row can be stretched as needed.
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {!kb-row} row The current row to be aligned.
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {!kb-row} prevRow The row above the current row.
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {!AlignmentOptions} params Options for aligning the keyset.
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} keyHeight The height of the keys in this row.
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} heightOffset The height offset caused by the rows above.
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function realignSpacebarRow(row, prevRow, params, keyHeight, heightOffset) {
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var allKeys = row.children;
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var stretchWeightBeforeSpace = 0;
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var stretchBefore = 0;
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var stretchWeightAfterSpace = 0;
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var stretchAfter = 0;
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var spaceIndex = -1;
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i=0; i< allKeys.length; i++) {
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (spaceIndex == -1) {
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (allKeys[i].classList.contains('space')) {
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          spaceIndex = i;
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          continue;
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } else {
3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          stretchWeightBeforeSpace += allKeys[i].weight;
3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          stretchBefore++;
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      } else {
3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        stretchWeightAfterSpace += allKeys[i].weight;
3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        stretchAfter++;
3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (spaceIndex == -1) {
3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      console.error("No spacebar found in this row.");
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var totalWeight = stretchWeightBeforeSpace +
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      stretchWeightAfterSpace +
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      allKeys[spaceIndex].weight;
3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var widthForKeys = params.availableWidth -
3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                       (params.pitchX * (allKeys.length - 1 ))
3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Number of pixels to assign per unit weight.
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var pixelsPerWeight = widthForKeys/totalWeight;
3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Predicted left edge of the space bar.
3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var spacePredictedLeft = params.offsetLeft +
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                          (spaceIndex * params.pitchX) +
3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                          (stretchWeightBeforeSpace * pixelsPerWeight);
3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var prevRowKeys = prevRow.children;
3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Find closest keys to the spacebar in order to align it to them.
3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var leftKey =
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        findClosestKey(prevRowKeys, spacePredictedLeft, params.pitchX, true);
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var spacePredictedRight = spacePredictedLeft +
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        allKeys[spaceIndex].weight * (params.keyWidth/100);
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var rightKey =
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        findClosestKey(prevRowKeys, spacePredictedRight, params.pitchX, false);
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var yOffset = params.offsetTop + heightOffset;
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Fix left side.
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var leftEdge = parseFloat(leftKey.style.left);
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var leftWidth = leftEdge - params.offsetLeft - params.pitchX;
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var leftKeys = allKeys.array().slice(0, spaceIndex);
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    redistribute(leftKeys,
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 params,
3965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 params.offsetLeft,
3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 leftWidth,
3985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 keyHeight,
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 yOffset);
4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Fix right side.
4015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var rightEdge = parseFloat(rightKey.style.left) +
4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        parseFloat(rightKey.style.width);
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var spacebarWidth = rightEdge - leftEdge;
4045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    updateKey(allKeys[spaceIndex],
4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              spacebarWidth,
4065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              keyHeight,
4075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              leftEdge,
4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              yOffset);
4095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var rightWidth =
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        params.availableWidth - (rightEdge - params.offsetLeft + params.pitchX);
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var rightKeys = allKeys.array().slice(spaceIndex + 1);
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    redistribute(rightKeys,
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 params,
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 rightEdge + params.pitchX,//xOffset.
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 rightWidth,
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 keyHeight,
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 yOffset);
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Realigns a given row based on the parameters provided.
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {!kb-row} row The row to realign.
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {!AlignmentOptions} params The parameters used to align the keyset.
4241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci   * @param {number} keyHeight The height of the keys.
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {number} heightOffset The offset caused by rows above it.
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function realignRow(row, params, keyHeight, heightOffset) {
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var all = row.children;
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var nStretch = 0;
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var stretchWeightSum = 0;
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var allSum = 0;
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Keeps track of where to distribute pixels caused by round off errors.
4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var deltaWidth = [];
4345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i = 0; i < all.length; i++) {
4355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      deltaWidth.push(0)
4365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var key = all[i];
437a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (key.weight == DEFAULT_KEY_WEIGHT){
4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        allSum += params.keyWidth;
4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      } else {
4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        var width =
441a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          Math.floor((params.keyWidth/DEFAULT_KEY_WEIGHT) * key.weight);
4425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        allSum += width;
4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (!key.stretch)
4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        continue;
4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      nStretch++;
4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      stretchWeightSum += key.weight;
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var nRegular = all.length - nStretch;
4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Extra space.
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var extra = params.availableWidth -
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                allSum -
4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                (params.pitchX * (all.length -1));
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var xOffset = params.offsetLeft;
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var alignment = row.align;
4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    switch (alignment) {
4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      case RowAlignment.STRETCH:
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        var extraPerWeight = extra/stretchWeightSum;
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for (var i = 0; i < all.length; i++) {
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          if (!all[i].stretch)
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            continue;
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          var delta = Math.floor(all[i].weight * extraPerWeight);
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          extra -= delta;
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          deltaWidth[i] = delta;
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          // All left-over pixels assigned to right most stretchable key.
4675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          nStretch--;
4685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          if (nStretch == 0)
4695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            deltaWidth[i] += extra;
4705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
4715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
4725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      case RowAlignment.CENTER:
4735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        xOffset += Math.floor(extra/2)
4745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
4755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      case RowAlignment.RIGHT:
4765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        xOffset += extra;
4775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
4785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      default:
4795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        break;
4805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    };
4815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var yOffset = params.offsetTop + heightOffset;
4835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var left = xOffset;
4845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i = 0; i < all.length; i++) {
4855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var key = all[i];
4865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var width = params.keyWidth;
487a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (key.weight != DEFAULT_KEY_WEIGHT)
488a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        width = Math.floor((params.keyWidth/DEFAULT_KEY_WEIGHT) * key.weight)
4895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      width += deltaWidth[i];
4905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      updateKey(key, width, keyHeight, left, yOffset)
4915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      left += (width + params.pitchX);
4925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
4935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
4965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Realigns the keysets in all layouts of the keyboard.
4975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
4985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function realignAll() {
499c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    resizeKeyboardContainer()
5005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keyboard = $('keyboard');
5015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var layoutParams = {};
5025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var idToLayout = function(id) {
5035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var parts = id.split('-');
5045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      parts.pop();
5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return parts.join('-');
5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keysets = keyboard.querySelectorAll('kb-keyset').array();
5095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i=0; i< keysets.length; i++) {
5105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var keyset = keysets[i];
5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var layout = idToLayout(keyset.id);
5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // Caches the layouts size parameters since all keysets in the same layout
5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // will have the same specs.
5145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (!(layout in layoutParams))
5155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        layoutParams[layout] = new AlignmentOptions(keyset);
5165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      realignKeyset(keyset, layoutParams[layout]);
5175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
518a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    exports.recordKeysets();
5195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  /**
5225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Realigns the keysets in the current layout of the keyboard.
5235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
5245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function realign() {
5255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keyboard = $('keyboard');
5265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var params = new AlignmentOptions();
527c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // Check if current window bounds are accurate.
528c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    resizeKeyboardContainer(params)
5295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var layout = keyboard.layout;
5305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var keysets =
5315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        keyboard.querySelectorAll('kb-keyset[id^=' + layout + ']').array();
5325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i = 0; i<keysets.length ; i++) {
5335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      realignKeyset(keysets[i], params);
5345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
5355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    keyboard.stale = false;
536a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    exports.recordKeysets();
5375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  /**
5405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * Realigns a given keyset.
5415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {Object} keyset The keyset to realign.
5425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   * @param {!AlignmentOptions} params The parameters used to align the keyset.
5435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)   */
5445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  function realignKeyset(keyset, params) {
5455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    var rows = keyset.querySelectorAll('kb-row').array();
5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    keyset.style.fontSize = (params.availableHeight /
5475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FONT_SIZE_RATIO / rows.length) + 'px';
5481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    var heightOffset = 0;
5495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (var i = 0; i < rows.length; i++) {
5505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var row = rows[i];
5515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      var rowHeight =
552a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          Math.floor(params.keyHeight * (row.weight / DEFAULT_KEY_WEIGHT));
5535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (row.querySelector('.space') && (i > 1)) {
5545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        realignSpacebarRow(row, rows[i-1], params, rowHeight, heightOffset)
5555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      } else {
5565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        realignRow(row, params, rowHeight, heightOffset);
5575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
5585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      heightOffset += (rowHeight + params.pitchY);
5595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
5605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
562c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  /**
563c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch   * Resizes the keyboard container if needed.
564c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch   * @params {AlignmentOptions=} opt_params Optional parameters to use. Defaults
565c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch   *   to the parameters of the current active keyset.
566c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch   */
567c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  function resizeKeyboardContainer(opt_params) {
568c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    var params = opt_params ? opt_params : new AlignmentOptions();
5690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (Math.abs(window.innerHeight - params.height) > RESIZE_THRESHOLD) {
570c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Cannot resize more than 50% of screen height due to crbug.com/338829.
571c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      window.resizeTo(params.width, params.height);
572c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
573c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
574c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
5755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  addEventListener('resize', onResize);
5765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  addEventListener('load', onResize);
5775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  exports.getKeyboardBounds = getKeyboardBounds_;
579a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  exports.binarySearch = binarySearch_;
580e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  exports.realignAll = realignAll;
5815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)})(this);
582c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch/**
5847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * Recursively replace all kb-key-import elements with imported documents.
5857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch * @param {!Document} content Document to process.
5867dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch */
5877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochfunction importHTML(content) {
5887dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  var dom = content.querySelector('template').createInstance();
5897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  var keyImports = dom.querySelectorAll('kb-key-import');
5907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (keyImports.length != 0) {
591f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    keyImports.array().forEach(function(element) {
5927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      if (element.importDoc(content)) {
5937dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        var generatedDom = importHTML(element.importDoc(content));
5947dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        element.parentNode.replaceChild(generatedDom, element);
5957dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      }
5967dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    });
5977dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
5987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return dom;
599bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch}
6007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
6017dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch/**
602e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  * Flatten the keysets which represents a keyboard layout.
603e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  */
604e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochfunction flattenKeysets() {
605e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  var keysets = $('keyboard').querySelectorAll('kb-keyset');
606e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (keysets.length > 0) {
607e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    keysets.array().forEach(function(element) {
608e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      element.flattenKeyset();
6097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    });
6107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
611bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3Ben Murdoch}
6127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
613e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochfunction resolveAudio() {
614a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  var keyboard = $('keyboard');
615a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  keyboard.addSound(Sound.DEFAULT);
616e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  var nodes = keyboard.querySelectorAll('[sound]').array();
617a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Get id's of all unique sounds.
618a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (var i = 0; i < nodes.length; i++) {
619a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var id = nodes[i].getAttribute('sound');
620a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    keyboard.addSound(id);
621a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
622a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
623a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
6244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Prevents all default actions of touch. Keyboard should use its own gesture
6254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// recognizer.
6264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)addEventListener('touchstart', function(e) { e.preventDefault() });
6274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)addEventListener('touchend', function(e) { e.preventDefault() });
6284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)addEventListener('touchmove', function(e) { e.preventDefault() });
629e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen MurdochaddEventListener('polymer-ready', function(e) {
630e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  flattenKeysets();
631e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  resolveAudio();
632e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch});
633c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochaddEventListener('stateChange', function(e) {
634c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (e.detail.value == $('keyboard').activeKeysetId)
635c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    realignAll();
636c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch})
637