Home | History | Annotate | Download | only in elements
      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