Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / iron-menu-behavior / iron-menu-behavior-extracted.js
blobb6238162a8c2ebb520288bb573fc24af30af98e2
3   /**
4    * `Polymer.IronMenuBehavior` implements accessible menu behavior.
5    *
6    * @demo demo/index.html
7    * @polymerBehavior Polymer.IronMenuBehavior
8    */
9   Polymer.IronMenuBehaviorImpl = {
11     properties: {
13       /**
14        * Returns the currently focused item.
15        * @type {?Object}
16        */
17       focusedItem: {
18         observer: '_focusedItemChanged',
19         readOnly: true,
20         type: Object
21       },
23       /**
24        * The attribute to use on menu items to look up the item title. Typing the first
25        * letter of an item when the menu is open focuses that item. If unset, `textContent`
26        * will be used.
27        */
28       attrForItemTitle: {
29         type: String
30       }
31     },
33     hostAttributes: {
34       'role': 'menu',
35       'tabindex': '0'
36     },
38     observers: [
39       '_updateMultiselectable(multi)'
40     ],
42     listeners: {
43       'focus': '_onFocus',
44       'keydown': '_onKeydown'
45     },
47     keyBindings: {
48       'up': '_onUpKey',
49       'down': '_onDownKey',
50       'esc': '_onEscKey',
51       'enter': '_onEnterKey',
52       'shift+tab:keydown': '_onShiftTabDown'
53     },
55     _updateMultiselectable: function(multi) {
56       if (multi) {
57         this.setAttribute('aria-multiselectable', 'true');
58       } else {
59         this.removeAttribute('aria-multiselectable');
60       }
61     },
63     _onShiftTabDown: function() {
64       var oldTabIndex;
66       Polymer.IronMenuBehaviorImpl._shiftTabPressed = true;
68       oldTabIndex = this.getAttribute('tabindex');
70       this.setAttribute('tabindex', '-1');
72       this.async(function() {
73         this.setAttribute('tabindex', oldTabIndex);
74         Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
75       // Note: polymer/polymer#1305
76       }, 1);
77     },
79     _applySelection: function(item, isSelected) {
80       if (isSelected) {
81         item.setAttribute('aria-selected', 'true');
82       } else {
83         item.removeAttribute('aria-selected');
84       }
86       Polymer.IronSelectableBehavior._applySelection.apply(this, arguments);
87     },
89     _focusedItemChanged: function(focusedItem, old) {
90       old && old.setAttribute('tabindex', '-1');
91       if (focusedItem) {
92         focusedItem.setAttribute('tabindex', '0');
93         focusedItem.focus();
94       }
95     },
97     select: function(value) {
98       if (this._defaultFocusAsync) {
99         this.cancelAsync(this._defaultFocusAsync);
100         this._defaultFocusAsync = null;
101       }
102       var item = this._valueToItem(value);
103       if (item && item.hasAttribute('disabled')) return;
104       this._setFocusedItem(item);
105       Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments);
106     },
108     _onFocus: function(event) {
109       if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) {
110         return;
111       }
112       // do not focus the menu itself
113       this.blur();
114       // clear the cached focus item
115       this._setFocusedItem(null);
116       this._defaultFocusAsync = this.async(function() {
117         // focus the selected item when the menu receives focus, or the first item
118         // if no item is selected
119         var selectedItem = this.multi ? (this.selectedItems && this.selectedItems[0]) : this.selectedItem;
120         if (selectedItem) {
121           this._setFocusedItem(selectedItem);
122         } else {
123           this._setFocusedItem(this.items[0]);
124         }
125       // async 100ms to wait for `select` to get called from `_itemActivate`
126       }, 100);
127     },
129     _onUpKey: function() {
130       // up and down arrows moves the focus
131       this._focusPrevious();
132     },
134     _onDownKey: function() {
135       this._focusNext();
136     },
138     _onEscKey: function() {
139       // esc blurs the control
140       this.focusedItem.blur();
141     },
143     _onEnterKey: function(event) {
144       // enter activates the item unless it is disabled
145       this._activateFocused(event.detail.keyboardEvent);
146     },
148     _onKeydown: function(event) {
149       if (this.keyboardEventMatchesKeys(event, 'up down esc enter')) {
150         return;
151       }
153       // all other keys focus the menu item starting with that character
154       this._focusWithKeyboardEvent(event);
155     },
157     _focusWithKeyboardEvent: function(event) {
158       for (var i = 0, item; item = this.items[i]; i++) {
159         var attr = this.attrForItemTitle || 'textContent';
160         var title = item[attr] || item.getAttribute(attr);
161         if (title && title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.keyCode).toLowerCase()) {
162           this._setFocusedItem(item);
163           break;
164         }
165       }
166     },
168     _activateFocused: function(event) {
169       if (!this.focusedItem.hasAttribute('disabled')) {
170         this._activateHandler(event);
171       }
172     },
174     _focusPrevious: function() {
175       var length = this.items.length;
176       var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length;
177       this._setFocusedItem(this.items[index]);
178     },
180     _focusNext: function() {
181       var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.length;
182       this._setFocusedItem(this.items[index]);
183     }
185   };
187   Polymer.IronMenuBehaviorImpl._shiftTabPressed = false;
189   /** @polymerBehavior Polymer.IronMenuBehavior */
190   Polymer.IronMenuBehavior = [
191     Polymer.IronMultiSelectableBehavior,
192     Polymer.IronA11yKeysBehavior,
193     Polymer.IronMenuBehaviorImpl
194   ];