aria-api 0.7.0 → 0.8.0

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,1325 +1,1293 @@
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
- var query = require('./lib/query.js');
3
- var name = require('./lib/name.js');
4
- var atree = require('./lib/atree.js');
5
-
6
- module.exports = {
7
- getRole: query.getRole,
8
- getAttribute: query.getAttribute,
9
- getName: name.getName,
10
- getDescription: name.getDescription,
11
-
12
- matches: query.matches,
13
- querySelector: query.querySelector,
14
- querySelectorAll: query.querySelectorAll,
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
- const attrs = require('./attrs');
23
-
24
- const _getOwner = function(node, owners) {
25
- if (node.nodeType === node.ELEMENT_NODE && node.id) {
26
- const selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
27
- if (owners) {
28
- for (const owner of owners) {
29
- if (owner.matches(selector)) {
30
- return owner;
31
- }
32
- }
33
- } else {
34
- return document.querySelector(selector);
35
- }
36
- }
37
- };
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.aria = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ // https://www.w3.org/TR/wai-aria/#state_prop_def
8
+ const attributes = {
9
+ 'activedescendant': 'id',
10
+ 'atomic': 'bool',
11
+ 'autocomplete': 'token',
12
+ 'braillelabel': 'string',
13
+ 'brailleroledescription': 'string',
14
+ 'busy': 'bool',
15
+ 'checked': 'tristate',
16
+ 'colcount': 'int',
17
+ 'colindex': 'int',
18
+ 'colindextext': 'string',
19
+ 'colspan': 'int',
20
+ 'controls': 'id-list',
21
+ 'current': 'token',
22
+ 'describedby': 'id-list',
23
+ 'description': 'string',
24
+ 'details': 'id',
25
+ 'disabled': 'bool',
26
+ 'dropeffect': 'token-list',
27
+ 'errormessage': 'id',
28
+ 'expanded': 'bool-undefined',
29
+ 'flowto': 'id-list',
30
+ 'grabbed': 'bool-undefined',
31
+ 'haspopup': 'token',
32
+ 'hidden': 'bool-undefined',
33
+ 'invalid': 'token',
34
+ 'keyshortcuts': 'string',
35
+ 'label': 'string',
36
+ 'labelledby': 'id-list',
37
+ 'level': 'int',
38
+ 'live': 'token',
39
+ 'modal': 'bool',
40
+ 'multiline': 'bool',
41
+ 'multiselectable': 'bool',
42
+ 'orientation': 'token',
43
+ 'owns': 'id-list',
44
+ 'placeholder': 'string',
45
+ 'posinset': 'int',
46
+ 'pressed': 'tristate',
47
+ 'readonly': 'bool',
48
+ 'relevant': 'token-list',
49
+ 'required': 'bool',
50
+ 'roledescription': 'string',
51
+ 'rowcount': 'int',
52
+ 'rowindex': 'int',
53
+ 'rowindextext': 'string',
54
+ 'rowspan': 'int',
55
+ 'selected': 'bool-undefined',
56
+ 'setsize': 'int',
57
+ 'sort': 'token',
58
+ 'valuemax': 'number',
59
+ 'valuemin': 'number',
60
+ 'valuenow': 'number',
61
+ 'valuetext': 'string',
62
+ };
38
63
 
39
- const _getParentNode = function(node, owners) {
40
- return _getOwner(node, owners) || node.parentNode;
41
- };
64
+ const attributeStrongMapping = {
65
+ 'disabled': 'disabled',
66
+ 'placeholder': 'placeholder',
67
+ 'readonly': 'readOnly',
68
+ 'required': 'required',
69
+ };
42
70
 
43
- const detectLoop = function(node, owners) {
44
- const seen = [node];
45
- while ((node = _getParentNode(node, owners))) {
46
- if (seen.includes(node)) {
47
- return true;
48
- }
49
- seen.push(node);
50
- }
51
- };
71
+ const attributeWeakMapping = {
72
+ 'checked': 'checked',
73
+ 'colspan': 'colSpan',
74
+ 'expanded': 'open',
75
+ 'multiselectable': 'multiple',
76
+ 'rowspan': 'rowSpan',
77
+ 'selected': 'selected',
78
+ };
52
79
 
53
- const getOwner = function(node, owners) {
54
- const owner = _getOwner(node, owners);
55
- if (owner && !detectLoop(node, owners)) {
56
- return owner;
57
- }
58
- };
80
+ // https://www.w3.org/TR/html/dom.html#sectioning-content-2
81
+ const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
82
+
83
+ const svgSelectors = function(selector) {
84
+ return [
85
+ // `${selector}:has(> title:not(:empty))`,
86
+ // `${selector}:has(> desc:not(:empty))`,
87
+ `${selector}[aria-label]`,
88
+ `${selector}[aria-roledescription]`,
89
+ `${selector}[aria-labelledby]`,
90
+ `${selector}[aria-describedby]`,
91
+ `${selector}[tabindex]`,
92
+ `${selector}[role]`,
93
+ ];
94
+ };
95
+
96
+ // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
97
+ // https://www.w3.org/TR/wai-aria/roles
98
+ const roles = {
99
+ alert: {
100
+ childRoles: ['alertdialog'],
101
+ defaults: {
102
+ 'live': 'assertive',
103
+ 'atomic': true,
104
+ },
105
+ },
106
+ alertdialog: {},
107
+ application: {},
108
+ article: {
109
+ selectors: ['article'],
110
+ childRoles: ['comment'],
111
+ },
112
+ banner: {
113
+ selectors: [`header:not(main *, ${scoped})`],
114
+ },
115
+ blockquote: {
116
+ selectors: ['blockquote'],
117
+ },
118
+ button: {
119
+ selectors: [
120
+ 'button',
121
+ 'input[type="button"]',
122
+ 'input[type="image"]',
123
+ 'input[type="reset"]',
124
+ 'input[type="submit"]',
125
+ 'summary',
126
+ ],
127
+ nameFromContents: true,
128
+ },
129
+ caption: {
130
+ selectors: ['caption', 'figcaption'],
131
+ },
132
+ cell: {
133
+ selectors: ['td', 'td ~ th:not([scope])'],
134
+ childRoles: ['columnheader', 'gridcell', 'rowheader'],
135
+ nameFromContents: true,
136
+ },
137
+ checkbox: {
138
+ selectors: ['input[type="checkbox"]'],
139
+ childRoles: ['switch'],
140
+ nameFromContents: true,
141
+ defaults: {
142
+ 'checked': 'false',
143
+ },
144
+ },
145
+ code: {
146
+ selectors: ['code'],
147
+ },
148
+ columnheader: {
149
+ selectors: ['th[scope="col"]'],
150
+ nameFromContents: true,
151
+ },
152
+ combobox: {
153
+ selectors: [
154
+ 'input:not([type])[list]',
155
+ 'input[type="email"][list]',
156
+ 'input[type="search"][list]',
157
+ 'input[type="tel"][list]',
158
+ 'input[type="text"][list]',
159
+ 'input[type="url"][list]',
160
+ 'select:not([size]):not([multiple])',
161
+ 'select[size="0"]:not([multiple])',
162
+ 'select[size="1"]:not([multiple])',
163
+ ],
164
+ defaults: {
165
+ 'expanded': false,
166
+ 'haspopup': 'listbox',
167
+ },
168
+ },
169
+ command: {
170
+ abstract: true,
171
+ childRoles: ['button', 'link', 'menuitem'],
172
+ },
173
+ comment: {
174
+ nameFromContents: true,
175
+ },
176
+ complementary: {
177
+ selectors: [
178
+ `aside:not(${scoped})`,
179
+ 'aside[aria-label]',
180
+ 'aside[aria-labelledby]',
181
+ 'aside[title]',
182
+ ],
183
+ },
184
+ composite: {
185
+ abstract: true,
186
+ childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
187
+ },
188
+ contentinfo: {
189
+ selectors: [`footer:not(main *, ${scoped})`],
190
+ },
191
+ definition: {
192
+ selectors: ['dd'],
193
+ },
194
+ deletion: {
195
+ selectors: ['del', 's'],
196
+ },
197
+ dialog: {
198
+ selectors: ['dialog'],
199
+ childRoles: ['alertdialog'],
200
+ },
201
+ 'doc-abstract': {},
202
+ 'doc-acknowledgments': {},
203
+ 'doc-afterword': {},
204
+ 'doc-appendix': {},
205
+ 'doc-backlink': {
206
+ nameFromContents: true,
207
+ },
208
+ 'doc-biblioentry': {},
209
+ 'doc-bibliography': {},
210
+ 'doc-biblioref': {
211
+ nameFromContents: true,
212
+ },
213
+ 'doc-chapter': {},
214
+ 'doc-colophon': {},
215
+ 'doc-conclusion': {},
216
+ 'doc-cover': {},
217
+ 'doc-credit': {},
218
+ 'doc-credits': {},
219
+ 'doc-dedication': {},
220
+ 'doc-endnote': {},
221
+ 'doc-endnotes': {},
222
+ 'doc-epilogue': {},
223
+ 'doc-epigraph': {},
224
+ 'doc-errata': {},
225
+ 'doc-example': {},
226
+ 'doc-footnote': {},
227
+ 'doc-foreword': {},
228
+ 'doc-glossary': {},
229
+ 'doc-glossref': {
230
+ nameFromContents: true,
231
+ },
232
+ 'doc-index': {},
233
+ 'doc-introduction': {},
234
+ 'doc-noteref': {
235
+ nameFromContents: true,
236
+ },
237
+ 'doc-notice': {},
238
+ 'doc-pagebreak': {
239
+ nameFromContents: true,
240
+ },
241
+ 'doc-pagefooter': {},
242
+ 'doc-pageheader': {},
243
+ 'doc-pagelist': {},
244
+ 'doc-part': {},
245
+ 'doc-preface': {},
246
+ 'doc-prologue': {},
247
+ 'doc-pullquote': {},
248
+ 'doc-qna': {},
249
+ 'doc-subtitle': {
250
+ nameFromContents: true,
251
+ },
252
+ 'doc-tip': {},
253
+ 'doc-toc': {},
254
+ document: {
255
+ selectors: ['html'],
256
+ childRoles: ['article', 'graphics-document'],
257
+ },
258
+ emphasis: {
259
+ selectors: ['em'],
260
+ },
261
+ feed: {},
262
+ figure: {
263
+ selectors: ['figure'],
264
+ childRoles: ['doc-example'],
265
+ },
266
+ form: {
267
+ selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
268
+ },
269
+ generic: {
270
+ selectors: [
271
+ 'a:not([*|href])',
272
+ 'area:not([*|href])',
273
+ `aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
274
+ 'b',
275
+ 'bdi',
276
+ 'bdo',
277
+ 'body',
278
+ 'data',
279
+ 'div',
280
+ // footer scoped
281
+ // header scoped
282
+ 'i',
283
+ 'li:not(ul > li):not(ol > li)',
284
+ 'pre',
285
+ 'q',
286
+ 'samp',
287
+ 'section:not([aria-label]):not([aria-labelledby]):not([title])',
288
+ 'small',
289
+ 'span',
290
+ 'u',
291
+ ],
292
+ },
293
+ 'graphics-document': {
294
+ selectors: ['svg'],
295
+ },
296
+ 'graphics-object': {
297
+ selectors: [
298
+ ...svgSelectors('symbol'),
299
+ ...svgSelectors('use'),
300
+ ],
301
+ },
302
+ 'graphics-symbol': {
303
+ selectors: [
304
+ ...svgSelectors('circle'),
305
+ ...svgSelectors('ellipse'),
306
+ ...svgSelectors('line'),
307
+ ...svgSelectors('path'),
308
+ ...svgSelectors('polygon'),
309
+ ...svgSelectors('polyline'),
310
+ ...svgSelectors('rect'),
311
+ ],
312
+ },
313
+ grid: {
314
+ childRoles: ['treegrid'],
315
+ },
316
+ gridcell: {
317
+ childRoles: ['columnheader', 'rowheader'],
318
+ nameFromContents: true,
319
+ },
320
+ group: {
321
+ selectors: [
322
+ 'address',
323
+ 'details',
324
+ 'fieldset',
325
+ 'hgroup',
326
+ 'optgroup',
327
+ ...svgSelectors('foreignObject'),
328
+ ...svgSelectors('g'),
329
+ 'text',
330
+ ...svgSelectors('textPath'),
331
+ ...svgSelectors('tspan'),
332
+ ],
333
+ childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
334
+ },
335
+ heading: {
336
+ selectors: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
337
+ nameFromContents: true,
338
+ defaults: {
339
+ 'level': 2,
340
+ },
341
+ },
342
+ image: {
343
+ selectors: [
344
+ 'img:not([alt=""])',
345
+ 'graphics-symbol',
346
+ ...svgSelectors('image'),
347
+ ...svgSelectors('mesh'),
348
+ ],
349
+ childRoles: ['doc-cover'],
350
+ },
351
+ input: {
352
+ abstract: true,
353
+ childRoles: [
354
+ 'checkbox',
355
+ 'combobox',
356
+ 'option',
357
+ 'radio',
358
+ 'slider',
359
+ 'spinbutton',
360
+ 'textbox',
361
+ ],
362
+ },
363
+ insertion: {
364
+ selectors: ['ins'],
365
+ },
366
+ landmark: {
367
+ abstract: true,
368
+ childRoles: [
369
+ 'banner',
370
+ 'complementary',
371
+ 'contentinfo',
372
+ 'doc-acknowledgments',
373
+ 'doc-afterword',
374
+ 'doc-appendix',
375
+ 'doc-bibliography',
376
+ 'doc-chapter',
377
+ 'doc-conclusion',
378
+ 'doc-credits',
379
+ 'doc-endnotes',
380
+ 'doc-epilogue',
381
+ 'doc-errata',
382
+ 'doc-foreword',
383
+ 'doc-glossary',
384
+ 'doc-introduction',
385
+ 'doc-part',
386
+ 'doc-preface',
387
+ 'doc-prologue',
388
+ 'form',
389
+ 'main',
390
+ 'navigation',
391
+ 'region',
392
+ 'search',
393
+ ],
394
+ },
395
+ link: {
396
+ selectors: ['a[*|href]', 'area[*|href]'],
397
+ childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
398
+ nameFromContents: true,
399
+ },
400
+ list: {
401
+ selectors: ['dl', 'ol', 'ul', 'menu'],
402
+ childRoles: ['feed'],
403
+ },
404
+ listbox: {
405
+ selectors: [
406
+ 'datalist',
407
+ 'select[multiple]',
408
+ 'select[size]:not([size="0"]):not([size="1"])',
409
+ ],
410
+ defaults: {
411
+ 'orientation': 'vertical',
412
+ },
413
+ },
414
+ listitem: {
415
+ selectors: ['ol > li', 'ul > li'],
416
+ childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
417
+ },
418
+ log: {
419
+ defaults: {
420
+ 'live': 'polite',
421
+ },
422
+ },
423
+ main: {
424
+ selectors: ['main'],
425
+ },
426
+ mark: {
427
+ selectors: ['mark'],
428
+ },
429
+ marquee: {},
430
+ math: {
431
+ selectors: ['math'],
432
+ },
433
+ meter: {
434
+ selectors: ['meter'],
435
+ defaults: {
436
+ 'valuemin': 0,
437
+ 'valuemax': 100,
438
+ },
439
+ },
440
+ menu: {
441
+ childRoles: ['menubar'],
442
+ defaults: {
443
+ 'orientation': 'vertical',
444
+ },
445
+ },
446
+ menubar: {
447
+ defaults: {
448
+ 'orientation': 'horizontal',
449
+ },
450
+ },
451
+ menuitem: {
452
+ childRoles: ['menuitemcheckbox', 'menuitemradio'],
453
+ nameFromContents: true,
454
+ },
455
+ menuitemcheckbox: {
456
+ nameFromContents: true,
457
+ defaults: {
458
+ 'checked': 'false',
459
+ },
460
+ },
461
+ menuitemradio: {
462
+ nameFromContents: true,
463
+ defaults: {
464
+ 'checked': 'false',
465
+ },
466
+ },
467
+ navigation: {
468
+ selectors: ['nav'],
469
+ childRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
470
+ },
471
+ none: {
472
+ selectors: ['img[alt=""]'],
473
+ },
474
+ note: {
475
+ childRoles: ['doc-notice', 'doc-tip'],
476
+ },
477
+ option: {
478
+ selectors: ['option'],
479
+ childRoles: ['treeitem'],
480
+ nameFromContents: true,
481
+ defaults: {
482
+ 'selected': 'false',
483
+ },
484
+ },
485
+ paragraph: {
486
+ selectors: ['p'],
487
+ },
488
+ progressbar: {
489
+ selectors: ['progress'],
490
+ defaults: {
491
+ 'valuemin': 0,
492
+ 'valuemax': 100,
493
+ },
494
+ },
495
+ radio: {
496
+ selectors: ['input[type="radio"]'],
497
+ childRoles: ['menuitemradio'],
498
+ nameFromContents: true,
499
+ defaults: {
500
+ 'checked': 'false',
501
+ },
502
+ },
503
+ radiogroup: {},
504
+ range: {
505
+ abstract: true,
506
+ childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
507
+ },
508
+ region: {
509
+ selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
510
+ },
511
+ roletype: {
512
+ abstract: true,
513
+ childRoles: ['structure', 'widget', 'window'],
514
+ },
515
+ row: {
516
+ selectors: ['tr'],
517
+ nameFromContents: true,
518
+ },
519
+ rowgroup: {
520
+ selectors: ['tbody', 'thead', 'tfoot'],
521
+ },
522
+ rowheader: {
523
+ selectors: ['th[scope="row"]', 'th:not([scope]):not(td ~ th)'],
524
+ nameFromContents: true,
525
+ },
526
+ scrollbar: {
527
+ defaults: {
528
+ 'orientation': 'vertical',
529
+ 'valuemin': 0,
530
+ 'valuemax': 100,
531
+ },
532
+ },
533
+ search: {
534
+ selectors: ['search'],
535
+ },
536
+ searchbox: {
537
+ selectors: ['input[type="search"]:not([list])'],
538
+ },
539
+ section: {
540
+ abstract: true,
541
+ childRoles: [
542
+ 'alert',
543
+ 'blockquote',
544
+ 'caption',
545
+ 'cell',
546
+ 'code',
547
+ 'definition',
548
+ 'deletion',
549
+ 'doc-abstract',
550
+ 'doc-colophon',
551
+ 'doc-credit',
552
+ 'doc-dedication',
553
+ 'doc-epigraph',
554
+ 'doc-footnote',
555
+ 'doc-pagefooter',
556
+ 'doc-pageheader',
557
+ 'doc-pullquote',
558
+ 'doc-qna',
559
+ 'emphasis',
560
+ 'figure',
561
+ 'group',
562
+ 'image',
563
+ 'insertion',
564
+ 'landmark',
565
+ 'list',
566
+ 'listitem',
567
+ 'log',
568
+ 'mark',
569
+ 'marquee',
570
+ 'math',
571
+ 'note',
572
+ 'paragraph',
573
+ 'status',
574
+ 'strong',
575
+ 'subscript',
576
+ 'suggestion',
577
+ 'superscript',
578
+ 'table',
579
+ 'tabpanel',
580
+ 'term',
581
+ 'time',
582
+ 'tooltip',
583
+ ],
584
+ },
585
+ sectionhead: {
586
+ abstract: true,
587
+ childRoles: [
588
+ 'columnheader',
589
+ 'doc-subtitle',
590
+ 'heading',
591
+ 'rowheader',
592
+ 'tab',
593
+ ],
594
+ nameFromContents: true,
595
+ },
596
+ select: {
597
+ abstract: true,
598
+ childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
599
+ },
600
+ separator: {
601
+ // assume not focussable because <hr> is not
602
+ selectors: ['hr'],
603
+ childRoles: ['doc-pagebreak'],
604
+ defaults: {
605
+ 'orientation': 'horizontal',
606
+ 'valuemin': 0,
607
+ 'valuemax': 100,
608
+ },
609
+ },
610
+ slider: {
611
+ selectors: ['input[type="range"]'],
612
+ defaults: {
613
+ 'orientation': 'horizontal',
614
+ 'valuemin': 0,
615
+ 'valuemax': 100,
616
+ // FIXME: halfway between actual valuemin and valuemax
617
+ 'valuenow': 50,
618
+ },
619
+ },
620
+ spinbutton: {
621
+ selectors: ['input[type="number"]'],
622
+ defaults: {
623
+ // FIXME: no valuemin/valuemax/valuenow
624
+ },
625
+ },
626
+ status: {
627
+ selectors: ['output'],
628
+ childRoles: ['timer'],
629
+ defaults: {
630
+ 'live': 'polite',
631
+ 'atomic': true,
632
+ },
633
+ },
634
+ strong: {
635
+ selectors: ['strong'],
636
+ },
637
+ structure: {
638
+ abstract: true,
639
+ childRoles: [
640
+ 'application',
641
+ 'document',
642
+ 'none',
643
+ 'generic',
644
+ 'range',
645
+ 'rowgroup',
646
+ 'section',
647
+ 'sectionhead',
648
+ 'separator',
649
+ ],
650
+ },
651
+ suggestion: {},
652
+ subscript: {
653
+ selectors: ['sub'],
654
+ },
655
+ superscript: {
656
+ selectors: ['sup'],
657
+ },
658
+ switch: {
659
+ nameFromContents: true,
660
+ defaults: {
661
+ 'checked': false,
662
+ },
663
+ },
664
+ tab: {
665
+ nameFromContents: true,
666
+ defaults: {
667
+ 'selected': false,
668
+ },
669
+ },
670
+ table: {
671
+ selectors: ['table'],
672
+ childRoles: ['grid'],
673
+ },
674
+ tablist: {
675
+ defaults: {
676
+ 'orientation': 'horizontal',
677
+ },
678
+ },
679
+ tabpanel: {},
680
+ term: {
681
+ selectors: ['dfn', 'dt'],
682
+ },
683
+ textbox: {
684
+ selectors: [
685
+ 'input:not([type]):not([list])',
686
+ 'input[type="email"]:not([list])',
687
+ 'input[type="tel"]:not([list])',
688
+ 'input[type="text"]:not([list])',
689
+ 'input[type="url"]:not([list])',
690
+ 'textarea',
691
+ ],
692
+ childRoles: ['searchbox'],
693
+ },
694
+ time: {
695
+ selectors: ['time'],
696
+ },
697
+ timer: {
698
+ defaults: {
699
+ 'live': 'off',
700
+ },
701
+ },
702
+ toolbar: {
703
+ defaults: {
704
+ 'orientation': 'horizontal',
705
+ },
706
+ },
707
+ tooltip: {
708
+ nameFromContents: true,
709
+ },
710
+ tree: {
711
+ childRoles: ['treegrid'],
712
+ defaults: {
713
+ 'orientation': 'vertical',
714
+ },
715
+ },
716
+ treegrid: {},
717
+ treeitem: {
718
+ nameFromContents: true,
719
+ },
720
+ widget: {
721
+ abstract: true,
722
+ childRoles: [
723
+ 'command',
724
+ 'composite',
725
+ 'gridcell',
726
+ 'input',
727
+ 'progressbar',
728
+ 'row',
729
+ 'scrollbar',
730
+ 'separator',
731
+ 'tab',
732
+ ],
733
+ },
734
+ window: {
735
+ abstract: true,
736
+ childRoles: ['dialog'],
737
+ },
738
+ };
59
739
 
60
- const getParentNode = function(node, owners) {
61
- return getOwner(node, owners) || node.parentNode;
62
- };
740
+ const getSubRoles = function(role) {
741
+ const children = (roles[role]).childRoles || [];
742
+ const descendents = children.map(getSubRoles);
63
743
 
64
- const isHidden = function(node) {
65
- return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
66
- };
744
+ const result = [role];
67
745
 
68
- const getChildNodes = function(node, owners) {
69
- const childNodes = [];
746
+ descendents.forEach(list => {
747
+ list.forEach(r => {
748
+ if (!result.includes(r)) {
749
+ result.push(r);
750
+ }
751
+ });
752
+ });
70
753
 
71
- for (let i = 0; i < node.childNodes.length; i++) {
72
- const child = node.childNodes[i];
73
- if (!getOwner(child, owners) && !isHidden(child)) {
74
- childNodes.push(child);
75
- }
76
- }
754
+ return result;
755
+ };
77
756
 
78
- if (node.nodeType === node.ELEMENT_NODE) {
79
- const owns = attrs.getAttribute(node, 'owns') || [];
80
- for (let i = 0; i < owns.length; i++) {
81
- const child = document.getElementById(owns[i]);
82
- // double check with getOwner for consistency
83
- if (child && getOwner(child, owners) === node && !isHidden(child)) {
84
- childNodes.push(child);
757
+ const attrsWithDefaults = [];
758
+
759
+ for (const role in roles) {
760
+ roles[role].subRoles = getSubRoles(role);
761
+ for (const key in roles[role].defaults) {
762
+ if (!attrsWithDefaults.includes(key)) {
763
+ attrsWithDefaults.push(key);
85
764
  }
86
765
  }
87
766
  }
88
767
 
89
- return childNodes;
90
- };
768
+ const aliases = {
769
+ 'presentation': 'none',
770
+ 'directory': 'list',
771
+ 'img': 'image',
772
+ };
773
+
774
+ const nameFromDescendant = {
775
+ 'figure': 'figcaption',
776
+ 'table': 'caption',
777
+ 'fieldset': 'legend',
778
+ };
91
779
 
92
- const walk = function(root, fn) {
93
- const owners = document.querySelectorAll('[aria-owns]');
94
- let queue = [root];
95
- while (queue.length) {
96
- const item = queue.shift();
97
- fn(item);
98
- queue = getChildNodes(item, owners).concat(queue);
99
- }
100
- };
780
+ const nameDefaults = {
781
+ 'input[type="submit"]': 'Submit',
782
+ 'input[type="reset"]': 'Reset',
783
+ 'summary': 'Details',
784
+ };
101
785
 
102
- const searchUp = function(node, test) {
103
- const candidate = getParentNode(node);
104
- if (candidate) {
105
- if (test(candidate)) {
106
- return candidate;
786
+ var unique = function(arr) {
787
+ return arr.filter((a, i) => arr.indexOf(a) === i);
788
+ };
789
+
790
+ var flatten = function(arr) {
791
+ return [].concat.apply([], arr);
792
+ };
793
+
794
+ var normalizeRoles = function(roles$1, includeAbstract) {
795
+ return unique(roles$1
796
+ .map(r => aliases[r] || r)
797
+ .filter(r => roles[r])
798
+ .filter(r => includeAbstract || !roles[r].abstract)
799
+ );
800
+ };
801
+
802
+ // candidates can be passed for performance optimization
803
+ const getRoleRaw = function(el, candidates) {
804
+ // TODO: filter out any invalid roles (e.g. name or context required)
805
+ const roles$1 = normalizeRoles(
806
+ (el.getAttribute('role') || '').toLowerCase().split(/\s+/)
807
+ );
808
+
809
+ if (roles$1.length > 1 && candidates) {
810
+ return [roles$1, candidates];
811
+ } else if (roles$1.length) {
812
+ for (const role of roles$1) {
813
+ if (!candidates || candidates.includes(role)) {
814
+ return role;
815
+ }
816
+ }
107
817
  } else {
108
- return searchUp(candidate, test);
818
+ for (const role of (candidates || Object.keys(roles))) {
819
+ const r = roles[role];
820
+ if (!r.abstract && r.selectors && el.matches(r.selectors.join(','))) {
821
+ return role;
822
+ }
823
+ }
109
824
  }
110
- }
111
- };
112
-
113
- module.exports = {
114
- 'getParentNode': getParentNode,
115
- 'getChildNodes': getChildNodes,
116
- 'walk': walk,
117
- 'searchUp': searchUp,
118
- };
119
-
120
- },{"./attrs":3}],3:[function(require,module,exports){
121
- const constants = require('./constants.js');
122
-
123
- var unique = function(arr) {
124
- return arr.filter((a, i) => arr.indexOf(a) === i);
125
- };
126
-
127
- var flatten = function(arr) {
128
- return [].concat.apply([], arr);
129
- };
130
-
131
- var normalizeRoles = function(roles, includeAbstract) {
132
- return unique(roles
133
- .map(r => constants.aliases[r] || r)
134
- .filter(r => constants.roles[r])
135
- .filter(r => includeAbstract || !constants.roles[r].abstract)
136
- );
137
- };
138
-
139
- // candidates can be passed for performance optimization
140
- const getRole = function(el, candidates) {
141
- // TODO: filter out any invalid roles (e.g. name or context required)
142
- const roles = normalizeRoles(
143
- (el.getAttribute('role') || '').toLowerCase().split(/\s+/)
144
- );
145
-
146
- if (roles.length > 1 && candidates) {
147
- return [roles, candidates];
148
- } else if (roles.length) {
149
- for (const role of roles) {
150
- if (!candidates || candidates.includes(role)) {
151
- return role;
825
+ };
826
+
827
+ const getRole = function(el) {
828
+ return getRoleRaw(el);
829
+ };
830
+
831
+ const hasRole = function(el, roles$1) {
832
+ const subRoles = normalizeRoles(roles$1, true).map(role => {
833
+ return roles[role].subRoles || [role];
834
+ });
835
+ return !!getRoleRaw(el, unique(flatten(subRoles)));
836
+ };
837
+
838
+ const getAttribute = function(el, key) {
839
+ if (attributeStrongMapping.hasOwnProperty(key)) {
840
+ const value = el[attributeStrongMapping[key]];
841
+ if (value) {
842
+ return value;
152
843
  }
153
844
  }
154
- } else {
155
- for (const role of (candidates || Object.keys(constants.roles))) {
156
- const r = constants.roles[role];
157
- if (!r.abstract && r.selectors && el.matches(r.selectors.join(','))) {
158
- return role;
845
+ if (key === 'readonly' && el.contentEditable) {
846
+ return false;
847
+ } else if (key === 'invalid' && el.checkValidity) {
848
+ return !el.checkValidity();
849
+ } else if (key === 'hidden') {
850
+ // workaround for chromium
851
+ if (el.matches('noscript')) {
852
+ return true;
853
+ }
854
+ if (el.matches('details:not([open]) > :not(summary)')) {
855
+ return true;
856
+ }
857
+ const style = window.getComputedStyle(el);
858
+ if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse') {
859
+ return true;
159
860
  }
160
861
  }
161
- }
162
- };
163
-
164
- const hasRole = function(el, roles) {
165
- const subRoles = normalizeRoles(roles, true).map(role => {
166
- return constants.roles[role].subRoles || [role];
167
- });
168
- return !!getRole(el, unique(flatten(subRoles)));
169
- };
170
-
171
- const getAttribute = function(el, key) {
172
- if (constants.attributeStrongMapping.hasOwnProperty(key)) {
173
- const value = el[constants.attributeStrongMapping[key]];
174
- if (value) {
175
- return value;
862
+
863
+ const type = attributes[key];
864
+ const raw = el.getAttribute('aria-' + key);
865
+
866
+ if (raw) {
867
+ if (type === 'bool') {
868
+ return raw === 'true';
869
+ } else if (type === 'tristate') {
870
+ return raw === 'true' ? true : raw === 'false' ? false : 'mixed';
871
+ } else if (type === 'bool-undefined') {
872
+ return raw === 'true' ? true : raw === 'false' ? false : undefined;
873
+ } else if (type === 'id-list') {
874
+ return raw.split(/\s+/);
875
+ } else if (type === 'integer') {
876
+ return parseInt(raw, 10);
877
+ } else if (type === 'number') {
878
+ return parseFloat(raw);
879
+ } else if (type === 'token-list') {
880
+ return raw.split(/\s+/);
881
+ } else {
882
+ return raw;
883
+ }
176
884
  }
177
- }
178
- if (key === 'readonly' && el.contentEditable) {
179
- return false;
180
- } else if (key === 'invalid' && el.checkValidity) {
181
- return !el.checkValidity();
182
- } else if (key === 'hidden') {
183
- // workaround for chromium
184
- if (el.matches('noscript')) {
185
- return true;
885
+
886
+ // TODO
887
+ // autocomplete
888
+ // contextmenu -> aria-haspopup
889
+ // indeterminate -> aria-checked="mixed"
890
+ // list -> aria-controls
891
+
892
+ if (key === 'level') {
893
+ for (let i = 1; i <= 6; i++) {
894
+ if (el.tagName.toLowerCase() === 'h' + i) {
895
+ return i;
896
+ }
897
+ }
898
+ } else if (attributeWeakMapping.hasOwnProperty(key)) {
899
+ return el[attributeWeakMapping[key]];
186
900
  }
187
- if (el.matches('details:not([open]) > :not(summary)')) {
188
- return true;
901
+
902
+ if (key in attrsWithDefaults) {
903
+ const role = getRole(el);
904
+ const defaults = roles[role].defaults;
905
+ if (defaults && defaults.hasOwnProperty(key)) {
906
+ return defaults[key];
907
+ }
189
908
  }
190
- const style = window.getComputedStyle(el);
191
- if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse') {
192
- return true;
909
+
910
+ if (type === 'bool' || type === 'tristate') {
911
+ return false;
193
912
  }
194
- }
913
+ };
195
914
 
196
- const type = constants.attributes[key];
197
- const raw = el.getAttribute('aria-' + key);
198
-
199
- if (raw) {
200
- if (type === 'bool') {
201
- return raw === 'true';
202
- } else if (type === 'tristate') {
203
- return raw === 'true' ? true : raw === 'false' ? false : 'mixed';
204
- } else if (type === 'bool-undefined') {
205
- return raw === 'true' ? true : raw === 'false' ? false : undefined;
206
- } else if (type === 'id-list') {
207
- return raw.split(/\s+/);
208
- } else if (type === 'integer') {
209
- return parseInt(raw, 10);
210
- } else if (type === 'number') {
211
- return parseFloat(raw);
212
- } else if (type === 'token-list') {
213
- return raw.split(/\s+/);
214
- } else {
215
- return raw;
915
+ const _getOwner = function(node, owners) {
916
+ if (node.nodeType === node.ELEMENT_NODE && node.id) {
917
+ const selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
918
+ if (owners) {
919
+ for (const owner of owners) {
920
+ if (owner.matches(selector)) {
921
+ return owner;
922
+ }
923
+ }
924
+ } else {
925
+ return document.querySelector(selector);
926
+ }
216
927
  }
217
- }
928
+ };
218
929
 
219
- // TODO
220
- // autocomplete
221
- // contextmenu -> aria-haspopup
222
- // indeterminate -> aria-checked="mixed"
223
- // list -> aria-controls
930
+ const _getParentNode = function(node, owners) {
931
+ return _getOwner(node, owners) || node.parentNode;
932
+ };
224
933
 
225
- if (key === 'level') {
226
- for (let i = 1; i <= 6; i++) {
227
- if (el.tagName.toLowerCase() === 'h' + i) {
228
- return i;
934
+ const detectLoop = function(node, owners) {
935
+ const seen = [node];
936
+ while ((node = _getParentNode(node, owners))) {
937
+ if (seen.includes(node)) {
938
+ return true;
229
939
  }
940
+ seen.push(node);
230
941
  }
231
- } else if (constants.attributeWeakMapping.hasOwnProperty(key)) {
232
- return el[constants.attributeWeakMapping[key]];
233
- }
942
+ };
234
943
 
235
- if (key in constants.attrsWithDefaults) {
236
- const role = getRole(el);
237
- const defaults = constants.roles[role].defaults;
238
- if (defaults && defaults.hasOwnProperty(key)) {
239
- return defaults[key];
944
+ const getOwner = function(node, owners) {
945
+ const owner = _getOwner(node, owners);
946
+ if (owner && !detectLoop(node, owners)) {
947
+ return owner;
240
948
  }
241
- }
949
+ };
242
950
 
243
- if (type === 'bool' || type === 'tristate') {
244
- return false;
245
- }
246
- };
247
-
248
- module.exports = {
249
- getRole: getRole,
250
- hasRole: hasRole,
251
- getAttribute: getAttribute,
252
- };
253
-
254
- },{"./constants.js":4}],4:[function(require,module,exports){
255
- // https://www.w3.org/TR/wai-aria/#state_prop_def
256
- exports.attributes = {
257
- 'activedescendant': 'id',
258
- 'atomic': 'bool',
259
- 'autocomplete': 'token',
260
- 'braillelabel': 'string',
261
- 'brailleroledescription': 'string',
262
- 'busy': 'bool',
263
- 'checked': 'tristate',
264
- 'colcount': 'int',
265
- 'colindex': 'int',
266
- 'colindextext': 'string',
267
- 'colspan': 'int',
268
- 'controls': 'id-list',
269
- 'current': 'token',
270
- 'describedby': 'id-list',
271
- 'description': 'string',
272
- 'details': 'id',
273
- 'disabled': 'bool',
274
- 'dropeffect': 'token-list',
275
- 'errormessage': 'id',
276
- 'expanded': 'bool-undefined',
277
- 'flowto': 'id-list',
278
- 'grabbed': 'bool-undefined',
279
- 'haspopup': 'token',
280
- 'hidden': 'bool-undefined',
281
- 'invalid': 'token',
282
- 'keyshortcuts': 'string',
283
- 'label': 'string',
284
- 'labelledby': 'id-list',
285
- 'level': 'int',
286
- 'live': 'token',
287
- 'modal': 'bool',
288
- 'multiline': 'bool',
289
- 'multiselectable': 'bool',
290
- 'orientation': 'token',
291
- 'owns': 'id-list',
292
- 'placeholder': 'string',
293
- 'posinset': 'int',
294
- 'pressed': 'tristate',
295
- 'readonly': 'bool',
296
- 'relevant': 'token-list',
297
- 'required': 'bool',
298
- 'roledescription': 'string',
299
- 'rowcount': 'int',
300
- 'rowindex': 'int',
301
- 'rowindextext': 'string',
302
- 'rowspan': 'int',
303
- 'selected': 'bool-undefined',
304
- 'setsize': 'int',
305
- 'sort': 'token',
306
- 'valuemax': 'number',
307
- 'valuemin': 'number',
308
- 'valuenow': 'number',
309
- 'valuetext': 'string',
310
- };
311
-
312
- exports.attributeStrongMapping = {
313
- 'disabled': 'disabled',
314
- 'placeholder': 'placeholder',
315
- 'readonly': 'readOnly',
316
- 'required': 'required',
317
- };
318
-
319
- exports.attributeWeakMapping = {
320
- 'checked': 'checked',
321
- 'colspan': 'colSpan',
322
- 'expanded': 'open',
323
- 'multiselectable': 'multiple',
324
- 'rowspan': 'rowSpan',
325
- 'selected': 'selected',
326
- };
327
-
328
- // https://www.w3.org/TR/html/dom.html#sectioning-content-2
329
- const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
330
-
331
- const svgSelectors = function(selector) {
332
- return [
333
- // `${selector}:has(> title:not(:empty))`,
334
- // `${selector}:has(> desc:not(:empty))`,
335
- `${selector}[aria-label]`,
336
- `${selector}[aria-roledescription]`,
337
- `${selector}[aria-labelledby]`,
338
- `${selector}[aria-describedby]`,
339
- `${selector}[tabindex]`,
340
- `${selector}[role]`,
341
- ];
342
- };
343
-
344
- // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
345
- // https://www.w3.org/TR/wai-aria/roles
346
- exports.roles = {
347
- alert: {
348
- childRoles: ['alertdialog'],
349
- defaults: {
350
- 'live': 'assertive',
351
- 'atomic': true,
352
- },
353
- },
354
- alertdialog: {},
355
- application: {},
356
- article: {
357
- selectors: ['article'],
358
- childRoles: ['comment'],
359
- },
360
- banner: {
361
- selectors: [`header:not(main *, ${scoped})`],
362
- },
363
- blockquote: {
364
- selectors: ['blockquote'],
365
- },
366
- button: {
367
- selectors: [
368
- 'button',
369
- 'input[type="button"]',
370
- 'input[type="image"]',
371
- 'input[type="reset"]',
372
- 'input[type="submit"]',
373
- 'summary',
374
- ],
375
- nameFromContents: true,
376
- },
377
- caption: {
378
- selectors: ['caption', 'figcaption'],
379
- },
380
- cell: {
381
- selectors: ['td', 'td ~ th:not([scope])'],
382
- childRoles: ['columnheader', 'gridcell', 'rowheader'],
383
- nameFromContents: true,
384
- },
385
- checkbox: {
386
- selectors: ['input[type="checkbox"]'],
387
- childRoles: ['switch'],
388
- nameFromContents: true,
389
- defaults: {
390
- 'checked': 'false',
391
- },
392
- },
393
- code: {
394
- selectors: ['code'],
395
- },
396
- columnheader: {
397
- selectors: ['th[scope="col"]'],
398
- nameFromContents: true,
399
- },
400
- combobox: {
401
- selectors: [
402
- 'input:not([type])[list]',
403
- 'input[type="email"][list]',
404
- 'input[type="search"][list]',
405
- 'input[type="tel"][list]',
406
- 'input[type="text"][list]',
407
- 'input[type="url"][list]',
408
- 'select:not([size]):not([multiple])',
409
- 'select[size="0"]:not([multiple])',
410
- 'select[size="1"]:not([multiple])',
411
- ],
412
- defaults: {
413
- 'expanded': false,
414
- 'haspopup': 'listbox',
415
- },
416
- },
417
- command: {
418
- abstract: true,
419
- childRoles: ['button', 'link', 'menuitem'],
420
- },
421
- comment: {
422
- nameFromContents: true,
423
- },
424
- complementary: {
425
- selectors: [
426
- `aside:not(${scoped})`,
427
- 'aside[aria-label]',
428
- 'aside[aria-labelledby]',
429
- 'aside[title]',
430
- ],
431
- },
432
- composite: {
433
- abstract: true,
434
- childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
435
- },
436
- contentinfo: {
437
- selectors: [`footer:not(main *, ${scoped})`],
438
- },
439
- definition: {
440
- selectors: ['dd'],
441
- },
442
- deletion: {
443
- selectors: ['del', 's'],
444
- },
445
- dialog: {
446
- selectors: ['dialog'],
447
- childRoles: ['alertdialog'],
448
- },
449
- 'doc-abstract': {},
450
- 'doc-acknowledgments': {},
451
- 'doc-afterword': {},
452
- 'doc-appendix': {},
453
- 'doc-backlink': {
454
- nameFromContents: true,
455
- },
456
- 'doc-biblioentry': {},
457
- 'doc-bibliography': {},
458
- 'doc-biblioref': {
459
- nameFromContents: true,
460
- },
461
- 'doc-chapter': {},
462
- 'doc-colophon': {},
463
- 'doc-conclusion': {},
464
- 'doc-cover': {},
465
- 'doc-credit': {},
466
- 'doc-credits': {},
467
- 'doc-dedication': {},
468
- 'doc-endnote': {},
469
- 'doc-endnotes': {},
470
- 'doc-epilogue': {},
471
- 'doc-epigraph': {},
472
- 'doc-errata': {},
473
- 'doc-example': {},
474
- 'doc-footnote': {},
475
- 'doc-foreword': {},
476
- 'doc-glossary': {},
477
- 'doc-glossref': {
478
- nameFromContents: true,
479
- },
480
- 'doc-index': {},
481
- 'doc-introduction': {},
482
- 'doc-noteref': {
483
- nameFromContents: true,
484
- },
485
- 'doc-notice': {},
486
- 'doc-pagebreak': {
487
- nameFromContents: true,
488
- },
489
- 'doc-pagefooter': {},
490
- 'doc-pageheader': {},
491
- 'doc-pagelist': {},
492
- 'doc-part': {},
493
- 'doc-preface': {},
494
- 'doc-prologue': {},
495
- 'doc-pullquote': {},
496
- 'doc-qna': {},
497
- 'doc-subtitle': {
498
- nameFromContents: true,
499
- },
500
- 'doc-tip': {},
501
- 'doc-toc': {},
502
- document: {
503
- selectors: ['html'],
504
- childRoles: ['article', 'graphics-document'],
505
- },
506
- emphasis: {
507
- selectors: ['em'],
508
- },
509
- feed: {},
510
- figure: {
511
- selectors: ['figure'],
512
- childRoles: ['doc-example'],
513
- },
514
- form: {
515
- selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
516
- },
517
- generic: {
518
- selectors: [
519
- 'a:not([*|href])',
520
- 'area:not([*|href])',
521
- `aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
522
- 'b',
523
- 'bdi',
524
- 'bdo',
525
- 'body',
526
- 'data',
527
- 'div',
528
- // footer scoped
529
- // header scoped
530
- 'i',
531
- 'li:not(ul > li):not(ol > li)',
532
- 'pre',
533
- 'q',
534
- 'samp',
535
- 'section:not([aria-label]):not([aria-labelledby]):not([title])',
536
- 'small',
537
- 'span',
538
- 'u',
539
- ],
540
- },
541
- 'graphics-document': {
542
- selectors: ['svg'],
543
- },
544
- 'graphics-object': {
545
- selectors: [
546
- ...svgSelectors('symbol'),
547
- ...svgSelectors('use'),
548
- ],
549
- },
550
- 'graphics-symbol': {
551
- selectors: [
552
- ...svgSelectors('circle'),
553
- ...svgSelectors('ellipse'),
554
- ...svgSelectors('line'),
555
- ...svgSelectors('path'),
556
- ...svgSelectors('polygon'),
557
- ...svgSelectors('polyline'),
558
- ...svgSelectors('rect'),
559
- ],
560
- },
561
- grid: {
562
- childRoles: ['treegrid'],
563
- },
564
- gridcell: {
565
- childRoles: ['columnheader', 'rowheader'],
566
- nameFromContents: true,
567
- },
568
- group: {
569
- selectors: [
570
- 'address',
571
- 'details',
572
- 'fieldset',
573
- 'hgroup',
574
- 'optgroup',
575
- ...svgSelectors('foreignObject'),
576
- ...svgSelectors('g'),
577
- 'text',
578
- ...svgSelectors('textPath'),
579
- ...svgSelectors('tspan'),
580
- ],
581
- childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
582
- },
583
- heading: {
584
- selectors: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
585
- nameFromContents: true,
586
- defaults: {
587
- 'level': 2,
588
- },
589
- },
590
- image: {
591
- selectors: [
592
- 'img:not([alt=""])',
593
- 'graphics-symbol',
594
- ...svgSelectors('image'),
595
- ...svgSelectors('mesh'),
596
- ],
597
- childRoles: ['doc-cover'],
598
- },
599
- input: {
600
- abstract: true,
601
- childRoles: [
602
- 'checkbox',
603
- 'combobox',
604
- 'option',
605
- 'radio',
606
- 'slider',
607
- 'spinbutton',
608
- 'textbox',
609
- ],
610
- },
611
- insertion: {
612
- selectors: ['ins'],
613
- },
614
- landmark: {
615
- abstract: true,
616
- childRoles: [
617
- 'banner',
618
- 'complementary',
619
- 'contentinfo',
620
- 'doc-acknowledgments',
621
- 'doc-afterword',
622
- 'doc-appendix',
623
- 'doc-bibliography',
624
- 'doc-chapter',
625
- 'doc-conclusion',
626
- 'doc-credits',
627
- 'doc-endnotes',
628
- 'doc-epilogue',
629
- 'doc-errata',
630
- 'doc-foreword',
631
- 'doc-glossary',
632
- 'doc-introduction',
633
- 'doc-part',
634
- 'doc-preface',
635
- 'doc-prologue',
636
- 'form',
637
- 'main',
638
- 'navigation',
639
- 'region',
640
- 'search',
641
- ],
642
- },
643
- link: {
644
- selectors: ['a[*|href]', 'area[href]'],
645
- childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
646
- nameFromContents: true,
647
- },
648
- list: {
649
- selectors: ['dl', 'ol', 'ul', 'menu'],
650
- childRoles: ['feed'],
651
- },
652
- listbox: {
653
- selectors: [
654
- 'datalist',
655
- 'select[multiple]',
656
- 'select[size]:not([size="0"]):not([size="1"])',
657
- ],
658
- defaults: {
659
- 'orientation': 'vertical',
660
- },
661
- },
662
- listitem: {
663
- selectors: ['ol > li', 'ul > li'],
664
- childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
665
- },
666
- log: {
667
- defaults: {
668
- 'live': 'polite',
669
- },
670
- },
671
- main: {
672
- selectors: ['main'],
673
- },
674
- mark: {
675
- selectors: ['mark'],
676
- },
677
- marquee: {},
678
- math: {
679
- selectors: ['math'],
680
- },
681
- meter: {
682
- selectors: ['meter'],
683
- defaults: {
684
- 'valuemin': 0,
685
- 'valuemax': 100,
686
- },
687
- },
688
- menu: {
689
- childRoles: ['menubar'],
690
- defaults: {
691
- 'orientation': 'vertical',
692
- },
693
- },
694
- menubar: {
695
- defaults: {
696
- 'orientation': 'horizontal',
697
- },
698
- },
699
- menuitem: {
700
- childRoles: ['menuitemcheckbox', 'menuitemradio'],
701
- nameFromContents: true,
702
- },
703
- menuitemcheckbox: {
704
- nameFromContents: true,
705
- defaults: {
706
- 'checked': 'false',
707
- },
708
- },
709
- menuitemradio: {
710
- nameFromContents: true,
711
- defaults: {
712
- 'checked': 'false',
713
- },
714
- },
715
- navigation: {
716
- selectors: ['nav'],
717
- childRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
718
- },
719
- none: {
720
- selectors: ['img[alt=""]'],
721
- },
722
- note: {
723
- childRoles: ['doc-notice', 'doc-tip'],
724
- },
725
- option: {
726
- selectors: ['option'],
727
- childRoles: ['treeitem'],
728
- nameFromContents: true,
729
- defaults: {
730
- 'selected': 'false',
731
- },
732
- },
733
- paragraph: {
734
- selectors: ['p'],
735
- },
736
- progressbar: {
737
- selectors: ['progress'],
738
- defaults: {
739
- 'valuemin': 0,
740
- 'valuemax': 100,
741
- },
742
- },
743
- radio: {
744
- selectors: ['input[type="radio"]'],
745
- childRoles: ['menuitemradio'],
746
- nameFromContents: true,
747
- defaults: {
748
- 'checked': 'false',
749
- },
750
- },
751
- radiogroup: {},
752
- range: {
753
- abstract: true,
754
- childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
755
- },
756
- region: {
757
- selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
758
- },
759
- roletype: {
760
- abstract: true,
761
- childRoles: ['structure', 'widget', 'window'],
762
- },
763
- row: {
764
- selectors: ['tr'],
765
- nameFromContents: true,
766
- },
767
- rowgroup: {
768
- selectors: ['tbody', 'thead', 'tfoot'],
769
- },
770
- rowheader: {
771
- selectors: ['th[scope="row"]', 'th:not([scope]):not(td ~ th)'],
772
- nameFromContents: true,
773
- },
774
- scrollbar: {
775
- defaults: {
776
- 'orientation': 'vertical',
777
- 'valuemin': 0,
778
- 'valuemax': 100,
779
- },
780
- },
781
- search: {
782
- selectors: ['search'],
783
- },
784
- searchbox: {
785
- selectors: ['input[type="search"]:not([list])'],
786
- },
787
- section: {
788
- abstract: true,
789
- childRoles: [
790
- 'alert',
791
- 'blockquote',
792
- 'caption',
793
- 'cell',
794
- 'code',
795
- 'definition',
796
- 'deletion',
797
- 'doc-abstract',
798
- 'doc-colophon',
799
- 'doc-credit',
800
- 'doc-dedication',
801
- 'doc-epigraph',
802
- 'doc-footnote',
803
- 'doc-pagefooter',
804
- 'doc-pageheader',
805
- 'doc-pullquote',
806
- 'doc-qna',
807
- 'emphasis',
808
- 'figure',
809
- 'group',
810
- 'image',
811
- 'insertion',
812
- 'landmark',
813
- 'list',
814
- 'listitem',
815
- 'log',
816
- 'mark',
817
- 'marquee',
818
- 'math',
819
- 'note',
820
- 'paragraph',
821
- 'status',
822
- 'strong',
823
- 'subscript',
824
- 'suggestion',
825
- 'superscript',
826
- 'table',
827
- 'tabpanel',
828
- 'term',
829
- 'time',
830
- 'tooltip',
831
- ],
832
- },
833
- sectionhead: {
834
- abstract: true,
835
- childRoles: [
836
- 'columnheader',
837
- 'doc-subtitle',
838
- 'heading',
839
- 'rowheader',
840
- 'tab',
841
- ],
842
- nameFromContents: true,
843
- },
844
- select: {
845
- abstract: true,
846
- childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
847
- },
848
- separator: {
849
- // assume not focussable because <hr> is not
850
- selectors: ['hr'],
851
- childRoles: ['doc-pagebreak'],
852
- defaults: {
853
- 'orientation': 'horizontal',
854
- 'valuemin': 0,
855
- 'valuemax': 100,
856
- },
857
- },
858
- slider: {
859
- selectors: ['input[type="range"]'],
860
- defaults: {
861
- 'orientation': 'horizontal',
862
- 'valuemin': 0,
863
- 'valuemax': 100,
864
- // FIXME: halfway between actual valuemin and valuemax
865
- 'valuenow': 50,
866
- },
867
- },
868
- spinbutton: {
869
- selectors: ['input[type="number"]'],
870
- defaults: {
871
- // FIXME: no valuemin/valuemax/valuenow
872
- },
873
- },
874
- status: {
875
- selectors: ['output'],
876
- childRoles: ['timer'],
877
- defaults: {
878
- 'live': 'polite',
879
- 'atomic': true,
880
- },
881
- },
882
- strong: {
883
- selectors: ['strong'],
884
- },
885
- structure: {
886
- abstract: true,
887
- childRoles: [
888
- 'application',
889
- 'document',
890
- 'none',
891
- 'generic',
892
- 'range',
893
- 'rowgroup',
894
- 'section',
895
- 'sectionhead',
896
- 'separator',
897
- ],
898
- },
899
- suggestion: {},
900
- subscript: {
901
- selectors: ['sub'],
902
- },
903
- superscript: {
904
- selectors: ['sup'],
905
- },
906
- switch: {
907
- nameFromContents: true,
908
- defaults: {
909
- 'checked': false,
910
- },
911
- },
912
- tab: {
913
- nameFromContents: true,
914
- defaults: {
915
- 'selected': false,
916
- },
917
- },
918
- table: {
919
- selectors: ['table'],
920
- childRoles: ['grid'],
921
- },
922
- tablist: {
923
- defaults: {
924
- 'orientation': 'horizontal',
925
- },
926
- },
927
- tabpanel: {},
928
- term: {
929
- selectors: ['dfn', 'dt'],
930
- },
931
- textbox: {
932
- selectors: [
933
- 'input:not([type]):not([list])',
934
- 'input[type="email"]:not([list])',
935
- 'input[type="tel"]:not([list])',
936
- 'input[type="text"]:not([list])',
937
- 'input[type="url"]:not([list])',
938
- 'textarea',
939
- ],
940
- childRoles: ['searchbox'],
941
- },
942
- time: {
943
- selectors: ['time'],
944
- },
945
- timer: {
946
- defaults: {
947
- 'live': 'off',
948
- },
949
- },
950
- toolbar: {
951
- defaults: {
952
- 'orientation': 'horizontal',
953
- },
954
- },
955
- tooltip: {
956
- nameFromContents: true,
957
- },
958
- tree: {
959
- childRoles: ['treegrid'],
960
- defaults: {
961
- 'orientation': 'vertical',
962
- },
963
- },
964
- treegrid: {},
965
- treeitem: {
966
- nameFromContents: true,
967
- },
968
- widget: {
969
- abstract: true,
970
- childRoles: [
971
- 'command',
972
- 'composite',
973
- 'gridcell',
974
- 'input',
975
- 'progressbar',
976
- 'row',
977
- 'scrollbar',
978
- 'separator',
979
- 'tab',
980
- ],
981
- },
982
- window: {
983
- abstract: true,
984
- childRoles: ['dialog'],
985
- },
986
- };
987
-
988
- const getSubRoles = function(role) {
989
- const children = (exports.roles[role]).childRoles || [];
990
- const descendents = children.map(getSubRoles);
991
-
992
- const result = [role];
993
-
994
- descendents.forEach(list => {
995
- list.forEach(r => {
996
- if (!result.includes(r)) {
997
- result.push(r);
998
- }
999
- });
1000
- });
951
+ const getParentNode = function(node, owners) {
952
+ return getOwner(node, owners) || node.parentNode;
953
+ };
1001
954
 
1002
- return result;
1003
- };
955
+ const isHidden = function(node) {
956
+ return node.nodeType === node.ELEMENT_NODE && getAttribute(node, 'hidden');
957
+ };
1004
958
 
1005
- exports.attrsWithDefaults = [];
959
+ const getChildNodes = function(node, owners) {
960
+ const childNodes = [];
1006
961
 
1007
- for (const role in exports.roles) {
1008
- exports.roles[role].subRoles = getSubRoles(role);
1009
- for (const key in exports.roles[role].defaults) {
1010
- if (!exports.attrsWithDefaults.includes(key)) {
1011
- exports.attrsWithDefaults.push(key);
962
+ for (let i = 0; i < node.childNodes.length; i++) {
963
+ const child = node.childNodes[i];
964
+ if (!getOwner(child, owners) && !isHidden(child)) {
965
+ childNodes.push(child);
966
+ }
1012
967
  }
1013
- }
1014
- }
1015
-
1016
- exports.aliases = {
1017
- 'presentation': 'none',
1018
- 'directory': 'list',
1019
- 'img': 'image',
1020
- };
1021
-
1022
- exports.nameFromDescendant = {
1023
- 'figure': 'figcaption',
1024
- 'table': 'caption',
1025
- 'fieldset': 'legend',
1026
- };
1027
-
1028
- exports.nameDefaults = {
1029
- 'input[type="submit"]': 'Submit',
1030
- 'input[type="reset"]': 'Reset',
1031
- 'summary': 'Details',
1032
- };
1033
-
1034
- },{}],5:[function(require,module,exports){
1035
- const constants = require('./constants.js');
1036
- const atree = require('./atree.js');
1037
- const query = require('./query.js');
1038
-
1039
- const addSpaces = function(text, el, pseudoSelector) {
1040
- // https://github.com/w3c/accname/issues/3
1041
- const styles = window.getComputedStyle(el, pseudoSelector);
1042
- const inline = styles.display === 'inline';
1043
- return inline ? text : ` ${text} `;
1044
- };
1045
-
1046
- const getPseudoContent = function(el, pseudoSelector) {
1047
- const styles = window.getComputedStyle(el, pseudoSelector);
1048
- let tail = styles.getPropertyValue('content').trim();
1049
- let ret = [];
1050
-
1051
- let match;
1052
- while (tail.length) {
1053
- if (match = tail.match(/^"([^"]*)"/)) {
1054
- ret.push(match[1]);
1055
- } else if (match = tail.match(/^([a-z-]+)\(([^)]*)\)/)) {
1056
- if (match[1] === 'attr') {
1057
- ret.push(el.getAttribute(match[2]) || '');
968
+
969
+ if (node.nodeType === node.ELEMENT_NODE) {
970
+ const owns = getAttribute(node, 'owns') || [];
971
+ for (let i = 0; i < owns.length; i++) {
972
+ const child = document.getElementById(owns[i]);
973
+ // double check with getOwner for consistency
974
+ if (child && getOwner(child, owners) === node && !isHidden(child)) {
975
+ childNodes.push(child);
976
+ }
1058
977
  }
1059
- } else if (match = tail.match(/^([a-z-]+)/)) {
1060
- if (match[1] === 'open-quote' || match[1] === 'close-quote') {
1061
- ret.push('"');
978
+ }
979
+
980
+ return childNodes;
981
+ };
982
+
983
+ const walk = function(root, fn) {
984
+ const owners = document.querySelectorAll('[aria-owns]');
985
+ let queue = [root];
986
+ while (queue.length) {
987
+ const item = queue.shift();
988
+ fn(item);
989
+ queue = getChildNodes(item, owners).concat(queue);
990
+ }
991
+ };
992
+
993
+ const searchUp = function(node, test) {
994
+ const candidate = getParentNode(node);
995
+ if (candidate) {
996
+ if (test(candidate)) {
997
+ return candidate;
998
+ } else {
999
+ return searchUp(candidate, test);
1062
1000
  }
1063
- } else if (match = tail.match(/^\//)) {
1064
- ret = [];
1001
+ }
1002
+ };
1003
+
1004
+ const matches = function(el, selector) {
1005
+ if (selector.substr(0, 1) === ':') {
1006
+ const attr = selector.substr(1);
1007
+ return getAttribute(el, attr);
1008
+ } else if (selector.substr(0, 1) === '[') {
1009
+ const match = /\[([a-z]+)="(.*)"\]/.exec(selector);
1010
+ const actual = getAttribute(el, match[1]);
1011
+ const rawValue = match[2];
1012
+ return actual.toString() === rawValue;
1065
1013
  } else {
1066
- // invalid content, ignore
1067
- return '';
1014
+ return hasRole(el, selector.split(','));
1068
1015
  }
1069
- tail = tail.slice(match[0].length).trim();
1070
- }
1016
+ };
1071
1017
 
1072
- return addSpaces(ret.join(''), el, pseudoSelector);
1073
- };
1018
+ const _querySelector = function(all) {
1019
+ return function(root, selector) {
1020
+ const results = [];
1021
+ try {
1022
+ walk(root, node => {
1023
+ if (node.nodeType === node.ELEMENT_NODE) {
1024
+ // FIXME: skip hidden elements
1025
+ if (matches(node, selector)) {
1026
+ results.push(node);
1027
+ if (!all) {
1028
+ throw 'StopIteration';
1029
+ }
1030
+ }
1031
+ }
1032
+ });
1033
+ } catch (e) {
1034
+ if (e !== 'StopIteration') {
1035
+ throw e;
1036
+ }
1037
+ }
1038
+ return all ? results : results[0];
1039
+ };
1040
+ };
1041
+
1042
+ const closest = function(el, selector) {
1043
+ return searchUp(el, candidate => {
1044
+ if (candidate.nodeType === candidate.ELEMENT_NODE) {
1045
+ return matches(candidate, selector);
1046
+ }
1047
+ });
1048
+ };
1074
1049
 
1075
- const getContent = function(root, ongoingLabelledBy, visited) {
1076
- const children = atree.getChildNodes(root);
1050
+ const querySelector = _querySelector();
1051
+ const querySelectorAll = _querySelector(true);
1052
+
1053
+ const addSpaces = function(text, el, pseudoSelector) {
1054
+ // https://github.com/w3c/accname/issues/3
1055
+ const styles = window.getComputedStyle(el, pseudoSelector);
1056
+ const inline = styles.display === 'inline';
1057
+ return inline ? text : ` ${text} `;
1058
+ };
1077
1059
 
1078
- let ret = '';
1079
- for (let i = 0; i < children.length; i++) {
1080
- const node = children[i];
1081
- if (node.nodeType === node.TEXT_NODE) {
1082
- ret += node.textContent;
1083
- } else if (node.nodeType === node.ELEMENT_NODE) {
1084
- if (node.tagName.toLowerCase() === 'br') {
1085
- ret += '\n';
1060
+ const getPseudoContent = function(el, pseudoSelector) {
1061
+ const styles = window.getComputedStyle(el, pseudoSelector);
1062
+ let tail = styles.getPropertyValue('content').trim();
1063
+ let ret = [];
1064
+
1065
+ let match;
1066
+ while (tail.length) {
1067
+ if ((match = tail.match(/^"([^"]*)"/))) {
1068
+ ret.push(match[1]);
1069
+ } else if ((match = tail.match(/^([a-z-]+)\(([^)]*)\)/))) {
1070
+ if (match[1] === 'attr') {
1071
+ ret.push(el.getAttribute(match[2]) || '');
1072
+ }
1073
+ } else if ((match = tail.match(/^([a-z-]+)/))) {
1074
+ if (match[1] === 'open-quote' || match[1] === 'close-quote') {
1075
+ ret.push('"');
1076
+ }
1077
+ } else if ((match = tail.match(/^\//))) {
1078
+ ret = [];
1086
1079
  } else {
1087
- ret += getName(node, true, ongoingLabelledBy, visited);
1080
+ // invalid content, ignore
1081
+ return '';
1088
1082
  }
1083
+ tail = tail.slice(match[0].length).trim();
1089
1084
  }
1090
- }
1091
1085
 
1092
- return ret;
1093
- };
1086
+ return addSpaces(ret.join(''), el, pseudoSelector);
1087
+ };
1094
1088
 
1095
- const allowNameFromContent = function(el) {
1096
- const role = query.getRole(el);
1097
- if (role) {
1098
- return constants.roles[role].nameFromContents;
1099
- }
1100
- };
1089
+ const getContent = function(root, ongoingLabelledBy, visited) {
1090
+ const children = getChildNodes(root);
1091
+
1092
+ let ret = '';
1093
+ for (let i = 0; i < children.length; i++) {
1094
+ const node = children[i];
1095
+ if (node.nodeType === node.TEXT_NODE) {
1096
+ const styles = window.getComputedStyle(node.parentElement);
1097
+ if (styles.textTransform === 'uppercase') {
1098
+ ret += node.textContent.toUpperCase();
1099
+ } else if (styles.textTransform === 'lowercase') {
1100
+ ret += node.textContent.toLowerCase();
1101
+ } else if (styles.textTransform === 'capitalize') {
1102
+ ret += node.textContent.replace(/\b\w/g, c => c.toUpperCase());
1103
+ } else {
1104
+ ret += node.textContent;
1105
+ }
1106
+ } else if (node.nodeType === node.ELEMENT_NODE) {
1107
+ if (node.tagName.toLowerCase() === 'br') {
1108
+ ret += '\n';
1109
+ } else {
1110
+ ret += getNameRaw(node, true, ongoingLabelledBy, visited);
1111
+ }
1112
+ }
1113
+ }
1101
1114
 
1102
- const getName = function(el, recursive, ongoingLabelledBy, visited, directReference) {
1103
- let ret = '';
1115
+ return ret;
1116
+ };
1104
1117
 
1105
- visited = visited || [];
1106
- if (visited.includes(el)) {
1107
- if (!directReference) {
1108
- return '';
1118
+ const allowNameFromContent = function(el) {
1119
+ const role = getRole(el);
1120
+ if (role) {
1121
+ return roles[role].nameFromContents;
1109
1122
  }
1110
- } else {
1111
- visited.push(el);
1112
- }
1123
+ };
1113
1124
 
1114
- // A
1115
- // handled in atree
1125
+ const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directReference) {
1126
+ let ret = '';
1116
1127
 
1117
- // B
1118
- if (!ongoingLabelledBy && el.matches('[aria-labelledby]')) {
1119
- const ids = el.getAttribute('aria-labelledby').split(/\s+/);
1120
- const strings = ids.map(id => {
1121
- const label = document.getElementById(id);
1122
- return label ? getName(label, true, true, visited, true) : '';
1123
- });
1124
- ret = strings.join(' ');
1125
- }
1128
+ visited = visited || [];
1129
+ if (visited.includes(el)) {
1130
+ if (!directReference) {
1131
+ return '';
1132
+ }
1133
+ } else {
1134
+ visited.push(el);
1135
+ }
1126
1136
 
1127
- // E (the current draft has this at this high priority)
1128
- if (!ret.trim() && recursive) {
1129
- if (query.matches(el, 'textbox')) {
1130
- ret = el.value || el.textContent;
1131
- } else if (query.matches(el, 'combobox,listbox')) {
1132
- const selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
1133
- if (selected) {
1134
- ret = getName(selected, recursive, ongoingLabelledBy, visited);
1135
- } else {
1136
- ret = el.value || '';
1137
+ // A
1138
+ // handled in atree
1139
+
1140
+ // B
1141
+ if (!ongoingLabelledBy && el.matches('[aria-labelledby]')) {
1142
+ const ids = el.getAttribute('aria-labelledby').split(/\s+/);
1143
+ const strings = ids.map(id => {
1144
+ const label = document.getElementById(id);
1145
+ return label ? getNameRaw(label, true, true, visited, true) : '';
1146
+ });
1147
+ ret = strings.join(' ');
1148
+ }
1149
+
1150
+ // E (the current draft has this at this high priority)
1151
+ if (!ret.trim() && recursive) {
1152
+ if (matches(el, 'textbox')) {
1153
+ ret = el.value || el.textContent;
1154
+ } else if (matches(el, 'combobox,listbox')) {
1155
+ const selected = querySelector(el, ':selected') || querySelector(el, 'option');
1156
+ if (selected) {
1157
+ ret = getNameRaw(selected, recursive, ongoingLabelledBy, visited);
1158
+ } else {
1159
+ ret = el.value || '';
1160
+ }
1161
+ } else if (matches(el, 'range')) {
1162
+ ret = '' + (getAttribute(el, 'valuetext') || getAttribute(el, 'valuenow') || el.value);
1137
1163
  }
1138
- } else if (query.matches(el, 'range')) {
1139
- ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
1140
1164
  }
1141
- }
1142
1165
 
1143
- // C
1144
- if (!ret.trim() && el.matches('[aria-label]')) {
1145
- // FIXME: may skip to 2E
1146
- ret = el.getAttribute('aria-label');
1147
- }
1166
+ // C
1167
+ if (!ret.trim() && el.matches('[aria-label]')) {
1168
+ // FIXME: may skip to 2E
1169
+ ret = el.getAttribute('aria-label');
1170
+ }
1148
1171
 
1149
- // D
1150
- if (!ret.trim() && !recursive && el.labels) {
1151
- const strings = Array.prototype.map.call(el.labels, label => {
1152
- return getName(label, true, ongoingLabelledBy, visited);
1153
- });
1154
- ret = strings.join(' ');
1155
- }
1156
- if (!ret.trim()) {
1157
- ret = el.alt || '';
1158
- }
1159
- if (!ret.trim() && el.matches('abbr,acronym') && el.title) {
1160
- ret = el.title;
1161
- }
1162
- if (!ret.trim()) {
1163
- for (const selector in constants.nameFromDescendant) {
1164
- if (el.matches(selector)) {
1165
- const descendant = el.querySelector(constants.nameFromDescendant[selector]);
1166
- if (descendant) {
1167
- ret = getName(descendant, true, ongoingLabelledBy, visited);
1172
+ // D
1173
+ if (!ret.trim() && !recursive && el.labels) {
1174
+ const strings = Array.prototype.map.call(el.labels, label => {
1175
+ return getNameRaw(label, true, ongoingLabelledBy, visited);
1176
+ });
1177
+ ret = strings.join(' ');
1178
+ }
1179
+ if (!ret.trim()) {
1180
+ ret = el.alt || '';
1181
+ }
1182
+ if (!ret.trim() && el.matches('abbr,acronym') && el.title) {
1183
+ ret = el.title;
1184
+ }
1185
+ if (!ret.trim()) {
1186
+ for (const selector in nameFromDescendant) {
1187
+ if (el.matches(selector)) {
1188
+ const descendant = el.querySelector(nameFromDescendant[selector]);
1189
+ if (descendant) {
1190
+ ret = getNameRaw(descendant, true, ongoingLabelledBy, visited);
1191
+ }
1168
1192
  }
1169
1193
  }
1170
1194
  }
1171
- }
1172
- if (!ret.trim() && el.matches('svg *')) {
1173
- const svgTitle = el.querySelector('title');
1174
- if (svgTitle && svgTitle.parentElement === el) {
1175
- ret = svgTitle.textContent;
1195
+ if (!ret.trim() && el.matches('svg *')) {
1196
+ const svgTitle = el.querySelector('title');
1197
+ if (svgTitle && svgTitle.parentElement === el) {
1198
+ ret = svgTitle.textContent;
1199
+ }
1200
+ }
1201
+ if (!ret.trim() && el.matches('a')) {
1202
+ ret = el.getAttribute('xlink:title') || '';
1176
1203
  }
1177
- }
1178
- if (!ret.trim() && el.matches('a')) {
1179
- ret = el.getAttribute('xlink:title') || '';
1180
- }
1181
1204
 
1182
- // F
1183
- // FIXME: menu is not mentioned in the spec
1184
- if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
1185
- ret = getContent(el, ongoingLabelledBy, visited);
1186
- }
1205
+ // F
1206
+ // FIXME: menu is not mentioned in the spec
1207
+ if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !matches(el, 'menu')) {
1208
+ ret = getContent(el, ongoingLabelledBy, visited);
1209
+ }
1187
1210
 
1188
- if (!ret.trim() && query.matches(el, 'button')) {
1189
- ret = el.value || '';
1190
- }
1211
+ if (!ret.trim() && matches(el, 'button')) {
1212
+ ret = el.value || '';
1213
+ }
1191
1214
 
1192
- if (!ret.trim()) {
1193
- for (const selector in constants.nameDefaults) {
1194
- if (el.matches(selector)) {
1195
- ret = constants.nameDefaults[selector];
1215
+ if (!ret.trim()) {
1216
+ for (const selector in nameDefaults) {
1217
+ if (el.matches(selector)) {
1218
+ ret = nameDefaults[selector];
1219
+ }
1196
1220
  }
1197
1221
  }
1198
- }
1199
1222
 
1200
- // G/H
1201
- // handled in getContent
1223
+ // G/H
1224
+ // handled in getContent
1202
1225
 
1203
- // I
1204
- if (!ret.trim()) {
1205
- ret = el.title || el.placeholder || '';
1206
- }
1207
-
1208
- // FIXME: not exactly sure about this, but it reduces the number of failing
1209
- // WPT tests. Whitespace is hard.
1210
- if (!ret.trim()) {
1211
- ret = ' ';
1212
- }
1226
+ // I
1227
+ if (!ret.trim()) {
1228
+ ret = el.title || el.placeholder || '';
1229
+ }
1213
1230
 
1214
- const before = getPseudoContent(el, ':before');
1215
- const after = getPseudoContent(el, ':after');
1216
- return addSpaces(before + ret + after, el);
1217
- };
1218
-
1219
- const getNameTrimmed = function(el) {
1220
- return getName(el)
1221
- .replace(/[ \n\r\t\f]+/g, ' ')
1222
- .replace(/^ /, '')
1223
- .replace(/ $/, '');
1224
- };
1225
-
1226
- const getDescription = function(el) {
1227
- let ret = '';
1228
-
1229
- if (el.matches('[aria-describedby]')) {
1230
- const ids = el.getAttribute('aria-describedby').split(/\s+/);
1231
- const strings = ids.map(id => {
1232
- const label = document.getElementById(id);
1233
- return label ? getName(label, true, true) : '';
1234
- });
1235
- ret = strings.join(' ');
1236
- } else if (el.matches('[aria-description]')) {
1237
- ret = el.getAttribute('aria-description');
1238
- } else if (el.matches('svg *')) {
1239
- const svgDesc = el.querySelector('desc');
1240
- if (svgDesc && svgDesc.parentElement === el) {
1241
- ret = svgDesc.textContent;
1231
+ // FIXME: not exactly sure about this, but it reduces the number of failing
1232
+ // WPT tests. Whitespace is hard.
1233
+ if (!ret.trim()) {
1234
+ ret = ' ';
1242
1235
  }
1243
- } else if (el.title) {
1244
- ret = el.title;
1245
- }
1246
- if (!ret.trim() && el.matches('a')) {
1247
- ret = el.getAttribute('xlink:title') || '';
1248
- }
1249
1236
 
1250
- ret = (ret || '').trim().replace(/\s+/g, ' ');
1237
+ const before = getPseudoContent(el, ':before');
1238
+ const after = getPseudoContent(el, ':after');
1239
+ return addSpaces(before + ret + after, el);
1240
+ };
1251
1241
 
1252
- if (ret === getNameTrimmed(el)) {
1253
- ret = '';
1254
- }
1242
+ const getName = function(el) {
1243
+ return getNameRaw(el)
1244
+ .replace(/[ \n\r\t\f]+/g, ' ')
1245
+ .replace(/^ /, '')
1246
+ .replace(/ $/, '');
1247
+ };
1255
1248
 
1256
- return ret;
1257
- };
1258
-
1259
- module.exports = {
1260
- getName: getNameTrimmed,
1261
- getDescription: getDescription,
1262
- };
1263
-
1264
- },{"./atree.js":2,"./constants.js":4,"./query.js":6}],6:[function(require,module,exports){
1265
- const attrs = require('./attrs.js');
1266
- const atree = require('./atree.js');
1267
-
1268
-
1269
- const matches = function(el, selector) {
1270
- if (selector.substr(0, 1) === ':') {
1271
- const attr = selector.substr(1);
1272
- return attrs.getAttribute(el, attr);
1273
- } else if (selector.substr(0, 1) === '[') {
1274
- const match = /\[([a-z]+)="(.*)"\]/.exec(selector);
1275
- const actual = attrs.getAttribute(el, match[1]);
1276
- const rawValue = match[2];
1277
- return actual.toString() === rawValue;
1278
- } else {
1279
- return attrs.hasRole(el, selector.split(','));
1280
- }
1281
- };
1282
-
1283
- const _querySelector = function(all) {
1284
- return function(root, selector) {
1285
- const results = [];
1286
- try {
1287
- atree.walk(root, node => {
1288
- if (node.nodeType === node.ELEMENT_NODE) {
1289
- // FIXME: skip hidden elements
1290
- if (matches(node, selector)) {
1291
- results.push(node);
1292
- if (!all) {
1293
- throw 'StopIteration';
1294
- }
1295
- }
1296
- }
1249
+ const getDescription = function(el) {
1250
+ let ret = '';
1251
+
1252
+ if (el.matches('[aria-describedby]')) {
1253
+ const ids = el.getAttribute('aria-describedby').split(/\s+/);
1254
+ const strings = ids.map(id => {
1255
+ const label = document.getElementById(id);
1256
+ return label ? getNameRaw(label, true, true) : '';
1297
1257
  });
1298
- } catch (e) {
1299
- if (e !== 'StopIteration') {
1300
- throw e;
1258
+ ret = strings.join(' ');
1259
+ } else if (el.matches('[aria-description]')) {
1260
+ ret = el.getAttribute('aria-description');
1261
+ } else if (el.matches('svg *')) {
1262
+ const svgDesc = el.querySelector('desc');
1263
+ if (svgDesc && svgDesc.parentElement === el) {
1264
+ ret = svgDesc.textContent;
1301
1265
  }
1266
+ } else if (el.title) {
1267
+ ret = el.title;
1268
+ }
1269
+ if (!ret.trim() && el.matches('a')) {
1270
+ ret = el.getAttribute('xlink:title') || '';
1302
1271
  }
1303
- return all ? results : results[0];
1304
- };
1305
- };
1306
1272
 
1307
- const closest = function(el, selector) {
1308
- return atree.searchUp(el, candidate => {
1309
- if (candidate.nodeType === candidate.ELEMENT_NODE) {
1310
- return matches(candidate, selector);
1273
+ ret = (ret || '').trim().replace(/\s+/g, ' ');
1274
+
1275
+ if (ret === getName(el)) {
1276
+ ret = '';
1311
1277
  }
1312
- });
1313
- };
1314
-
1315
- module.exports = {
1316
- getRole: el => attrs.getRole(el),
1317
- getAttribute: attrs.getAttribute,
1318
- matches: matches,
1319
- querySelector: _querySelector(),
1320
- querySelectorAll: _querySelector(true),
1321
- closest: closest,
1322
- };
1323
-
1324
- },{"./atree.js":2,"./attrs.js":3}]},{},[1])(1)
1325
- });
1278
+
1279
+ return ret;
1280
+ };
1281
+
1282
+ exports.closest = closest;
1283
+ exports.getAttribute = getAttribute;
1284
+ exports.getChildNodes = getChildNodes;
1285
+ exports.getDescription = getDescription;
1286
+ exports.getName = getName;
1287
+ exports.getParentNode = getParentNode;
1288
+ exports.getRole = getRole;
1289
+ exports.matches = matches;
1290
+ exports.querySelector = querySelector;
1291
+ exports.querySelectorAll = querySelectorAll;
1292
+
1293
+ }));