aria-api 0.4.6 → 0.4.7

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/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(function(landmark) {
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);
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 owner = document.querySelector('[aria-owns~="' + CSS.escape(node.id) + '"]');
27
- if (owner) {
28
- return owner;
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
- if (node.nodeType === node.ELEMENT_NODE && node.id) {
49
- var owner = document.querySelector('[aria-owns~="' + CSS.escape(node.id) + '"]');
50
- if (owner && !detectLoop(node)) {
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
- fn(root);
90
- getChildNodes(root).forEach(function(child) {
91
- walk(child, fn);
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,30 @@ 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
- return el.getAttribute('role');
120
- }
121
- for (var role in constants.roles) {
122
- var selector = (constants.roles[role].selectors || []).join(',');
123
- if (selector && (!candidates || candidates.includes(role)) && el.matches(selector)) {
126
+ var role = el.getAttribute('role');
127
+ if (!candidates || candidates.includes(role)) {
124
128
  return role;
129
+ } else {
130
+ return;
125
131
  }
126
132
  }
127
-
128
- if (!candidates ||
129
- candidates.includes('banner') ||
130
- candidates.includes('contentinfo')) {
131
- if (!el.matches(constants.scoped)) {
132
- if (el.matches('header')) {
133
- return 'banner';
134
- }
135
- if (el.matches('footer')) {
136
- return 'contentinfo';
133
+ var roles = candidates ? candidates : Object.keys(constants.roles);
134
+ for (var role of roles) {
135
+ var r = constants.roles[role];
136
+ if (r) {
137
+ var selector = (r.selectors || []).join(',');
138
+ if (selector && el.matches(selector)) {
139
+ return role;
137
140
  }
138
141
  }
139
142
  }
140
143
  };
141
144
 
142
145
  var hasRole = function(el, roles) {
143
- var candidates = [].concat.apply([], roles.map(function(role) {
146
+ var candidates = [].concat.apply([], roles.map(role => {
144
147
  return (constants.roles[role] || {}).subRoles || [role];
145
148
  }));
146
- var actual = getRole(el, candidates);
147
- return candidates.includes(actual);
149
+ return !!getRole(el, candidates);
148
150
  };
149
151
 
150
152
  var getAttribute = function(el, key) {
@@ -299,6 +301,14 @@ exports.attributeWeakMapping = {
299
301
  'selected': 'selected',
300
302
  };
301
303
 
304
+ var scoped = [
305
+ 'main *',
306
+ // https://www.w3.org/TR/html/dom.html#sectioning-content-2
307
+ 'article *', 'aside *', 'nav *', 'section *',
308
+ // https://www.w3.org/TR/html/sections.html#sectioning-roots
309
+ 'blockquote *', 'details *', 'dialog *', 'fieldset *', 'figure *', 'td *',
310
+ ].join(',');
311
+
302
312
  // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
303
313
  // https://www.w3.org/TR/wai-aria/roles
304
314
  exports.roles = {
@@ -312,6 +322,9 @@ exports.roles = {
312
322
  article: {
313
323
  selectors: ['article'],
314
324
  },
325
+ banner: {
326
+ selectors: ['header:not(' + scoped + ')'],
327
+ },
315
328
  button: {
316
329
  selectors: [
317
330
  'button',
@@ -366,6 +379,9 @@ exports.roles = {
366
379
  composite: {
367
380
  childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
368
381
  },
382
+ contentinfo: {
383
+ selectors: ['footer:not(' + scoped + ')'],
384
+ },
369
385
  definition: {
370
386
  selectors: ['dd'],
371
387
  },
@@ -731,22 +747,14 @@ exports.roles = {
731
747
  },
732
748
  };
733
749
 
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
750
  var getSubRoles = function(role) {
743
751
  var children = (exports.roles[role] || {}).childRoles || [];
744
752
  var descendents = children.map(getSubRoles);
745
753
 
746
754
  var result = [role];
747
755
 
748
- descendents.forEach(function(list) {
749
- list.forEach(function(r) {
756
+ descendents.forEach(list => {
757
+ list.forEach(r => {
750
758
  if (!result.includes(r)) {
751
759
  result.push(r);
752
760
  }
@@ -858,7 +866,7 @@ var getName = function(el, recursive, visited, directReference) {
858
866
  // B
859
867
  if (!recursive && el.matches('[aria-labelledby]')) {
860
868
  var ids = el.getAttribute('aria-labelledby').split(/\s+/);
861
- var strings = ids.map(function(id) {
869
+ var strings = ids.map(id => {
862
870
  var label = document.getElementById(id);
863
871
  return label ? getName(label, true, visited, true) : '';
864
872
  });
@@ -873,7 +881,7 @@ var getName = function(el, recursive, visited, directReference) {
873
881
 
874
882
  // D
875
883
  if (!ret.trim() && !recursive && el.labels) {
876
- var strings = Array.prototype.map.call(el.labels, function(label) {
884
+ var strings = Array.prototype.map.call(el.labels, label => {
877
885
  return getName(label, true, visited);
878
886
  });
879
887
  ret = strings.join(' ');
@@ -959,7 +967,7 @@ var getDescription = function(el) {
959
967
 
960
968
  if (el.matches('[aria-describedby]')) {
961
969
  var ids = el.getAttribute('aria-describedby').split(/\s+/);
962
- var strings = ids.map(function(id) {
970
+ var strings = ids.map(id => {
963
971
  var label = document.getElementById(id);
964
972
  return label ? getName(label, true) : '';
965
973
  });
@@ -1009,7 +1017,7 @@ var _querySelector = function(all) {
1009
1017
  return function(root, role) {
1010
1018
  var results = [];
1011
1019
  try {
1012
- atree.walk(root, function(node) {
1020
+ atree.walk(root, node => {
1013
1021
  if (node.nodeType === node.ELEMENT_NODE) {
1014
1022
  // FIXME: skip hidden elements
1015
1023
  if (matches(node, role)) {
@@ -1030,7 +1038,7 @@ var _querySelector = function(all) {
1030
1038
  };
1031
1039
 
1032
1040
  var closest = function(el, selector) {
1033
- return atree.searchUp(el, function(candidate) {
1041
+ return atree.searchUp(el, candidate => {
1034
1042
  if (candidate.nodeType === candidate.ELEMENT_NODE) {
1035
1043
  return matches(candidate, selector);
1036
1044
  }
@@ -1038,9 +1046,7 @@ var closest = function(el, selector) {
1038
1046
  };
1039
1047
 
1040
1048
  module.exports = {
1041
- getRole: function(el) {
1042
- return attrs.getRole(el);
1043
- },
1049
+ getRole: el => attrs.getRole(el),
1044
1050
  getAttribute: attrs.getAttribute,
1045
1051
  matches: matches,
1046
1052
  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 owner = document.querySelector('[aria-owns~="' + CSS.escape(node.id) + '"]');
6
- if (owner) {
7
- return owner;
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
- if (node.nodeType === node.ELEMENT_NODE && node.id) {
28
- var owner = document.querySelector('[aria-owns~="' + CSS.escape(node.id) + '"]');
29
- if (owner && !detectLoop(node)) {
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
- fn(root);
69
- getChildNodes(root).forEach(function(child) {
70
- walk(child, fn);
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,30 @@ 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
- return el.getAttribute('role');
7
- }
8
- for (var role in constants.roles) {
9
- var selector = (constants.roles[role].selectors || []).join(',');
10
- if (selector && (!candidates || candidates.includes(role)) && el.matches(selector)) {
6
+ var role = el.getAttribute('role');
7
+ if (!candidates || candidates.includes(role)) {
11
8
  return role;
9
+ } else {
10
+ return;
12
11
  }
13
12
  }
14
-
15
- if (!candidates ||
16
- candidates.includes('banner') ||
17
- candidates.includes('contentinfo')) {
18
- if (!el.matches(constants.scoped)) {
19
- if (el.matches('header')) {
20
- return 'banner';
21
- }
22
- if (el.matches('footer')) {
23
- return 'contentinfo';
13
+ var roles = candidates ? candidates : Object.keys(constants.roles);
14
+ for (var role of roles) {
15
+ var r = constants.roles[role];
16
+ if (r) {
17
+ var selector = (r.selectors || []).join(',');
18
+ if (selector && el.matches(selector)) {
19
+ return role;
24
20
  }
25
21
  }
26
22
  }
27
23
  };
28
24
 
29
25
  var hasRole = function(el, roles) {
30
- var candidates = [].concat.apply([], roles.map(function(role) {
26
+ var candidates = [].concat.apply([], roles.map(role => {
31
27
  return (constants.roles[role] || {}).subRoles || [role];
32
28
  }));
33
- var actual = getRole(el, candidates);
34
- return candidates.includes(actual);
29
+ return !!getRole(el, candidates);
35
30
  };
36
31
 
37
32
  var getAttribute = function(el, key) {
package/lib/constants.js CHANGED
@@ -66,6 +66,14 @@ exports.attributeWeakMapping = {
66
66
  'selected': 'selected',
67
67
  };
68
68
 
69
+ var scoped = [
70
+ 'main *',
71
+ // https://www.w3.org/TR/html/dom.html#sectioning-content-2
72
+ 'article *', 'aside *', 'nav *', 'section *',
73
+ // https://www.w3.org/TR/html/sections.html#sectioning-roots
74
+ 'blockquote *', 'details *', 'dialog *', 'fieldset *', 'figure *', 'td *',
75
+ ].join(',');
76
+
69
77
  // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
70
78
  // https://www.w3.org/TR/wai-aria/roles
71
79
  exports.roles = {
@@ -79,6 +87,9 @@ exports.roles = {
79
87
  article: {
80
88
  selectors: ['article'],
81
89
  },
90
+ banner: {
91
+ selectors: ['header:not(' + scoped + ')'],
92
+ },
82
93
  button: {
83
94
  selectors: [
84
95
  'button',
@@ -133,6 +144,9 @@ exports.roles = {
133
144
  composite: {
134
145
  childRoles: ['grid', 'select', 'spinbutton', 'tablist'],
135
146
  },
147
+ contentinfo: {
148
+ selectors: ['footer:not(' + scoped + ')'],
149
+ },
136
150
  definition: {
137
151
  selectors: ['dd'],
138
152
  },
@@ -498,22 +512,14 @@ exports.roles = {
498
512
  },
499
513
  };
500
514
 
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
515
  var getSubRoles = function(role) {
510
516
  var children = (exports.roles[role] || {}).childRoles || [];
511
517
  var descendents = children.map(getSubRoles);
512
518
 
513
519
  var result = [role];
514
520
 
515
- descendents.forEach(function(list) {
516
- list.forEach(function(r) {
521
+ descendents.forEach(list => {
522
+ list.forEach(r => {
517
523
  if (!result.includes(r)) {
518
524
  result.push(r);
519
525
  }
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(function(id) {
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, function(label) {
90
+ var strings = Array.prototype.map.call(el.labels, label => {
91
91
  return getName(label, true, visited);
92
92
  });
93
93
  ret = strings.join(' ');
@@ -173,7 +173,7 @@ var getDescription = function(el) {
173
173
 
174
174
  if (el.matches('[aria-describedby]')) {
175
175
  var ids = el.getAttribute('aria-describedby').split(/\s+/);
176
- var strings = ids.map(function(id) {
176
+ var strings = ids.map(id => {
177
177
  var label = document.getElementById(id);
178
178
  return label ? getName(label, true) : '';
179
179
  });
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, function(node) {
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, function(candidate) {
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: function(el) {
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.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "Access ARIA information from JavaScript",
5
5
  "main": "index.js",
6
6
  "keywords": [