aria-api 0.5.0 → 0.7.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
@@ -19,13 +19,13 @@ module.exports = {
19
19
  };
20
20
 
21
21
  },{"./lib/atree.js":2,"./lib/name.js":5,"./lib/query.js":6}],2:[function(require,module,exports){
22
- var attrs = require('./attrs');
22
+ const attrs = require('./attrs');
23
23
 
24
- var _getOwner = function(node, owners) {
24
+ const _getOwner = function(node, owners) {
25
25
  if (node.nodeType === node.ELEMENT_NODE && node.id) {
26
- var selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
26
+ const selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
27
27
  if (owners) {
28
- for (var owner of owners) {
28
+ for (const owner of owners) {
29
29
  if (owner.matches(selector)) {
30
30
  return owner;
31
31
  }
@@ -36,12 +36,12 @@ var _getOwner = function(node, owners) {
36
36
  }
37
37
  };
38
38
 
39
- var _getParentNode = function(node, owners) {
39
+ const _getParentNode = function(node, owners) {
40
40
  return _getOwner(node, owners) || node.parentNode;
41
41
  };
42
42
 
43
- var detectLoop = function(node, owners) {
44
- var seen = [node];
43
+ const detectLoop = function(node, owners) {
44
+ const seen = [node];
45
45
  while ((node = _getParentNode(node, owners))) {
46
46
  if (seen.includes(node)) {
47
47
  return true;
@@ -50,35 +50,35 @@ var detectLoop = function(node, owners) {
50
50
  }
51
51
  };
52
52
 
53
- var getOwner = function(node, owners) {
54
- var owner = _getOwner(node, owners);
53
+ const getOwner = function(node, owners) {
54
+ const owner = _getOwner(node, owners);
55
55
  if (owner && !detectLoop(node, owners)) {
56
56
  return owner;
57
57
  }
58
58
  };
59
59
 
60
- var getParentNode = function(node, owners) {
60
+ const getParentNode = function(node, owners) {
61
61
  return getOwner(node, owners) || node.parentNode;
62
62
  };
63
63
 
64
- var isHidden = function(node) {
64
+ const isHidden = function(node) {
65
65
  return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
66
66
  };
67
67
 
68
- var getChildNodes = function(node, owners) {
69
- var childNodes = [];
68
+ const getChildNodes = function(node, owners) {
69
+ const childNodes = [];
70
70
 
71
- for (var i = 0; i < node.childNodes.length; i++) {
72
- var child = node.childNodes[i];
71
+ for (let i = 0; i < node.childNodes.length; i++) {
72
+ const child = node.childNodes[i];
73
73
  if (!getOwner(child, owners) && !isHidden(child)) {
74
74
  childNodes.push(child);
75
75
  }
76
76
  }
77
77
 
78
78
  if (node.nodeType === node.ELEMENT_NODE) {
79
- var owns = attrs.getAttribute(node, 'owns') || [];
80
- for (var i = 0; i < owns.length; i++) {
81
- var child = document.getElementById(owns[i]);
79
+ const owns = attrs.getAttribute(node, 'owns') || [];
80
+ for (let i = 0; i < owns.length; i++) {
81
+ const child = document.getElementById(owns[i]);
82
82
  // double check with getOwner for consistency
83
83
  if (child && getOwner(child, owners) === node && !isHidden(child)) {
84
84
  childNodes.push(child);
@@ -89,18 +89,18 @@ var getChildNodes = function(node, owners) {
89
89
  return childNodes;
90
90
  };
91
91
 
92
- var walk = function(root, fn) {
93
- var owners = document.querySelectorAll('[aria-owns]');
94
- var queue = [root];
92
+ const walk = function(root, fn) {
93
+ const owners = document.querySelectorAll('[aria-owns]');
94
+ let queue = [root];
95
95
  while (queue.length) {
96
- var item = queue.shift();
96
+ const item = queue.shift();
97
97
  fn(item);
98
98
  queue = getChildNodes(item, owners).concat(queue);
99
99
  }
100
100
  };
101
101
 
102
- var searchUp = function(node, test) {
103
- var candidate = getParentNode(node);
102
+ const searchUp = function(node, test) {
103
+ const candidate = getParentNode(node);
104
104
  if (candidate) {
105
105
  if (test(candidate)) {
106
106
  return candidate;
@@ -118,44 +118,59 @@ module.exports = {
118
118
  };
119
119
 
120
120
  },{"./attrs":3}],3:[function(require,module,exports){
121
- var constants = require('./constants.js');
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
+ };
122
138
 
123
139
  // candidates can be passed for performance optimization
124
- var getRole = function(el, candidates) {
125
- if (el.hasAttribute('role')) {
126
- var roles = el.getAttribute('role').toLowerCase().split(/\s+/);
127
- if (roles.length > 1 && candidates) {
128
- return [roles, candidates];
129
- }
130
- for (var role of roles) {
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) {
131
150
  if (!candidates || candidates.includes(role)) {
132
151
  return role;
133
152
  }
134
153
  }
135
154
  } else {
136
- var roles = candidates ? candidates : Object.keys(constants.roles);
137
- for (var role of roles) {
138
- var r = constants.roles[role];
139
- if (r) {
140
- var selector = (r.selectors || []).join(',');
141
- if (selector && el.matches(selector)) {
142
- return role;
143
- }
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;
144
159
  }
145
160
  }
146
161
  }
147
162
  };
148
163
 
149
- var hasRole = function(el, roles) {
150
- var candidates = [].concat.apply([], roles.map(role => {
151
- return (constants.roles[role] || {}).subRoles || [role];
152
- }));
153
- return !!getRole(el, candidates);
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)));
154
169
  };
155
170
 
156
- var getAttribute = function(el, key) {
171
+ const getAttribute = function(el, key) {
157
172
  if (constants.attributeStrongMapping.hasOwnProperty(key)) {
158
- var value = el[constants.attributeStrongMapping[key]];
173
+ const value = el[constants.attributeStrongMapping[key]];
159
174
  if (value) {
160
175
  return value;
161
176
  }
@@ -172,14 +187,14 @@ var getAttribute = function(el, key) {
172
187
  if (el.matches('details:not([open]) > :not(summary)')) {
173
188
  return true;
174
189
  }
175
- var style = window.getComputedStyle(el);
176
- if (style.display === 'none' || style.visibility === 'hidden') {
190
+ const style = window.getComputedStyle(el);
191
+ if (style.display === 'none' || style.visibility === 'hidden' || style.visibility === 'collapse') {
177
192
  return true;
178
193
  }
179
194
  }
180
195
 
181
- var type = constants.attributes[key];
182
- var raw = el.getAttribute('aria-' + key);
196
+ const type = constants.attributes[key];
197
+ const raw = el.getAttribute('aria-' + key);
183
198
 
184
199
  if (raw) {
185
200
  if (type === 'bool') {
@@ -208,7 +223,7 @@ var getAttribute = function(el, key) {
208
223
  // list -> aria-controls
209
224
 
210
225
  if (key === 'level') {
211
- for (var i = 1; i <= 6; i++) {
226
+ for (let i = 1; i <= 6; i++) {
212
227
  if (el.tagName.toLowerCase() === 'h' + i) {
213
228
  return i;
214
229
  }
@@ -218,8 +233,8 @@ var getAttribute = function(el, key) {
218
233
  }
219
234
 
220
235
  if (key in constants.attrsWithDefaults) {
221
- var role = getRole(el);
222
- var defaults = (constants.roles[role] || {}).defaults;
236
+ const role = getRole(el);
237
+ const defaults = constants.roles[role].defaults;
223
238
  if (defaults && defaults.hasOwnProperty(key)) {
224
239
  return defaults[key];
225
240
  }
@@ -242,14 +257,18 @@ exports.attributes = {
242
257
  'activedescendant': 'id',
243
258
  'atomic': 'bool',
244
259
  'autocomplete': 'token',
260
+ 'braillelabel': 'string',
261
+ 'brailleroledescription': 'string',
245
262
  'busy': 'bool',
246
263
  'checked': 'tristate',
247
264
  'colcount': 'int',
248
265
  'colindex': 'int',
266
+ 'colindextext': 'string',
249
267
  'colspan': 'int',
250
268
  'controls': 'id-list',
251
269
  'current': 'token',
252
270
  'describedby': 'id-list',
271
+ 'description': 'string',
253
272
  'details': 'id',
254
273
  'disabled': 'bool',
255
274
  'dropeffect': 'token-list',
@@ -279,6 +298,7 @@ exports.attributes = {
279
298
  'roledescription': 'string',
280
299
  'rowcount': 'int',
281
300
  'rowindex': 'int',
301
+ 'rowindextext': 'string',
282
302
  'rowspan': 'int',
283
303
  'selected': 'bool-undefined',
284
304
  'setsize': 'int',
@@ -306,7 +326,20 @@ exports.attributeWeakMapping = {
306
326
  };
307
327
 
308
328
  // https://www.w3.org/TR/html/dom.html#sectioning-content-2
309
- var scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
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
+ };
310
343
 
311
344
  // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
312
345
  // https://www.w3.org/TR/wai-aria/roles
@@ -318,8 +351,11 @@ exports.roles = {
318
351
  'atomic': true,
319
352
  },
320
353
  },
354
+ alertdialog: {},
355
+ application: {},
321
356
  article: {
322
357
  selectors: ['article'],
358
+ childRoles: ['comment'],
323
359
  },
324
360
  banner: {
325
361
  selectors: [`header:not(main *, ${scoped})`],
@@ -342,7 +378,7 @@ exports.roles = {
342
378
  selectors: ['caption', 'figcaption'],
343
379
  },
344
380
  cell: {
345
- selectors: ['td', 'th:not([scope])'],
381
+ selectors: ['td', 'td ~ th:not([scope])'],
346
382
  childRoles: ['columnheader', 'gridcell', 'rowheader'],
347
383
  nameFromContents: true,
348
384
  },
@@ -379,8 +415,12 @@ exports.roles = {
379
415
  },
380
416
  },
381
417
  command: {
418
+ abstract: true,
382
419
  childRoles: ['button', 'link', 'menuitem'],
383
420
  },
421
+ comment: {
422
+ nameFromContents: true,
423
+ },
384
424
  complementary: {
385
425
  selectors: [
386
426
  `aside:not(${scoped})`,
@@ -390,6 +430,7 @@ exports.roles = {
390
430
  ],
391
431
  },
392
432
  composite: {
433
+ abstract: true,
393
434
  childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
394
435
  },
395
436
  contentinfo: {
@@ -405,24 +446,59 @@ exports.roles = {
405
446
  selectors: ['dialog'],
406
447
  childRoles: ['alertdialog'],
407
448
  },
449
+ 'doc-abstract': {},
450
+ 'doc-acknowledgments': {},
451
+ 'doc-afterword': {},
452
+ 'doc-appendix': {},
408
453
  'doc-backlink': {
409
454
  nameFromContents: true,
410
455
  },
456
+ 'doc-biblioentry': {},
457
+ 'doc-bibliography': {},
411
458
  'doc-biblioref': {
412
459
  nameFromContents: true,
413
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': {},
414
477
  'doc-glossref': {
415
478
  nameFromContents: true,
416
479
  },
480
+ 'doc-index': {},
481
+ 'doc-introduction': {},
417
482
  'doc-noteref': {
418
483
  nameFromContents: true,
419
484
  },
485
+ 'doc-notice': {},
420
486
  'doc-pagebreak': {
421
487
  nameFromContents: true,
422
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': {},
423
497
  'doc-subtitle': {
424
498
  nameFromContents: true,
425
499
  },
500
+ 'doc-tip': {},
501
+ 'doc-toc': {},
426
502
  document: {
427
503
  selectors: ['html'],
428
504
  childRoles: ['article', 'graphics-document'],
@@ -430,6 +506,7 @@ exports.roles = {
430
506
  emphasis: {
431
507
  selectors: ['em'],
432
508
  },
509
+ feed: {},
433
510
  figure: {
434
511
  selectors: ['figure'],
435
512
  childRoles: ['doc-example'],
@@ -438,11 +515,49 @@ exports.roles = {
438
515
  selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
439
516
  },
440
517
  generic: {
441
- // too many selectors to list
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
+ ],
442
540
  },
443
541
  'graphics-document': {
444
542
  selectors: ['svg'],
445
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
+ },
446
561
  grid: {
447
562
  childRoles: ['treegrid'],
448
563
  },
@@ -457,7 +572,11 @@ exports.roles = {
457
572
  'fieldset',
458
573
  'hgroup',
459
574
  'optgroup',
575
+ ...svgSelectors('foreignObject'),
576
+ ...svgSelectors('g'),
460
577
  'text',
578
+ ...svgSelectors('textPath'),
579
+ ...svgSelectors('tspan'),
461
580
  ],
462
581
  childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
463
582
  },
@@ -468,11 +587,17 @@ exports.roles = {
468
587
  'level': 2,
469
588
  },
470
589
  },
471
- img: {
472
- selectors: ['img:not([alt=""])', 'graphics-symbol'],
590
+ image: {
591
+ selectors: [
592
+ 'img:not([alt=""])',
593
+ 'graphics-symbol',
594
+ ...svgSelectors('image'),
595
+ ...svgSelectors('mesh'),
596
+ ],
473
597
  childRoles: ['doc-cover'],
474
598
  },
475
599
  input: {
600
+ abstract: true,
476
601
  childRoles: [
477
602
  'checkbox',
478
603
  'combobox',
@@ -487,6 +612,7 @@ exports.roles = {
487
612
  selectors: ['ins'],
488
613
  },
489
614
  landmark: {
615
+ abstract: true,
490
616
  childRoles: [
491
617
  'banner',
492
618
  'complementary',
@@ -515,13 +641,13 @@ exports.roles = {
515
641
  ],
516
642
  },
517
643
  link: {
518
- selectors: ['a[href]', 'area[href]'],
644
+ selectors: ['a[*|href]', 'area[href]'],
519
645
  childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
520
646
  nameFromContents: true,
521
647
  },
522
648
  list: {
523
649
  selectors: ['dl', 'ol', 'ul', 'menu'],
524
- childRoles: ['directory', 'feed'],
650
+ childRoles: ['feed'],
525
651
  },
526
652
  listbox: {
527
653
  selectors: [
@@ -534,7 +660,7 @@ exports.roles = {
534
660
  },
535
661
  },
536
662
  listitem: {
537
- selectors: ['li'],
663
+ selectors: ['ol > li', 'ul > li'],
538
664
  childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
539
665
  },
540
666
  log: {
@@ -545,6 +671,10 @@ exports.roles = {
545
671
  main: {
546
672
  selectors: ['main'],
547
673
  },
674
+ mark: {
675
+ selectors: ['mark'],
676
+ },
677
+ marquee: {},
548
678
  math: {
549
679
  selectors: ['math'],
550
680
  },
@@ -567,11 +697,10 @@ exports.roles = {
567
697
  },
568
698
  },
569
699
  menuitem: {
570
- childRoles: ['menuitemcheckbox'],
700
+ childRoles: ['menuitemcheckbox', 'menuitemradio'],
571
701
  nameFromContents: true,
572
702
  },
573
703
  menuitemcheckbox: {
574
- childRoles: ['menuitemradio'],
575
704
  nameFromContents: true,
576
705
  defaults: {
577
706
  'checked': 'false',
@@ -587,6 +716,9 @@ exports.roles = {
587
716
  selectors: ['nav'],
588
717
  childRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
589
718
  },
719
+ none: {
720
+ selectors: ['img[alt=""]'],
721
+ },
590
722
  note: {
591
723
  childRoles: ['doc-notice', 'doc-tip'],
592
724
  },
@@ -601,9 +733,6 @@ exports.roles = {
601
733
  paragraph: {
602
734
  selectors: ['p'],
603
735
  },
604
- presentation: {
605
- selectors: ['img[alt=""]'],
606
- },
607
736
  progressbar: {
608
737
  selectors: ['progress'],
609
738
  defaults: {
@@ -619,13 +748,16 @@ exports.roles = {
619
748
  'checked': 'false',
620
749
  },
621
750
  },
751
+ radiogroup: {},
622
752
  range: {
753
+ abstract: true,
623
754
  childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
624
755
  },
625
756
  region: {
626
757
  selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
627
758
  },
628
759
  roletype: {
760
+ abstract: true,
629
761
  childRoles: ['structure', 'widget', 'window'],
630
762
  },
631
763
  row: {
@@ -636,7 +768,7 @@ exports.roles = {
636
768
  selectors: ['tbody', 'thead', 'tfoot'],
637
769
  },
638
770
  rowheader: {
639
- selectors: ['th[scope="row"]'],
771
+ selectors: ['th[scope="row"]', 'th:not([scope]):not(td ~ th)'],
640
772
  nameFromContents: true,
641
773
  },
642
774
  scrollbar: {
@@ -653,6 +785,7 @@ exports.roles = {
653
785
  selectors: ['input[type="search"]:not([list])'],
654
786
  },
655
787
  section: {
788
+ abstract: true,
656
789
  childRoles: [
657
790
  'alert',
658
791
  'blockquote',
@@ -674,12 +807,13 @@ exports.roles = {
674
807
  'emphasis',
675
808
  'figure',
676
809
  'group',
677
- 'img',
810
+ 'image',
678
811
  'insertion',
679
812
  'landmark',
680
813
  'list',
681
814
  'listitem',
682
815
  'log',
816
+ 'mark',
683
817
  'marquee',
684
818
  'math',
685
819
  'note',
@@ -687,6 +821,7 @@ exports.roles = {
687
821
  'status',
688
822
  'strong',
689
823
  'subscript',
824
+ 'suggestion',
690
825
  'superscript',
691
826
  'table',
692
827
  'tabpanel',
@@ -696,6 +831,7 @@ exports.roles = {
696
831
  ],
697
832
  },
698
833
  sectionhead: {
834
+ abstract: true,
699
835
  childRoles: [
700
836
  'columnheader',
701
837
  'doc-subtitle',
@@ -706,6 +842,7 @@ exports.roles = {
706
842
  nameFromContents: true,
707
843
  },
708
844
  select: {
845
+ abstract: true,
709
846
  childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
710
847
  },
711
848
  separator: {
@@ -746,12 +883,12 @@ exports.roles = {
746
883
  selectors: ['strong'],
747
884
  },
748
885
  structure: {
886
+ abstract: true,
749
887
  childRoles: [
750
888
  'application',
751
889
  'document',
752
890
  'none',
753
891
  'generic',
754
- 'presentation',
755
892
  'range',
756
893
  'rowgroup',
757
894
  'section',
@@ -759,6 +896,7 @@ exports.roles = {
759
896
  'separator',
760
897
  ],
761
898
  },
899
+ suggestion: {},
762
900
  subscript: {
763
901
  selectors: ['sub'],
764
902
  },
@@ -786,6 +924,7 @@ exports.roles = {
786
924
  'orientation': 'horizontal',
787
925
  },
788
926
  },
927
+ tabpanel: {},
789
928
  term: {
790
929
  selectors: ['dfn', 'dt'],
791
930
  },
@@ -822,10 +961,12 @@ exports.roles = {
822
961
  'orientation': 'vertical',
823
962
  },
824
963
  },
964
+ treegrid: {},
825
965
  treeitem: {
826
966
  nameFromContents: true,
827
967
  },
828
968
  widget: {
969
+ abstract: true,
829
970
  childRoles: [
830
971
  'command',
831
972
  'composite',
@@ -839,15 +980,16 @@ exports.roles = {
839
980
  ],
840
981
  },
841
982
  window: {
983
+ abstract: true,
842
984
  childRoles: ['dialog'],
843
985
  },
844
986
  };
845
987
 
846
- var getSubRoles = function(role) {
847
- var children = (exports.roles[role] || {}).childRoles || [];
848
- var descendents = children.map(getSubRoles);
988
+ const getSubRoles = function(role) {
989
+ const children = (exports.roles[role]).childRoles || [];
990
+ const descendents = children.map(getSubRoles);
849
991
 
850
- var result = [role];
992
+ const result = [role];
851
993
 
852
994
  descendents.forEach(list => {
853
995
  list.forEach(r => {
@@ -862,18 +1004,20 @@ var getSubRoles = function(role) {
862
1004
 
863
1005
  exports.attrsWithDefaults = [];
864
1006
 
865
- for (var role in exports.roles) {
1007
+ for (const role in exports.roles) {
866
1008
  exports.roles[role].subRoles = getSubRoles(role);
867
- for (var key in exports.roles[role].defaults) {
1009
+ for (const key in exports.roles[role].defaults) {
868
1010
  if (!exports.attrsWithDefaults.includes(key)) {
869
1011
  exports.attrsWithDefaults.push(key);
870
1012
  }
871
1013
  }
872
1014
  }
873
- exports.roles['none'] = exports.roles['none'] || {};
874
- exports.roles['none'].subRoles = ['none', 'presentation'];
875
- exports.roles['presentation'] = exports.roles['presentation'] || {};
876
- exports.roles['presentation'].subRoles = ['presentation', 'none'];
1015
+
1016
+ exports.aliases = {
1017
+ 'presentation': 'none',
1018
+ 'directory': 'list',
1019
+ 'img': 'image',
1020
+ };
877
1021
 
878
1022
  exports.nameFromDescendant = {
879
1023
  'figure': 'figcaption',
@@ -888,45 +1032,59 @@ exports.nameDefaults = {
888
1032
  };
889
1033
 
890
1034
  },{}],5:[function(require,module,exports){
891
- var constants = require('./constants.js');
892
- var atree = require('./atree.js');
893
- var query = require('./query.js');
894
-
895
- var getPseudoContent = function(node, selector) {
896
- var styles = window.getComputedStyle(node, selector);
897
- var ret = styles.getPropertyValue('content');
898
- var inline = styles.display.substr(0, 6) === 'inline';
899
- if (!ret) {
900
- return '';
901
- }
902
- if (ret.substr(0, 1) !== '"') {
903
- return '';
904
- } else {
905
- if (inline) {
906
- return ret.slice(1, -1);
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]) || '');
1058
+ }
1059
+ } else if (match = tail.match(/^([a-z-]+)/)) {
1060
+ if (match[1] === 'open-quote' || match[1] === 'close-quote') {
1061
+ ret.push('"');
1062
+ }
1063
+ } else if (match = tail.match(/^\//)) {
1064
+ ret = [];
907
1065
  } else {
908
- return ' ' + ret.slice(1, -1) + ' ';
1066
+ // invalid content, ignore
1067
+ return '';
909
1068
  }
1069
+ tail = tail.slice(match[0].length).trim();
910
1070
  }
1071
+
1072
+ return addSpaces(ret.join(''), el, pseudoSelector);
911
1073
  };
912
1074
 
913
- var getContent = function(root, visited) {
914
- var children = atree.getChildNodes(root);
1075
+ const getContent = function(root, ongoingLabelledBy, visited) {
1076
+ const children = atree.getChildNodes(root);
915
1077
 
916
- var ret = '';
917
- for (var i = 0; i < children.length; i++) {
918
- var node = children[i];
1078
+ let ret = '';
1079
+ for (let i = 0; i < children.length; i++) {
1080
+ const node = children[i];
919
1081
  if (node.nodeType === node.TEXT_NODE) {
920
1082
  ret += node.textContent;
921
1083
  } else if (node.nodeType === node.ELEMENT_NODE) {
922
1084
  if (node.tagName.toLowerCase() === 'br') {
923
1085
  ret += '\n';
924
- } else if (window.getComputedStyle(node).display.substr(0, 6) === 'inline' &&
925
- node.tagName.toLowerCase() !== 'input' &&
926
- node.tagName.toLowerCase() !== 'img') { // https://github.com/w3c/accname/issues/3
927
- ret += getName(node, true, visited);
928
1086
  } else {
929
- ret += ' ' + getName(node, true, visited) + ' ';
1087
+ ret += getName(node, true, ongoingLabelledBy, visited);
930
1088
  }
931
1089
  }
932
1090
  }
@@ -934,18 +1092,15 @@ var getContent = function(root, visited) {
934
1092
  return ret;
935
1093
  };
936
1094
 
937
- var allowNameFromContent = function(el) {
938
- var role = query.getRole(el);
939
- return (constants.roles[role] || {}).nameFromContents;
940
- };
941
-
942
- var isInLabelForOtherWidget = function(el) {
943
- var label = el.parentElement.closest('label');
944
- return label && (!el.labels || !Array.prototype.includes.call(el.labels, label));
1095
+ const allowNameFromContent = function(el) {
1096
+ const role = query.getRole(el);
1097
+ if (role) {
1098
+ return constants.roles[role].nameFromContents;
1099
+ }
945
1100
  };
946
1101
 
947
- var getName = function(el, recursive, visited, directReference) {
948
- var ret = '';
1102
+ const getName = function(el, recursive, ongoingLabelledBy, visited, directReference) {
1103
+ let ret = '';
949
1104
 
950
1105
  visited = visited || [];
951
1106
  if (visited.includes(el)) {
@@ -960,15 +1115,31 @@ var getName = function(el, recursive, visited, directReference) {
960
1115
  // handled in atree
961
1116
 
962
1117
  // B
963
- if (!recursive && el.matches('[aria-labelledby]')) {
964
- var ids = el.getAttribute('aria-labelledby').split(/\s+/);
965
- var strings = ids.map(id => {
966
- var label = document.getElementById(id);
967
- return label ? getName(label, true, visited, true) : '';
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) : '';
968
1123
  });
969
1124
  ret = strings.join(' ');
970
1125
  }
971
1126
 
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
+ }
1138
+ } else if (query.matches(el, 'range')) {
1139
+ ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
1140
+ }
1141
+ }
1142
+
972
1143
  // C
973
1144
  if (!ret.trim() && el.matches('[aria-label]')) {
974
1145
  // FIXME: may skip to 2E
@@ -977,14 +1148,11 @@ var getName = function(el, recursive, visited, directReference) {
977
1148
 
978
1149
  // D
979
1150
  if (!ret.trim() && !recursive && el.labels) {
980
- var strings = Array.prototype.map.call(el.labels, label => {
981
- return getName(label, true, visited);
1151
+ const strings = Array.prototype.map.call(el.labels, label => {
1152
+ return getName(label, true, ongoingLabelledBy, visited);
982
1153
  });
983
1154
  ret = strings.join(' ');
984
1155
  }
985
- if (!ret.trim()) {
986
- ret = el.placeholder || '';
987
- }
988
1156
  if (!ret.trim()) {
989
1157
  ret = el.alt || '';
990
1158
  }
@@ -992,48 +1160,37 @@ var getName = function(el, recursive, visited, directReference) {
992
1160
  ret = el.title;
993
1161
  }
994
1162
  if (!ret.trim()) {
995
- for (var selector in constants.nameFromDescendant) {
1163
+ for (const selector in constants.nameFromDescendant) {
996
1164
  if (el.matches(selector)) {
997
- var descendant = el.querySelector(constants.nameFromDescendant[selector]);
1165
+ const descendant = el.querySelector(constants.nameFromDescendant[selector]);
998
1166
  if (descendant) {
999
- ret = getName(descendant, true, visited);
1167
+ ret = getName(descendant, true, ongoingLabelledBy, visited);
1000
1168
  }
1001
1169
  }
1002
1170
  }
1003
1171
  }
1004
1172
  if (!ret.trim() && el.matches('svg *')) {
1005
- var svgTitle = el.querySelector('title');
1173
+ const svgTitle = el.querySelector('title');
1006
1174
  if (svgTitle && svgTitle.parentElement === el) {
1007
1175
  ret = svgTitle.textContent;
1008
1176
  }
1009
1177
  }
1010
-
1011
- // E
1012
- if (!ret.trim() && (recursive || isInLabelForOtherWidget(el) || query.matches(el, 'button'))) {
1013
- if (query.matches(el, 'textbox,button,combobox,listbox,range')) {
1014
- if (query.matches(el, 'textbox,button')) {
1015
- ret = el.value || el.textContent;
1016
- } else if (query.matches(el, 'combobox,listbox')) {
1017
- var selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
1018
- if (selected) {
1019
- ret = getName(selected, recursive, visited);
1020
- } else {
1021
- ret = el.value || '';
1022
- }
1023
- } else if (query.matches(el, 'range')) {
1024
- ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
1025
- }
1026
- }
1178
+ if (!ret.trim() && el.matches('a')) {
1179
+ ret = el.getAttribute('xlink:title') || '';
1027
1180
  }
1028
1181
 
1029
1182
  // F
1030
1183
  // FIXME: menu is not mentioned in the spec
1031
1184
  if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
1032
- ret = getContent(el, visited);
1185
+ ret = getContent(el, ongoingLabelledBy, visited);
1186
+ }
1187
+
1188
+ if (!ret.trim() && query.matches(el, 'button')) {
1189
+ ret = el.value || '';
1033
1190
  }
1034
1191
 
1035
1192
  if (!ret.trim()) {
1036
- for (var selector in constants.nameDefaults) {
1193
+ for (const selector in constants.nameDefaults) {
1037
1194
  if (el.matches(selector)) {
1038
1195
  ret = constants.nameDefaults[selector];
1039
1196
  }
@@ -1044,9 +1201,8 @@ var getName = function(el, recursive, visited, directReference) {
1044
1201
  // handled in getContent
1045
1202
 
1046
1203
  // I
1047
- // FIXME: presentation not mentioned in the spec
1048
- if (!ret.trim() && (directReference || !query.matches(el, 'presentation'))) {
1049
- ret = el.title || '';
1204
+ if (!ret.trim()) {
1205
+ ret = el.title || el.placeholder || '';
1050
1206
  }
1051
1207
 
1052
1208
  // FIXME: not exactly sure about this, but it reduces the number of failing
@@ -1055,35 +1211,41 @@ var getName = function(el, recursive, visited, directReference) {
1055
1211
  ret = ' ';
1056
1212
  }
1057
1213
 
1058
- var before = getPseudoContent(el, ':before');
1059
- var after = getPseudoContent(el, ':after');
1060
- return before + ret + after;
1214
+ const before = getPseudoContent(el, ':before');
1215
+ const after = getPseudoContent(el, ':after');
1216
+ return addSpaces(before + ret + after, el);
1061
1217
  };
1062
1218
 
1063
- var getNameTrimmed = function(el) {
1064
- return getName(el).replace(/\s+/g, ' ').trim();
1219
+ const getNameTrimmed = function(el) {
1220
+ return getName(el)
1221
+ .replace(/[ \n\r\t\f]+/g, ' ')
1222
+ .replace(/^ /, '')
1223
+ .replace(/ $/, '');
1065
1224
  };
1066
1225
 
1067
- var getDescription = function(el) {
1068
- var ret = '';
1226
+ const getDescription = function(el) {
1227
+ let ret = '';
1069
1228
 
1070
1229
  if (el.matches('[aria-describedby]')) {
1071
- var ids = el.getAttribute('aria-describedby').split(/\s+/);
1072
- var strings = ids.map(id => {
1073
- var label = document.getElementById(id);
1074
- return label ? getName(label, true) : '';
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) : '';
1075
1234
  });
1076
1235
  ret = strings.join(' ');
1077
1236
  } else if (el.matches('[aria-description]')) {
1078
1237
  ret = el.getAttribute('aria-description');
1079
1238
  } else if (el.matches('svg *')) {
1080
- var svgDesc = el.querySelector('desc');
1239
+ const svgDesc = el.querySelector('desc');
1081
1240
  if (svgDesc && svgDesc.parentElement === el) {
1082
1241
  ret = svgDesc.textContent;
1083
1242
  }
1084
1243
  } else if (el.title) {
1085
1244
  ret = el.title;
1086
1245
  }
1246
+ if (!ret.trim() && el.matches('a')) {
1247
+ ret = el.getAttribute('xlink:title') || '';
1248
+ }
1087
1249
 
1088
1250
  ret = (ret || '').trim().replace(/\s+/g, ' ');
1089
1251
 
@@ -1100,34 +1262,32 @@ module.exports = {
1100
1262
  };
1101
1263
 
1102
1264
  },{"./atree.js":2,"./constants.js":4,"./query.js":6}],6:[function(require,module,exports){
1103
- var attrs = require('./attrs.js');
1104
- var atree = require('./atree.js');
1105
-
1265
+ const attrs = require('./attrs.js');
1266
+ const atree = require('./atree.js');
1106
1267
 
1107
- var matches = function(el, selector) {
1108
- var actual;
1109
1268
 
1269
+ const matches = function(el, selector) {
1110
1270
  if (selector.substr(0, 1) === ':') {
1111
- var attr = selector.substr(1);
1271
+ const attr = selector.substr(1);
1112
1272
  return attrs.getAttribute(el, attr);
1113
1273
  } else if (selector.substr(0, 1) === '[') {
1114
- var match = /\[([a-z]+)="(.*)"\]/.exec(selector);
1115
- actual = attrs.getAttribute(el, match[1]);
1116
- var rawValue = match[2];
1274
+ const match = /\[([a-z]+)="(.*)"\]/.exec(selector);
1275
+ const actual = attrs.getAttribute(el, match[1]);
1276
+ const rawValue = match[2];
1117
1277
  return actual.toString() === rawValue;
1118
1278
  } else {
1119
1279
  return attrs.hasRole(el, selector.split(','));
1120
1280
  }
1121
1281
  };
1122
1282
 
1123
- var _querySelector = function(all) {
1124
- return function(root, role) {
1125
- var results = [];
1283
+ const _querySelector = function(all) {
1284
+ return function(root, selector) {
1285
+ const results = [];
1126
1286
  try {
1127
1287
  atree.walk(root, node => {
1128
1288
  if (node.nodeType === node.ELEMENT_NODE) {
1129
1289
  // FIXME: skip hidden elements
1130
- if (matches(node, role)) {
1290
+ if (matches(node, selector)) {
1131
1291
  results.push(node);
1132
1292
  if (!all) {
1133
1293
  throw 'StopIteration';
@@ -1144,7 +1304,7 @@ var _querySelector = function(all) {
1144
1304
  };
1145
1305
  };
1146
1306
 
1147
- var closest = function(el, selector) {
1307
+ const closest = function(el, selector) {
1148
1308
  return atree.searchUp(el, candidate => {
1149
1309
  if (candidate.nodeType === candidate.ELEMENT_NODE) {
1150
1310
  return matches(candidate, selector);