aria-api 0.4.6 → 0.5.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/LICENSE +1 -1
- package/README.md +5 -5
- package/dist/aria.js +201 -88
- package/lib/atree.js +32 -25
- package/lib/attrs.js +18 -19
- package/lib/constants.js +132 -34
- package/lib/name.js +16 -5
- package/lib/query.js +3 -5
- package/package.json +3 -3
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ browserify. There is also an UMD build included as `dist/aria.js`.
|
|
|
20
20
|
|
|
21
21
|
var aria = require('aria-api'):
|
|
22
22
|
|
|
23
|
-
aria.querySelector('landmark').forEach(
|
|
23
|
+
aria.querySelector('landmark').forEach(landmark => {
|
|
24
24
|
if (!aria.matches(landmark, ':hidden')) {
|
|
25
25
|
var role = aria.getRole(landmark);
|
|
26
26
|
var name = aria.getName(landmark);
|
|
@@ -106,11 +106,11 @@ That said, this is what I think it could also be used for:
|
|
|
106
106
|
|
|
107
107
|
# Implemented standards
|
|
108
108
|
|
|
109
|
-
- [Accessible Rich Internet Applications 1.
|
|
110
|
-
- [Core Accessibility API Mappings 1.1](https://www.w3.org/TR/core-aam-1.1/)
|
|
109
|
+
- [Accessible Rich Internet Applications 1.2](https://www.w3.org/TR/wai-aria-1.2/)
|
|
111
110
|
- [HTML Accessibility API Mappings 1.0](https://www.w3.org/TR/html-aam-1.0/)
|
|
111
|
+
- [SVG Accessibility API Mappings](https://www.w3.org/TR/svg-aam-1.0/) (very incomplete)
|
|
112
112
|
- [WAI-ARIA Graphics Module 1.0](https://www.w3.org/TR/graphics-aria-1.0/)
|
|
113
|
-
- [Digital Publishing WAI-ARIA Module 1.
|
|
113
|
+
- [Digital Publishing WAI-ARIA Module 1.1](https://www.w3.org/TR/dpub-aria-1.1/)
|
|
114
114
|
- [Accessible Name and Description Computation 1.1](https://www.w3.org/TR/accname-1.1/)
|
|
115
115
|
|
|
116
116
|
I try to update the code whenever a new version of these specs becomes a
|
|
@@ -135,7 +135,7 @@ recommendation.
|
|
|
135
135
|
|
|
136
136
|
# Related projects
|
|
137
137
|
|
|
138
|
-
- [Visual ARIA Bookmarklet](
|
|
138
|
+
- [Visual ARIA Bookmarklet](https://whatsock.github.io/visual-aria/github-bookmarklet/visual-aria.htm):
|
|
139
139
|
Displays role, name, and description in any website. Maintained by one of
|
|
140
140
|
the editors of the [accname]() spec.
|
|
141
141
|
- [axe-core](https://github.com/dequelabs/axe-core/) and
|
package/dist/aria.js
CHANGED
|
@@ -21,52 +21,56 @@ module.exports = {
|
|
|
21
21
|
},{"./lib/atree.js":2,"./lib/name.js":5,"./lib/query.js":6}],2:[function(require,module,exports){
|
|
22
22
|
var attrs = require('./attrs');
|
|
23
23
|
|
|
24
|
-
var _getOwner = function(node) {
|
|
24
|
+
var _getOwner = function(node, owners) {
|
|
25
25
|
if (node.nodeType === node.ELEMENT_NODE && node.id) {
|
|
26
|
-
var
|
|
27
|
-
if (
|
|
28
|
-
|
|
26
|
+
var selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
|
|
27
|
+
if (owners) {
|
|
28
|
+
for (var owner of owners) {
|
|
29
|
+
if (owner.matches(selector)) {
|
|
30
|
+
return owner;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
return document.querySelector(selector);
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
37
|
};
|
|
32
38
|
|
|
33
|
-
var _getParentNode = function(node) {
|
|
34
|
-
return _getOwner(node) || node.parentNode;
|
|
39
|
+
var _getParentNode = function(node, owners) {
|
|
40
|
+
return _getOwner(node, owners) || node.parentNode;
|
|
35
41
|
};
|
|
36
42
|
|
|
37
|
-
var detectLoop = function(node) {
|
|
38
|
-
var seen = [node]
|
|
39
|
-
while ((node = _getParentNode(node))) {
|
|
43
|
+
var detectLoop = function(node, owners) {
|
|
44
|
+
var seen = [node];
|
|
45
|
+
while ((node = _getParentNode(node, owners))) {
|
|
40
46
|
if (seen.includes(node)) {
|
|
41
47
|
return true;
|
|
42
48
|
}
|
|
43
|
-
seen.push(node)
|
|
49
|
+
seen.push(node);
|
|
44
50
|
}
|
|
45
51
|
};
|
|
46
52
|
|
|
47
|
-
var getOwner = function(node) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return owner;
|
|
52
|
-
}
|
|
53
|
+
var getOwner = function(node, owners) {
|
|
54
|
+
var owner = _getOwner(node, owners);
|
|
55
|
+
if (owner && !detectLoop(node, owners)) {
|
|
56
|
+
return owner;
|
|
53
57
|
}
|
|
54
58
|
};
|
|
55
59
|
|
|
56
|
-
var getParentNode = function(node) {
|
|
57
|
-
return getOwner(node) || node.parentNode;
|
|
60
|
+
var getParentNode = function(node, owners) {
|
|
61
|
+
return getOwner(node, owners) || node.parentNode;
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
var isHidden = function(node) {
|
|
61
65
|
return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
|
|
62
66
|
};
|
|
63
67
|
|
|
64
|
-
var getChildNodes = function(node) {
|
|
68
|
+
var getChildNodes = function(node, owners) {
|
|
65
69
|
var childNodes = [];
|
|
66
70
|
|
|
67
71
|
for (var i = 0; i < node.childNodes.length; i++) {
|
|
68
72
|
var child = node.childNodes[i];
|
|
69
|
-
if (!getOwner(child) && !isHidden(child)) {
|
|
73
|
+
if (!getOwner(child, owners) && !isHidden(child)) {
|
|
70
74
|
childNodes.push(child);
|
|
71
75
|
}
|
|
72
76
|
}
|
|
@@ -76,7 +80,7 @@ var getChildNodes = function(node) {
|
|
|
76
80
|
for (var i = 0; i < owns.length; i++) {
|
|
77
81
|
var child = document.getElementById(owns[i]);
|
|
78
82
|
// double check with getOwner for consistency
|
|
79
|
-
if (child && getOwner(child) === node && !isHidden(child)) {
|
|
83
|
+
if (child && getOwner(child, owners) === node && !isHidden(child)) {
|
|
80
84
|
childNodes.push(child);
|
|
81
85
|
}
|
|
82
86
|
}
|
|
@@ -86,10 +90,13 @@ var getChildNodes = function(node) {
|
|
|
86
90
|
};
|
|
87
91
|
|
|
88
92
|
var walk = function(root, fn) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
var owners = document.querySelectorAll('[aria-owns]');
|
|
94
|
+
var queue = [root];
|
|
95
|
+
while (queue.length) {
|
|
96
|
+
var item = queue.shift();
|
|
97
|
+
fn(item);
|
|
98
|
+
queue = getChildNodes(item, owners).concat(queue);
|
|
99
|
+
}
|
|
93
100
|
};
|
|
94
101
|
|
|
95
102
|
var searchUp = function(node, test) {
|
|
@@ -116,35 +123,34 @@ var constants = require('./constants.js');
|
|
|
116
123
|
// candidates can be passed for performance optimization
|
|
117
124
|
var getRole = function(el, candidates) {
|
|
118
125
|
if (el.hasAttribute('role')) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
var selector = (constants.roles[role].selectors || []).join(',');
|
|
123
|
-
if (selector && (!candidates || candidates.includes(role)) && el.matches(selector)) {
|
|
124
|
-
return role;
|
|
126
|
+
var roles = el.getAttribute('role').toLowerCase().split(/\s+/);
|
|
127
|
+
if (roles.length > 1 && candidates) {
|
|
128
|
+
return [roles, candidates];
|
|
125
129
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
candidates.includes('banner') ||
|
|
130
|
-
candidates.includes('contentinfo')) {
|
|
131
|
-
if (!el.matches(constants.scoped)) {
|
|
132
|
-
if (el.matches('header')) {
|
|
133
|
-
return 'banner';
|
|
130
|
+
for (var role of roles) {
|
|
131
|
+
if (!candidates || candidates.includes(role)) {
|
|
132
|
+
return role;
|
|
134
133
|
}
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
}
|
|
135
|
+
} 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
|
+
}
|
|
137
144
|
}
|
|
138
145
|
}
|
|
139
146
|
}
|
|
140
147
|
};
|
|
141
148
|
|
|
142
149
|
var hasRole = function(el, roles) {
|
|
143
|
-
var candidates = [].concat.apply([], roles.map(
|
|
150
|
+
var candidates = [].concat.apply([], roles.map(role => {
|
|
144
151
|
return (constants.roles[role] || {}).subRoles || [role];
|
|
145
152
|
}));
|
|
146
|
-
|
|
147
|
-
return candidates.includes(actual);
|
|
153
|
+
return !!getRole(el, candidates);
|
|
148
154
|
};
|
|
149
155
|
|
|
150
156
|
var getAttribute = function(el, key) {
|
|
@@ -299,6 +305,9 @@ exports.attributeWeakMapping = {
|
|
|
299
305
|
'selected': 'selected',
|
|
300
306
|
};
|
|
301
307
|
|
|
308
|
+
// https://www.w3.org/TR/html/dom.html#sectioning-content-2
|
|
309
|
+
var scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
|
|
310
|
+
|
|
302
311
|
// https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
|
|
303
312
|
// https://www.w3.org/TR/wai-aria/roles
|
|
304
313
|
exports.roles = {
|
|
@@ -312,6 +321,12 @@ exports.roles = {
|
|
|
312
321
|
article: {
|
|
313
322
|
selectors: ['article'],
|
|
314
323
|
},
|
|
324
|
+
banner: {
|
|
325
|
+
selectors: [`header:not(main *, ${scoped})`],
|
|
326
|
+
},
|
|
327
|
+
blockquote: {
|
|
328
|
+
selectors: ['blockquote'],
|
|
329
|
+
},
|
|
315
330
|
button: {
|
|
316
331
|
selectors: [
|
|
317
332
|
'button',
|
|
@@ -323,26 +338,32 @@ exports.roles = {
|
|
|
323
338
|
],
|
|
324
339
|
nameFromContents: true,
|
|
325
340
|
},
|
|
341
|
+
caption: {
|
|
342
|
+
selectors: ['caption', 'figcaption'],
|
|
343
|
+
},
|
|
326
344
|
cell: {
|
|
327
|
-
selectors: ['td'],
|
|
328
|
-
childRoles: ['gridcell', 'rowheader'],
|
|
345
|
+
selectors: ['td', 'th:not([scope])'],
|
|
346
|
+
childRoles: ['columnheader', 'gridcell', 'rowheader'],
|
|
329
347
|
nameFromContents: true,
|
|
330
348
|
},
|
|
331
349
|
checkbox: {
|
|
332
350
|
selectors: ['input[type="checkbox"]'],
|
|
333
|
-
childRoles: ['
|
|
351
|
+
childRoles: ['switch'],
|
|
334
352
|
nameFromContents: true,
|
|
335
353
|
defaults: {
|
|
336
354
|
'checked': 'false',
|
|
337
355
|
},
|
|
338
356
|
},
|
|
357
|
+
code: {
|
|
358
|
+
selectors: ['code'],
|
|
359
|
+
},
|
|
339
360
|
columnheader: {
|
|
340
361
|
selectors: ['th[scope="col"]'],
|
|
341
362
|
nameFromContents: true,
|
|
342
363
|
},
|
|
343
364
|
combobox: {
|
|
344
365
|
selectors: [
|
|
345
|
-
|
|
366
|
+
'input:not([type])[list]',
|
|
346
367
|
'input[type="email"][list]',
|
|
347
368
|
'input[type="search"][list]',
|
|
348
369
|
'input[type="tel"][list]',
|
|
@@ -361,14 +382,25 @@ exports.roles = {
|
|
|
361
382
|
childRoles: ['button', 'link', 'menuitem'],
|
|
362
383
|
},
|
|
363
384
|
complementary: {
|
|
364
|
-
selectors: [
|
|
385
|
+
selectors: [
|
|
386
|
+
`aside:not(${scoped})`,
|
|
387
|
+
'aside[aria-label]',
|
|
388
|
+
'aside[aria-labelledby]',
|
|
389
|
+
'aside[title]',
|
|
390
|
+
],
|
|
365
391
|
},
|
|
366
392
|
composite: {
|
|
367
393
|
childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
|
|
368
394
|
},
|
|
395
|
+
contentinfo: {
|
|
396
|
+
selectors: [`footer:not(main *, ${scoped})`],
|
|
397
|
+
},
|
|
369
398
|
definition: {
|
|
370
399
|
selectors: ['dd'],
|
|
371
400
|
},
|
|
401
|
+
deletion: {
|
|
402
|
+
selectors: ['del', 's'],
|
|
403
|
+
},
|
|
372
404
|
dialog: {
|
|
373
405
|
selectors: ['dialog'],
|
|
374
406
|
childRoles: ['alertdialog'],
|
|
@@ -385,16 +417,32 @@ exports.roles = {
|
|
|
385
417
|
'doc-noteref': {
|
|
386
418
|
nameFromContents: true,
|
|
387
419
|
},
|
|
420
|
+
'doc-pagebreak': {
|
|
421
|
+
nameFromContents: true,
|
|
422
|
+
},
|
|
423
|
+
'doc-subtitle': {
|
|
424
|
+
nameFromContents: true,
|
|
425
|
+
},
|
|
388
426
|
document: {
|
|
389
|
-
selectors: ['
|
|
427
|
+
selectors: ['html'],
|
|
390
428
|
childRoles: ['article', 'graphics-document'],
|
|
391
429
|
},
|
|
430
|
+
emphasis: {
|
|
431
|
+
selectors: ['em'],
|
|
432
|
+
},
|
|
392
433
|
figure: {
|
|
393
434
|
selectors: ['figure'],
|
|
435
|
+
childRoles: ['doc-example'],
|
|
394
436
|
},
|
|
395
437
|
form: {
|
|
396
438
|
selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
|
|
397
439
|
},
|
|
440
|
+
generic: {
|
|
441
|
+
// too many selectors to list
|
|
442
|
+
},
|
|
443
|
+
'graphics-document': {
|
|
444
|
+
selectors: ['svg'],
|
|
445
|
+
},
|
|
398
446
|
grid: {
|
|
399
447
|
childRoles: ['treegrid'],
|
|
400
448
|
},
|
|
@@ -403,7 +451,14 @@ exports.roles = {
|
|
|
403
451
|
nameFromContents: true,
|
|
404
452
|
},
|
|
405
453
|
group: {
|
|
406
|
-
selectors: [
|
|
454
|
+
selectors: [
|
|
455
|
+
'address',
|
|
456
|
+
'details',
|
|
457
|
+
'fieldset',
|
|
458
|
+
'hgroup',
|
|
459
|
+
'optgroup',
|
|
460
|
+
'text',
|
|
461
|
+
],
|
|
407
462
|
childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
|
|
408
463
|
},
|
|
409
464
|
heading: {
|
|
@@ -418,7 +473,18 @@ exports.roles = {
|
|
|
418
473
|
childRoles: ['doc-cover'],
|
|
419
474
|
},
|
|
420
475
|
input: {
|
|
421
|
-
childRoles: [
|
|
476
|
+
childRoles: [
|
|
477
|
+
'checkbox',
|
|
478
|
+
'combobox',
|
|
479
|
+
'option',
|
|
480
|
+
'radio',
|
|
481
|
+
'slider',
|
|
482
|
+
'spinbutton',
|
|
483
|
+
'textbox',
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
insertion: {
|
|
487
|
+
selectors: ['ins'],
|
|
422
488
|
},
|
|
423
489
|
landmark: {
|
|
424
490
|
childRoles: [
|
|
@@ -449,16 +515,17 @@ exports.roles = {
|
|
|
449
515
|
],
|
|
450
516
|
},
|
|
451
517
|
link: {
|
|
452
|
-
selectors: ['a[href]', 'area[href]'
|
|
518
|
+
selectors: ['a[href]', 'area[href]'],
|
|
453
519
|
childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
|
|
454
520
|
nameFromContents: true,
|
|
455
521
|
},
|
|
456
522
|
list: {
|
|
457
|
-
selectors: ['dl', 'ol', 'ul'],
|
|
523
|
+
selectors: ['dl', 'ol', 'ul', 'menu'],
|
|
458
524
|
childRoles: ['directory', 'feed'],
|
|
459
525
|
},
|
|
460
526
|
listbox: {
|
|
461
527
|
selectors: [
|
|
528
|
+
'datalist',
|
|
462
529
|
'select[multiple]',
|
|
463
530
|
'select[size]:not([size="0"]):not([size="1"])',
|
|
464
531
|
],
|
|
@@ -467,7 +534,7 @@ exports.roles = {
|
|
|
467
534
|
},
|
|
468
535
|
},
|
|
469
536
|
listitem: {
|
|
470
|
-
selectors: ['
|
|
537
|
+
selectors: ['li'],
|
|
471
538
|
childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
|
|
472
539
|
},
|
|
473
540
|
log: {
|
|
@@ -481,8 +548,14 @@ exports.roles = {
|
|
|
481
548
|
math: {
|
|
482
549
|
selectors: ['math'],
|
|
483
550
|
},
|
|
551
|
+
meter: {
|
|
552
|
+
selectors: ['meter'],
|
|
553
|
+
defaults: {
|
|
554
|
+
'valuemin': 0,
|
|
555
|
+
'valuemax': 100,
|
|
556
|
+
},
|
|
557
|
+
},
|
|
484
558
|
menu: {
|
|
485
|
-
selectors: ['menu[type="context"]'],
|
|
486
559
|
childRoles: ['menubar'],
|
|
487
560
|
defaults: {
|
|
488
561
|
'orientation': 'vertical',
|
|
@@ -494,12 +567,10 @@ exports.roles = {
|
|
|
494
567
|
},
|
|
495
568
|
},
|
|
496
569
|
menuitem: {
|
|
497
|
-
selectors: ['menuitem[type="command"]'],
|
|
498
570
|
childRoles: ['menuitemcheckbox'],
|
|
499
571
|
nameFromContents: true,
|
|
500
572
|
},
|
|
501
573
|
menuitemcheckbox: {
|
|
502
|
-
selectors: ['menuitem[type="checkbox"]'],
|
|
503
574
|
childRoles: ['menuitemradio'],
|
|
504
575
|
nameFromContents: true,
|
|
505
576
|
defaults: {
|
|
@@ -507,7 +578,6 @@ exports.roles = {
|
|
|
507
578
|
},
|
|
508
579
|
},
|
|
509
580
|
menuitemradio: {
|
|
510
|
-
selectors: ['menuitem[type="radio"]'],
|
|
511
581
|
nameFromContents: true,
|
|
512
582
|
defaults: {
|
|
513
583
|
'checked': 'false',
|
|
@@ -528,11 +598,18 @@ exports.roles = {
|
|
|
528
598
|
'selected': 'false',
|
|
529
599
|
},
|
|
530
600
|
},
|
|
601
|
+
paragraph: {
|
|
602
|
+
selectors: ['p'],
|
|
603
|
+
},
|
|
531
604
|
presentation: {
|
|
532
605
|
selectors: ['img[alt=""]'],
|
|
533
606
|
},
|
|
534
607
|
progressbar: {
|
|
535
608
|
selectors: ['progress'],
|
|
609
|
+
defaults: {
|
|
610
|
+
'valuemin': 0,
|
|
611
|
+
'valuemax': 100,
|
|
612
|
+
},
|
|
536
613
|
},
|
|
537
614
|
radio: {
|
|
538
615
|
selectors: ['input[type="radio"]'],
|
|
@@ -543,7 +620,7 @@ exports.roles = {
|
|
|
543
620
|
},
|
|
544
621
|
},
|
|
545
622
|
range: {
|
|
546
|
-
childRoles: ['progressbar', 'scrollbar', 'slider', 'spinbutton'],
|
|
623
|
+
childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
|
|
547
624
|
},
|
|
548
625
|
region: {
|
|
549
626
|
selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
|
|
@@ -557,7 +634,6 @@ exports.roles = {
|
|
|
557
634
|
},
|
|
558
635
|
rowgroup: {
|
|
559
636
|
selectors: ['tbody', 'thead', 'tfoot'],
|
|
560
|
-
nameFromContents: true,
|
|
561
637
|
},
|
|
562
638
|
rowheader: {
|
|
563
639
|
selectors: ['th[scope="row"]'],
|
|
@@ -568,29 +644,38 @@ exports.roles = {
|
|
|
568
644
|
'orientation': 'vertical',
|
|
569
645
|
'valuemin': 0,
|
|
570
646
|
'valuemax': 100,
|
|
571
|
-
// FIXME: halfway between actual valuemin and valuemax
|
|
572
|
-
'valuenow': 50,
|
|
573
647
|
},
|
|
574
648
|
},
|
|
649
|
+
search: {
|
|
650
|
+
selectors: ['search'],
|
|
651
|
+
},
|
|
575
652
|
searchbox: {
|
|
576
653
|
selectors: ['input[type="search"]:not([list])'],
|
|
577
654
|
},
|
|
578
655
|
section: {
|
|
579
656
|
childRoles: [
|
|
580
657
|
'alert',
|
|
658
|
+
'blockquote',
|
|
659
|
+
'caption',
|
|
581
660
|
'cell',
|
|
661
|
+
'code',
|
|
582
662
|
'definition',
|
|
663
|
+
'deletion',
|
|
583
664
|
'doc-abstract',
|
|
584
665
|
'doc-colophon',
|
|
585
666
|
'doc-credit',
|
|
586
667
|
'doc-dedication',
|
|
587
668
|
'doc-epigraph',
|
|
588
|
-
'doc-example',
|
|
589
669
|
'doc-footnote',
|
|
670
|
+
'doc-pagefooter',
|
|
671
|
+
'doc-pageheader',
|
|
672
|
+
'doc-pullquote',
|
|
590
673
|
'doc-qna',
|
|
674
|
+
'emphasis',
|
|
591
675
|
'figure',
|
|
592
676
|
'group',
|
|
593
677
|
'img',
|
|
678
|
+
'insertion',
|
|
594
679
|
'landmark',
|
|
595
680
|
'list',
|
|
596
681
|
'listitem',
|
|
@@ -598,10 +683,15 @@ exports.roles = {
|
|
|
598
683
|
'marquee',
|
|
599
684
|
'math',
|
|
600
685
|
'note',
|
|
686
|
+
'paragraph',
|
|
601
687
|
'status',
|
|
688
|
+
'strong',
|
|
689
|
+
'subscript',
|
|
690
|
+
'superscript',
|
|
602
691
|
'table',
|
|
603
692
|
'tabpanel',
|
|
604
693
|
'term',
|
|
694
|
+
'time',
|
|
605
695
|
'tooltip',
|
|
606
696
|
],
|
|
607
697
|
},
|
|
@@ -616,7 +706,7 @@ exports.roles = {
|
|
|
616
706
|
nameFromContents: true,
|
|
617
707
|
},
|
|
618
708
|
select: {
|
|
619
|
-
childRoles: ['
|
|
709
|
+
childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
|
|
620
710
|
},
|
|
621
711
|
separator: {
|
|
622
712
|
// assume not focussable because <hr> is not
|
|
@@ -624,6 +714,8 @@ exports.roles = {
|
|
|
624
714
|
childRoles: ['doc-pagebreak'],
|
|
625
715
|
defaults: {
|
|
626
716
|
'orientation': 'horizontal',
|
|
717
|
+
'valuemin': 0,
|
|
718
|
+
'valuemax': 100,
|
|
627
719
|
},
|
|
628
720
|
},
|
|
629
721
|
slider: {
|
|
@@ -639,8 +731,7 @@ exports.roles = {
|
|
|
639
731
|
spinbutton: {
|
|
640
732
|
selectors: ['input[type="number"]'],
|
|
641
733
|
defaults: {
|
|
642
|
-
// FIXME: no valuemin/valuemax
|
|
643
|
-
'valuenow': 0,
|
|
734
|
+
// FIXME: no valuemin/valuemax/valuenow
|
|
644
735
|
},
|
|
645
736
|
},
|
|
646
737
|
status: {
|
|
@@ -651,18 +742,29 @@ exports.roles = {
|
|
|
651
742
|
'atomic': true,
|
|
652
743
|
},
|
|
653
744
|
},
|
|
745
|
+
strong: {
|
|
746
|
+
selectors: ['strong'],
|
|
747
|
+
},
|
|
654
748
|
structure: {
|
|
655
749
|
childRoles: [
|
|
656
750
|
'application',
|
|
657
751
|
'document',
|
|
658
752
|
'none',
|
|
753
|
+
'generic',
|
|
659
754
|
'presentation',
|
|
755
|
+
'range',
|
|
660
756
|
'rowgroup',
|
|
661
757
|
'section',
|
|
662
758
|
'sectionhead',
|
|
663
759
|
'separator',
|
|
664
760
|
],
|
|
665
761
|
},
|
|
762
|
+
subscript: {
|
|
763
|
+
selectors: ['sub'],
|
|
764
|
+
},
|
|
765
|
+
superscript: {
|
|
766
|
+
selectors: ['sup'],
|
|
767
|
+
},
|
|
666
768
|
switch: {
|
|
667
769
|
nameFromContents: true,
|
|
668
770
|
defaults: {
|
|
@@ -698,6 +800,14 @@ exports.roles = {
|
|
|
698
800
|
],
|
|
699
801
|
childRoles: ['searchbox'],
|
|
700
802
|
},
|
|
803
|
+
time: {
|
|
804
|
+
selectors: ['time'],
|
|
805
|
+
},
|
|
806
|
+
timer: {
|
|
807
|
+
defaults: {
|
|
808
|
+
'live': 'off',
|
|
809
|
+
},
|
|
810
|
+
},
|
|
701
811
|
toolbar: {
|
|
702
812
|
defaults: {
|
|
703
813
|
'orientation': 'horizontal',
|
|
@@ -721,8 +831,10 @@ exports.roles = {
|
|
|
721
831
|
'composite',
|
|
722
832
|
'gridcell',
|
|
723
833
|
'input',
|
|
724
|
-
'
|
|
834
|
+
'progressbar',
|
|
725
835
|
'row',
|
|
836
|
+
'scrollbar',
|
|
837
|
+
'separator',
|
|
726
838
|
'tab',
|
|
727
839
|
],
|
|
728
840
|
},
|
|
@@ -731,22 +843,14 @@ exports.roles = {
|
|
|
731
843
|
},
|
|
732
844
|
};
|
|
733
845
|
|
|
734
|
-
exports.scoped = [
|
|
735
|
-
'main *',
|
|
736
|
-
// https://www.w3.org/TR/html/dom.html#sectioning-content-2
|
|
737
|
-
'article *', 'aside *', 'nav *', 'section *',
|
|
738
|
-
// https://www.w3.org/TR/html/sections.html#sectioning-roots
|
|
739
|
-
'blockquote *', 'details *', 'dialog *', 'fieldset *', 'figure *', 'td *',
|
|
740
|
-
].join(',');
|
|
741
|
-
|
|
742
846
|
var getSubRoles = function(role) {
|
|
743
847
|
var children = (exports.roles[role] || {}).childRoles || [];
|
|
744
848
|
var descendents = children.map(getSubRoles);
|
|
745
849
|
|
|
746
850
|
var result = [role];
|
|
747
851
|
|
|
748
|
-
descendents.forEach(
|
|
749
|
-
list.forEach(
|
|
852
|
+
descendents.forEach(list => {
|
|
853
|
+
list.forEach(r => {
|
|
750
854
|
if (!result.includes(r)) {
|
|
751
855
|
result.push(r);
|
|
752
856
|
}
|
|
@@ -858,7 +962,7 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
858
962
|
// B
|
|
859
963
|
if (!recursive && el.matches('[aria-labelledby]')) {
|
|
860
964
|
var ids = el.getAttribute('aria-labelledby').split(/\s+/);
|
|
861
|
-
var strings = ids.map(
|
|
965
|
+
var strings = ids.map(id => {
|
|
862
966
|
var label = document.getElementById(id);
|
|
863
967
|
return label ? getName(label, true, visited, true) : '';
|
|
864
968
|
});
|
|
@@ -873,7 +977,7 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
873
977
|
|
|
874
978
|
// D
|
|
875
979
|
if (!ret.trim() && !recursive && el.labels) {
|
|
876
|
-
var strings = Array.prototype.map.call(el.labels,
|
|
980
|
+
var strings = Array.prototype.map.call(el.labels, label => {
|
|
877
981
|
return getName(label, true, visited);
|
|
878
982
|
});
|
|
879
983
|
ret = strings.join(' ');
|
|
@@ -897,6 +1001,12 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
897
1001
|
}
|
|
898
1002
|
}
|
|
899
1003
|
}
|
|
1004
|
+
if (!ret.trim() && el.matches('svg *')) {
|
|
1005
|
+
var svgTitle = el.querySelector('title');
|
|
1006
|
+
if (svgTitle && svgTitle.parentElement === el) {
|
|
1007
|
+
ret = svgTitle.textContent;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
900
1010
|
|
|
901
1011
|
// E
|
|
902
1012
|
if (!ret.trim() && (recursive || isInLabelForOtherWidget(el) || query.matches(el, 'button'))) {
|
|
@@ -959,15 +1069,20 @@ var getDescription = function(el) {
|
|
|
959
1069
|
|
|
960
1070
|
if (el.matches('[aria-describedby]')) {
|
|
961
1071
|
var ids = el.getAttribute('aria-describedby').split(/\s+/);
|
|
962
|
-
var strings = ids.map(
|
|
1072
|
+
var strings = ids.map(id => {
|
|
963
1073
|
var label = document.getElementById(id);
|
|
964
1074
|
return label ? getName(label, true) : '';
|
|
965
1075
|
});
|
|
966
1076
|
ret = strings.join(' ');
|
|
1077
|
+
} else if (el.matches('[aria-description]')) {
|
|
1078
|
+
ret = el.getAttribute('aria-description');
|
|
1079
|
+
} else if (el.matches('svg *')) {
|
|
1080
|
+
var svgDesc = el.querySelector('desc');
|
|
1081
|
+
if (svgDesc && svgDesc.parentElement === el) {
|
|
1082
|
+
ret = svgDesc.textContent;
|
|
1083
|
+
}
|
|
967
1084
|
} else if (el.title) {
|
|
968
1085
|
ret = el.title;
|
|
969
|
-
} else if (el.placeholder) {
|
|
970
|
-
ret = el.placeholder;
|
|
971
1086
|
}
|
|
972
1087
|
|
|
973
1088
|
ret = (ret || '').trim().replace(/\s+/g, ' ');
|
|
@@ -1009,7 +1124,7 @@ var _querySelector = function(all) {
|
|
|
1009
1124
|
return function(root, role) {
|
|
1010
1125
|
var results = [];
|
|
1011
1126
|
try {
|
|
1012
|
-
atree.walk(root,
|
|
1127
|
+
atree.walk(root, node => {
|
|
1013
1128
|
if (node.nodeType === node.ELEMENT_NODE) {
|
|
1014
1129
|
// FIXME: skip hidden elements
|
|
1015
1130
|
if (matches(node, role)) {
|
|
@@ -1030,7 +1145,7 @@ var _querySelector = function(all) {
|
|
|
1030
1145
|
};
|
|
1031
1146
|
|
|
1032
1147
|
var closest = function(el, selector) {
|
|
1033
|
-
return atree.searchUp(el,
|
|
1148
|
+
return atree.searchUp(el, candidate => {
|
|
1034
1149
|
if (candidate.nodeType === candidate.ELEMENT_NODE) {
|
|
1035
1150
|
return matches(candidate, selector);
|
|
1036
1151
|
}
|
|
@@ -1038,9 +1153,7 @@ var closest = function(el, selector) {
|
|
|
1038
1153
|
};
|
|
1039
1154
|
|
|
1040
1155
|
module.exports = {
|
|
1041
|
-
getRole:
|
|
1042
|
-
return attrs.getRole(el);
|
|
1043
|
-
},
|
|
1156
|
+
getRole: el => attrs.getRole(el),
|
|
1044
1157
|
getAttribute: attrs.getAttribute,
|
|
1045
1158
|
matches: matches,
|
|
1046
1159
|
querySelector: _querySelector(),
|
package/lib/atree.js
CHANGED
|
@@ -1,51 +1,55 @@
|
|
|
1
1
|
var attrs = require('./attrs');
|
|
2
2
|
|
|
3
|
-
var _getOwner = function(node) {
|
|
3
|
+
var _getOwner = function(node, owners) {
|
|
4
4
|
if (node.nodeType === node.ELEMENT_NODE && node.id) {
|
|
5
|
-
var
|
|
6
|
-
if (
|
|
7
|
-
|
|
5
|
+
var selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
|
|
6
|
+
if (owners) {
|
|
7
|
+
for (var owner of owners) {
|
|
8
|
+
if (owner.matches(selector)) {
|
|
9
|
+
return owner;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
} else {
|
|
13
|
+
return document.querySelector(selector);
|
|
8
14
|
}
|
|
9
15
|
}
|
|
10
16
|
};
|
|
11
17
|
|
|
12
|
-
var _getParentNode = function(node) {
|
|
13
|
-
return _getOwner(node) || node.parentNode;
|
|
18
|
+
var _getParentNode = function(node, owners) {
|
|
19
|
+
return _getOwner(node, owners) || node.parentNode;
|
|
14
20
|
};
|
|
15
21
|
|
|
16
|
-
var detectLoop = function(node) {
|
|
17
|
-
var seen = [node]
|
|
18
|
-
while ((node = _getParentNode(node))) {
|
|
22
|
+
var detectLoop = function(node, owners) {
|
|
23
|
+
var seen = [node];
|
|
24
|
+
while ((node = _getParentNode(node, owners))) {
|
|
19
25
|
if (seen.includes(node)) {
|
|
20
26
|
return true;
|
|
21
27
|
}
|
|
22
|
-
seen.push(node)
|
|
28
|
+
seen.push(node);
|
|
23
29
|
}
|
|
24
30
|
};
|
|
25
31
|
|
|
26
|
-
var getOwner = function(node) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return owner;
|
|
31
|
-
}
|
|
32
|
+
var getOwner = function(node, owners) {
|
|
33
|
+
var owner = _getOwner(node, owners);
|
|
34
|
+
if (owner && !detectLoop(node, owners)) {
|
|
35
|
+
return owner;
|
|
32
36
|
}
|
|
33
37
|
};
|
|
34
38
|
|
|
35
|
-
var getParentNode = function(node) {
|
|
36
|
-
return getOwner(node) || node.parentNode;
|
|
39
|
+
var getParentNode = function(node, owners) {
|
|
40
|
+
return getOwner(node, owners) || node.parentNode;
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
var isHidden = function(node) {
|
|
40
44
|
return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
|
|
41
45
|
};
|
|
42
46
|
|
|
43
|
-
var getChildNodes = function(node) {
|
|
47
|
+
var getChildNodes = function(node, owners) {
|
|
44
48
|
var childNodes = [];
|
|
45
49
|
|
|
46
50
|
for (var i = 0; i < node.childNodes.length; i++) {
|
|
47
51
|
var child = node.childNodes[i];
|
|
48
|
-
if (!getOwner(child) && !isHidden(child)) {
|
|
52
|
+
if (!getOwner(child, owners) && !isHidden(child)) {
|
|
49
53
|
childNodes.push(child);
|
|
50
54
|
}
|
|
51
55
|
}
|
|
@@ -55,7 +59,7 @@ var getChildNodes = function(node) {
|
|
|
55
59
|
for (var i = 0; i < owns.length; i++) {
|
|
56
60
|
var child = document.getElementById(owns[i]);
|
|
57
61
|
// double check with getOwner for consistency
|
|
58
|
-
if (child && getOwner(child) === node && !isHidden(child)) {
|
|
62
|
+
if (child && getOwner(child, owners) === node && !isHidden(child)) {
|
|
59
63
|
childNodes.push(child);
|
|
60
64
|
}
|
|
61
65
|
}
|
|
@@ -65,10 +69,13 @@ var getChildNodes = function(node) {
|
|
|
65
69
|
};
|
|
66
70
|
|
|
67
71
|
var walk = function(root, fn) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
var owners = document.querySelectorAll('[aria-owns]');
|
|
73
|
+
var queue = [root];
|
|
74
|
+
while (queue.length) {
|
|
75
|
+
var item = queue.shift();
|
|
76
|
+
fn(item);
|
|
77
|
+
queue = getChildNodes(item, owners).concat(queue);
|
|
78
|
+
}
|
|
72
79
|
};
|
|
73
80
|
|
|
74
81
|
var searchUp = function(node, test) {
|
package/lib/attrs.js
CHANGED
|
@@ -3,35 +3,34 @@ var constants = require('./constants.js');
|
|
|
3
3
|
// candidates can be passed for performance optimization
|
|
4
4
|
var getRole = function(el, candidates) {
|
|
5
5
|
if (el.hasAttribute('role')) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var selector = (constants.roles[role].selectors || []).join(',');
|
|
10
|
-
if (selector && (!candidates || candidates.includes(role)) && el.matches(selector)) {
|
|
11
|
-
return role;
|
|
6
|
+
var roles = el.getAttribute('role').toLowerCase().split(/\s+/);
|
|
7
|
+
if (roles.length > 1 && candidates) {
|
|
8
|
+
return [roles, candidates];
|
|
12
9
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
candidates.includes('banner') ||
|
|
17
|
-
candidates.includes('contentinfo')) {
|
|
18
|
-
if (!el.matches(constants.scoped)) {
|
|
19
|
-
if (el.matches('header')) {
|
|
20
|
-
return 'banner';
|
|
10
|
+
for (var role of roles) {
|
|
11
|
+
if (!candidates || candidates.includes(role)) {
|
|
12
|
+
return role;
|
|
21
13
|
}
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
}
|
|
15
|
+
} 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
|
+
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
var hasRole = function(el, roles) {
|
|
30
|
-
var candidates = [].concat.apply([], roles.map(
|
|
30
|
+
var candidates = [].concat.apply([], roles.map(role => {
|
|
31
31
|
return (constants.roles[role] || {}).subRoles || [role];
|
|
32
32
|
}));
|
|
33
|
-
|
|
34
|
-
return candidates.includes(actual);
|
|
33
|
+
return !!getRole(el, candidates);
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
var getAttribute = function(el, key) {
|
package/lib/constants.js
CHANGED
|
@@ -66,6 +66,9 @@ exports.attributeWeakMapping = {
|
|
|
66
66
|
'selected': 'selected',
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
// https://www.w3.org/TR/html/dom.html#sectioning-content-2
|
|
70
|
+
var scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
|
|
71
|
+
|
|
69
72
|
// https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
|
|
70
73
|
// https://www.w3.org/TR/wai-aria/roles
|
|
71
74
|
exports.roles = {
|
|
@@ -79,6 +82,12 @@ exports.roles = {
|
|
|
79
82
|
article: {
|
|
80
83
|
selectors: ['article'],
|
|
81
84
|
},
|
|
85
|
+
banner: {
|
|
86
|
+
selectors: [`header:not(main *, ${scoped})`],
|
|
87
|
+
},
|
|
88
|
+
blockquote: {
|
|
89
|
+
selectors: ['blockquote'],
|
|
90
|
+
},
|
|
82
91
|
button: {
|
|
83
92
|
selectors: [
|
|
84
93
|
'button',
|
|
@@ -90,26 +99,32 @@ exports.roles = {
|
|
|
90
99
|
],
|
|
91
100
|
nameFromContents: true,
|
|
92
101
|
},
|
|
102
|
+
caption: {
|
|
103
|
+
selectors: ['caption', 'figcaption'],
|
|
104
|
+
},
|
|
93
105
|
cell: {
|
|
94
|
-
selectors: ['td'],
|
|
95
|
-
childRoles: ['gridcell', 'rowheader'],
|
|
106
|
+
selectors: ['td', 'th:not([scope])'],
|
|
107
|
+
childRoles: ['columnheader', 'gridcell', 'rowheader'],
|
|
96
108
|
nameFromContents: true,
|
|
97
109
|
},
|
|
98
110
|
checkbox: {
|
|
99
111
|
selectors: ['input[type="checkbox"]'],
|
|
100
|
-
childRoles: ['
|
|
112
|
+
childRoles: ['switch'],
|
|
101
113
|
nameFromContents: true,
|
|
102
114
|
defaults: {
|
|
103
115
|
'checked': 'false',
|
|
104
116
|
},
|
|
105
117
|
},
|
|
118
|
+
code: {
|
|
119
|
+
selectors: ['code'],
|
|
120
|
+
},
|
|
106
121
|
columnheader: {
|
|
107
122
|
selectors: ['th[scope="col"]'],
|
|
108
123
|
nameFromContents: true,
|
|
109
124
|
},
|
|
110
125
|
combobox: {
|
|
111
126
|
selectors: [
|
|
112
|
-
|
|
127
|
+
'input:not([type])[list]',
|
|
113
128
|
'input[type="email"][list]',
|
|
114
129
|
'input[type="search"][list]',
|
|
115
130
|
'input[type="tel"][list]',
|
|
@@ -128,14 +143,25 @@ exports.roles = {
|
|
|
128
143
|
childRoles: ['button', 'link', 'menuitem'],
|
|
129
144
|
},
|
|
130
145
|
complementary: {
|
|
131
|
-
selectors: [
|
|
146
|
+
selectors: [
|
|
147
|
+
`aside:not(${scoped})`,
|
|
148
|
+
'aside[aria-label]',
|
|
149
|
+
'aside[aria-labelledby]',
|
|
150
|
+
'aside[title]',
|
|
151
|
+
],
|
|
132
152
|
},
|
|
133
153
|
composite: {
|
|
134
154
|
childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
|
|
135
155
|
},
|
|
156
|
+
contentinfo: {
|
|
157
|
+
selectors: [`footer:not(main *, ${scoped})`],
|
|
158
|
+
},
|
|
136
159
|
definition: {
|
|
137
160
|
selectors: ['dd'],
|
|
138
161
|
},
|
|
162
|
+
deletion: {
|
|
163
|
+
selectors: ['del', 's'],
|
|
164
|
+
},
|
|
139
165
|
dialog: {
|
|
140
166
|
selectors: ['dialog'],
|
|
141
167
|
childRoles: ['alertdialog'],
|
|
@@ -152,16 +178,32 @@ exports.roles = {
|
|
|
152
178
|
'doc-noteref': {
|
|
153
179
|
nameFromContents: true,
|
|
154
180
|
},
|
|
181
|
+
'doc-pagebreak': {
|
|
182
|
+
nameFromContents: true,
|
|
183
|
+
},
|
|
184
|
+
'doc-subtitle': {
|
|
185
|
+
nameFromContents: true,
|
|
186
|
+
},
|
|
155
187
|
document: {
|
|
156
|
-
selectors: ['
|
|
188
|
+
selectors: ['html'],
|
|
157
189
|
childRoles: ['article', 'graphics-document'],
|
|
158
190
|
},
|
|
191
|
+
emphasis: {
|
|
192
|
+
selectors: ['em'],
|
|
193
|
+
},
|
|
159
194
|
figure: {
|
|
160
195
|
selectors: ['figure'],
|
|
196
|
+
childRoles: ['doc-example'],
|
|
161
197
|
},
|
|
162
198
|
form: {
|
|
163
199
|
selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
|
|
164
200
|
},
|
|
201
|
+
generic: {
|
|
202
|
+
// too many selectors to list
|
|
203
|
+
},
|
|
204
|
+
'graphics-document': {
|
|
205
|
+
selectors: ['svg'],
|
|
206
|
+
},
|
|
165
207
|
grid: {
|
|
166
208
|
childRoles: ['treegrid'],
|
|
167
209
|
},
|
|
@@ -170,7 +212,14 @@ exports.roles = {
|
|
|
170
212
|
nameFromContents: true,
|
|
171
213
|
},
|
|
172
214
|
group: {
|
|
173
|
-
selectors: [
|
|
215
|
+
selectors: [
|
|
216
|
+
'address',
|
|
217
|
+
'details',
|
|
218
|
+
'fieldset',
|
|
219
|
+
'hgroup',
|
|
220
|
+
'optgroup',
|
|
221
|
+
'text',
|
|
222
|
+
],
|
|
174
223
|
childRoles: ['row', 'select', 'toolbar', 'graphics-object'],
|
|
175
224
|
},
|
|
176
225
|
heading: {
|
|
@@ -185,7 +234,18 @@ exports.roles = {
|
|
|
185
234
|
childRoles: ['doc-cover'],
|
|
186
235
|
},
|
|
187
236
|
input: {
|
|
188
|
-
childRoles: [
|
|
237
|
+
childRoles: [
|
|
238
|
+
'checkbox',
|
|
239
|
+
'combobox',
|
|
240
|
+
'option',
|
|
241
|
+
'radio',
|
|
242
|
+
'slider',
|
|
243
|
+
'spinbutton',
|
|
244
|
+
'textbox',
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
insertion: {
|
|
248
|
+
selectors: ['ins'],
|
|
189
249
|
},
|
|
190
250
|
landmark: {
|
|
191
251
|
childRoles: [
|
|
@@ -216,16 +276,17 @@ exports.roles = {
|
|
|
216
276
|
],
|
|
217
277
|
},
|
|
218
278
|
link: {
|
|
219
|
-
selectors: ['a[href]', 'area[href]'
|
|
279
|
+
selectors: ['a[href]', 'area[href]'],
|
|
220
280
|
childRoles: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
|
|
221
281
|
nameFromContents: true,
|
|
222
282
|
},
|
|
223
283
|
list: {
|
|
224
|
-
selectors: ['dl', 'ol', 'ul'],
|
|
284
|
+
selectors: ['dl', 'ol', 'ul', 'menu'],
|
|
225
285
|
childRoles: ['directory', 'feed'],
|
|
226
286
|
},
|
|
227
287
|
listbox: {
|
|
228
288
|
selectors: [
|
|
289
|
+
'datalist',
|
|
229
290
|
'select[multiple]',
|
|
230
291
|
'select[size]:not([size="0"]):not([size="1"])',
|
|
231
292
|
],
|
|
@@ -234,7 +295,7 @@ exports.roles = {
|
|
|
234
295
|
},
|
|
235
296
|
},
|
|
236
297
|
listitem: {
|
|
237
|
-
selectors: ['
|
|
298
|
+
selectors: ['li'],
|
|
238
299
|
childRoles: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
|
|
239
300
|
},
|
|
240
301
|
log: {
|
|
@@ -248,8 +309,14 @@ exports.roles = {
|
|
|
248
309
|
math: {
|
|
249
310
|
selectors: ['math'],
|
|
250
311
|
},
|
|
312
|
+
meter: {
|
|
313
|
+
selectors: ['meter'],
|
|
314
|
+
defaults: {
|
|
315
|
+
'valuemin': 0,
|
|
316
|
+
'valuemax': 100,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
251
319
|
menu: {
|
|
252
|
-
selectors: ['menu[type="context"]'],
|
|
253
320
|
childRoles: ['menubar'],
|
|
254
321
|
defaults: {
|
|
255
322
|
'orientation': 'vertical',
|
|
@@ -261,12 +328,10 @@ exports.roles = {
|
|
|
261
328
|
},
|
|
262
329
|
},
|
|
263
330
|
menuitem: {
|
|
264
|
-
selectors: ['menuitem[type="command"]'],
|
|
265
331
|
childRoles: ['menuitemcheckbox'],
|
|
266
332
|
nameFromContents: true,
|
|
267
333
|
},
|
|
268
334
|
menuitemcheckbox: {
|
|
269
|
-
selectors: ['menuitem[type="checkbox"]'],
|
|
270
335
|
childRoles: ['menuitemradio'],
|
|
271
336
|
nameFromContents: true,
|
|
272
337
|
defaults: {
|
|
@@ -274,7 +339,6 @@ exports.roles = {
|
|
|
274
339
|
},
|
|
275
340
|
},
|
|
276
341
|
menuitemradio: {
|
|
277
|
-
selectors: ['menuitem[type="radio"]'],
|
|
278
342
|
nameFromContents: true,
|
|
279
343
|
defaults: {
|
|
280
344
|
'checked': 'false',
|
|
@@ -295,11 +359,18 @@ exports.roles = {
|
|
|
295
359
|
'selected': 'false',
|
|
296
360
|
},
|
|
297
361
|
},
|
|
362
|
+
paragraph: {
|
|
363
|
+
selectors: ['p'],
|
|
364
|
+
},
|
|
298
365
|
presentation: {
|
|
299
366
|
selectors: ['img[alt=""]'],
|
|
300
367
|
},
|
|
301
368
|
progressbar: {
|
|
302
369
|
selectors: ['progress'],
|
|
370
|
+
defaults: {
|
|
371
|
+
'valuemin': 0,
|
|
372
|
+
'valuemax': 100,
|
|
373
|
+
},
|
|
303
374
|
},
|
|
304
375
|
radio: {
|
|
305
376
|
selectors: ['input[type="radio"]'],
|
|
@@ -310,7 +381,7 @@ exports.roles = {
|
|
|
310
381
|
},
|
|
311
382
|
},
|
|
312
383
|
range: {
|
|
313
|
-
childRoles: ['progressbar', 'scrollbar', 'slider', 'spinbutton'],
|
|
384
|
+
childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
|
|
314
385
|
},
|
|
315
386
|
region: {
|
|
316
387
|
selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
|
|
@@ -324,7 +395,6 @@ exports.roles = {
|
|
|
324
395
|
},
|
|
325
396
|
rowgroup: {
|
|
326
397
|
selectors: ['tbody', 'thead', 'tfoot'],
|
|
327
|
-
nameFromContents: true,
|
|
328
398
|
},
|
|
329
399
|
rowheader: {
|
|
330
400
|
selectors: ['th[scope="row"]'],
|
|
@@ -335,29 +405,38 @@ exports.roles = {
|
|
|
335
405
|
'orientation': 'vertical',
|
|
336
406
|
'valuemin': 0,
|
|
337
407
|
'valuemax': 100,
|
|
338
|
-
// FIXME: halfway between actual valuemin and valuemax
|
|
339
|
-
'valuenow': 50,
|
|
340
408
|
},
|
|
341
409
|
},
|
|
410
|
+
search: {
|
|
411
|
+
selectors: ['search'],
|
|
412
|
+
},
|
|
342
413
|
searchbox: {
|
|
343
414
|
selectors: ['input[type="search"]:not([list])'],
|
|
344
415
|
},
|
|
345
416
|
section: {
|
|
346
417
|
childRoles: [
|
|
347
418
|
'alert',
|
|
419
|
+
'blockquote',
|
|
420
|
+
'caption',
|
|
348
421
|
'cell',
|
|
422
|
+
'code',
|
|
349
423
|
'definition',
|
|
424
|
+
'deletion',
|
|
350
425
|
'doc-abstract',
|
|
351
426
|
'doc-colophon',
|
|
352
427
|
'doc-credit',
|
|
353
428
|
'doc-dedication',
|
|
354
429
|
'doc-epigraph',
|
|
355
|
-
'doc-example',
|
|
356
430
|
'doc-footnote',
|
|
431
|
+
'doc-pagefooter',
|
|
432
|
+
'doc-pageheader',
|
|
433
|
+
'doc-pullquote',
|
|
357
434
|
'doc-qna',
|
|
435
|
+
'emphasis',
|
|
358
436
|
'figure',
|
|
359
437
|
'group',
|
|
360
438
|
'img',
|
|
439
|
+
'insertion',
|
|
361
440
|
'landmark',
|
|
362
441
|
'list',
|
|
363
442
|
'listitem',
|
|
@@ -365,10 +444,15 @@ exports.roles = {
|
|
|
365
444
|
'marquee',
|
|
366
445
|
'math',
|
|
367
446
|
'note',
|
|
447
|
+
'paragraph',
|
|
368
448
|
'status',
|
|
449
|
+
'strong',
|
|
450
|
+
'subscript',
|
|
451
|
+
'superscript',
|
|
369
452
|
'table',
|
|
370
453
|
'tabpanel',
|
|
371
454
|
'term',
|
|
455
|
+
'time',
|
|
372
456
|
'tooltip',
|
|
373
457
|
],
|
|
374
458
|
},
|
|
@@ -383,7 +467,7 @@ exports.roles = {
|
|
|
383
467
|
nameFromContents: true,
|
|
384
468
|
},
|
|
385
469
|
select: {
|
|
386
|
-
childRoles: ['
|
|
470
|
+
childRoles: ['listbox', 'menu', 'radiogroup', 'tree'],
|
|
387
471
|
},
|
|
388
472
|
separator: {
|
|
389
473
|
// assume not focussable because <hr> is not
|
|
@@ -391,6 +475,8 @@ exports.roles = {
|
|
|
391
475
|
childRoles: ['doc-pagebreak'],
|
|
392
476
|
defaults: {
|
|
393
477
|
'orientation': 'horizontal',
|
|
478
|
+
'valuemin': 0,
|
|
479
|
+
'valuemax': 100,
|
|
394
480
|
},
|
|
395
481
|
},
|
|
396
482
|
slider: {
|
|
@@ -406,8 +492,7 @@ exports.roles = {
|
|
|
406
492
|
spinbutton: {
|
|
407
493
|
selectors: ['input[type="number"]'],
|
|
408
494
|
defaults: {
|
|
409
|
-
// FIXME: no valuemin/valuemax
|
|
410
|
-
'valuenow': 0,
|
|
495
|
+
// FIXME: no valuemin/valuemax/valuenow
|
|
411
496
|
},
|
|
412
497
|
},
|
|
413
498
|
status: {
|
|
@@ -418,18 +503,29 @@ exports.roles = {
|
|
|
418
503
|
'atomic': true,
|
|
419
504
|
},
|
|
420
505
|
},
|
|
506
|
+
strong: {
|
|
507
|
+
selectors: ['strong'],
|
|
508
|
+
},
|
|
421
509
|
structure: {
|
|
422
510
|
childRoles: [
|
|
423
511
|
'application',
|
|
424
512
|
'document',
|
|
425
513
|
'none',
|
|
514
|
+
'generic',
|
|
426
515
|
'presentation',
|
|
516
|
+
'range',
|
|
427
517
|
'rowgroup',
|
|
428
518
|
'section',
|
|
429
519
|
'sectionhead',
|
|
430
520
|
'separator',
|
|
431
521
|
],
|
|
432
522
|
},
|
|
523
|
+
subscript: {
|
|
524
|
+
selectors: ['sub'],
|
|
525
|
+
},
|
|
526
|
+
superscript: {
|
|
527
|
+
selectors: ['sup'],
|
|
528
|
+
},
|
|
433
529
|
switch: {
|
|
434
530
|
nameFromContents: true,
|
|
435
531
|
defaults: {
|
|
@@ -465,6 +561,14 @@ exports.roles = {
|
|
|
465
561
|
],
|
|
466
562
|
childRoles: ['searchbox'],
|
|
467
563
|
},
|
|
564
|
+
time: {
|
|
565
|
+
selectors: ['time'],
|
|
566
|
+
},
|
|
567
|
+
timer: {
|
|
568
|
+
defaults: {
|
|
569
|
+
'live': 'off',
|
|
570
|
+
},
|
|
571
|
+
},
|
|
468
572
|
toolbar: {
|
|
469
573
|
defaults: {
|
|
470
574
|
'orientation': 'horizontal',
|
|
@@ -488,8 +592,10 @@ exports.roles = {
|
|
|
488
592
|
'composite',
|
|
489
593
|
'gridcell',
|
|
490
594
|
'input',
|
|
491
|
-
'
|
|
595
|
+
'progressbar',
|
|
492
596
|
'row',
|
|
597
|
+
'scrollbar',
|
|
598
|
+
'separator',
|
|
493
599
|
'tab',
|
|
494
600
|
],
|
|
495
601
|
},
|
|
@@ -498,22 +604,14 @@ exports.roles = {
|
|
|
498
604
|
},
|
|
499
605
|
};
|
|
500
606
|
|
|
501
|
-
exports.scoped = [
|
|
502
|
-
'main *',
|
|
503
|
-
// https://www.w3.org/TR/html/dom.html#sectioning-content-2
|
|
504
|
-
'article *', 'aside *', 'nav *', 'section *',
|
|
505
|
-
// https://www.w3.org/TR/html/sections.html#sectioning-roots
|
|
506
|
-
'blockquote *', 'details *', 'dialog *', 'fieldset *', 'figure *', 'td *',
|
|
507
|
-
].join(',');
|
|
508
|
-
|
|
509
607
|
var getSubRoles = function(role) {
|
|
510
608
|
var children = (exports.roles[role] || {}).childRoles || [];
|
|
511
609
|
var descendents = children.map(getSubRoles);
|
|
512
610
|
|
|
513
611
|
var result = [role];
|
|
514
612
|
|
|
515
|
-
descendents.forEach(
|
|
516
|
-
list.forEach(
|
|
613
|
+
descendents.forEach(list => {
|
|
614
|
+
list.forEach(r => {
|
|
517
615
|
if (!result.includes(r)) {
|
|
518
616
|
result.push(r);
|
|
519
617
|
}
|
package/lib/name.js
CHANGED
|
@@ -72,7 +72,7 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
72
72
|
// B
|
|
73
73
|
if (!recursive && el.matches('[aria-labelledby]')) {
|
|
74
74
|
var ids = el.getAttribute('aria-labelledby').split(/\s+/);
|
|
75
|
-
var strings = ids.map(
|
|
75
|
+
var strings = ids.map(id => {
|
|
76
76
|
var label = document.getElementById(id);
|
|
77
77
|
return label ? getName(label, true, visited, true) : '';
|
|
78
78
|
});
|
|
@@ -87,7 +87,7 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
87
87
|
|
|
88
88
|
// D
|
|
89
89
|
if (!ret.trim() && !recursive && el.labels) {
|
|
90
|
-
var strings = Array.prototype.map.call(el.labels,
|
|
90
|
+
var strings = Array.prototype.map.call(el.labels, label => {
|
|
91
91
|
return getName(label, true, visited);
|
|
92
92
|
});
|
|
93
93
|
ret = strings.join(' ');
|
|
@@ -111,6 +111,12 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
if (!ret.trim() && el.matches('svg *')) {
|
|
115
|
+
var svgTitle = el.querySelector('title');
|
|
116
|
+
if (svgTitle && svgTitle.parentElement === el) {
|
|
117
|
+
ret = svgTitle.textContent;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
114
120
|
|
|
115
121
|
// E
|
|
116
122
|
if (!ret.trim() && (recursive || isInLabelForOtherWidget(el) || query.matches(el, 'button'))) {
|
|
@@ -173,15 +179,20 @@ var getDescription = function(el) {
|
|
|
173
179
|
|
|
174
180
|
if (el.matches('[aria-describedby]')) {
|
|
175
181
|
var ids = el.getAttribute('aria-describedby').split(/\s+/);
|
|
176
|
-
var strings = ids.map(
|
|
182
|
+
var strings = ids.map(id => {
|
|
177
183
|
var label = document.getElementById(id);
|
|
178
184
|
return label ? getName(label, true) : '';
|
|
179
185
|
});
|
|
180
186
|
ret = strings.join(' ');
|
|
187
|
+
} else if (el.matches('[aria-description]')) {
|
|
188
|
+
ret = el.getAttribute('aria-description');
|
|
189
|
+
} else if (el.matches('svg *')) {
|
|
190
|
+
var svgDesc = el.querySelector('desc');
|
|
191
|
+
if (svgDesc && svgDesc.parentElement === el) {
|
|
192
|
+
ret = svgDesc.textContent;
|
|
193
|
+
}
|
|
181
194
|
} else if (el.title) {
|
|
182
195
|
ret = el.title;
|
|
183
|
-
} else if (el.placeholder) {
|
|
184
|
-
ret = el.placeholder;
|
|
185
196
|
}
|
|
186
197
|
|
|
187
198
|
ret = (ret || '').trim().replace(/\s+/g, ' ');
|
package/lib/query.js
CHANGED
|
@@ -22,7 +22,7 @@ var _querySelector = function(all) {
|
|
|
22
22
|
return function(root, role) {
|
|
23
23
|
var results = [];
|
|
24
24
|
try {
|
|
25
|
-
atree.walk(root,
|
|
25
|
+
atree.walk(root, node => {
|
|
26
26
|
if (node.nodeType === node.ELEMENT_NODE) {
|
|
27
27
|
// FIXME: skip hidden elements
|
|
28
28
|
if (matches(node, role)) {
|
|
@@ -43,7 +43,7 @@ var _querySelector = function(all) {
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
var closest = function(el, selector) {
|
|
46
|
-
return atree.searchUp(el,
|
|
46
|
+
return atree.searchUp(el, candidate => {
|
|
47
47
|
if (candidate.nodeType === candidate.ELEMENT_NODE) {
|
|
48
48
|
return matches(candidate, selector);
|
|
49
49
|
}
|
|
@@ -51,9 +51,7 @@ var closest = function(el, selector) {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
module.exports = {
|
|
54
|
-
getRole:
|
|
55
|
-
return attrs.getRole(el);
|
|
56
|
-
},
|
|
54
|
+
getRole: el => attrs.getRole(el),
|
|
57
55
|
getAttribute: attrs.getAttribute,
|
|
58
56
|
matches: matches,
|
|
59
57
|
querySelector: _querySelector(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aria-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Access ARIA information from JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"brfs": "^2.0.0",
|
|
15
15
|
"browserify": "^17.0.0",
|
|
16
16
|
"expect": "^1.20.2",
|
|
17
|
-
"mocha": "^
|
|
18
|
-
"mocha-headless-chrome": "^
|
|
17
|
+
"mocha": "^10.2.0",
|
|
18
|
+
"mocha-headless-chrome": "^4.0.0"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"index.js",
|