aria-api 0.6.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,1255 +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
+ };
59
95
 
60
- const getParentNode = function(node, owners) {
61
- return getOwner(node, owners) || node.parentNode;
62
- };
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
+ };
63
739
 
64
- const isHidden = function(node) {
65
- return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
66
- };
740
+ const getSubRoles = function(role) {
741
+ const children = (roles[role]).childRoles || [];
742
+ const descendents = children.map(getSubRoles);
67
743
 
68
- const getChildNodes = function(node, owners) {
69
- const childNodes = [];
744
+ const result = [role];
70
745
 
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
- }
746
+ descendents.forEach(list => {
747
+ list.forEach(r => {
748
+ if (!result.includes(r)) {
749
+ result.push(r);
750
+ }
751
+ });
752
+ });
77
753
 
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);
754
+ return result;
755
+ };
756
+
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
+ };
91
773
 
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
- };
774
+ const nameFromDescendant = {
775
+ 'figure': 'figcaption',
776
+ 'table': 'caption',
777
+ 'fieldset': 'legend',
778
+ };
779
+
780
+ const nameDefaults = {
781
+ 'input[type="submit"]': 'Submit',
782
+ 'input[type="reset"]': 'Reset',
783
+ 'summary': 'Details',
784
+ };
785
+
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
+ };
101
801
 
