aria-api 0.2.7 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/aria.js CHANGED
@@ -1,6 +1,7 @@
1
1
  (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.aria = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
2
  var query = require('./lib/query.js');
3
3
  var name = require('./lib/name.js');
4
+ var atree = require('./lib/atree.js');
4
5
 
5
6
  module.exports = {
6
7
  getRole: query.getRole,
@@ -12,70 +13,275 @@ module.exports = {
12
13
  querySelector: query.querySelector,
13
14
  querySelectorAll: query.querySelectorAll,
14
15
  closest: query.closest,
16
+
17
+ getParentNode: atree.getParentNode,
18
+ getChildNodes: atree.getChildNodes,
19
+ };
20
+
21
+ },{"./lib/atree.js":2,"./lib/name.js":5,"./lib/query.js":6}],2:[function(require,module,exports){
22
+ var attrs = require('./attrs');
23
+
24
+ var _getOwner = function(node) {
25
+ if (node.nodeType === node.ELEMENT_NODE && node.id) {
26
+ var owner = document.querySelector('[aria-owns~="' + CSS.escape(node.id) + '"]');
27
+ if (owner) {
28
+ return owner;
29
+ }
30
+ }
31
+ };
32
+
33
+ var _getParentNode = function(node) {
34
+ return _getOwner(node) || node.parentNode;
35
+ };
36
+
37
+ var detectLoop = function(node) {
38
+ var tmp = _getParentNode(node);
39
+ while (tmp) {
40
+ if (tmp === node) {
41
+ return true;
42
+ }
43
+ tmp = _getParentNode(tmp);
44
+ }
45
+ };
46
+
47
+ var getOwner = function(node) {
48
+ if (node.nodeType === node.ELEMENT_NODE && node.id) {
49
+ var owner = document.querySelector('[aria-owns~="' + CSS.escape(node.id) + '"]');
50
+ if (owner && !detectLoop(node)) {
51
+ return owner;
52
+ }
53
+ }
54
+ };
55
+
56
+ var getParentNode = function(node) {
57
+ return getOwner(node) || node.parentNode;
58
+ };
59
+
60
+ var isHidden = function(node) {
61
+ return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
62
+ };
63
+
64
+ var getChildNodes = function(node) {
65
+ var childNodes = [];
66
+
67
+ for (var i = 0; i < node.childNodes.length; i++) {
68
+ var child = node.childNodes[i];
69
+ if (!getOwner(child) && !isHidden(child)) {
70
+ childNodes.push(child);
71
+ }
72
+ }
73
+
74
+ if (node.nodeType === node.ELEMENT_NODE) {
75
+ var owns = attrs.getAttribute(node, 'owns') || [];
76
+ for (var i = 0; i < owns.length; i++) {
77
+ var child = document.getElementById(owns[i]);
78
+ // double check with getOwner for consistency
79
+ if (child && getOwner(child) === node && !isHidden(child)) {
80
+ childNodes.push(child);
81
+ }
82
+ }
83
+ }
84
+
85
+ return childNodes;
86
+ };
87
+
88
+ var walk = function(root, fn) {
89
+ fn(root);
90
+ getChildNodes(root).forEach(function(child) {
91
+ walk(child, fn);
92
+ });
93
+ };
94
+
95
+ var searchUp = function(node, test) {
96
+ var candidate = getParentNode(node);
97
+ if (candidate) {
98
+ if (test(candidate)) {
99
+ return candidate;
100
+ } else {
101
+ return searchUp(candidate, test);
102
+ }
103
+ }
104
+ };
105
+
106
+ module.exports = {
107
+ 'getParentNode': getParentNode,
108
+ 'getChildNodes': getChildNodes,
109
+ 'walk': walk,
110
+ 'searchUp': searchUp,
111
+ };
112
+
113
+ },{"./attrs":3}],3:[function(require,module,exports){
114
+ var constants = require('./constants.js');
115
+
116
+ // candidates can be passed for performance optimization
117
+ var getRole = function(el, candidates) {
118
+ if (el.hasAttribute('role')) {
119
+ return el.getAttribute('role');
120
+ }
121
+ for (var role in constants.roles) {
122
+ var selector = (constants.roles[role].selectors || []).join(',');
123
+ if (selector && (!candidates || candidates.includes(role)) && el.matches(selector)) {
124
+ return role;
125
+ }
126
+ }
127
+
128
+ if (!candidates ||
129
+ candidates.includes('banner') ||
130
+ candidates.includes('contentinfo')) {
131
+ if (!el.matches(constants.scoped)) {
132
+ if (el.matches('header')) {
133
+ return 'banner';
134
+ }
135
+ if (el.matches('footer')) {
136
+ return 'contentinfo';
137
+ }
138
+ }
139
+ }
140
+ };
141
+
142
+ var hasRole = function(el, roles) {
143
+ var candidates = [].concat.apply([], roles.map(function(role) {
144
+ return (constants.roles[role] || {}).subRoles || [role];
145
+ }));
146
+ var actual = getRole(el, candidates);
147
+ return candidates.includes(actual);
148
+ };
149
+
150
+ var getAttribute = function(el, key) {
151
+ if (constants.attributeStrongMapping.hasOwnProperty(key)) {
152
+ var value = el[constants.attributeStrongMapping[key]];
153
+ if (value) {
154
+ return value;
155
+ }
156
+ }
157
+ if (key === 'readonly' && el.contentEditable) {
158
+ return false;
159
+ } else if (key === 'invalid' && el.checkValidity) {
160
+ return !el.checkValidity();
161
+ } else if (key === 'hidden') {
162
+ // workaround for chromium
163
+ if (el.matches('noscript')) {
164
+ return true;
165
+ }
166
+ var style = window.getComputedStyle(el);
167
+ if (style.display === 'none' || style.visibility === 'hidden') {
168
+ return true;
169
+ }
170
+ }
171
+
172
+ var type = constants.attributes[key];
173
+ var raw = el.getAttribute('aria-' + key);
174
+
175
+ if (raw) {
176
+ if (type === 'bool') {
177
+ return raw === 'true';
178
+ } else if (type === 'tristate') {
179
+ return raw === 'true' ? true : raw === 'false' ? false : 'mixed';
180
+ } else if (type === 'bool-undefined') {
181
+ return raw === 'true' ? true : raw === 'false' ? false : undefined;
182
+ } else if (type === 'id-list') {
183
+ return raw.split(/\s+/);
184
+ } else if (type === 'integer') {
185
+ return parseInt(raw, 10);
186
+ } else if (type === 'number') {
187
+ return parseFloat(raw);
188
+ } else if (type === 'token-list') {
189
+ return raw.split(/\s+/);
190
+ } else {
191
+ return raw;
192
+ }
193
+ }
194
+
195
+ // TODO
196
+ // autocomplete
197
+ // contextmenu -> aria-haspopup
198
+ // indeterminate -> aria-checked="mixed"
199
+ // list -> aria-controls
200
+
201
+ if (key === 'level') {
202
+ for (var i = 1; i <= 6; i++) {
203
+ if (el.tagName.toLowerCase() === 'h' + i) {
204
+ return i;
205
+ }
206
+ }
207
+ } else if (constants.attributeWeakMapping.hasOwnProperty(key)) {
208
+ return el[constants.attributeWeakMapping[key]];
209
+ }
210
+
211
+ if (key in constants.attrsWithDefaults) {
212
+ var role = getRole(el);
213
+ var defaults = (constants.roles[role] || {}).defaults;
214
+ if (defaults && defaults.hasOwnProperty(key)) {
215
+ return defaults[key];
216
+ }
217
+ }
218
+
219
+ if (type === 'bool' || type === 'tristate') {
220
+ return false;
221
+ }
222
+ };
223
+
224
+ module.exports = {
225
+ getRole: getRole,
226
+ hasRole: hasRole,
227
+ getAttribute: getAttribute,
15
228
  };
16
229
 
17
- },{"./lib/name.js":3,"./lib/query.js":4}],2:[function(require,module,exports){
230
+ },{"./constants.js":4}],4:[function(require,module,exports){
231
+ // https://www.w3.org/TR/wai-aria/#state_prop_def
18
232
  exports.attributes = {
19
- // widget
233
+ 'activedescendant': 'id',
234
+ 'atomic': 'bool',
20
235
  'autocomplete': 'token',
236
+ 'busy': 'bool',
21
237
  'checked': 'tristate',
238
+ 'colcount': 'int',
239
+ 'colindex': 'int',
240
+ 'colspan': 'int',
241
+ 'controls': 'id-list',
22
242
  'current': 'token',
243
+ 'describedby': 'id-list',
244
+ 'details': 'id',
23
245
  'disabled': 'bool',
246
+ 'dropeffect': 'token-list',
247
+ 'errormessage': 'id',
24
248
  'expanded': 'bool-undefined',
249
+ 'flowto': 'id-list',
250
+ 'grabbed': 'bool-undefined',
25
251
  'haspopup': 'token',
26
- 'hidden': 'bool', // !
252
+ 'hidden': 'bool-undefined',
27
253
  'invalid': 'token',
28
254
  'keyshortcuts': 'string',
29
255
  'label': 'string',
256
+ 'labelledby': 'id-list',
30
257
  'level': 'int',
258
+ 'live': 'token',
31
259
  'modal': 'bool',
32
260
  'multiline': 'bool',
33
261
  'multiselectable': 'bool',
34
262
  'orientation': 'token',
263
+ 'owns': 'id-list',
35
264
  'placeholder': 'string',
265
+ 'posinset': 'int',
36
266
  'pressed': 'tristate',
37
267
  'readonly': 'bool',
268
+ 'relevant': 'token-list',
38
269
  'required': 'bool',
39
270
  'roledescription': 'string',
40
- 'selected': 'bool-undefined',
41
- 'valuemax': 'number',
42
- 'valuemin': 'number',
43
- 'valuenow': 'number',
44
- 'valuetext': 'string',
45
-
46
- // live
47
- 'atomic': 'bool',
48
- 'busy': 'bool',
49
- 'live': 'token',
50
- 'relevant': 'token-list',
51
-
52
- // dragndrop
53
- 'dropeffect': 'token-list',
54
- 'grabbed': 'bool-undefined',
55
-
56
- // relationship
57
- 'activedescendant': 'id',
58
- 'colcount': 'int',
59
- 'colindex': 'int',
60
- 'colspan': 'int',
61
- 'controls': 'id-list',
62
- 'describedby': 'id-list',
63
- 'details': 'id',
64
- 'errormessage': 'id',
65
- 'flowto': 'id-list',
66
- 'labelledby': 'id-list',
67
- 'owns': 'id-list',
68
- 'posinset': 'int',
69
271
  'rowcount': 'int',
70
272
  'rowindex': 'int',
71
273
  'rowspan': 'int',
274
+ 'selected': 'bool-undefined',
72
275
  'setsize': 'int',
73
276
  'sort': 'token',
277
+ 'valuemax': 'number',
278
+ 'valuemin': 'number',
279
+ 'valuenow': 'number',
280
+ 'valuetext': 'string',
74
281
  };
75
282
 
76
283
  exports.attributeStrongMapping = {
77
284
  'disabled': 'disabled',
78
- 'hidden': 'hidden',
79
285
  'placeholder': 'placeholder',
80
286
  'readonly': 'readOnly',
81
287
  'required': 'required',
@@ -90,209 +296,506 @@ exports.attributeWeakMapping = {
90
296
  'selected': 'selected',
91
297
  };
92
298
 
93
- // https://www.w3.org/TR/html-aria/#docconformance
94
- exports.extraSelectors = {
95
- article: ['article'],
96
- button: [
97
- 'button',
98
- 'input[type="button"]',
99
- 'input[type="image"]',
100
- 'input[type="reset"]',
101
- 'input[type="submit"]',
102
- 'summary',
103
- ],
104
- cell: ['td'],
105
- checkbox: ['input[type="checkbox"]'],
106
- combobox: [
299
+ // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
300
+ // https://www.w3.org/TR/wai-aria/roles
301
+ exports.roles = {
302
+ alert: {
303
+ childRoles: ['alertdialog'],
304
+ defaults: {
305
+ 'live': 'assertive',
306
+ 'atomic': true,
307
+ },
308
+ },
309
+ article: {
310
+ selectors: ['article'],
311
+ },
312
+ blockquote: {
313
+ selectors: ['blockquote'],
314
+ },
315
+ button: {
316
+ selectors: [
317
+ 'button',
318
+ 'input[type="button"]',
319
+ 'input[type="image"]',
320
+ 'input[type="reset"]',
321
+ 'input[type="submit"]',
322
+ 'summary',
323
+ ],
324
+ nameFromContents: true,
325
+ },
326
+ caption: {
327
+ selectors: ['caption'], // not sure about figcaption, ARIA and HTML-AAM differ
328
+ },
329
+ cell: {
330
+ selectors: ['td'],
331
+ childRoles: ['gridcell', 'rowheader'],
332
+ nameFromContents: true,
333
+ },
334
+ checkbox: {
335
+ selectors: ['input[type="checkbox"]'],
336
+ childRoles: ['switch'],
337
+ nameFromContents: true,
338
+ },
339
+ code: {
340
+ selectors: ['code'],
341
+ },
342
+ columnheader: {
343
+ selectors: ['th[scope="col"]'],
344
+ nameFromContents: true,
345
+ },
346
+ combobox: {
347
+ selectors: [
107
348
  'input:not([type])[list]',
108
- 'input[type="email"][list]',
109
- 'input[type="search"][list]',
110
- 'input[type="tel"][list]',
111
- 'input[type="text"][list]',
112
- 'input[type="url"][list]',
113
- 'select:not([size]):not([multiple])',
114
- 'select[size="0"]:not([multiple])',
115
- 'select[size="1"]:not([multiple])',
116
- ],
117
- complementary: ['aside'],
118
- definition: ['dd'],
119
- dialog: ['dialog'],
120
- document: ['body'],
121
- figure: ['figure'],
122
- form: ['form[aria-label]', 'form[aria-labelledby]'],
123
- group: ['details', 'optgroup'],
124
- heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
125
- img: ['img:not([alt=""])', 'graphics-symbol'],
126
- link: ['a[href]', 'area[href]', 'link[href]'],
127
- list: ['dl', 'ol', 'ul'],
128
- listbox: [
129
- 'select[multiple]',
130
- 'select[size]:not([size="0"]):not([size="1"])',
131
- ],
132
- listitem: ['dt', 'ul > li', 'ol > li'],
133
- main: ['main'],
134
- math: ['math'],
135
- menuitemcheckbox: ['menuitem[type="checkbox"]'],
136
- menuitem: ['menuitem[type="command"]'],
137
- menuitemradio: ['menuitem[type="radio"]'],
138
- menu: ['menu[type="context"]'],
139
- navigation: ['nav'],
140
- option: ['option'],
141
- progressbar: ['progress'],
142
- radio: ['input[type="radio"]'],
143
- region: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
144
- rowgroup: ['tbody', 'thead', 'tfoot'],
145
- row: ['tr'],
146
- searchbox: ['input[type="search"]:not([list])'],
147
- separator: ['hr'],
148
- slider: ['input[type="range"]'],
149
- spinbutton: ['input[type="number"]'],
150
- status: ['output'],
151
- table: ['table'],
152
- term: ['dfn', 'dt'],
153
- textbox: [
154
- 'input:not([type]):not([list])',
155
- 'input[type="email"]:not([list])',
156
- 'input[type="tel"]:not([list])',
157
- 'input[type="text"]:not([list])',
158
- 'input[type="url"]:not([list])',
159
- 'textarea',
160
- ],
161
-
162
- // if scope is missing, it is calculated automatically
163
- rowheader: ['th[scope="row"]'],
164
- columnheader: ['th[scope="col"]'],
349
+ 'input[type="email"][list]',
350
+ 'input[type="search"][list]',
351
+ 'input[type="tel"][list]',
352
+ 'input[type="text"][list]',
353
+ 'input[type="url"][list]',
354
+ 'select:not([size]):not([multiple])',
355
+ 'select[size="0"]:not([multiple])',
356
+ 'select[size="1"]:not([multiple])',
357
+ ],
358
+ defaults: {
359
+ 'haspopup': 'listbox',
360
+ },
361
+ },
362
+ command: {
363
+ childRoles: ['button', 'link', 'menuitem'],
364
+ },
365
+ complementary: {
366
+ selectors: ['aside'],
367
+ },
368
+ composite: {
369
+ childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
370
+ },
371
+ definition: {
372
+ selectors: ['dd'],
373
+ },
374
+ deletion: {
375
+ selectors: ['del'],
376
+ },
377
+ dialog: {
378
+ selectors: ['dialog'],
379
+ childRoles: ['alertdialog'],
380
+ },
381
+ 'doc-backlink': {
382
+ nameFromContents: true,
383
+ },
384
+ 'doc-biblioref': {
385
+ nameFromContents: true,
386
+ },
387
+ 'doc-glossref': {
388
+ nameFromContents: true,
389
+ },
390
+ 'doc-noteref': {
391
+ nameFromContents: true,
392
+ },
393
+ 'doc-pagebreak': {
394
+ nameFromContents: true,
395
+ },
396
+ 'doc-subtitle': {
397
+ nameFromContents: true,
398
+ },
399
+ document: {
400
+ selector: ['html'],
401
+ childRoles: ['article', 'graphics-document'],
402
+ },
403
+ emphasis: {
404
+ selectors: ['em'],
405
+ },
406
+ figure: {
407
+ selectors: ['figure'],
408
+ childRoles: ['doc-example'],
409
+ },
410
+ form: {
411
+ selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
412
+ },
413
+ generic: {
414
+ selectors: [
415
+ 'body',
416
+ 'div',
417
+ 'form:not([aria-label]):not([aria-labelledby]):not([title])',
418
+ 'hgroup',
419
+ 'span',
420
+ ],
421
+ },
422
+ grid: {
423
+ childRoles: ['treegrid'],
424
+ },
425
+ gridcell: {
426
+ childRoles: ['columnheader', 'rowheader'],
427
+ nameFromContents: true,
428
+ },
429
+ group: {
430
+ selectors: ['details', 'fieldset', 'optgroup'],
431
+ childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
432
+ },
433
+ heading: {
434
+ selectors: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
435
+ nameFromContents: true,
436
+ },
437
+ img: {
438
+ selectors: ['img:not([alt=""])', 'graphics-symbol'],
439
+ childRoles: ['doc-cover'],
440
+ },
441
+ input: {
442
+ childRoles: ['checkbox', 'combobox', 'option', 'radio', 'slider', 'spinbutton', 'textbox'],
443
+ },
444
+ insertion: {
445
+ selectors: ['ins'],
446
+ },
447
+ landmark: {
448
+ childRoles: [
449
+ 'banner',
450
+ 'complementary',
451
+ 'contentinfo',
452
+ 'doc-acknowledgments',
453
+ 'doc-afterword',
454
+ 'doc-appendix',
455
+ 'doc-bibliography',
456
+ 'doc-chapter',
457
+ 'doc-conclusion',
458
+ 'doc-credits',
459
+ 'doc-endnotes',
460
+ 'doc-epilogue',
461
+ 'doc-errata',
462
+ 'doc-foreword',
463
+ 'doc-glossary',
464
+ 'doc-introduction',
465
+ 'doc-part',
466
+ 'doc-preface',
467
+ 'doc-prologue',
468
+ 'form',
469
+ 'main',
470
+ 'navigation',
471
+ 'region',
472
+ 'search',
473
+ ],
474
+ },
475
+ link: {
476
+ selectors: ['a[href]', 'area[href]', 'link[href]'],
477
+ childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
478
+ nameFromContents: true,
479
+ },
480
+ list: {
481
+ selectors: ['ol', 'ul', 'menu'],
482
+ childRoles: ['directory', 'feed'],
483
+ },
484
+ listbox: {
485
+ selectors: [
486
+ 'datalist',
487
+ 'select[multiple]',
488
+ 'select[size]:not([size="0"]):not([size="1"])',
489
+ ],
490
+ defaults: {
491
+ 'orientation': 'vertical',
492
+ },
493
+ },
494
+ listitem: {
495
+ selectors: ['ul > li', 'ol > li', 'menu > li'],
496
+ childRoles: ['treeitem'],
497
+ },
498
+ log: {
499
+ defaults: {
500
+ 'live': 'polite',
501
+ },
502
+ },
503
+ main: {
504
+ selectors: ['main'],
505
+ },
506
+ math: {
507
+ selectors: ['math'],
508
+ },
509
+ meter: {
510
+ selectors: ['meter'],
511
+ defaults: {
512
+ 'valuemin': 0,
513
+ 'valuemax': 100,
514
+ },
515
+ },
516
+ menu: {
517
+ childRoles: ['menubar'],
518
+ defaults: {
519
+ 'orientation': 'vertical',
520
+ },
521
+ },
522
+ menubar: {
523
+ defaults: {
524
+ 'orientation': 'horizontal',
525
+ },
526
+ },
527
+ menuitem: {
528
+ selectors: ['menuitem[type="command"]'],
529
+ childRoles: ['menuitemcheckbox'],
530
+ nameFromContents: true,
531
+ },
532
+ menuitemcheckbox: {
533
+ selectors: ['menuitem[type="checkbox"]'],
534
+ childRoles: ['menuitemradio'],
535
+ nameFromContents: true,
536
+ },
537
+ menuitemradio: {
538
+ selectors: ['menuitem[type="radio"]'],
539
+ nameFromContents: true,
540
+ },
541
+ navigation: {
542
+ selectors: ['nav'],
543
+ childRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
544
+ },
545
+ note: {
546
+ childRoles: ['doc-notice', 'doc-tip'],
547
+ },
548
+ option: {
549
+ selectors: ['option'],
550
+ childRoles: ['treeitem'],
551
+ nameFromContents: true,
552
+ defaults: {
553
+ 'selected': 'false',
554
+ },
555
+ },
556
+ paragraph: {
557
+ selectors: ['p'],
558
+ },
559
+ progressbar: {
560
+ selectors: ['progress'],
561
+ defaults: {
562
+ 'valuemin': 0,
563
+ 'valuemax': 100,
564
+ },
565
+ },
566
+ radio: {
567
+ selectors: ['input[type="radio"]'],
568
+ nameFromContents: true,
569
+ },
570
+ range: {
571
+ childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
572
+ },
573
+ region: {
574
+ selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
575
+ },
576
+ roletype: {
577
+ childRoles: ['structure', 'widget', 'window'],
578
+ },
579
+ row: {
580
+ selectors: ['tr'],
581
+ nameFromContents: true,
582
+ },
583
+ rowgroup: {
584
+ selectors: ['tbody', 'thead', 'tfoot'],
585
+ },
586
+ rowheader: {
587
+ selectors: ['th[scope="row"]'],
588
+ nameFromContents: true,
589
+ },
590
+ scrollbar: {
591
+ defaults: {
592
+ 'orientation': 'vertical',
593
+ 'valuemin': 0,
594
+ 'valuemax': 100,
595
+ // FIXME: halfway between actual valuemin and valuemax
596
+ 'valuenow': 50,
597
+ },
598
+ },
599
+ searchbox: {
600
+ selectors: ['input[type="search"]:not([list])'],
601
+ },
602
+ section: {
603
+ childRoles: [
604
+ 'alert',
605
+ 'blockquote',
606
+ 'caption',
607
+ 'cell',
608
+ 'code',
609
+ 'definition',
610
+ 'deletion',
611
+ 'doc-abstract',
612
+ 'doc-colophon',
613
+ 'doc-credit',
614
+ 'doc-dedication',
615
+ 'doc-epigraph',
616
+ 'doc-footnote',
617
+ 'doc-pagefooter',
618
+ 'doc-pageheader',
619
+ 'doc-pullquote',
620
+ 'doc-qna',
621
+ 'emphasis',
622
+ 'figure',
623
+ 'group',
624
+ 'img',
625
+ 'insertion',
626
+ 'landmark',
627
+ 'list',
628
+ 'listitem',
629
+ 'log',
630
+ 'marquee',
631
+ 'math',
632
+ 'note',
633
+ 'paragraph',
634
+ 'status',
635
+ 'strong',
636
+ 'subscript',
637
+ 'superscript',
638
+ 'table',
639
+ 'tabpanel',
640
+ 'term',
641
+ 'time',
642
+ 'tooltip',
643
+ ],
644
+ },
645
+ sectionhead: {
646
+ childRoles: [
647
+ 'columnheader',
648
+ 'doc-subtitle',
649
+ 'heading',
650
+ 'rowheader',
651
+ 'tab',
652
+ ],
653
+ nameFromContents: true,
654
+ },
655
+ select: {
656
+ childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
657
+ },
658
+ separator: {
659
+ // assume not focussable because <hr> is not
660
+ selectors: ['hr'],
661
+ childRoles: ['doc-pagebreak'],
662
+ defaults: {
663
+ 'orientation': 'horizontal',
664
+ },
665
+ },
666
+ slider: {
667
+ selectors: ['input[type="range"]'],
668
+ defaults: {
669
+ 'orientation': 'horizontal',
670
+ 'valuemin': 0,
671
+ 'valuemax': 100,
672
+ // FIXME: halfway between actual valuemin and valuemax
673
+ 'valuenow': 50,
674
+ },
675
+ },
676
+ spinbutton: {
677
+ selectors: ['input[type="number"]'],
678
+ defaults: {
679
+ // FIXME: no valuemin/valuemax/valuenow
680
+ },
681
+ },
682
+ status: {
683
+ selectors: ['output'],
684
+ childRoles: ['timer'],
685
+ defaults: {
686
+ 'live': 'polite',
687
+ 'atomic': true,
688
+ },
689
+ },
690
+ strong: {
691
+ selectors: ['strong'],
692
+ },
693
+ structure: {
694
+ childRoles: [
695
+ 'application',
696
+ 'document',
697
+ 'none',
698
+ 'generic',
699
+ 'presentation',
700
+ 'range',
701
+ 'rowgroup',
702
+ 'section',
703
+ 'sectionhead',
704
+ 'separator',
705
+ ],
706
+ },
707
+ subscript: {
708
+ selectors: ['sub'],
709
+ },
710
+ superscript: {
711
+ selectors: ['sup'],
712
+ },
713
+ switch: {
714
+ nameFromContents: true,
715
+ },
716
+ tab: {
717
+ nameFromContents: true,
718
+ defaults: {
719
+ 'selected': false,
720
+ },
721
+ },
722
+ table: {
723
+ selectors: ['table'],
724
+ childRoles: ['grid'],
725
+ },
726
+ tablist: {
727
+ defaults: {
728
+ 'orientation': 'horizontal',
729
+ },
730
+ },
731
+ term: {
732
+ selectors: ['dfn', 'dt'],
733
+ },
734
+ textbox: {
735
+ selectors: [
736
+ 'input:not([type]):not([list])',
737
+ 'input[type="email"]:not([list])',
738
+ 'input[type="tel"]:not([list])',
739
+ 'input[type="text"]:not([list])',
740
+ 'input[type="url"]:not([list])',
741
+ 'textarea',
742
+ ],
743
+ childRoles: ['searchbox'],
744
+ },
745
+ time: {
746
+ selectors: ['time'],
747
+ },
748
+ toolbar: {
749
+ defaults: {
750
+ 'orientation': 'horizontal',
751
+ },
752
+ },
753
+ tooltip: {
754
+ nameFromContents: true,
755
+ },
756
+ tree: {
757
+ childRoles: ['treegrid'],
758
+ defaults: {
759
+ 'orientation': 'vertical',
760
+ },
761
+ },
762
+ treeitem: {
763
+ nameFromContents: true,
764
+ },
765
+ widget: {
766
+ childRoles: [
767
+ 'command',
768
+ 'composite',
769
+ 'gridcell',
770
+ 'input',
771
+ 'progressbar',
772
+ 'row',
773
+ 'scrollbar',
774
+ 'tab',
775
+ ],
776
+ },
777
+ window: {
778
+ childRoles: ['dialog'],
779
+ },
165
780
  };
166
781
 
167
782
  exports.scoped = [
168
- 'article *', 'aside *', 'main *', 'nav *', 'section *',
783
+ 'main *',
784
+ // https://www.w3.org/TR/html/dom.html#sectioning-content-2
785
+ 'article *', 'aside *', 'nav *', 'section *',
786
+ // https://www.w3.org/TR/html/sections.html#sectioning-roots
787
+ 'blockquote *', 'details *', 'dialog *', 'fieldset *', 'figure *', 'td *',
169
788
  ].join(',');
170
789
 
171
- // https://www.w3.org/TR/wai-aria/roles
172
- var subRoles = {
173
- cell: ['gridcell', 'rowheader'],
174
- command: ['button', 'link', 'menuitem'],
175
- composite: ['grid', 'select', 'spinbutton', 'tablist'],
176
- img: ['doc-cover'],
177
- input: ['checkbox', 'option', 'radio', 'slider', 'spinbutton', 'textbox'],
178
- landmark: [
179
- 'banner',
180
- 'complementary',
181
- 'contentinfo',
182
- 'doc-acknowledgments',
183
- 'doc-afterword',
184
- 'doc-appendix',
185
- 'doc-bibliography',
186
- 'doc-chapter',
187
- 'doc-conclusion',
188
- 'doc-credits',
189
- 'doc-endnotes',
190
- 'doc-epilogue',
191
- 'doc-errata',
192
- 'doc-foreword',
193
- 'doc-glossary',
194
- 'doc-introduction',
195
- 'doc-part',
196
- 'doc-preface',
197
- 'doc-prologue',
198
- 'form',
199
- 'main',
200
- 'navigation',
201
- 'region',
202
- 'search',
203
- ],
204
- range: ['progressbar', 'scrollbar', 'slider', 'spinbutton'],
205
- roletype: ['structure', 'widget', 'window'],
206
- section: [
207
- 'alert',
208
- 'cell',
209
- 'definition',
210
- 'doc-abstract',
211
- 'doc-colophon',
212
- 'doc-credit',
213
- 'doc-dedication',
214
- 'doc-epigraph',
215
- 'doc-example',
216
- 'doc-footnote',
217
- 'doc-qna',
218
- 'figure',
219
- 'group',
220
- 'img',
221
- 'landmark',
222
- 'list',
223
- 'listitem',
224
- 'log',
225
- 'marquee',
226
- 'math',
227
- 'note',
228
- 'status',
229
- 'table',
230
- 'tabpanel',
231
- 'term',
232
- 'tooltip',
233
- ],
234
- sectionhead: [
235
- 'columnheader',
236
- 'doc-subtitle',
237
- 'heading',
238
- 'rowheader',
239
- 'tab',
240
- ],
241
- select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'],
242
- separator: ['doc-pagebreak'],
243
- structure: [
244
- 'application',
245
- 'document',
246
- 'none',
247
- 'presentation',
248
- 'rowgroup',
249
- 'section',
250
- 'sectionhead',
251
- 'separator',
252
- ],
253
- table: ['grid'],
254
- textbox: ['searchbox'],
255
- widget: [
256
- 'command',
257
- 'composite',
258
- 'gridcell',
259
- 'input',
260
- 'range',
261
- 'row',
262
- 'separator',
263
- 'tab',
264
- ],
265
- window: ['dialog'],
266
- alert: ['alertdialog'],
267
- checkbox: ['menuitemcheckbox', 'switch'],
268
- dialog: ['alertdialog'],
269
- gridcell: ['columnheader', 'rowheader'],
270
- menuitem: ['menuitemcheckbox'],
271
- menuitemcheckbox: ['menuitemradio'],
272
- option: ['treeitem'],
273
- radio: ['menuitemradio'],
274
- status: ['timer'],
275
- grid: ['treegrid'],
276
- menu: ['menubar'],
277
- tree: ['treegrid'],
278
- document: ['article', 'graphics-document'],
279
- group: ['row', 'select', 'toolbar', 'graphics-object'],
280
- link: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
281
- list: ['directory', 'feed'],
282
- listitem: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
283
- navigation: ['doc-index', 'doc-pagelist', 'doc-toc'],
284
- note: ['doc-notice', 'doc-tip'],
285
- };
286
-
287
790
  var getSubRoles = function(role) {
288
- var children = subRoles[role] || [];
791
+ var children = (exports.roles[role] || {}).childRoles || [];
289
792
  var descendents = children.map(getSubRoles);
290
793
 
291
794
  var result = [role];
292
795
 
293
796
  descendents.forEach(function(list) {
294
797
  list.forEach(function(r) {
295
- if (result.indexOf(r) === -1) {
798
+ if (!result.includes(r)) {
296
799
  result.push(r);
297
800
  }
298
801
  });
@@ -301,38 +804,20 @@ var getSubRoles = function(role) {
301
804
  return result;
302
805
  };
303
806
 
304
- exports.subRoles = {};
305
- for (var role in subRoles) {
306
- exports.subRoles[role] = getSubRoles(role);
307
- }
308
- exports.subRoles['none'] = ['none', 'presentation'];
309
- exports.subRoles['presentation'] = ['presentation', 'none'];
807
+ exports.attrsWithDefaults = [];
310
808
 
311
- exports.nameFromContents = [
312
- 'button',
313
- 'checkbox',
314
- 'columnheader',
315
- 'doc-backlink',
316
- 'doc-biblioref',
317
- 'doc-glossref',
318
- 'doc-noteref',
319
- 'gridcell',
320
- 'heading',
321
- 'link',
322
- 'menuitem',
323
- 'menuitemcheckbox',
324
- 'menuitemradio',
325
- 'option',
326
- 'radio',
327
- 'row',
328
- 'rowgroup',
329
- 'rowheader',
330
- 'sectionhead',
331
- 'tab',
332
- 'tooltip',
333
- 'treeitem',
334
- 'switch',
335
- ];
809
+ for (var role in exports.roles) {
810
+ exports.roles[role].subRoles = getSubRoles(role);
811
+ for (var key in exports.roles[role].defaults) {
812
+ if (!exports.attrsWithDefaults.includes(key)) {
813
+ exports.attrsWithDefaults.push(key);
814
+ }
815
+ }
816
+ }
817
+ exports.roles['none'] = exports.roles['none'] || {};
818
+ exports.roles['none'].subRoles = ['none', 'presentation'];
819
+ exports.roles['presentation'] = exports.roles['presentation'] || {};
820
+ exports.roles['presentation'].subRoles = ['presentation', 'none'];
336
821
 
337
822
  exports.nameFromDescendant = {
338
823
  'figure': 'figcaption',
@@ -357,10 +842,10 @@ exports.labelable = [
357
842
  'textarea',
358
843
  ];
359
844
 
360
- },{}],3:[function(require,module,exports){
845
+ },{}],5:[function(require,module,exports){
361
846
  var constants = require('./constants.js');
847
+ var atree = require('./atree.js');
362
848
  var query = require('./query.js');
363
- var util = require('./util.js');
364
849
 
365
850
  var getPseudoContent = function(node, selector) {
366
851
  var styles = window.getComputedStyle(node, selector);
@@ -380,24 +865,8 @@ var getPseudoContent = function(node, selector) {
380
865
  }
381
866
  };
382
867
 
383
- var getContent = function(root, referenced, owned) {
384
- var children = [];
385
-
386
- for (var i = 0; i < root.childNodes.length; i++) {
387
- var node = root.childNodes[i];
388
- if (!node.id || !document.querySelector('[aria-owns~="' + node.id + '"]')) {
389
- children.push(node);
390
- }
391
- }
392
-
393
- var owns = query.getAttribute(root, 'owns') || [];
394
- for (var i = 0; i < owns.length; i++) {
395
- var child = document.getElementById(owns[i]);
396
- if (child && child !== root && owned.indexOf(child.id) === -1) {
397
- children.push(child);
398
- owned.push(child.id);
399
- }
400
- }
868
+ var getContent = function(root, visited) {
869
+ var children = atree.getChildNodes(root);
401
870
 
402
871
  var ret = '';
403
872
  for (var i = 0; i < children.length; i++) {
@@ -410,9 +879,9 @@ var getContent = function(root, referenced, owned) {
410
879
  } else if (window.getComputedStyle(node).display.substr(0, 6) === 'inline' &&
411
880
  node.tagName.toLowerCase() !== 'input' &&
412
881
  node.tagName.toLowerCase() !== 'img') { // https://github.com/w3c/accname/issues/3
413
- ret += getName(node, true, referenced, owned);
882
+ ret += getName(node, true, visited);
414
883
  } else {
415
- ret += ' ' + getName(node, true, referenced, owned) + ' ';
884
+ ret += ' ' + getName(node, true, visited) + ' ';
416
885
  }
417
886
  }
418
887
  }
@@ -422,7 +891,7 @@ var getContent = function(root, referenced, owned) {
422
891
 
423
892
  var allowNameFromContent = function(el) {
424
893
  var role = query.getRole(el);
425
- return role && constants.nameFromContents.indexOf(role) !== -1;
894
+ return (constants.roles[role] || {}).nameFromContents;
426
895
  };
427
896
 
428
897
  var isLabelable = function(el) {
@@ -430,58 +899,46 @@ var isLabelable = function(el) {
430
899
  return el.matches(selector);
431
900
  };
432
901
 
433
- // Control.labels is part of the standard, but not supported in most browsers
434
- var getLabelNodes = function(element) {
435
- var labels = [];
436
- var labelable = constants.labelable.join(',');
437
- util.walkDOM(document.body, function(node) {
438
- if (node.tagName && node.tagName.toLowerCase() === 'label') {
439
- if (node.getAttribute('for')) {
440
- if (element.id && node.getAttribute('for') === element.id) {
441
- labels.push(node);
442
- }
443
- } else if (node.querySelector(labelable) === element) {
444
- labels.push(node);
445
- }
446
- }
447
- });
448
- return labels;
449
- };
450
-
451
902
  var isInLabelForOtherWidget = function(el) {
452
903
  var label = el.parentElement.closest('label');
453
- var ownLabels = getLabelNodes(el);
454
- return label && ownLabels.indexOf(label) === -1;
904
+ return label && !Array.prototype.includes.call(el.labels, label);
455
905
  };
456
906
 
457
- var getName = function(el, recursive, referenced, owned) {
907
+ var getName = function(el, recursive, visited, directReference) {
458
908
  var ret = '';
459
- var owned = owned || [];
460
909
 
461
- // A
462
- if (query.getAttribute(el, 'hidden', referenced)) {
463
- return '';
910
+ visited = visited || [];
911
+ if (visited.includes(el)) {
912
+ if (!directReference) {
913
+ return '';
914
+ }
915
+ } else {
916
+ visited.push(el);
464
917
  }
465
918
 
919
+ // A
920
+ // handled in atree
921
+
466
922
  // B
467
923
  if (!recursive && el.matches('[aria-labelledby]')) {
468
924
  var ids = el.getAttribute('aria-labelledby').split(/\s+/);
469
925
  var strings = ids.map(function(id) {
470
926
  var label = document.getElementById(id);
471
- return label ? getName(label, true, label, owned) : '';
927
+ return label ? getName(label, true, visited, true) : '';
472
928
  });
473
929
  ret = strings.join(' ');
474
930
  }
475
931
 
476
932
  // C
477
933
  if (!ret.trim() && el.matches('[aria-label]')) {
934
+ // FIXME: may skip to 2E
478
935
  ret = el.getAttribute('aria-label');
479
936
  }
480
937
 
481
938
  // D
482
939
  if (!ret.trim() && !recursive && isLabelable(el)) {
483
- var strings = getLabelNodes(el).map(function(label) {
484
- return getName(label, true, label, owned);
940
+ var strings = Array.prototype.map.call(el.labels, function(label) {
941
+ return getName(label, true, visited);
485
942
  });
486
943
  ret = strings.join(' ');
487
944
  }
@@ -499,7 +956,7 @@ var getName = function(el, recursive, referenced, owned) {
499
956
  if (el.matches(selector)) {
500
957
  var descendant = el.querySelector(constants.nameFromDescendant[selector]);
501
958
  if (descendant) {
502
- ret = getName(descendant, true, descendant, owned);
959
+ ret = getName(descendant, true, visited);
503
960
  }
504
961
  }
505
962
  }
@@ -513,7 +970,7 @@ var getName = function(el, recursive, referenced, owned) {
513
970
  } else if (query.matches(el, 'combobox,listbox')) {
514
971
  var selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
515
972
  if (selected) {
516
- ret = getName(selected, recursive, referenced, owned);
973
+ ret = getName(selected, recursive, visited);
517
974
  } else {
518
975
  ret = el.value || '';
519
976
  }
@@ -526,18 +983,9 @@ var getName = function(el, recursive, referenced, owned) {
526
983
  // F
527
984
  // FIXME: menu is not mentioned in the spec
528
985
  if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
529
- ret = getContent(el, referenced, owned);
530
- }
531
-
532
- // TODO: G
533
- // TODO: H
534
-
535
- // FIXME: not mentioned in the spec
536
- if (!ret.trim() && query.matches(el, 'presentation')) {
537
- return getContent(el, referenced, owned);
986
+ ret = getContent(el, visited);
538
987
  }
539
988
 
540
- // FIXME: not mentioned in the spec
541
989
  if (!ret.trim()) {
542
990
  for (var selector in constants.nameDefaults) {
543
991
  if (el.matches(selector)) {
@@ -546,8 +994,12 @@ var getName = function(el, recursive, referenced, owned) {
546
994
  }
547
995
  }
548
996
 
997
+ // G/H
998
+ // handled in getContent
999
+
549
1000
  // I
550
- if (!ret.trim()) {
1001
+ // FIXME: presentation not mentioned in the spec
1002
+ if (!ret.trim() && !query.matches(el, 'presentation')) {
551
1003
  ret = el.title || '';
552
1004
  }
553
1005
 
@@ -562,13 +1014,12 @@ var getNameTrimmed = function(el) {
562
1014
 
563
1015
  var getDescription = function(el) {
564
1016
  var ret = '';
565
- var owned = [];
566
1017
 
567
1018
  if (el.matches('[aria-describedby]')) {
568
1019
  var ids = el.getAttribute('aria-describedby').split(/\s+/);
569
1020
  var strings = ids.map(function(id) {
570
1021
  var label = document.getElementById(id);
571
- return label ? getName(label, true, label, owned) : '';
1022
+ return label ? getName(label, true) : '';
572
1023
  });
573
1024
  ret = strings.join(' ');
574
1025
  } else if (el.title) {
@@ -591,194 +1042,69 @@ module.exports = {
591
1042
  getDescription: getDescription,
592
1043
  };
593
1044
 
594
- },{"./constants.js":2,"./query.js":4,"./util.js":5}],4:[function(require,module,exports){
595
- var constants = require('./constants.js');
596
- var util = require('./util.js');
597
-
598
- var getSubRoles = function(roles) {
599
- return [].concat.apply([], roles.map(function(role) {
600
- return constants.subRoles[role] || [role];
601
- }));
602
- };
603
-
604
- // candidates can be passed for performance optimization
605
- var _getRole = function(el, candidates) {
606
- if (el.hasAttribute('role')) {
607
- return el.getAttribute('role');
608
- }
609
- for (var role in constants.extraSelectors) {
610
- var selector = constants.extraSelectors[role].join(',');
611
- if ((!candidates || candidates.indexOf(role) !== -1) && el.matches(selector)) {
612
- return role;
613
- }
614
- }
615
-
616
- if (!candidates ||
617
- candidates.indexOf('banner') !== -1 ||
618
- candidates.indexOf('contentinfo') !== -1) {
619
- var scoped = el.matches(constants.scoped);
620
-
621
- if (el.matches('header') && !scoped) {
622
- return 'banner';
623
- }
624
- if (el.matches('footer') && !scoped) {
625
- return 'contentinfo';
626
- }
627
- }
628
- };
629
-
630
- var getAttribute = function(el, key, _hiddenRoot) {
631
- if (key === 'hidden' && el === _hiddenRoot) { // used for name calculation
632
- return false;
633
- }
634
-
635
- if (constants.attributeStrongMapping.hasOwnProperty(key)) {
636
- var value = el[constants.attributeStrongMapping[key]];
637
- if (value) {
638
- return value;
639
- }
640
- }
641
- if (key === 'readonly' && el.contentEditable) {
642
- return false;
643
- } else if (key === 'invalid' && el.checkValidity) {
644
- return !el.checkValidity();
645
- } else if (key === 'hidden') {
646
- var style = window.getComputedStyle(el);
647
- if (style.display === 'none' || style.visibility === 'hidden') {
648
- return true;
649
- }
650
- }
651
-
652
- var type = constants.attributes[key];
653
- var raw = el.getAttribute('aria-' + key);
654
-
655
- if (raw) {
656
- if (type === 'bool') {
657
- return raw === 'true';
658
- } else if (type === 'tristate') {
659
- return raw === 'true' ? true : raw === 'false' ? false : 'mixed';
660
- } else if (type === 'bool-undefined') {
661
- return raw === 'true' ? true : raw === 'false' ? false : undefined;
662
- } else if (type === 'id-list') {
663
- return raw.split(/\s+/);
664
- } else if (type === 'integer') {
665
- return parseInt(raw);
666
- } else if (type === 'number') {
667
- return parseFloat(raw);
668
- } else if (type === 'token-list') {
669
- return raw.split(/\s+/);
670
- } else {
671
- return raw;
672
- }
673
- }
674
-
675
- // TODO
676
- // autocomplete
677
- // contextmenu -> aria-haspopup
678
- // indeterminate -> aria-checked="mixed"
679
- // list -> aria-controls
1045
+ },{"./atree.js":2,"./constants.js":4,"./query.js":6}],6:[function(require,module,exports){
1046
+ var attrs = require('./attrs.js');
1047
+ var atree = require('./atree.js');
680
1048
 
681
- if (key === 'level') {
682
- for (var i = 1; i <= 6; i++) {
683
- if (el.tagName.toLowerCase() === 'h' + i) {
684
- return i;
685
- }
686
- }
687
- } else if (key === 'hidden') {
688
- if (el.clientHeight === 0) { // rough check for performance
689
- return el.parentNode && getAttribute(el.parentNode, 'hidden', _hiddenRoot);
690
- }
691
- } else if (constants.attributeWeakMapping.hasOwnProperty(key)) {
692
- return el[constants.attributeWeakMapping[key]];
693
- }
694
-
695
- if (type === 'bool' || type === 'tristate') {
696
- return false;
697
- }
698
- };
699
1049
 
700
1050
  var matches = function(el, selector) {
701
1051
  var actual;
702
1052
 
703
1053
  if (selector.substr(0, 1) === ':') {
704
1054
  var attr = selector.substr(1);
705
- return getAttribute(el, attr);
1055
+ return attrs.getAttribute(el, attr);
706
1056
  } else if (selector.substr(0, 1) === '[') {
707
1057
  var match = /\[([a-z]+)="(.*)"\]/.exec(selector);
708
- actual = getAttribute(el, match[1]);
1058
+ actual = attrs.getAttribute(el, match[1]);
709
1059
  var rawValue = match[2];
710
- return actual.toString() == rawValue;
1060
+ return actual.toString() === rawValue;
711
1061
  } else {
712
- var candidates = getSubRoles(selector.split(','));
713
- actual = _getRole(el, candidates);
714
- return candidates.indexOf(actual) !== -1;
1062
+ return attrs.hasRole(el, selector.split(','));
715
1063
  }
716
1064
  };
717
1065
 
718
1066
  var _querySelector = function(all) {
719
1067
  return function(root, role) {
720
1068
  var results = [];
721
- util.walkDOM(root, function(node) {
722
- if (node.nodeType === node.ELEMENT_NODE) {
723
- // FIXME: skip hidden elements
724
- if (matches(node, role)) {
725
- results.push(node);
726
- if (!all) {
727
- return false;
1069
+ try {
1070
+ atree.walk(root, function(node) {
1071
+ if (node.nodeType === node.ELEMENT_NODE) {
1072
+ // FIXME: skip hidden elements
1073
+ if (matches(node, role)) {
1074
+ results.push(node);
1075
+ if (!all) {
1076
+ throw 'StopIteration';
1077
+ }
728
1078
  }
729
1079
  }
1080
+ });
1081
+ } catch (e) {
1082
+ if (e !== 'StopIteration') {
1083
+ throw e;
730
1084
  }
731
- });
1085
+ }
732
1086
  return all ? results : results[0];
733
1087
  };
734
1088
  };
735
1089
 
736
1090
  var closest = function(el, selector) {
737
- return util.searchUp(el, function(candidate) {
738
- return matches(candidate, selector);
1091
+ return atree.searchUp(el, function(candidate) {
1092
+ if (candidate.nodeType === candidate.ELEMENT_NODE) {
1093
+ return matches(candidate, selector);
1094
+ }
739
1095
  });
740
1096
  };
741
1097
 
742
1098
  module.exports = {
743
1099
  getRole: function(el) {
744
- return _getRole(el);
1100
+ return attrs.getRole(el);
745
1101
  },
746
- getAttribute: getAttribute,
1102
+ getAttribute: attrs.getAttribute,
747
1103
  matches: matches,
748
1104
  querySelector: _querySelector(),
749
1105
  querySelectorAll: _querySelector(true),
750
1106
  closest: closest,
751
1107
  };
752
1108
 
753
- },{"./constants.js":2,"./util.js":5}],5:[function(require,module,exports){
754
- var walkDOM = function(root, fn) {
755
- if (fn(root) === false) {
756
- return false;
757
- }
758
- var node = root.firstChild;
759
- while (node) {
760
- if (walkDOM(node, fn) === false) {
761
- return false;
762
- }
763
- node = node.nextSibling;
764
- }
765
- };
766
-
767
- var searchUp = function(el, test) {
768
- var candidate = el.parentElement;
769
- if (candidate) {
770
- if (test(candidate)) {
771
- return candidate;
772
- } else {
773
- return searchUp(candidate, test);
774
- }
775
- }
776
- };
777
-
778
- module.exports = {
779
- walkDOM: walkDOM,
780
- searchUp: searchUp,
781
- };
782
-
783
- },{}]},{},[1])(1)
1109
+ },{"./atree.js":2,"./attrs.js":3}]},{},[1])(1)
784
1110
  });