aria-api 0.5.0 → 0.6.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/lib/atree.js CHANGED
@@ -1,10 +1,10 @@
1
- var attrs = require('./attrs');
1
+ const attrs = require('./attrs');
2
2
 
3
- var _getOwner = function(node, owners) {
3
+ const _getOwner = function(node, owners) {
4
4
  if (node.nodeType === node.ELEMENT_NODE && node.id) {
5
- var selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
5
+ const selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
6
6
  if (owners) {
7
- for (var owner of owners) {
7
+ for (const owner of owners) {
8
8
  if (owner.matches(selector)) {
9
9
  return owner;
10
10
  }
@@ -15,12 +15,12 @@ var _getOwner = function(node, owners) {
15
15
  }
16
16
  };
17
17
 
18
- var _getParentNode = function(node, owners) {
18
+ const _getParentNode = function(node, owners) {
19
19
  return _getOwner(node, owners) || node.parentNode;
20
20
  };
21
21
 
22
- var detectLoop = function(node, owners) {
23
- var seen = [node];
22
+ const detectLoop = function(node, owners) {
23
+ const seen = [node];
24
24
  while ((node = _getParentNode(node, owners))) {
25
25
  if (seen.includes(node)) {
26
26
  return true;
@@ -29,35 +29,35 @@ var detectLoop = function(node, owners) {
29
29
  }
30
30
  };
31
31
 
32
- var getOwner = function(node, owners) {
33
- var owner = _getOwner(node, owners);
32
+ const getOwner = function(node, owners) {
33
+ const owner = _getOwner(node, owners);
34
34
  if (owner && !detectLoop(node, owners)) {
35
35
  return owner;
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 isHidden = function(node) {
43
+ const isHidden = function(node) {
44
44
  return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
45
45
  };
46
46
 
47
- var getChildNodes = function(node, owners) {
48
- var childNodes = [];
47
+ const getChildNodes = function(node, owners) {
48
+ const childNodes = [];
49
49
 
50
- for (var i = 0; i < node.childNodes.length; i++) {
51
- var child = node.childNodes[i];
50
+ for (let i = 0; i < node.childNodes.length; i++) {
51
+ const child = node.childNodes[i];
52
52
  if (!getOwner(child, owners) && !isHidden(child)) {
53
53
  childNodes.push(child);
54
54
  }
55
55
  }
56
56
 
57
57
  if (node.nodeType === node.ELEMENT_NODE) {
58
- var owns = attrs.getAttribute(node, 'owns') || [];
59
- for (var i = 0; i < owns.length; i++) {
60
- var child = document.getElementById(owns[i]);
58
+ const owns = attrs.getAttribute(node, 'owns') || [];
59
+ for (let i = 0; i < owns.length; i++) {
60
+ const child = document.getElementById(owns[i]);
61
61
  // double check with getOwner for consistency
62
62
  if (child && getOwner(child, owners) === node && !isHidden(child)) {
63
63
  childNodes.push(child);
@@ -68,18 +68,18 @@ var getChildNodes = function(node, owners) {
68
68
  return childNodes;
69
69
  };
70
70
 
71
- var walk = function(root, fn) {
72
- var owners = document.querySelectorAll('[aria-owns]');
73
- var queue = [root];
71
+ const walk = function(root, fn) {
72
+ const owners = document.querySelectorAll('[aria-owns]');
73
+ let queue = [root];
74
74
  while (queue.length) {
75
- var item = queue.shift();
75
+ const item = queue.shift();
76
76
  fn(item);
77
77
  queue = getChildNodes(item, owners).concat(queue);
78
78
  }
79
79
  };
80
80
 
81
- var searchUp = function(node, test) {
82
- var candidate = getParentNode(node);
81
+ const searchUp = function(node, test) {
82
+ const candidate = getParentNode(node);
83
83
  if (candidate) {
84
84
  if (test(candidate)) {
85
85
  return candidate;
package/lib/attrs.js CHANGED
@@ -1,41 +1,56 @@
1
- var constants = require('./constants.js');
1
+ const constants = require('./constants.js');
2
+
3
+ var unique = function(arr) {
4
+ return arr.filter((a, i) => arr.indexOf(a) === i);
5
+ };
6
+
7
+ var flatten = function(arr) {
8
+ return [].concat.apply([], arr);
9
+ };
10
+
11
+ var normalizeRoles = function(roles, includeAbstract) {
12
+ return unique(roles
13
+ .map(r => constants.aliases[r] || r)
14
+ .filter(r => constants.roles[r])
15
+ .filter(r => includeAbstract || !constants.roles[r].abstract)
16
+ );
17
+ };
2
18
 
3
19
  // candidates can be passed for performance optimization
4
- var getRole = function(el, candidates) {
5
- if (el.hasAttribute('role')) {
6
- var roles = el.getAttribute('role').toLowerCase().split(/\s+/);
7
- if (roles.length > 1 && candidates) {
8
- return [roles, candidates];
9
- }
10
- for (var role of roles) {
20
+ const getRole = function(el, candidates) {
21
+ // TODO: filter out any invalid roles (e.g. name or context required)
22
+ const roles = normalizeRoles(
23
+ (el.getAttribute('role') || '').toLowerCase().split(/\s+/)
24
+ );
25
+
26
+ if (roles.length > 1 && candidates) {
27
+ return [roles, candidates];
28
+ } else if (roles.length) {
29
+ for (const role of roles) {
11
30
  if (!candidates || candidates.includes(role)) {
12
31
  return role;
13
32
  }
14
33
  }
15
34
  } else {
16
- var roles = candidates ? candidates : Object.keys(constants.roles);
17
- for (var role of roles) {
18
- var r = constants.roles[role];
19
- if (r) {
20
- var selector = (r.selectors || []).join(',');
21
- if (selector && el.matches(selector)) {
22
- return role;
23
- }
35
+ for (const role of (candidates || Object.keys(constants.roles))) {
36
+ const r = constants.roles[role];
37
+ if (!r.abstract && r.selectors && el.matches(r.selectors.join(','))) {
38
+ return role;
24
39
  }
25
40
  }
26
41
  }
27
42
  };
28
43
 
29
- var hasRole = function(el, roles) {
30
- var candidates = [].concat.apply([], roles.map(role => {
31
- return (constants.roles[role] || {}).subRoles || [role];
32
- }));
33
- return !!getRole(el, candidates);
44
+ const hasRole = function(el, roles) {
45
+ const subRoles = normalizeRoles(roles, true).map(role => {
46
+ return constants.roles[role].subRoles || [role];
47
+ });
48
+ return !!getRole(el, unique(flatten(subRoles)));
34
49
  };
35
50
 
36
- var getAttribute = function(el, key) {
51
+ const getAttribute = function(el, key) {
37
52
  if (constants.attributeStrongMapping.hasOwnProperty(key)) {
38
- var value = el[constants.attributeStrongMapping[key]];
53
+ const value = el[constants.attributeStrongMapping[key]];
39
54
  if (value) {
40
55
  return value;
41
56
  }
@@ -52,14 +67,14 @@ var getAttribute = function(el, key) {
52
67
  if (el.matches('details:not([open]) > :not(summary)')) {
53
68
  return true;
54
69
  }
55
- var style = window.getComputedStyle(el);
70
+ const style = window.getComputedStyle(el);
56
71
  if (style.display === 'none' || style.visibility === 'hidden') {
57
72
  return true;
58
73
  }
59
74
  }
60
75
 
61
- var type = constants.attributes[key];
62
- var raw = el.getAttribute('aria-' + key);
76
+ const type = constants.attributes[key];
77
+ const raw = el.getAttribute('aria-' + key);
63
78
 
64
79
  if (raw) {
65
80
  if (type === 'bool') {
@@ -88,7 +103,7 @@ var getAttribute = function(el, key) {
88
103
  // list -> aria-controls
89
104
 
90
105
  if (key === 'level') {
91
- for (var i = 1; i <= 6; i++) {
106
+ for (let i = 1; i <= 6; i++) {
92
107
  if (el.tagName.toLowerCase() === 'h' + i) {
93
108
  return i;
94
109
  }
@@ -98,8 +113,8 @@ var getAttribute = function(el, key) {
98
113
  }
99
114
 
100
115
  if (key in constants.attrsWithDefaults) {
101
- var role = getRole(el);
102
- var defaults = (constants.roles[role] || {}).defaults;
116
+ const role = getRole(el);
117
+ const defaults = constants.roles[role].defaults;
103
118
  if (defaults && defaults.hasOwnProperty(key)) {
104
119
  return defaults[key];
105
120
  }
package/lib/constants.js CHANGED
@@ -67,7 +67,7 @@ exports.attributeWeakMapping = {
67
67
  };
68
68
 
69
69
  // https://www.w3.org/TR/html/dom.html#sectioning-content-2
70
- var scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
70
+ const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
71
71
 
72
72
  // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
73
73
  // https://www.w3.org/TR/wai-aria/roles
@@ -79,6 +79,8 @@ exports.roles = {
79
79
  'atomic': true,
80
80
  },
81
81
  },
82
+ alertdialog: {},
83
+ application: {},
82
84
  article: {
83
85
  selectors: ['article'],
84
86
  },
@@ -103,7 +105,7 @@ exports.roles = {
103
105
  selectors: ['caption', 'figcaption'],
104
106
  },
105
107
  cell: {
106
- selectors: ['td', 'th:not([scope])'],
108
+ selectors: ['td', 'td ~ th:not([scope])'],
107
109
  childRoles: ['columnheader', 'gridcell', 'rowheader'],
108
110
  nameFromContents: true,
109
111
  },
@@ -140,6 +142,7 @@ exports.roles = {
140
142
  },
141
143
  },
142
144
  command: {
145
+ abstract: true,
143
146
  childRoles: ['button', 'link', 'menuitem'],
144
147
  },
145
148
  complementary: {
@@ -151,6 +154,7 @@ exports.roles = {
151
154
  ],
152
155
  },
153
156
  composite: {
157
+ abstract: true,
154
158
  childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
155
159
  },
156
160
  contentinfo: {
@@ -166,24 +170,59 @@ exports.roles = {
166
170
  selectors: ['dialog'],
167
171
  childRoles: ['alertdialog'],
168
172
  },
173
+ 'doc-abstract': {},
174
+ 'doc-acknowledgments': {},
175
+ 'doc-afterword': {},
176
+ 'doc-appendix': {},
169
177
  'doc-backlink': {
170
178
  nameFromContents: true,
171
179
  },
180
+ 'doc-biblioentry': {},
181
+ 'doc-bibliography': {},
172
182
  'doc-biblioref': {
173
183
  nameFromContents: true,
174
184
  },
185
+ 'doc-chapter': {},
186
+ 'doc-colophon': {},
187
+ 'doc-conclusion': {},
188
+ 'doc-cover': {},
189
+ 'doc-credit': {},
190
+ 'doc-credits': {},
191
+ 'doc-dedication': {},
192
+ 'doc-endnote': {},
193
+ 'doc-endnotes': {},
194
+ 'doc-epilogue': {},
195
+ 'doc-epigraph': {},
196
+ 'doc-errata': {},
197
+ 'doc-example': {},
198
+ 'doc-footnote': {},
199
+ 'doc-foreword': {},
200
+ 'doc-glossary': {},
175
201
  'doc-glossref': {
176
202
  nameFromContents: true,
177
203
  },
204
+ 'doc-index': {},
205
+ 'doc-introduction': {},
178
206
  'doc-noteref': {
179
207
  nameFromContents: true,
180
208
  },
209
+ 'doc-notice': {},
181
210
  'doc-pagebreak': {
182
211
  nameFromContents: true,
183
212
  },
213
+ 'doc-pagefooter': {},
214
+ 'doc-pageheader': {},
215
+ 'doc-pagelist': {},
216
+ 'doc-part': {},
217
+ 'doc-preface': {},
218
+ 'doc-prologue': {},
219
+ 'doc-pullquote': {},
220
+ 'doc-qna': {},
184
221
  'doc-subtitle': {
185
222
  nameFromContents: true,
186
223
  },
224
+ 'doc-tip': {},
225
+ 'doc-toc': {},
187
226
  document: {
188
227
  selectors: ['html'],
189
228
  childRoles: ['article', 'graphics-document'],
@@ -191,6 +230,7 @@ exports.roles = {
191
230
  emphasis: {
192
231
  selectors: ['em'],
193
232
  },
233
+ feed: {},
194
234
  figure: {
195
235
  selectors: ['figure'],
196
236
  childRoles: ['doc-example'],
@@ -199,11 +239,34 @@ exports.roles = {
199
239
  selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
200
240
  },
201
241
  generic: {
202
- // too many selectors to list
242
+ selectors: [
243
+ 'a:not([href])',
244
+ 'area:not([href])',
245
+ `aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
246
+ 'b',
247
+ 'bdi',
248
+ 'bdo',
249
+ 'body',
250
+ 'data',
251
+ 'div',
252
+ // footer scoped
253
+ // header scoped
254
+ 'i',
255
+ 'li:not(ul > li):not(ol > li)',
256
+ 'pre',
257
+ 'q',
258
+ 'samp',
259
+ 'section:not([aria-label]):not([aria-labelledby]):not([title])',
260
+ 'small',
261
+ 'span',
262
+ 'u',
263
+ ],
203
264
  },
204
265
  'graphics-document': {
205
266
  selectors: ['svg'],
206
267
  },
268
+ 'graphics-object': {},
269
+ 'graphics-symbol': {},
207
270
  grid: {
208
271
  childRoles: ['treegrid'],
209
272
  },
@@ -234,6 +297,7 @@ exports.roles = {
234
297
  childRoles: ['doc-cover'],
235
298
  },
236
299
  input: {
300
+ abstract: true,
237
301
  childRoles: [
238
302
  'checkbox',
239
303
  'combobox',
@@ -248,6 +312,7 @@ exports.roles = {
248
312
  selectors: ['ins'],
249
313
  },
250
314
  landmark: {
315
+ abstract: true,
251
316
  childRoles: [
252
317
  'banner',
253
318
  'complementary',
@@ -282,7 +347,7 @@ exports.roles = {
282
347
  },
283
348
  list: {
284
349
  selectors: ['dl', 'ol', 'ul', 'menu'],
285
- childRoles: ['directory', 'feed'],
350
+ childRoles: ['feed'],
286
351
  },
287
352
  listbox: {
288
353
  selectors: [
@@ -295,7 +360,7 @@ exports.roles = {
295
360
  },
296
361
  },
297
362
  listitem: {
298
- selectors: ['li'],
363
+ selectors: ['ol > li', 'ul > li'],
299
364
  childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
300
365
  },
301
366
  log: {
@@ -306,6 +371,7 @@ exports.roles = {
306
371
  main: {
307
372
  selectors: ['main'],
308
373
  },
374
+ marquee: {},
309
375
  math: {
310
376
  selectors: ['math'],
311
377
  },
@@ -348,6 +414,9 @@ exports.roles = {
348
414
  selectors: ['nav'],
349
415
  childRoles: ['doc-index', 'doc-pagelist', 'doc-toc'],
350
416
  },
417
+ none: {
418
+ selectors: ['img[alt=""]'],
419
+ },
351
420
  note: {
352
421
  childRoles: ['doc-notice', 'doc-tip'],
353
422
  },
@@ -362,9 +431,6 @@ exports.roles = {
362
431
  paragraph: {
363
432
  selectors: ['p'],
364
433
  },
365
- presentation: {
366
- selectors: ['img[alt=""]'],
367
- },
368
434
  progressbar: {
369
435
  selectors: ['progress'],
370
436
  defaults: {
@@ -380,13 +446,16 @@ exports.roles = {
380
446
  'checked': 'false',
381
447
  },
382
448
  },
449
+ radiogroup: {},
383
450
  range: {
451
+ abstract: true,
384
452
  childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
385
453
  },
386
454
  region: {
387
455
  selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
388
456
  },
389
457
  roletype: {
458
+ abstract: true,
390
459
  childRoles: ['structure', 'widget', 'window'],
391
460
  },
392
461
  row: {
@@ -397,7 +466,7 @@ exports.roles = {
397
466
  selectors: ['tbody', 'thead', 'tfoot'],
398
467
  },
399
468
  rowheader: {
400
- selectors: ['th[scope="row"]'],
469
+ selectors: ['th[scope="row"]', 'th:not([scope]):not(td ~ th)'],
401
470
  nameFromContents: true,
402
471
  },
403
472
  scrollbar: {
@@ -414,6 +483,7 @@ exports.roles = {
414
483
  selectors: ['input[type="search"]:not([list])'],
415
484
  },
416
485
  section: {
486
+ abstract: true,
417
487
  childRoles: [
418
488
  'alert',
419
489
  'blockquote',
@@ -457,6 +527,7 @@ exports.roles = {
457
527
  ],
458
528
  },
459
529
  sectionhead: {
530
+ abstract: true,
460
531
  childRoles: [
461
532
  'columnheader',
462
533
  'doc-subtitle',
@@ -467,6 +538,7 @@ exports.roles = {
467
538
  nameFromContents: true,
468
539
  },
469
540
  select: {
541
+ abstract: true,
470
542
  childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
471
543
  },
472
544
  separator: {
@@ -507,12 +579,12 @@ exports.roles = {
507
579
  selectors: ['strong'],
508
580
  },
509
581
  structure: {
582
+ abstract: true,
510
583
  childRoles: [
511
584
  'application',
512
585
  'document',
513
586
  'none',
514
587
  'generic',
515
- 'presentation',
516
588
  'range',
517
589
  'rowgroup',
518
590
  'section',
@@ -520,6 +592,7 @@ exports.roles = {
520
592
  'separator',
521
593
  ],
522
594
  },
595
+ suggestion: {},
523
596
  subscript: {
524
597
  selectors: ['sub'],
525
598
  },
@@ -547,6 +620,7 @@ exports.roles = {
547
620
  'orientation': 'horizontal',
548
621
  },
549
622
  },
623
+ tabpanel: {},
550
624
  term: {
551
625
  selectors: ['dfn', 'dt'],
552
626
  },
@@ -583,10 +657,12 @@ exports.roles = {
583
657
  'orientation': 'vertical',
584
658
  },
585
659
  },
660
+ treegrid: {},
586
661
  treeitem: {
587
662
  nameFromContents: true,
588
663
  },
589
664
  widget: {
665
+ abstract: true,
590
666
  childRoles: [
591
667
  'command',
592
668
  'composite',
@@ -600,15 +676,16 @@ exports.roles = {
600
676
  ],
601
677
  },
602
678
  window: {
679
+ abstract: true,
603
680
  childRoles: ['dialog'],
604
681
  },
605
682
  };
606
683
 
607
- var getSubRoles = function(role) {
608
- var children = (exports.roles[role] || {}).childRoles || [];
609
- var descendents = children.map(getSubRoles);
684
+ const getSubRoles = function(role) {
685
+ const children = (exports.roles[role]).childRoles || [];
686
+ const descendents = children.map(getSubRoles);
610
687
 
611
- var result = [role];
688
+ const result = [role];
612
689
 
613
690
  descendents.forEach(list => {
614
691
  list.forEach(r => {
@@ -623,18 +700,19 @@ var getSubRoles = function(role) {
623
700
 
624
701
  exports.attrsWithDefaults = [];
625
702
 
626
- for (var role in exports.roles) {
703
+ for (const role in exports.roles) {
627
704
  exports.roles[role].subRoles = getSubRoles(role);
628
- for (var key in exports.roles[role].defaults) {
705
+ for (const key in exports.roles[role].defaults) {
629
706
  if (!exports.attrsWithDefaults.includes(key)) {
630
707
  exports.attrsWithDefaults.push(key);
631
708
  }
632
709
  }
633
710
  }
634
- exports.roles['none'] = exports.roles['none'] || {};
635
- exports.roles['none'].subRoles = ['none', 'presentation'];
636
- exports.roles['presentation'] = exports.roles['presentation'] || {};
637
- exports.roles['presentation'].subRoles = ['presentation', 'none'];
711
+
712
+ exports.aliases = {
713
+ 'presentation': 'none',
714
+ 'directory': 'list',
715
+ };
638
716
 
639
717
  exports.nameFromDescendant = {
640
718
  'figure': 'figcaption',