102
- const searchUp = function(node, test) {
103
- const candidate = getParentNode(node);
104
- if (candidate) {
105
- if (test(candidate)) {
106
- return candidate;
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') {
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
- 'busy': 'bool',
261
- 'checked': 'tristate',
262
- 'colcount': 'int',
263
- 'colindex': 'int',
264
- 'colspan': 'int',
265
- 'controls': 'id-list',
266
- 'current': 'token',
267
- 'describedby': 'id-list',
268
- 'details': 'id',
269
- 'disabled': 'bool',
270
- 'dropeffect': 'token-list',
271
- 'errormessage': 'id',
272
- 'expanded': 'bool-undefined',
273
- 'flowto': 'id-list',
274
- 'grabbed': 'bool-undefined',
275
- 'haspopup': 'token',
276
- 'hidden': 'bool-undefined',
277
- 'invalid': 'token',
278
- 'keyshortcuts': 'string',
279
- 'label': 'string',
280
- 'labelledby': 'id-list',
281
- 'level': 'int',
282
- 'live': 'token',
283
- 'modal': 'bool',
284
- 'multiline': 'bool',
285
- 'multiselectable': 'bool',
286
- 'orientation': 'token',
287
- 'owns': 'id-list',
288
- 'placeholder': 'string',
289
- 'posinset': 'int',
290
- 'pressed': 'tristate',
291
- 'readonly': 'bool',
292
- 'relevant': 'token-list',
293
- 'required': 'bool',
294
- 'roledescription': 'string',
295
- 'rowcount': 'int',
296
- 'rowindex': 'int',
297
- 'rowspan': 'int',
298
- 'selected': 'bool-undefined',
299
- 'setsize': 'int',
300
- 'sort': 'token',
301
- 'valuemax': 'number',
302
- 'valuemin': 'number',
303
- 'valuenow': 'number',
304
- 'valuetext': 'string',
305
- };
306
-
307
- exports.attributeStrongMapping = {
308
- 'disabled': 'disabled',
309
- 'placeholder': 'placeholder',
310
- 'readonly': 'readOnly',
311
- 'required': 'required',
312
- };
313
-
314
- exports.attributeWeakMapping = {
315
- 'checked': 'checked',
316
- 'colspan': 'colSpan',
317
- 'expanded': 'open',
318
- 'multiselectable': 'multiple',
319
- 'rowspan': 'rowSpan',
320
- 'selected': 'selected',
321
- };
322
-
323
- // https://www.w3.org/TR/html/dom.html#sectioning-content-2
324
- const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
325
-
326
- // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
327
- // https://www.w3.org/TR/wai-aria/roles
328
- exports.roles = {
329
- alert: {
330
- childRoles: ['alertdialog'],
331
- defaults: {
332
- 'live': 'assertive',
333
- 'atomic': true,
334
- },
335
- },
336
- alertdialog: {},
337
- application: {},
338
- article: {
339
- selectors: ['article'],
340
- },
341
- banner: {
342
- selectors: [`header:not(main *, ${scoped})`],
343
- },
344
- blockquote: {
345
- selectors: ['blockquote'],
346
- },
347
- button: {
348
- selectors: [
349
- 'button',
350
- 'input[type="button"]',
351
- 'input[type="image"]',
352
- 'input[type="reset"]',
353
- 'input[type="submit"]',
354
- 'summary',
355
- ],
356
- nameFromContents: true,
357
- },
358
- caption: {
359
- selectors: ['caption', 'figcaption'],
360
- },
361
- cell: {
362
- selectors: ['td', 'td ~ th:not([scope])'],
363
- childRoles: ['columnheader', 'gridcell', 'rowheader'],
364
- nameFromContents: true,
365
- },
366
- checkbox: {
367
- selectors: ['input[type="checkbox"]'],
368
- childRoles: ['switch'],
369
- nameFromContents: true,
370
- defaults: {
371
- 'checked': 'false',
372
- },
373
- },
374
- code: {
375
- selectors: ['code'],
376
- },
377
- columnheader: {
378
- selectors: ['th[scope="col"]'],
379
- nameFromContents: true,
380
- },
381
- combobox: {
382
- selectors: [
383
- 'input:not([type])[list]',
384
- 'input[type="email"][list]',
385
- 'input[type="search"][list]',
386
- 'input[type="tel"][list]',
387
- 'input[type="text"][list]',
388
- 'input[type="url"][list]',
389
- 'select:not([size]):not([multiple])',
390
- 'select[size="0"]:not([multiple])',
391
- 'select[size="1"]:not([multiple])',
392
- ],
393
- defaults: {
394
- 'expanded': false,
395
- 'haspopup': 'listbox',
396
- },
397
- },
398
- command: {
399
- abstract: true,
400
- childRoles: ['button', 'link', 'menuitem'],
401
- },
402
- complementary: {
403
- selectors: [
404
- `aside:not(${scoped})`,
405
- 'aside[aria-label]',
406
- 'aside[aria-labelledby]',
407
- 'aside[title]',
408
- ],
409
- },
410
- composite: {
411
- abstract: true,
412
- childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
413
- },
414
- contentinfo: {
415
- selectors: [`footer:not(main *, ${scoped})`],
416
- },
417
- definition: {
418
- selectors: ['dd'],
419
- },
420
- deletion: {
421
- selectors: ['del', 's'],
422
- },
423
- dialog: {
424
- selectors: ['dialog'],
425
- childRoles: ['alertdialog'],
426
- },
427
- 'doc-abstract': {},
428
- 'doc-acknowledgments': {},
429
- 'doc-afterword': {},
430
- 'doc-appendix': {},
431
- 'doc-backlink': {
432
- nameFromContents: true,
433
- },
434
- 'doc-biblioentry': {},
435
- 'doc-bibliography': {},
436
- 'doc-biblioref': {
437
- nameFromContents: true,
438
- },
439
- 'doc-chapter': {},
440
- 'doc-colophon': {},
441
- 'doc-conclusion': {},
442
- 'doc-cover': {},
443
- 'doc-credit': {},
444
- 'doc-credits': {},
445
- 'doc-dedication': {},
446
- 'doc-endnote': {},
447
- 'doc-endnotes': {},
448
- 'doc-epilogue': {},
449
- 'doc-epigraph': {},
450
- 'doc-errata': {},
451
- 'doc-example': {},
452
- 'doc-footnote': {},
453
- 'doc-foreword': {},
454
- 'doc-glossary': {},
455
- 'doc-glossref': {
456
- nameFromContents: true,
457
- },
458
- 'doc-index': {},
459
- 'doc-introduction': {},
460
- 'doc-noteref': {
461
- nameFromContents: true,
462
- },
463
- 'doc-notice': {},
464
- 'doc-pagebreak': {
465
- nameFromContents: true,
466
- },
467
- 'doc-pagefooter': {},
468
- 'doc-pageheader': {},
469
- 'doc-pagelist': {},
470
- 'doc-part': {},
471
- 'doc-preface': {},
472
- 'doc-prologue': {},
473
- 'doc-pullquote': {},
474
- 'doc-qna': {},
475
- 'doc-subtitle': {
476
- nameFromContents: true,
477
- },
478
- 'doc-tip': {},
479
- 'doc-toc': {},
480
- document: {
481
- selectors: ['html'],
482
- childRoles: ['article', 'graphics-document'],
483
- },
484
- emphasis: {
485
- selectors: ['em'],
486
- },
487
- feed: {},
488
- figure: {
489
- selectors: ['figure'],
490
- childRoles: ['doc-example'],
491
- },
492
- form: {
493
- selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
494
- },
495
- generic: {
496
- selectors: [
497
- 'a:not([href])',
498
- 'area:not([href])',
499
- `aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
500
- 'b',
501
- 'bdi',
502
- 'bdo',
503
- 'body',
504
- 'data',
505
- 'div',
506
- // footer scoped
507
- // header scoped
508
- 'i',
509
- 'li:not(ul > li):not(ol > li)',
510
- 'pre',
511
- 'q',
512
- 'samp',
513
- 'section:not([aria-label]):not([aria-labelledby]):not([title])',
514
- 'small',
515
- 'span',
516
- 'u',
517
- ],
518
- },
519
- 'graphics-document': {
520
- selectors: ['svg'],
521
- },
522
- 'graphics-object': {},
523
- 'graphics-symbol': {},
524
- grid: {
525
- childRoles: ['treegrid'],
526
- },
527
- gridcell: {
528
- childRoles: ['columnheader', 'rowheader'],
529
- nameFromContents: true,
530
- },
531
- group: {
532
- selectors: [
533
- 'address',
534
- 'details',
535
- 'fieldset',
536
- 'hgroup',
537
- 'optgroup',
538
- 'text',
539
- ],
540
- childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
541
- },
542
- heading: {
543
- selectors: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
544
- nameFromContents: true,
545
- defaults: {
546
- 'level': 2,
547
- },
548
- },
549
- img: {
550
- selectors: ['img:not([alt=""])', 'graphics-symbol'],
551
- childRoles: ['doc-cover'],
552
- },
553
- input: {
554
- abstract: true,
555
- childRoles: [
556
- 'checkbox',
557
- 'combobox',
558
- 'option',
559
- 'radio',
560
- 'slider',
561
- 'spinbutton',
562
- 'textbox',
563
- ],
564
- },
565
- insertion: {
566
- selectors: ['ins'],
567
- },
568
- landmark: {
569
- abstract: true,
570
- childRoles: [
571
- 'banner',
572
- 'complementary',
573
- 'contentinfo',
574
- 'doc-acknowledgments',
575
- 'doc-afterword',
576
- 'doc-appendix',
577
- 'doc-bibliography',
578
- 'doc-chapter',
579
- 'doc-conclusion',
580
- 'doc-credits',
581
- 'doc-endnotes',
582
- 'doc-epilogue',
583
- 'doc-errata',
584
- 'doc-foreword',
585
- 'doc-glossary',
586
- 'doc-introduction',
587
- 'doc-part',
588
- 'doc-preface',
589
- 'doc-prologue',
590
- 'form',
591
- 'main',
592
- 'navigation',
593
- 'region',
594
- 'search',
595
- ],
596
- },
597
- link: {
598
- selectors: ['a[href]', 'area[href]'],
599
- childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
600
- nameFromContents: true,
601
- },
602
- list: {
603
- selectors: ['dl', 'ol', 'ul', 'menu'],
604
- childRoles: ['feed'],
605
- },
606
- listbox: {
607
- selectors: [
608
- 'datalist',
609
- 'select[multiple]',
610
- 'select[size]:not([size="0"]):not([size="1"])',
611
- ],
612
- defaults: {
613
- 'orientation': 'vertical',
614
- },
615
- },
616
- listitem: {
617
- selectors: ['ol > li', 'ul > li'],
618
- childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
619
- },
620
- log: {
621
- defaults: {
622
- 'live': 'polite',
623
- },
624
- },
625
- main: {
626
- selectors: ['main'],
627
- },
628
- marquee: {},
629
- math: {
630
- selectors: ['math'],
631
- },
632
- meter: {
633
- selectors: ['meter'],
634
- defaults: {
635
- 'valuemin': 0,
636
- 'valuemax': 100,
637
- },
638
- },
639
- menu: {
640
- childRoles: ['menubar'],
641
- defaults: {
642
- 'orientation': 'vertical',
643
- },
644
- },
645
- menubar: {
646
- defaults: {
647
- 'orientation': 'horizontal',
648
- },
649
- },
650
- menuitem: {
651
- childRoles: ['menuitemcheckbox'],
652
- nameFromContents: true,
653
- },
654
- menuitemcheckbox: {
655
- childRoles: ['menuitemradio'],
656
- nameFromContents: true,
657
- defaults: {
658
- 'checked': 'false',
659
- },
660
- },
661
- menuitemradio: {
662
- nameFromContents: true,
663
- defaults: {
664
- 'checked': 'false',
665
- },
666
- },
667
- navigation: {
668
- selectors: ['nav'],
669
- childRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
670
- },
671
- none: {
672
- selectors: ['img[alt=""]'],
673
- },
674
- note: {
675
- childRoles: ['doc-notice', 'doc-tip'],
676
- },
677
- option: {
678
- selectors: ['option'],
679
- childRoles: ['treeitem'],
680
- nameFromContents: true,
681
- defaults: {
682
- 'selected': 'false',
683
- },
684
- },
685
- paragraph: {
686
- selectors: ['p'],
687
- },
688
- progressbar: {
689
- selectors: ['progress'],
690
- defaults: {
691
- 'valuemin': 0,
692
- 'valuemax': 100,
693
- },
694
- },
695
- radio: {
696
- selectors: ['input[type="radio"]'],
697
- childRoles: ['menuitemradio'],
698
- nameFromContents: true,
699
- defaults: {
700
- 'checked': 'false',
701
- },
702
- },
703
- radiogroup: {},
704
- range: {
705
- abstract: true,
706
- childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
707
- },
708
- region: {
709
- selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
710
- },
711
- roletype: {
712
- abstract: true,
713
- childRoles: ['structure', 'widget', 'window'],
714
- },
715
- row: {
716
- selectors: ['tr'],
717
- nameFromContents: true,
718
- },
719
- rowgroup: {
720
- selectors: ['tbody', 'thead', 'tfoot'],
721
- },
722
- rowheader: {
723
- selectors: ['th[scope="row"]', 'th:not([scope]):not(td ~ th)'],
724
- nameFromContents: true,
725
- },
726
- scrollbar: {
727
- defaults: {
728
- 'orientation': 'vertical',
729
- 'valuemin': 0,
730
- 'valuemax': 100,
731
- },
732
- },
733
- search: {
734
- selectors: ['search'],
735
- },
736
- searchbox: {
737
- selectors: ['input[type="search"]:not([list])'],
738
- },
739
- section: {
740
- abstract: true,
741
- childRoles: [
742
- 'alert',
743
- 'blockquote',
744
- 'caption',
745
- 'cell',
746
- 'code',
747
- 'definition',
748
- 'deletion',
749
- 'doc-abstract',
750
- 'doc-colophon',
751
- 'doc-credit',
752
- 'doc-dedication',
753
- 'doc-epigraph',
754
- 'doc-footnote',
755
- 'doc-pagefooter',
756
- 'doc-pageheader',
757
- 'doc-pullquote',
758
- 'doc-qna',
759
- 'emphasis',
760
- 'figure',
761
- 'group',
762
- 'img',
763
- 'insertion',
764
- 'landmark',
765
- 'list',
766
- 'listitem',
767
- 'log',
768
- 'marquee',
769
- 'math',
770
- 'note',
771
- 'paragraph',
772
- 'status',
773
- 'strong',
774
- 'subscript',
775
- 'superscript',
776
- 'table',
777
- 'tabpanel',
778
- 'term',
779
- 'time',
780
- 'tooltip',
781
- ],
782
- },
783
- sectionhead: {
784
- abstract: true,
785
- childRoles: [
786
- 'columnheader',
787
- 'doc-subtitle',
788
- 'heading',
789
- 'rowheader',
790
- 'tab',
791
- ],
792
- nameFromContents: true,
793
- },
794
- select: {
795
- abstract: true,
796
- childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
797
- },
798
- separator: {
799
- // assume not focussable because <hr> is not
800
- selectors: ['hr'],
801
- childRoles: ['doc-pagebreak'],
802
- defaults: {
803
- 'orientation': 'horizontal',
804
- 'valuemin': 0,
805
- 'valuemax': 100,
806
- },
807
- },
808
- slider: {
809
- selectors: ['input[type="range"]'],
810
- defaults: {
811
- 'orientation': 'horizontal',
812
- 'valuemin': 0,
813
- 'valuemax': 100,
814
- // FIXME: halfway between actual valuemin and valuemax
815
- 'valuenow': 50,
816
- },
817
- },
818
- spinbutton: {
819
- selectors: ['input[type="number"]'],
820
- defaults: {
821
- // FIXME: no valuemin/valuemax/valuenow
822
- },
823
- },
824
- status: {
825
- selectors: ['output'],
826
- childRoles: ['timer'],
827
- defaults: {
828
- 'live': 'polite',
829
- 'atomic': true,
830
- },
831
- },
832
- strong: {
833
- selectors: ['strong'],
834
- },
835
- structure: {
836
- abstract: true,
837
- childRoles: [
838
- 'application',
839
- 'document',
840
- 'none',
841
- 'generic',
842
- 'range',
843
- 'rowgroup',
844
- 'section',
845
- 'sectionhead',
846
- 'separator',
847
- ],
848
- },
849
- suggestion: {},
850
- subscript: {
851
- selectors: ['sub'],
852
- },
853
- superscript: {
854
- selectors: ['sup'],
855
- },
856
- switch: {
857
- nameFromContents: true,
858
- defaults: {
859
- 'checked': false,
860
- },
861
- },
862
- tab: {
863
- nameFromContents: true,
864
- defaults: {
865
- 'selected': false,
866
- },
867
- },
868
- table: {
869
- selectors: ['table'],
870
- childRoles: ['grid'],
871
- },
872
- tablist: {
873
- defaults: {
874
- 'orientation': 'horizontal',
875
- },
876
- },
877
- tabpanel: {},
878
- term: {
879
- selectors: ['dfn', 'dt'],
880
- },
881
- textbox: {
882
- selectors: [
883
- 'input:not([type]):not([list])',
884
- 'input[type="email"]:not([list])',
885
- 'input[type="tel"]:not([list])',
886
- 'input[type="text"]:not([list])',
887
- 'input[type="url"]:not([list])',
888
- 'textarea',
889
- ],
890
- childRoles: ['searchbox'],
891
- },
892
- time: {
893
- selectors: ['time'],
894
- },
895
- timer: {
896
- defaults: {
897
- 'live': 'off',
898
- },
899
- },
900
- toolbar: {
901
- defaults: {
902
- 'orientation': 'horizontal',
903
- },
904
- },
905
- tooltip: {
906
- nameFromContents: true,
907
- },
908
- tree: {
909
- childRoles: ['treegrid'],
910
- defaults: {
911
- 'orientation': 'vertical',
912
- },
913
- },
914
- treegrid: {},
915
- treeitem: {
916
- nameFromContents: true,
917
- },
918
- widget: {
919
- abstract: true,
920
- childRoles: [
921
- 'command',
922
- 'composite',
923
- 'gridcell',
924
- 'input',
925
- 'progressbar',
926
- 'row',
927
- 'scrollbar',
928
- 'separator',
929
- 'tab',
930
- ],
931
- },
932
- window: {
933
- abstract: true,
934
- childRoles: ['dialog'],
935
- },
936
- };
937
-
938
- const getSubRoles = function(role) {
939
- const children = (exports.roles[role]).childRoles || [];
940
- const descendents = children.map(getSubRoles);
941
-
942
- const result = [role];
943
-
944
- descendents.forEach(list => {
945
- list.forEach(r => {
946
- if (!result.includes(r)) {
947
- result.push(r);
948
- }
949
- });
950
- });
951
+ const getParentNode = function(node, owners) {
952
+ return getOwner(node, owners) || node.parentNode;
953
+ };
951
954
 
952
- return result;
953
- };
955
+ const isHidden = function(node) {
956
+ return node.nodeType === node.ELEMENT_NODE && getAttribute(node, 'hidden');
957
+ };
954
958
 
955
- exports.attrsWithDefaults = [];
959
+ const getChildNodes = function(node, owners) {
960
+ const childNodes = [];
956
961
 
957
- for (const role in exports.roles) {
958
- exports.roles[role].subRoles = getSubRoles(role);
959
- for (const key in exports.roles[role].defaults) {
960
- if (!exports.attrsWithDefaults.includes(key)) {
961
- exports.attrsWithDefaults.push(key);
962
- }
963
- }
964
- }
965
-
966
- exports.aliases = {
967
- 'presentation': 'none',
968
- 'directory': 'list',
969
- };
970
-
971
- exports.nameFromDescendant = {
972
- 'figure': 'figcaption',
973
- 'table': 'caption',
974
- 'fieldset': 'legend',
975
- };
976
-
977
- exports.nameDefaults = {
978
- 'input[type="submit"]': 'Submit',
979
- 'input[type="reset"]': 'Reset',
980
- 'summary': 'Details',
981
- };
982
-
983
- },{}],5:[function(require,module,exports){
984
- const constants = require('./constants.js');
985
- const atree = require('./atree.js');
986
- const query = require('./query.js');
987
-
988
- const getPseudoContent = function(node, selector) {
989
- const styles = window.getComputedStyle(node, selector);
990
- const ret = styles.getPropertyValue('content');
991
- const inline = styles.display.substr(0, 6) === 'inline';
992
- if (!ret) {
993
- return '';
994
- }
995
- if (ret.substr(0, 1) !== '"') {
996
- return '';
997
- } else {
998
- if (inline) {
999
- return ret.slice(1, -1);
1000
- } else {
1001
- return ' ' + ret.slice(1, -1) + ' ';
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
+ }
1002
967
  }
1003
- }
1004
- };
1005
-
1006
- const getContent = function(root, visited) {
1007
- const children = atree.getChildNodes(root);
1008
-
1009
- let ret = '';
1010
- for (let i = 0; i < children.length; i++) {
1011
- const node = children[i];
1012
- if (node.nodeType === node.TEXT_NODE) {
1013
- ret += node.textContent;
1014
- } else if (node.nodeType === node.ELEMENT_NODE) {
1015
- if (node.tagName.toLowerCase() === 'br') {
1016
- ret += '\n';
1017
- } else if (window.getComputedStyle(node).display.substr(0, 6) === 'inline' &&
1018
- node.tagName.toLowerCase() !== 'input' &&
1019
- node.tagName.toLowerCase() !== 'img') { // https://github.com/w3c/accname/issues/3
1020
- ret += getName(node, true, visited);
1021
- } else {
1022
- ret += ' ' + getName(node, true, visited) + ' ';
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
+ }
1023
977
  }
1024
978
  }
1025
- }
1026
979
 
1027
- return ret;
1028
- };
980
+ return childNodes;
981
+ };
1029
982
 
1030
- const allowNameFromContent = function(el) {
1031
- const role = query.getRole(el);
1032
- if (role) {
1033
- return constants.roles[role].nameFromContents;
1034
- }
1035
- };
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
+ };
1036
992
 
1037
- const getName = function(el, recursive, visited, directReference) {
1038
- let ret = '';
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);
1000
+ }
1001
+ }
1002
+ };
1039
1003
 
1040
- visited = visited || [];
1041
- if (visited.includes(el)) {
1042
- if (!directReference) {
1043
- return '';
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;
1013
+ } else {
1014
+ return hasRole(el, selector.split(','));
1044
1015
  }
1045
- } else {
1046
- visited.push(el);
1047
- }
1016
+ };
1048
1017
 
1049
- // A
1050
- // handled in atree
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
+ };
1051
1041
 
1052
- // B
1053
- if (!recursive && el.matches('[aria-labelledby]')) {
1054
- const ids = el.getAttribute('aria-labelledby').split(/\s+/);
1055
- const strings = ids.map(id => {
1056
- const label = document.getElementById(id);
1057
- return label ? getName(label, true, visited, true) : '';
1042
+ const closest = function(el, selector) {
1043
+ return searchUp(el, candidate => {
1044
+ if (candidate.nodeType === candidate.ELEMENT_NODE) {
1045
+ return matches(candidate, selector);
1046
+ }
1058
1047
  });
1059
- ret = strings.join(' ');
1060
- }
1048
+ };
1049
+
1050
+ const querySelector = _querySelector();
1051
+ const querySelectorAll = _querySelector(true);
1061
1052
 
1062
- // E (the current draft has this at this high priority)
1063
- if (!ret.trim() && recursive) {
1064
- if (query.matches(el, 'textbox,button')) {
1065
- ret = el.value || el.textContent;
1066
- } else if (query.matches(el, 'combobox,listbox')) {
1067
- const selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
1068
- if (selected) {
1069
- ret = getName(selected, recursive, visited);
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
+ };
1059
+
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 = [];
1070
1079
  } else {
1071
- ret = el.value || '';
1080
+ // invalid content, ignore
1081
+ return '';
1072
1082
  }
1073
- } else if (query.matches(el, 'range')) {
1074
- ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
1083
+ tail = tail.slice(match[0].length).trim();
1075
1084
  }
1076
- }
1077
1085
 
1078
- // C
1079
- if (!ret.trim() && el.matches('[aria-label]')) {
1080
- // FIXME: may skip to 2E
1081
- ret = el.getAttribute('aria-label');
1082
- }
1086
+ return addSpaces(ret.join(''), el, pseudoSelector);
1087
+ };
1083
1088
 
1084
- // D
1085
- if (!ret.trim() && !recursive && el.labels) {
1086
- const strings = Array.prototype.map.call(el.labels, label => {
1087
- return getName(label, true, visited);
1088
- });
1089
- ret = strings.join(' ');
1090
- }
1091
- if (!ret.trim()) {
1092
- ret = el.placeholder || '';
1093
- }
1094
- if (!ret.trim()) {
1095
- ret = el.alt || '';
1096
- }
1097
- if (!ret.trim() && el.matches('abbr,acronym') && el.title) {
1098
- ret = el.title;
1099
- }
1100
- if (!ret.trim()) {
1101
- for (const selector in constants.nameFromDescendant) {
1102
- if (el.matches(selector)) {
1103
- const descendant = el.querySelector(constants.nameFromDescendant[selector]);
1104
- if (descendant) {
1105
- ret = getName(descendant, true, visited);
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);
1106
1111
  }
1107
1112
  }
1108
1113
  }
1109
- }
1110
- if (!ret.trim() && el.matches('svg *')) {
1111
- const svgTitle = el.querySelector('title');
1112
- if (svgTitle && svgTitle.parentElement === el) {
1113
- ret = svgTitle.textContent;
1114
+
1115
+ return ret;
1116
+ };
1117
+
1118
+ const allowNameFromContent = function(el) {
1119
+ const role = getRole(el);
1120
+ if (role) {
1121
+ return roles[role].nameFromContents;
1114
1122
  }
1115
- }
1123
+ };
1116
1124
 
1117
- // F
1118
- // FIXME: menu is not mentioned in the spec
1119
- if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
1120
- ret = getContent(el, visited);
1121
- }
1125
+ const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directReference) {
1126
+ let ret = '';
1122
1127
 
1123
- if (!ret.trim() && query.matches(el, 'button')) {
1124
- ret = el.value || '';
1125
- }
1128
+ visited = visited || [];
1129
+ if (visited.includes(el)) {
1130
+ if (!directReference) {
1131
+ return '';
1132
+ }
1133
+ } else {
1134
+ visited.push(el);
1135
+ }
1136
+
1137
+ // A
1138
+ // handled in atree
1126
1139
 
1127
- if (!ret.trim()) {
1128
- for (const selector in constants.nameDefaults) {
1129
- if (el.matches(selector)) {
1130
- ret = constants.nameDefaults[selector];
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);
1131
1163
  }
1132
1164
  }
1133
- }
1134
1165
 
1135
- // G/H
1136
- // handled in getContent
1166
+ // C
1167
+ if (!ret.trim() && el.matches('[aria-label]')) {
1168
+ // FIXME: may skip to 2E
1169
+ ret = el.getAttribute('aria-label');
1170
+ }
1137
1171
 
1138
- // I
1139
- // FIXME: presentation not mentioned in the spec
1140
- if (!ret.trim() && (directReference || !query.matches(el, 'presentation'))) {
1141
- ret = el.title || '';
1142
- }
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
+ }
1192
+ }
1193
+ }
1194
+ }
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') || '';
1203
+ }
1143
1204
 
1144
- // FIXME: not exactly sure about this, but it reduces the number of failing
1145
- // WPT tests. Whitespace is hard.
1146
- if (!ret.trim()) {
1147
- ret = ' ';
1148
- }
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
+ }
1149
1210
 
1150
- const before = getPseudoContent(el, ':before');
1151
- const after = getPseudoContent(el, ':after');
1152
- return before + ret + after;
1153
- };
1211
+ if (!ret.trim() && matches(el, 'button')) {
1212
+ ret = el.value || '';
1213
+ }
1154
1214
 
1155
- const getNameTrimmed = function(el) {
1156
- return getName(el).replace(/\s+/g, ' ').trim();
1157
- };
1215
+ if (!ret.trim()) {
1216
+ for (const selector in nameDefaults) {
1217
+ if (el.matches(selector)) {
1218
+ ret = nameDefaults[selector];
1219
+ }
1220
+ }
1221
+ }
1158
1222
 
1159
- const getDescription = function(el) {
1160
- let ret = '';
1223
+ // G/H
1224
+ // handled in getContent
1161
1225
 
1162
- if (el.matches('[aria-describedby]')) {
1163
- const ids = el.getAttribute('aria-describedby').split(/\s+/);
1164
- const strings = ids.map(id => {
1165
- const label = document.getElementById(id);
1166
- return label ? getName(label, true) : '';
1167
- });
1168
- ret = strings.join(' ');
1169
- } else if (el.matches('[aria-description]')) {
1170
- ret = el.getAttribute('aria-description');
1171
- } else if (el.matches('svg *')) {
1172
- const svgDesc = el.querySelector('desc');
1173
- if (svgDesc && svgDesc.parentElement === el) {
1174
- ret = svgDesc.textContent;
1226
+ // I
1227
+ if (!ret.trim()) {
1228
+ ret = el.title || el.placeholder || '';
1175
1229
  }
1176
- } else if (el.title) {
1177
- ret = el.title;
1178
- }
1179
1230
 
1180
- ret = (ret || '').trim().replace(/\s+/g, ' ');
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 = ' ';
1235
+ }
1181
1236
 
1182
- if (ret === getNameTrimmed(el)) {
1183
- ret = '';
1184
- }
1237
+ const before = getPseudoContent(el, ':before');
1238
+ const after = getPseudoContent(el, ':after');
1239
+ return addSpaces(before + ret + after, el);
1240
+ };
1185
1241
 
1186
- return ret;
1187
- };
1188
-
1189
- module.exports = {
1190
- getName: getNameTrimmed,
1191
- getDescription: getDescription,
1192
- };
1193
-
1194
- },{"./atree.js":2,"./constants.js":4,"./query.js":6}],6:[function(require,module,exports){
1195
- const attrs = require('./attrs.js');
1196
- const atree = require('./atree.js');
1197
-
1198
-
1199
- const matches = function(el, selector) {
1200
- if (selector.substr(0, 1) === ':') {
1201
- const attr = selector.substr(1);
1202
- return attrs.getAttribute(el, attr);
1203
- } else if (selector.substr(0, 1) === '[') {
1204
- const match = /\[([a-z]+)="(.*)"\]/.exec(selector);
1205
- const actual = attrs.getAttribute(el, match[1]);
1206
- const rawValue = match[2];
1207
- return actual.toString() === rawValue;
1208
- } else {
1209
- return attrs.hasRole(el, selector.split(','));
1210
- }
1211
- };
1212
-
1213
- const _querySelector = function(all) {
1214
- return function(root, selector) {
1215
- const results = [];
1216
- try {
1217
- atree.walk(root, node => {
1218
- if (node.nodeType === node.ELEMENT_NODE) {
1219
- // FIXME: skip hidden elements
1220
- if (matches(node, selector)) {
1221
- results.push(node);
1222
- if (!all) {
1223
- throw 'StopIteration';
1224
- }
1225
- }
1226
- }
1242
+ const getName = function(el) {
1243
+ return getNameRaw(el)
1244
+ .replace(/[ \n\r\t\f]+/g, ' ')
1245
+ .replace(/^ /, '')
1246
+ .replace(/ $/, '');
1247
+ };
1248
+
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) : '';
1227
1257
  });
1228
- } catch (e) {
1229
- if (e !== 'StopIteration') {
1230
- 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;
1231
1265
  }
1266
+ } else if (el.title) {
1267
+ ret = el.title;
1268
+ }
1269
+ if (!ret.trim() && el.matches('a')) {
1270
+ ret = el.getAttribute('xlink:title') || '';
1232
1271
  }
1233
- return all ? results : results[0];
1234
- };
1235
- };
1236
1272
 
1237
- const closest = function(el, selector) {
1238
- return atree.searchUp(el, candidate => {
1239
- if (candidate.nodeType === candidate.ELEMENT_NODE) {
1240
- return matches(candidate, selector);
1273
+ ret = (ret || '').trim().replace(/\s+/g, ' ');
1274
+
1275
+ if (ret === getName(el)) {
1276
+ ret = '';
1241
1277
  }
1242
- });
1243
- };
1244
-
1245
- module.exports = {
1246
- getRole: el => attrs.getRole(el),
1247
- getAttribute: attrs.getAttribute,
1248
- matches: matches,
1249
- querySelector: _querySelector(),
1250
- querySelectorAll: _querySelector(true),
1251
- closest: closest,
1252
- };
1253
-
1254
- },{"./atree.js":2,"./attrs.js":3}]},{},[1])(1)
1255
- });
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
+ }));