• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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