blob: 963097ecb755a60109e99d723b1a7e89f79fda33 [file] [log] [blame]
<!--
-- Copyright 2013 The Chromium Authors. All rights reserved.
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
-->
<polymer-element name="kb-key-base"
on-pointerdown="down" on-pointerup="up" on-pointerout="out"
attributes="accents char invert repeat hintText toKeyset toLayout">
<script>
/**
* The long-press delay in milliseconds before long-press handler is
* invoked.
* @const
* @type {number}
*/
var LONGPRESS_DELAY_MSEC = 500;
/**
* The maximum number of elements in one keyset rule.
* @const
* @type {number}
*/
var MAXIMUM_NUM_OF_RULE_ELEMENTS = 3;
/**
* The minumum number of elements in one keyset rule.
* @const
* @type {number}
*/
var MINIMUM_NUM_OF_RULE_ELEMENTS = 2;
/**
* The index of event type element in the splitted keyset rule.
* @const
* @type {number}
*/
var EVENT_TYPE = 0;
/**
* The index of toKeyset element in the splitted keyset rule.
* @const
* @type {number}
*/
var TO_KEYSET = 1;
/**
* The index of nextKeyset element in the splitted keyset rule.
* @const
* @type {number}
*/
var NEXT_KEYSET = 2;
/**
* The index offset of toKeyset and nextKeyset elements in splitted keyset
* rule array and the array in keysetRules.
* @const
* @type {number}
*/
var OFFSET = 1;
/**
* The minumum number of elements in one keyset rule.
* @const {number}
*/
var MINIMUM_NUM_OF_RULE_ELEMENTS = 2;
Polymer('kb-key-base', {
repeat: false,
invert: false,
longPressTimer: null,
pointerId: undefined,
/**
* The keyset transition rules. It defines which keyset to transit to on
* which key events. It consists at most four rules (down, up, long, dbl).
* If no rule is defined for a key event, the event wont trigger a keyset
* change.
* @type {Object.<string, Array.<string>}
*/
keysetRules: null,
ready: function() {
if (this.toKeyset) {
// Parsing keyset rules from toKeyset attribute string.
// An rule can be defined as: (down|up|long|dbl):keysetid[:keysetid]
// and each rule are separated by a semicolon. The first element
// defines the event this rule applies to. The second element defines
// what keyset to transit to after event received. The third optional
// element defines what keyset to transit to after a key is pressed in
// the new keyset. It is useful when you want to transit to a not
// locked keyset. For example, after transit to a upper case keyset,
// it may make sense to transit back to lower case after user typed
// any key at the upper case keyset.
var rules =
this.toKeyset.replace(/(\r\n|\n|\r| )/g, '').split(';');
this.keysetRules = {};
var self = this;
rules.forEach(function(element) {
if (element == '')
return;
var keyValues = element.split(':', MAXIMUM_NUM_OF_RULE_ELEMENTS);
if (keyValues.length < MINIMUM_NUM_OF_RULE_ELEMENTS) {
console.error('Invalid keyset rules: ' + element);
return;
}
self.keysetRules[keyValues[EVENT_TYPE]] = [keyValues[TO_KEYSET],
(keyValues[NEXT_KEYSET] ? keyValues[NEXT_KEYSET] : null)];
});
}
},
down: function(event) {
this.pointerId = event.pointerId;
var detail = this.populateDetails('down');
this.fire('key-down', detail);
this.longPressTimer = this.generateLongPressTimer();
},
out: function(event) {
this.classList.remove('active');
clearTimeout(this.longPressTimer);
},
up: function(event) {
this.autoRelease();
},
/**
* Releases the pressed key programmatically and fires key-up event. This
* should be called if a second key is pressed while the first key is not
* released yet.
*/
autoRelease: function() {
if (this.pointerId === undefined)
return;
// Invalidates the pointerId so the subsequent pointerup event is a
// noop.
this.pointerId = undefined;
clearTimeout(this.longPressTimer);
var detail = this.populateDetails('up');
this.fire('key-up', detail);
},
/**
* Drops the pressed key.
*/
dropKey: function() {
this.classList.remove('active');
clearTimeout(this.longPressTimer);
this.pointerId = undefined;
},
/**
* Character value associated with the key. Typically, the value is a
* single character, but may be multi-character in cases like a ".com"
* button.
* @return {string}
*/
get charValue() {
return this.invert ? this.hintText : (this.char || this.textContent);
},
/* Hint text value associated with the key. Typically, the value is a
* single character.
* @return {string}
*/
get hintTextValue() {
return this.invert ? (this.char || this.textContent) : this.hintText;
},
/**
* Returns a subset of the key attributes.
* @param {string} caller The id of the function which called
* populateDetails.
*/
populateDetails: function(caller) {
var detail = {
char: this.charValue,
toLayout: this.toLayout,
repeat: this.repeat
};
switch (caller) {
case ('up'):
if (this.keysetRules && this.keysetRules.up != undefined) {
detail.toKeyset = this.keysetRules.up[TO_KEYSET - OFFSET];
detail.nextKeyset = this.keysetRules.up[NEXT_KEYSET - OFFSET];
}
break;
case ('down'):
if (this.keysetRules && this.keysetRules.down != undefined) {
detail.toKeyset = this.keysetRules.down[TO_KEYSET - OFFSET];
detail.nextKeyset = this.keysetRules.down[NEXT_KEYSET - OFFSET];
}
break;
default:
break;
}
return detail;
},
generateLongPressTimer: function() {
return this.asyncMethod(function() {
var detail = {
char: this.charValue,
hintText: this.hintTextValue
};
if (this.keysetRules && this.keysetRules.long != undefined) {
detail.toKeyset = this.keysetRules.long[TO_KEYSET - OFFSET];
detail.nextKeyset = this.keysetRules.long[NEXT_KEYSET - OFFSET];
}
this.fire('key-longpress', detail);
}, null, LONGPRESS_DELAY_MSEC);
},
});
</script>
</polymer-element>