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