Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Side by Side Diff: third_party/polymer/v1_0/components-chromium/iron-overlay-behavior/iron-focusables-helper-extracted.js

Issue 2633003003: Update iron-overlay-behavior to version 1.10.2. (Closed)
Patch Set: Rebased. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 (function() {
2 'use strict';
3
4 var p = Element.prototype;
5 var matches = p.matches || p.matchesSelector || p.mozMatchesSelector ||
6 p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
7
8 Polymer.IronFocusablesHelper = {
9
10 /**
11 * Returns a sorted array of tabbable nodes, including the root node.
12 * It searches the tabbable nodes in the light and shadow dom of the chidr en,
13 * sorting the result by tabindex.
14 * @param {!Node} node
15 * @return {Array<HTMLElement>}
16 */
17 getTabbableNodes: function(node) {
18 var result = [];
19 // If there is at least one element with tabindex > 0, we need to sort
20 // the final array by tabindex.
21 var needsSortByTabIndex = this._collectTabbableNodes(node, result);
22 if (needsSortByTabIndex) {
23 return this._sortByTabIndex(result);
24 }
25 return result;
26 },
27
28 /**
29 * Returns if a element is focusable.
30 * @param {!HTMLElement} element
31 * @return {boolean}
32 */
33 isFocusable: function(element) {
34 // From http://stackoverflow.com/a/1600194/4228703:
35 // There isn't a definite list, it's up to the browser. The only
36 // standard we have is DOM Level 2 HTML https://www.w3.org/TR/DOM-Level- 2-HTML/html.html,
37 // according to which the only elements that have a focus() method are
38 // HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement and
39 // HTMLAnchorElement. This notably omits HTMLButtonElement and
40 // HTMLAreaElement.
41 // Referring to these tests with tabbables in different browsers
42 // http://allyjs.io/data-tables/focusable.html
43
44 // Elements that cannot be focused if they have [disabled] attribute.
45 if (matches.call(element, 'input, select, textarea, button, object')) {
46 return matches.call(element, ':not([disabled])');
47 }
48 // Elements that can be focused even if they have [disabled] attribute.
49 return matches.call(element,
50 'a[href], area[href], iframe, [tabindex], [contentEditable]');
51 },
52
53 /**
54 * Returns if a element is tabbable. To be tabbable, a element must be
55 * focusable, visible, and with a tabindex !== -1.
56 * @param {!HTMLElement} element
57 * @return {boolean}
58 */
59 isTabbable: function(element) {
60 return this.isFocusable(element) &&
61 matches.call(element, ':not([tabindex="-1"])') &&
62 this._isVisible(element);
63 },
64
65 /**
66 * Returns the normalized element tabindex. If not focusable, returns -1.
67 * It checks for the attribute "tabindex" instead of the element property
68 * `tabIndex` since browsers assign different values to it.
69 * e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
70 * @param {!HTMLElement} element
71 * @return {!number}
72 * @private
73 */
74 _normalizedTabIndex: function(element) {
75 if (this.isFocusable(element)) {
76 var tabIndex = element.getAttribute('tabindex') || 0;
77 return Number(tabIndex);
78 }
79 return -1;
80 },
81
82 /**
83 * Searches for nodes that are tabbable and adds them to the `result` arra y.
84 * Returns if the `result` array needs to be sorted by tabindex.
85 * @param {!Node} node The starting point for the search; added to `result `
86 * if tabbable.
87 * @param {!Array<HTMLElement>} result
88 * @return {boolean}
89 * @private
90 */
91 _collectTabbableNodes: function(node, result) {
92 // If not an element or not visible, no need to explore children.
93 if (node.nodeType !== Node.ELEMENT_NODE || !this._isVisible(node)) {
94 return false;
95 }
96 var element = /** @type {HTMLElement} */ (node);
97 var tabIndex = this._normalizedTabIndex(element);
98 var needsSortByTabIndex = tabIndex > 0;
99 if (tabIndex >= 0) {
100 result.push(element);
101 }
102
103 // In ShadowDOM v1, tab order is affected by the order of distrubution.
104 // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B];
105 // in ShadowDOM v0 tab order is not affected by the distrubution order,
106 // in fact getTabbableNodes(#root) returns [#B, #A].
107 // <div id="root">
108 // <!-- shadow -->
109 // <slot name="a">
110 // <slot name="b">
111 // <!-- /shadow -->
112 // <input id="A" slot="a">
113 // <input id="B" slot="b" tabindex="1">
114 // </div>
115 // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0.
116 var children;
117 if (element.localName === 'content') {
118 children = Polymer.dom(element).getDistributedNodes();
119 } else {
120 // Use shadow root if possible, will check for distributed nodes.
121 children = Polymer.dom(element.root || element).children;
122 }
123 for (var i = 0; i < children.length; i++) {
124 // Ensure method is always invoked to collect tabbable children.
125 var needsSort = this._collectTabbableNodes(children[i], result);
126 needsSortByTabIndex = needsSortByTabIndex || needsSort;
127 }
128 return needsSortByTabIndex;
129 },
130
131 /**
132 * Returns false if the element has `visibility: hidden` or `display: none `
133 * @param {!HTMLElement} element
134 * @return {boolean}
135 * @private
136 */
137 _isVisible: function(element) {
138 // Check inline style first to save a re-flow. If looks good, check also
139 // computed style.
140 var style = element.style;
141 if (style.visibility !== 'hidden' && style.display !== 'none') {
142 style = window.getComputedStyle(element);
143 return (style.visibility !== 'hidden' && style.display !== 'none');
144 }
145 return false;
146 },
147
148 /**
149 * Sorts an array of tabbable elements by tabindex. Returns a new array.
150 * @param {!Array<HTMLElement>} tabbables
151 * @return {Array<HTMLElement>}
152 * @private
153 */
154 _sortByTabIndex: function(tabbables) {
155 // Implement a merge sort as Array.prototype.sort does a non-stable sort
156 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Glo bal_Objects/Array/sort
157 var len = tabbables.length;
158 if (len < 2) {
159 return tabbables;
160 }
161 var pivot = Math.ceil(len / 2);
162 var left = this._sortByTabIndex(tabbables.slice(0, pivot));
163 var right = this._sortByTabIndex(tabbables.slice(pivot));
164 return this._mergeSortByTabIndex(left, right);
165 },
166
167 /**
168 * Merge sort iterator, merges the two arrays into one, sorted by tab inde x.
169 * @param {!Array<HTMLElement>} left
170 * @param {!Array<HTMLElement>} right
171 * @return {Array<HTMLElement>}
172 * @private
173 */
174 _mergeSortByTabIndex: function(left, right) {
175 var result = [];
176 while ((left.length > 0) && (right.length > 0)) {
177 if (this._hasLowerTabOrder(left[0], right[0])) {
178 result.push(right.shift());
179 } else {
180 result.push(left.shift());
181 }
182 }
183
184 return result.concat(left, right);
185 },
186
187 /**
188 * Returns if element `a` has lower tab order compared to element `b`
189 * (both elements are assumed to be focusable and tabbable).
190 * Elements with tabindex = 0 have lower tab order compared to elements
191 * with tabindex > 0.
192 * If both have same tabindex, it returns false.
193 * @param {!HTMLElement} a
194 * @param {!HTMLElement} b
195 * @return {boolean}
196 * @private
197 */
198 _hasLowerTabOrder: function(a, b) {
199 // Normalize tabIndexes
200 // e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
201 var ati = Math.max(a.tabIndex, 0);
202 var bti = Math.max(b.tabIndex, 0);
203 return (ati === 0 || bti === 0) ? bti > ati : ati > bti;
204 }
205 };
206 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698