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/dist/aria.js +235 -145
- package/lib/atree.js +24 -24
- package/lib/attrs.js +44 -29
- package/lib/constants.js +98 -20
- package/lib/name.js +57 -58
- package/lib/query.js +12 -14
- package/package.json +1 -1
package/lib/name.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
const constants = require('./constants.js');
|
|
2
|
+
const atree = require('./atree.js');
|
|
3
|
+
const query = require('./query.js');
|
|
4
|
+
|
|
5
|
+
const getPseudoContent = function(node, selector) {
|
|
6
|
+
const styles = window.getComputedStyle(node, selector);
|
|
7
|
+
const ret = styles.getPropertyValue('content');
|
|
8
|
+
const inline = styles.display.substr(0, 6) === 'inline';
|
|
9
9
|
if (!ret) {
|
|
10
10
|
return '';
|
|
11
11
|
}
|
|
@@ -20,12 +20,12 @@ var getPseudoContent = function(node, selector) {
|
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const getContent = function(root, visited) {
|
|
24
|
+
const children = atree.getChildNodes(root);
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
for (
|
|
28
|
-
|
|
26
|
+
let ret = '';
|
|
27
|
+
for (let i = 0; i < children.length; i++) {
|
|
28
|
+
const node = children[i];
|
|
29
29
|
if (node.nodeType === node.TEXT_NODE) {
|
|
30
30
|
ret += node.textContent;
|
|
31
31
|
} else if (node.nodeType === node.ELEMENT_NODE) {
|
|
@@ -44,18 +44,15 @@ var getContent = function(root, visited) {
|
|
|
44
44
|
return ret;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
var isInLabelForOtherWidget = function(el) {
|
|
53
|
-
var label = el.parentElement.closest('label');
|
|
54
|
-
return label && (!el.labels || !Array.prototype.includes.call(el.labels, label));
|
|
47
|
+
const allowNameFromContent = function(el) {
|
|
48
|
+
const role = query.getRole(el);
|
|
49
|
+
if (role) {
|
|
50
|
+
return constants.roles[role].nameFromContents;
|
|
51
|
+
}
|
|
55
52
|
};
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
const getName = function(el, recursive, visited, directReference) {
|
|
55
|
+
let ret = '';
|
|
59
56
|
|
|
60
57
|
visited = visited || [];
|
|
61
58
|
if (visited.includes(el)) {
|
|
@@ -71,14 +68,30 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
71
68
|
|
|
72
69
|
// B
|
|
73
70
|
if (!recursive && el.matches('[aria-labelledby]')) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
const ids = el.getAttribute('aria-labelledby').split(/\s+/);
|
|
72
|
+
const strings = ids.map(id => {
|
|
73
|
+
const label = document.getElementById(id);
|
|
77
74
|
return label ? getName(label, true, visited, true) : '';
|
|
78
75
|
});
|
|
79
76
|
ret = strings.join(' ');
|
|
80
77
|
}
|
|
81
78
|
|
|
79
|
+
// E (the current draft has this at this high priority)
|
|
80
|
+
if (!ret.trim() && recursive) {
|
|
81
|
+
if (query.matches(el, 'textbox,button')) {
|
|
82
|
+
ret = el.value || el.textContent;
|
|
83
|
+
} else if (query.matches(el, 'combobox,listbox')) {
|
|
84
|
+
const selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
|
|
85
|
+
if (selected) {
|
|
86
|
+
ret = getName(selected, recursive, visited);
|
|
87
|
+
} else {
|
|
88
|
+
ret = el.value || '';
|
|
89
|
+
}
|
|
90
|
+
} else if (query.matches(el, 'range')) {
|
|
91
|
+
ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
82
95
|
// C
|
|
83
96
|
if (!ret.trim() && el.matches('[aria-label]')) {
|
|
84
97
|
// FIXME: may skip to 2E
|
|
@@ -87,7 +100,7 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
87
100
|
|
|
88
101
|
// D
|
|
89
102
|
if (!ret.trim() && !recursive && el.labels) {
|
|
90
|
-
|
|
103
|
+
const strings = Array.prototype.map.call(el.labels, label => {
|
|
91
104
|
return getName(label, true, visited);
|
|
92
105
|
});
|
|
93
106
|
ret = strings.join(' ');
|
|
@@ -102,9 +115,9 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
102
115
|
ret = el.title;
|
|
103
116
|
}
|
|
104
117
|
if (!ret.trim()) {
|
|
105
|
-
for (
|
|
118
|
+
for (const selector in constants.nameFromDescendant) {
|
|
106
119
|
if (el.matches(selector)) {
|
|
107
|
-
|
|
120
|
+
const descendant = el.querySelector(constants.nameFromDescendant[selector]);
|
|
108
121
|
if (descendant) {
|
|
109
122
|
ret = getName(descendant, true, visited);
|
|
110
123
|
}
|
|
@@ -112,38 +125,24 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
112
125
|
}
|
|
113
126
|
}
|
|
114
127
|
if (!ret.trim() && el.matches('svg *')) {
|
|
115
|
-
|
|
128
|
+
const svgTitle = el.querySelector('title');
|
|
116
129
|
if (svgTitle && svgTitle.parentElement === el) {
|
|
117
130
|
ret = svgTitle.textContent;
|
|
118
131
|
}
|
|
119
132
|
}
|
|
120
133
|
|
|
121
|
-
// E
|
|
122
|
-
if (!ret.trim() && (recursive || isInLabelForOtherWidget(el) || query.matches(el, 'button'))) {
|
|
123
|
-
if (query.matches(el, 'textbox,button,combobox,listbox,range')) {
|
|
124
|
-
if (query.matches(el, 'textbox,button')) {
|
|
125
|
-
ret = el.value || el.textContent;
|
|
126
|
-
} else if (query.matches(el, 'combobox,listbox')) {
|
|
127
|
-
var selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
|
|
128
|
-
if (selected) {
|
|
129
|
-
ret = getName(selected, recursive, visited);
|
|
130
|
-
} else {
|
|
131
|
-
ret = el.value || '';
|
|
132
|
-
}
|
|
133
|
-
} else if (query.matches(el, 'range')) {
|
|
134
|
-
ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
134
|
// F
|
|
140
135
|
// FIXME: menu is not mentioned in the spec
|
|
141
136
|
if (!ret.trim() && (recursive || allowNameFromContent(el) || el.closest('label')) && !query.matches(el, 'menu')) {
|
|
142
137
|
ret = getContent(el, visited);
|
|
143
138
|
}
|
|
144
139
|
|
|
140
|
+
if (!ret.trim() && query.matches(el, 'button')) {
|
|
141
|
+
ret = el.value || '';
|
|
142
|
+
}
|
|
143
|
+
|
|
145
144
|
if (!ret.trim()) {
|
|
146
|
-
for (
|
|
145
|
+
for (const selector in constants.nameDefaults) {
|
|
147
146
|
if (el.matches(selector)) {
|
|
148
147
|
ret = constants.nameDefaults[selector];
|
|
149
148
|
}
|
|
@@ -165,29 +164,29 @@ var getName = function(el, recursive, visited, directReference) {
|
|
|
165
164
|
ret = ' ';
|
|
166
165
|
}
|
|
167
166
|
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
const before = getPseudoContent(el, ':before');
|
|
168
|
+
const after = getPseudoContent(el, ':after');
|
|
170
169
|
return before + ret + after;
|
|
171
170
|
};
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
const getNameTrimmed = function(el) {
|
|
174
173
|
return getName(el).replace(/\s+/g, ' ').trim();
|
|
175
174
|
};
|
|
176
175
|
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
const getDescription = function(el) {
|
|
177
|
+
let ret = '';
|
|
179
178
|
|
|
180
179
|
if (el.matches('[aria-describedby]')) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
const ids = el.getAttribute('aria-describedby').split(/\s+/);
|
|
181
|
+
const strings = ids.map(id => {
|
|
182
|
+
const label = document.getElementById(id);
|
|
184
183
|
return label ? getName(label, true) : '';
|
|
185
184
|
});
|
|
186
185
|
ret = strings.join(' ');
|
|
187
186
|
} else if (el.matches('[aria-description]')) {
|
|
188
187
|
ret = el.getAttribute('aria-description');
|
|
189
188
|
} else if (el.matches('svg *')) {
|
|
190
|
-
|
|
189
|
+
const svgDesc = el.querySelector('desc');
|
|
191
190
|
if (svgDesc && svgDesc.parentElement === el) {
|
|
192
191
|
ret = svgDesc.textContent;
|
|
193
192
|
}
|
package/lib/query.js
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const attrs = require('./attrs.js');
|
|
2
|
+
const atree = require('./atree.js');
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
var actual;
|
|
7
|
-
|
|
5
|
+
const matches = function(el, selector) {
|
|
8
6
|
if (selector.substr(0, 1) === ':') {
|
|
9
|
-
|
|
7
|
+
const attr = selector.substr(1);
|
|
10
8
|
return attrs.getAttribute(el, attr);
|
|
11
9
|
} else if (selector.substr(0, 1) === '[') {
|
|
12
|
-
|
|
13
|
-
actual = attrs.getAttribute(el, match[1]);
|
|
14
|
-
|
|
10
|
+
const match = /\[([a-z]+)="(.*)"\]/.exec(selector);
|
|
11
|
+
const actual = attrs.getAttribute(el, match[1]);
|
|
12
|
+
const rawValue = match[2];
|
|
15
13
|
return actual.toString() === rawValue;
|
|
16
14
|
} else {
|
|
17
15
|
return attrs.hasRole(el, selector.split(','));
|
|
18
16
|
}
|
|
19
17
|
};
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
return function(root,
|
|
23
|
-
|
|
19
|
+
const _querySelector = function(all) {
|
|
20
|
+
return function(root, selector) {
|
|
21
|
+
const results = [];
|
|
24
22
|
try {
|
|
25
23
|
atree.walk(root, node => {
|
|
26
24
|
if (node.nodeType === node.ELEMENT_NODE) {
|
|
27
25
|
// FIXME: skip hidden elements
|
|
28
|
-
if (matches(node,
|
|
26
|
+
if (matches(node, selector)) {
|
|
29
27
|
results.push(node);
|
|
30
28
|
if (!all) {
|
|
31
29
|
throw 'StopIteration';
|
|
@@ -42,7 +40,7 @@ var _querySelector = function(all) {
|
|
|
42
40
|
};
|
|
43
41
|
};
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
const closest = function(el, selector) {
|
|
46
44
|
return atree.searchUp(el, candidate => {
|
|
47
45
|
if (candidate.nodeType === candidate.ELEMENT_NODE) {
|
|
48
46
|
return matches(candidate, selector);
|