1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * The long-press delay in milliseconds before long-press handler is 7 * invoked. 8 * @const 9 * @type {number} 10 */ 11var LONGPRESS_DELAY_MSEC = 500; 12 13/** 14 * The maximum number of elements in one keyset rule. 15 * @const 16 * @type {number} 17 */ 18var MAXIMUM_NUM_OF_RULE_ELEMENTS = 3; 19 20/** 21 * The minumum number of elements in one keyset rule. 22 * @const 23 * @type {number} 24 */ 25var MINIMUM_NUM_OF_RULE_ELEMENTS = 2; 26 27/** 28 * The index of event type element in the splitted keyset rule. 29 * @const 30 * @type {number} 31 */ 32var EVENT_TYPE = 0; 33 34/** 35 * The index of toKeyset element in the splitted keyset rule. 36 * @const 37 * @type {number} 38 */ 39var TO_KEYSET = 1; 40 41/** 42 * The index of nextKeyset element in the splitted keyset rule. 43 * @const 44 * @type {number} 45 */ 46var NEXT_KEYSET = 2; 47 48/** 49 * The index offset of toKeyset and nextKeyset elements in splitted keyset 50 * rule array and the array in keysetRules. 51 * @const 52 * @type {number} 53 */ 54var OFFSET = 1; 55 56/** 57 * The minumum number of elements in one keyset rule. 58 * @const {number} 59 */ 60var MINIMUM_NUM_OF_RULE_ELEMENTS = 2; 61 62Polymer('kb-key-base', { 63 repeat: false, 64 invert: false, 65 longPressTimer: null, 66 pointerId: undefined, 67 68 /** 69 * The keyset transition rules. It defines which keyset to transit to on 70 * which key events. It consists at most four rules (down, up, long, dbl). 71 * If no rule is defined for a key event, the event wont trigger a keyset 72 * change. 73 * @type {Object.<string, Array.<string>} 74 */ 75 keysetRules: null, 76 77 ready: function() { 78 if (this.toKeyset) { 79 // Parsing keyset rules from toKeyset attribute string. 80 // An rule can be defined as: (down|up|long|dbl):keysetid[:keysetid] 81 // and each rule are separated by a semicolon. The first element 82 // defines the event this rule applies to. The second element defines 83 // what keyset to transit to after event received. The third optional 84 // element defines what keyset to transit to after a key is pressed in 85 // the new keyset. It is useful when you want to transit to a not 86 // locked keyset. For example, after transit to a upper case keyset, 87 // it may make sense to transit back to lower case after user typed 88 // any key at the upper case keyset. 89 var rules = 90 this.toKeyset.replace(/(\r\n|\n|\r| )/g, '').split(';'); 91 this.keysetRules = {}; 92 var self = this; 93 rules.forEach(function(element) { 94 if (element == '') 95 return; 96 var keyValues = element.split(':', MAXIMUM_NUM_OF_RULE_ELEMENTS); 97 if (keyValues.length < MINIMUM_NUM_OF_RULE_ELEMENTS) { 98 console.error('Invalid keyset rules: ' + element); 99 return; 100 } 101 self.keysetRules[keyValues[EVENT_TYPE]] = [keyValues[TO_KEYSET], 102 (keyValues[NEXT_KEYSET] ? keyValues[NEXT_KEYSET] : null)]; 103 }); 104 } 105 }, 106 107 down: function(event) { 108 this.pointerId = event.pointerId; 109 var detail = this.populateDetails('down'); 110 this.fire('key-down', detail); 111 this.longPressTimer = this.generateLongPressTimer(); 112 }, 113 out: function(event) { 114 this.classList.remove('active'); 115 clearTimeout(this.longPressTimer); 116 }, 117 up: function(event) { 118 this.generateKeyup(); 119 }, 120 121 /** 122 * Releases the pressed key programmatically and fires key-up event. This 123 * should be called if a second key is pressed while the first key is not 124 * released yet. 125 */ 126 autoRelease: function() { 127 this.generateKeyup(); 128 }, 129 130 /** 131 * Drops the pressed key. 132 */ 133 dropKey: function() { 134 this.classList.remove('active'); 135 clearTimeout(this.longPressTimer); 136 this.pointerId = undefined; 137 }, 138 139 /** 140 * Populates details for this key, and then fires a key-up event. 141 */ 142 generateKeyup: function() { 143 if (this.pointerId === undefined) 144 return; 145 146 // Invalidates the pointerId so the subsequent pointerup event is a 147 // noop. 148 this.pointerId = undefined; 149 clearTimeout(this.longPressTimer); 150 var detail = this.populateDetails('up'); 151 this.fire('key-up', detail); 152 }, 153 154 /** 155 * Character value associated with the key. Typically, the value is a 156 * single character, but may be multi-character in cases like a ".com" 157 * button. 158 * @return {string} 159 */ 160 get charValue() { 161 return this.invert ? this.hintText : (this.char || this.textContent); 162 }, 163 164 /** 165 * Hint text value associated with the key. Typically, the value is a 166 * single character. 167 * @return {string} 168 */ 169 get hintTextValue() { 170 return this.invert ? (this.char || this.textContent) : this.hintText; 171 }, 172 173 /** 174 * Handles a swipe flick that originated from this key. 175 * @param {detail} detail The details of the swipe. 176 */ 177 onFlick: function(detail) { 178 if (!(detail.direction & SwipeDirection.UP) || !this.hintTextValue) 179 return; 180 var typeDetails = {char: this.hintTextValue}; 181 this.fire('type-key', typeDetails); 182 }, 183 184 /** 185 * Returns a subset of the key attributes. 186 * @param {string} caller The id of the function which called 187 * populateDetails. 188 */ 189 populateDetails: function(caller) { 190 var detail = { 191 char: this.charValue, 192 toLayout: this.toLayout, 193 repeat: this.repeat 194 }; 195 196 switch (caller) { 197 case ('up'): 198 if (this.keysetRules && this.keysetRules.up != undefined) { 199 detail.toKeyset = this.keysetRules.up[TO_KEYSET - OFFSET]; 200 detail.nextKeyset = this.keysetRules.up[NEXT_KEYSET - OFFSET]; 201 } 202 break; 203 case ('down'): 204 if (this.keysetRules && this.keysetRules.down != undefined) { 205 detail.toKeyset = this.keysetRules.down[TO_KEYSET - OFFSET]; 206 detail.nextKeyset = this.keysetRules.down[NEXT_KEYSET - OFFSET]; 207 } 208 break; 209 default: 210 break; 211 } 212 return detail; 213 }, 214 215 generateLongPressTimer: function() { 216 return this.async(function() { 217 var detail = { 218 char: this.charValue, 219 hintText: this.hintTextValue 220 }; 221 if (this.keysetRules && this.keysetRules.long != undefined) { 222 detail.toKeyset = this.keysetRules.long[TO_KEYSET - OFFSET]; 223 detail.nextKeyset = this.keysetRules.long[NEXT_KEYSET - OFFSET]; 224 } 225 this.fire('key-longpress', detail); 226 }, null, LONGPRESS_DELAY_MSEC); 227 }, 228}); 229