aria-api 0.9.0 → 0.9.2

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
@@ -18,7 +18,7 @@ rollup. There is also an UMD build included as `dist/aria.js`.
18
18
 
19
19
  # Usage
20
20
 
21
- var aria = require('aria-api'):
21
+ var aria = require('aria-api');
22
22
 
23
23
  aria.querySelector('landmark').forEach(landmark => {
24
24
  if (!aria.matches(landmark, ':hidden')) {
package/dist/aria.js CHANGED
@@ -21,7 +21,7 @@
21
21
  'current': 'token',
22
22
  'describedby': 'id-list',
23
23
  'description': 'string',
24
- 'details': 'id',
24
+ 'details': 'id-list',
25
25
  'disabled': 'bool',
26
26
  'dropeffect': 'token-list',
27
27
  'errormessage': 'id',
@@ -75,12 +75,13 @@
75
75
  'multiselectable': 'multiple',
76
76
  'rowspan': 'rowSpan',
77
77
  'selected': 'selected',
78
+ 'valuenow': 'value',
78
79
  };
79
80
 
80
81
  // https://www.w3.org/TR/html/dom.html#sectioning-content-2
81
82
  const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
82
83
 
83
- const svgSelectors = function(selector) {
84
+ function svgSelectors(selector) {
84
85
  return [
85
86
  // `${selector}:has(> title:not(:empty))`,
86
87
  // `${selector}:has(> desc:not(:empty))`,
@@ -91,7 +92,7 @@
91
92
  `${selector}[tabindex]`,
92
93
  `${selector}[role]`,
93
94
  ];
94
- };
95
+ }
95
96
 
96
97
  // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
97
98
  // https://www.w3.org/TR/wai-aria/roles
@@ -122,7 +123,7 @@
122
123
  'input[type="image"]',
123
124
  'input[type="reset"]',
124
125
  'input[type="submit"]',
125
- 'summary',
126
+ 'details > summary:first-of-type',
126
127
  ],
127
128
  nameFromContents: true,
128
129
  },
@@ -174,12 +175,7 @@
174
175
  nameFromContents: true,
175
176
  },
176
177
  complementary: {
177
- selectors: [
178
- `aside:not(${scoped})`,
179
- 'aside[aria-label]',
180
- 'aside[aria-labelledby]',
181
- 'aside[title]',
182
- ],
178
+ selectors: ['aside'],
183
179
  },
184
180
  composite: {
185
181
  abstract: true,
@@ -252,7 +248,6 @@
252
248
  'doc-tip': {},
253
249
  'doc-toc': {},
254
250
  document: {
255
- selectors: ['html'],
256
251
  childRoles: ['article', 'graphics-document'],
257
252
  },
258
253
  emphasis: {
@@ -264,13 +259,12 @@
264
259
  childRoles: ['doc-example'],
265
260
  },
266
261
  form: {
267
- selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
262
+ selectors: ['form'],
268
263
  },
269
264
  generic: {
270
265
  selectors: [
271
266
  'a:not([*|href])',
272
267
  'area:not([*|href])',
273
- `aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
274
268
  'b',
275
269
  'bdi',
276
270
  'bdo',
@@ -279,12 +273,12 @@
279
273
  'div',
280
274
  // footer scoped
281
275
  // header scoped
276
+ 'html',
282
277
  'i',
283
278
  'li:not(ul > li):not(ol > li)',
284
279
  'pre',
285
280
  'q',
286
281
  'samp',
287
- 'section:not([aria-label]):not([aria-labelledby]):not([title])',
288
282
  'small',
289
283
  'span',
290
284
  'u',
@@ -506,7 +500,7 @@
506
500
  childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
507
501
  },
508
502
  region: {
509
- selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
503
+ selectors: ['section'],
510
504
  },
511
505
  roletype: {
512
506
  abstract: true,
@@ -737,7 +731,7 @@
737
731
  },
738
732
  };
739
733
 
740
- const getSubRoles = function(role) {
734
+ function getSubRoles(role) {
741
735
  const children = (roles[role]).childRoles || [];
742
736
  const descendents = children.map(getSubRoles);
743
737
 
@@ -752,7 +746,7 @@
752
746
  });
753
747
 
754
748
  return result;
755
- };
749
+ }
756
750
 
757
751
  const attrsWithDefaults = [];
758
752
 
@@ -783,59 +777,86 @@
783
777
  'summary': 'Details',
784
778
  };
785
779
 
786
- var unique = function(arr) {
780
+ function unique(arr) {
787
781
  return arr.filter((a, i) => arr.indexOf(a) === i);
788
- };
782
+ }
789
783
 
790
- var flatten = function(arr) {
784
+ function flatten(arr) {
791
785
  return [].concat.apply([], arr);
792
- };
786
+ }
793
787
 
794
- var normalizeRoles = function(roles$1, includeAbstract) {
788
+ function normalizeRoles(roles$1, includeAbstract) {
795
789
  return unique(roles$1
796
790
  .map(r => aliases[r] || r)
797
791
  .filter(r => roles[r])
798
792
  .filter(r => includeAbstract || !roles[r].abstract)
799
793
  );
800
- };
794
+ }
795
+
796
+ function hasName(el) {
797
+ // FIXME: we should do full getName() here. However, that would be
798
+ // complex and slow. In practice, this function is only called for
799
+ // roles that do not allow nameFromContent. Still, we do not verify
800
+ // that labelledby results in a non-empty name.
801
+ return (
802
+ (el.getAttribute('aria-label') || '').trim().length > 0
803
+ || (el.getAttribute('title') || '').trim().length > 0
804
+ || (el.getAttribute('aria-labelledby') || '').split(/\s+/).some(id => {
805
+ return el.getRootNode().getElementById(id);
806
+ })
807
+ );
808
+ }
809
+
810
+ function checkExtraConditions(el, role) {
811
+ if (role === 'region' || role === 'form') {
812
+ return hasName(el);
813
+ } else if (role === 'complementary') {
814
+ return el.matches('[role~="complementary"]') || el.matches(`:not(${scoped})`) || hasName(el);
815
+ } else {
816
+ return true;
817
+ }
818
+ }
801
819
 
802
820
  // candidates can be passed for performance optimization
803
- const getRoleRaw = function(el, candidates) {
804
- // TODO: filter out any invalid roles (e.g. name or context required)
821
+ function getRoleRaw(el, candidates) {
822
+ // TODO: filter out any invalid roles (e.g. context required)
805
823
  const roles$1 = normalizeRoles(
806
824
  (el.getAttribute('role') || '').toLowerCase().split(/\s+/)
807
825
  );
808
826
 
809
- if (roles$1.length > 1 && candidates) {
810
- return [roles$1, candidates];
811
- } else if (roles$1.length) {
812
- for (const role of roles$1) {
813
- if (!candidates || candidates.includes(role)) {
814
- return role;
827
+ for (const role of roles$1) {
828
+ if (checkExtraConditions(el, role)) {
829
+ if (candidates && !candidates.includes(role)) {
830
+ return null;
815
831
  }
832
+ return role;
816
833
  }
817
- } else {
818
- for (const role of (candidates || Object.keys(roles))) {
819
- const r = roles[role];
820
- if (!r.abstract && r.selectors && el.matches(r.selectors.join(','))) {
821
- return role;
822
- }
834
+ }
835
+ for (const role of (candidates || Object.keys(roles))) {
836
+ const r = roles[role];
837
+ if (
838
+ !r.abstract
839
+ && r.selectors
840
+ && el.matches(r.selectors.join(','))
841
+ && checkExtraConditions(el, role)
842
+ ) {
843
+ return role;
823
844
  }
824
845
  }
825
- };
846
+ }
826
847
 
827
- const getRole = function(el) {
848
+ function getRole(el) {
828
849
  return getRoleRaw(el);
829
- };
850
+ }
830
851
 
831
- const hasRole = function(el, roles$1) {
852
+ function hasRole(el, roles$1) {
832
853
  const subRoles = normalizeRoles(roles$1, true).map(role => {
833
854
  return roles[role].subRoles || [role];
834
855
  });
835
856
  return !!getRoleRaw(el, unique(flatten(subRoles)));
836
- };
857
+ }
837
858
 
838
- const getAttribute = function(el, key) {
859
+ function getAttribute(el, key) {
839
860
  if (attributeStrongMapping.hasOwnProperty(key)) {
840
861
  const value = el[attributeStrongMapping[key]];
841
862
  if (value) {
@@ -899,7 +920,7 @@
899
920
  return el[attributeWeakMapping[key]];
900
921
  }
901
922
 
902
- if (key in attrsWithDefaults) {
923
+ if (attrsWithDefaults.includes(key)) {
903
924
  const role = getRole(el);
904
925
  const defaults = roles[role].defaults;
905
926
  if (defaults && defaults.hasOwnProperty(key)) {
@@ -910,20 +931,20 @@
910
931
  if (type === 'bool' || type === 'tristate') {
911
932
  return false;
912
933
  }
913
- };
934
+ }
914
935
 
915
- const _getOwner = function(node) {
936
+ function _getOwner(node) {
916
937
  if (node.nodeType === node.ELEMENT_NODE && node.id) {
917
938
  const selector = `[aria-owns~="${CSS.escape(node.id)}"]`;
918
939
  return node.getRootNode().querySelector(selector);
919
940
  }
920
- };
941
+ }
921
942
 
922
- const _getParentNode = function(node) {
943
+ function _getParentNode(node) {
923
944
  return _getOwner(node) || node.parentNode || node.host;
924
- };
945
+ }
925
946
 
926
- const detectLoop = function(node) {
947
+ function detectLoop(node) {
927
948
  const seen = [node];
928
949
  while ((node = _getParentNode(node))) {
929
950
  if (seen.includes(node)) {
@@ -931,24 +952,24 @@
931
952
  }
932
953
  seen.push(node);
933
954
  }
934
- };
955
+ }
935
956
 
936
- const getOwner = function(node) {
957
+ function getOwner(node) {
937
958
  const owner = _getOwner(node);
938
959
  if (owner && !detectLoop(node)) {
939
960
  return owner;
940
961
  }
941
- };
962
+ }
942
963
 
943
- const getParentNode = function(node) {
964
+ function getParentNode(node) {
944
965
  return getOwner(node) || node.parentNode || node.host;
945
- };
966
+ }
946
967
 
947
- const isHidden = function(node) {
968
+ function isHidden(node) {
948
969
  return node.nodeType === node.ELEMENT_NODE && getAttribute(node, 'hidden');
949
- };
970
+ }
950
971
 
951
- const getChildNodes = function(node) {
972
+ function getChildNodes(node) {
952
973
  const childNodes = [];
953
974
  let rawChildNodes = [];
954
975
 
@@ -979,18 +1000,18 @@
979
1000
  }
980
1001
 
981
1002
  return childNodes;
982
- };
1003
+ }
983
1004
 
984
- const walk = function(root, fn) {
1005
+ function walk(root, fn) {
985
1006
  let queue = [root];
986
1007
  while (queue.length) {
987
1008
  const item = queue.shift();
988
1009
  fn(item);
989
1010
  queue = getChildNodes(item).concat(queue);
990
1011
  }
991
- };
1012
+ }
992
1013
 
993
- const searchUp = function(node, test) {
1014
+ function searchUp(node, test) {
994
1015
  const candidate = getParentNode(node);
995
1016
  if (candidate) {
996
1017
  if (test(candidate)) {
@@ -999,9 +1020,9 @@
999
1020
  return searchUp(candidate, test);
1000
1021
  }
1001
1022
  }
1002
- };
1023
+ }
1003
1024
 
1004
- const matches = function(el, selector) {
1025
+ function matches(el, selector) {
1005
1026
  if (selector.substr(0, 1) === ':') {
1006
1027
  const attr = selector.substr(1);
1007
1028
  return getAttribute(el, attr);
@@ -1013,9 +1034,9 @@
1013
1034
  } else {
1014
1035
  return hasRole(el, selector.split(','));
1015
1036
  }
1016
- };
1037
+ }
1017
1038
 
1018
- const _querySelector = function(all) {
1039
+ function _querySelector(all) {
1019
1040
  return function(root, selector) {
1020
1041
  const results = [];
1021
1042
  try {
@@ -1037,27 +1058,27 @@
1037
1058
  }
1038
1059
  return all ? results : results[0];
1039
1060
  };
1040
- };
1061
+ }
1041
1062
 
1042
- const closest = function(el, selector) {
1063
+ function closest(el, selector) {
1043
1064
  return searchUp(el, candidate => {
1044
1065
  if (candidate.nodeType === candidate.ELEMENT_NODE) {
1045
1066
  return matches(candidate, selector);
1046
1067
  }
1047
1068
  });
1048
- };
1069
+ }
1049
1070
 
1050
1071
  const querySelector = _querySelector();
1051
1072
  const querySelectorAll = _querySelector(true);
1052
1073
 
1053
- const addSpaces = function(text, el, pseudoSelector) {
1074
+ function addSpaces(text, el, pseudoSelector) {
1054
1075
  // https://github.com/w3c/accname/issues/3
1055
1076
  const styles = window.getComputedStyle(el, pseudoSelector);
1056
1077
  const inline = styles.display === 'inline';
1057
1078
  return inline ? text : ` ${text} `;
1058
- };
1079
+ }
1059
1080
 
1060
- const getPseudoContent = function(el, pseudoSelector) {
1081
+ function getPseudoContent(el, pseudoSelector) {
1061
1082
  const styles = window.getComputedStyle(el, pseudoSelector);
1062
1083
  let tail = styles.getPropertyValue('content').trim();
1063
1084
  let ret = [];
@@ -1084,22 +1105,26 @@
1084
1105
  }
1085
1106
 
1086
1107
  return addSpaces(ret.join(''), el, pseudoSelector);
1087
- };
1108
+ }
1088
1109
 
1089
- const getContent = function(root, ongoingLabelledBy, visited) {
1110
+ function getContent(root, ongoingLabelledBy, visited) {
1090
1111
  const children = getChildNodes(root);
1091
1112
 
1092
1113
  let ret = '';
1093
1114
  for (let i = 0; i < children.length; i++) {
1094
1115
  const node = children[i];
1095
1116
  if (node.nodeType === node.TEXT_NODE) {
1096
- const styles = window.getComputedStyle(node.parentElement);
1097
- if (styles.textTransform === 'uppercase') {
1098
- ret += node.textContent.toUpperCase();
1099
- } else if (styles.textTransform === 'lowercase') {
1100
- ret += node.textContent.toLowerCase();
1101
- } else if (styles.textTransform === 'capitalize') {
1102
- ret += node.textContent.replace(/\b\w/g, c => c.toUpperCase());
1117
+ if (node.parentElement) {
1118
+ const styles = window.getComputedStyle(node.parentElement);
1119
+ if (styles.textTransform === 'uppercase') {
1120
+ ret += node.textContent.toUpperCase();
1121
+ } else if (styles.textTransform === 'lowercase') {
1122
+ ret += node.textContent.toLowerCase();
1123
+ } else if (styles.textTransform === 'capitalize') {
1124
+ ret += node.textContent.replace(/\b\w/g, c => c.toUpperCase());
1125
+ } else {
1126
+ ret += node.textContent;
1127
+ }
1103
1128
  } else {
1104
1129
  ret += node.textContent;
1105
1130
  }
@@ -1113,16 +1138,16 @@
1113
1138
  }
1114
1139
 
1115
1140
  return ret;
1116
- };
1141
+ }
1117
1142
 
1118
- const allowNameFromContent = function(el) {
1143
+ function allowNameFromContent(el) {
1119
1144
  const role = getRole(el);
1120
1145
  if (role) {
1121
1146
  return roles[role].nameFromContents;
1122
1147
  }
1123
- };
1148
+ }
1124
1149
 
1125
- const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directReference) {
1150
+ function getNameRaw(el, recursive, ongoingLabelledBy, visited, directReference) {
1126
1151
  let ret = '';
1127
1152
 
1128
1153
  visited = visited || [];
@@ -1170,7 +1195,7 @@
1170
1195
  }
1171
1196
 
1172
1197
  // D
1173
- if (!ret.trim() && !recursive && el.labels) {
1198
+ if (!ret.trim() && el.labels) {
1174
1199
  const strings = Array.prototype.map.call(el.labels, label => {
1175
1200
  return getNameRaw(label, true, ongoingLabelledBy, visited);
1176
1201
  });
@@ -1237,16 +1262,16 @@
1237
1262
  const before = getPseudoContent(el, ':before');
1238
1263
  const after = getPseudoContent(el, ':after');
1239
1264
  return addSpaces(before + ret + after, el);
1240
- };
1265
+ }
1241
1266
 
1242
- const getName = function(el) {
1267
+ function getName(el) {
1243
1268
  return getNameRaw(el)
1244
1269
  .replace(/[ \n\r\t\f]+/g, ' ')
1245
1270
  .replace(/^ /, '')
1246
1271
  .replace(/ $/, '');
1247
- };
1272
+ }
1248
1273
 
1249
- const getDescription = function(el) {
1274
+ function getDescription(el) {
1250
1275
  let ret = '';
1251
1276
 
1252
1277
  if (el.matches('[aria-describedby]')) {
@@ -1277,7 +1302,7 @@
1277
1302
  }
1278
1303
 
1279
1304
  return ret;
1280
- };
1305
+ }
1281
1306
 
1282
1307
  exports.closest = closest;
1283
1308
  exports.getAttribute = getAttribute;
package/lib/atree.js CHANGED
@@ -1,17 +1,17 @@
1
- import * as attrs from './attrs';
1
+ import * as attrs from './attrs.js';
2
2
 
3
- const _getOwner = function(node) {
3
+ function _getOwner(node) {
4
4
  if (node.nodeType === node.ELEMENT_NODE && node.id) {
5
5
  const selector = `[aria-owns~="${CSS.escape(node.id)}"]`;
6
6
  return node.getRootNode().querySelector(selector);
7
7
  }
8
- };
8
+ }
9
9
 
10
- const _getParentNode = function(node) {
10
+ function _getParentNode(node) {
11
11
  return _getOwner(node) || node.parentNode || node.host;
12
- };
12
+ }
13
13
 
14
- const detectLoop = function(node) {
14
+ function detectLoop(node) {
15
15
  const seen = [node];
16
16
  while ((node = _getParentNode(node))) {
17
17
  if (seen.includes(node)) {
@@ -19,24 +19,24 @@ const detectLoop = function(node) {
19
19
  }
20
20
  seen.push(node);
21
21
  }
22
- };
22
+ }
23
23
 
24
- const getOwner = function(node) {
24
+ function getOwner(node) {
25
25
  const owner = _getOwner(node);
26
26
  if (owner && !detectLoop(node)) {
27
27
  return owner;
28
28
  }
29
- };
29
+ }
30
30
 
31
- export const getParentNode = function(node) {
31
+ export function getParentNode(node) {
32
32
  return getOwner(node) || node.parentNode || node.host;
33
- };
33
+ }
34
34
 
35
- const isHidden = function(node) {
35
+ function isHidden(node) {
36
36
  return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
37
- };
37
+ }
38
38
 
39
- export const getChildNodes = function(node) {
39
+ export function getChildNodes(node) {
40
40
  const childNodes = [];
41
41
  let rawChildNodes = [];
42
42
 
@@ -67,18 +67,18 @@ export const getChildNodes = function(node) {
67
67
  }
68
68
 
69
69
  return childNodes;
70
- };
70
+ }
71
71
 
72
- export const walk = function(root, fn) {
72
+ export function walk(root, fn) {
73
73
  let queue = [root];
74
74
  while (queue.length) {
75
75
  const item = queue.shift();
76
76
  fn(item);
77
77
  queue = getChildNodes(item).concat(queue);
78
78
  }
79
- };
79
+ }
80
80
 
81
- export const searchUp = function(node, test) {
81
+ export function searchUp(node, test) {
82
82
  const candidate = getParentNode(node);
83
83
  if (candidate) {
84
84
  if (test(candidate)) {
@@ -87,4 +87,4 @@ export const searchUp = function(node, test) {
87
87
  return searchUp(candidate, test);
88
88
  }
89
89
  }
90
- };
90
+ }
package/lib/attrs.js CHANGED
@@ -1,58 +1,85 @@
1
1
  import * as constants from './constants.js';
2
2
 
3
- var unique = function(arr) {
3
+ function unique(arr) {
4
4
  return arr.filter((a, i) => arr.indexOf(a) === i);
5
- };
5
+ }
6
6
 
7
- var flatten = function(arr) {
7
+ function flatten(arr) {
8
8
  return [].concat.apply([], arr);
9
- };
9
+ }
10
10
 
11
- var normalizeRoles = function(roles, includeAbstract) {
11
+ function normalizeRoles(roles, includeAbstract) {
12
12
  return unique(roles
13
13
  .map(r => constants.aliases[r] || r)
14
14
  .filter(r => constants.roles[r])
15
15
  .filter(r => includeAbstract || !constants.roles[r].abstract)
16
16
  );
17
- };
17
+ }
18
+
19
+ function hasName(el) {
20
+ // FIXME: we should do full getName() here. However, that would be
21
+ // complex and slow. In practice, this function is only called for
22
+ // roles that do not allow nameFromContent. Still, we do not verify
23
+ // that labelledby results in a non-empty name.
24
+ return (
25
+ (el.getAttribute('aria-label') || '').trim().length > 0
26
+ || (el.getAttribute('title') || '').trim().length > 0
27
+ || (el.getAttribute('aria-labelledby') || '').split(/\s+/).some(id => {
28
+ return el.getRootNode().getElementById(id);
29
+ })
30
+ );
31
+ }
32
+
33
+ function checkExtraConditions(el, role) {
34
+ if (role === 'region' || role === 'form') {
35
+ return hasName(el);
36
+ } else if (role === 'complementary') {
37
+ return el.matches('[role~="complementary"]') || el.matches(`:not(${constants.scoped})`) || hasName(el);
38
+ } else {
39
+ return true;
40
+ }
41
+ }
18
42
 
19
43
  // candidates can be passed for performance optimization
20
- const getRoleRaw = function(el, candidates) {
21
- // TODO: filter out any invalid roles (e.g. name or context required)
44
+ function getRoleRaw(el, candidates) {
45
+ // TODO: filter out any invalid roles (e.g. context required)
22
46
  const roles = normalizeRoles(
23
47
  (el.getAttribute('role') || '').toLowerCase().split(/\s+/)
24
48
  );
25
49
 
26
- if (roles.length > 1 && candidates) {
27
- return [roles, candidates];
28
- } else if (roles.length) {
29
- for (const role of roles) {
30
- if (!candidates || candidates.includes(role)) {
31
- return role;
50
+ for (const role of roles) {
51
+ if (checkExtraConditions(el, role)) {
52
+ if (candidates && !candidates.includes(role)) {
53
+ return null;
32
54
  }
55
+ return role;
33
56
  }
34
- } else {
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;
39
- }
57
+ }
58
+ for (const role of (candidates || Object.keys(constants.roles))) {
59
+ const r = constants.roles[role];
60
+ if (
61
+ !r.abstract
62
+ && r.selectors
63
+ && el.matches(r.selectors.join(','))
64
+ && checkExtraConditions(el, role)
65
+ ) {
66
+ return role;
40
67
  }
41
68
  }
42
- };
69
+ }
43
70
 
44
- export const getRole = function(el) {
71
+ export function getRole(el) {
45
72
  return getRoleRaw(el);
46
- };
73
+ }
47
74
 
48
- export const hasRole = function(el, roles) {
75
+ export function hasRole(el, roles) {
49
76
  const subRoles = normalizeRoles(roles, true).map(role => {
50
77
  return constants.roles[role].subRoles || [role];
51
78
  });
52
79
  return !!getRoleRaw(el, unique(flatten(subRoles)));
53
- };
80
+ }
54
81
 
55
- export const getAttribute = function(el, key) {
82
+ export function getAttribute(el, key) {
56
83
  if (constants.attributeStrongMapping.hasOwnProperty(key)) {
57
84
  const value = el[constants.attributeStrongMapping[key]];
58
85
  if (value) {
@@ -116,7 +143,7 @@ export const getAttribute = function(el, key) {
116
143
  return el[constants.attributeWeakMapping[key]];
117
144
  }
118
145
 
119
- if (key in constants.attrsWithDefaults) {
146
+ if (constants.attrsWithDefaults.includes(key)) {
120
147
  const role = getRole(el);
121
148
  const defaults = constants.roles[role].defaults;
122
149
  if (defaults && defaults.hasOwnProperty(key)) {
@@ -127,4 +154,4 @@ export const getAttribute = function(el, key) {
127
154
  if (type === 'bool' || type === 'tristate') {
128
155
  return false;
129
156
  }
130
- };
157
+ }
package/lib/constants.js CHANGED
@@ -15,7 +15,7 @@ export const attributes = {
15
15
  'current': 'token',
16
16
  'describedby': 'id-list',
17
17
  'description': 'string',
18
- 'details': 'id',
18
+ 'details': 'id-list',
19
19
  'disabled': 'bool',
20
20
  'dropeffect': 'token-list',
21
21
  'errormessage': 'id',
@@ -69,12 +69,13 @@ export const attributeWeakMapping = {
69
69
  'multiselectable': 'multiple',
70
70
  'rowspan': 'rowSpan',
71
71
  'selected': 'selected',
72
+ 'valuenow': 'value',
72
73
  };
73
74
 
74
75
  // https://www.w3.org/TR/html/dom.html#sectioning-content-2
75
- const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
76
+ export const scoped = ['article *', 'aside *', 'nav *', 'section *'].join(',');
76
77
 
77
- const svgSelectors = function(selector) {
78
+ function svgSelectors(selector) {
78
79
  return [
79
80
  // `${selector}:has(> title:not(:empty))`,
80
81
  // `${selector}:has(> desc:not(:empty))`,
@@ -85,7 +86,7 @@ const svgSelectors = function(selector) {
85
86
  `${selector}[tabindex]`,
86
87
  `${selector}[role]`,
87
88
  ];
88
- };
89
+ }
89
90
 
90
91
  // https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings
91
92
  // https://www.w3.org/TR/wai-aria/roles
@@ -116,7 +117,7 @@ export const roles = {
116
117
  'input[type="image"]',
117
118
  'input[type="reset"]',
118
119
  'input[type="submit"]',
119
- 'summary',
120
+ 'details > summary:first-of-type',
120
121
  ],
121
122
  nameFromContents: true,
122
123
  },
@@ -168,12 +169,7 @@ export const roles = {
168
169
  nameFromContents: true,
169
170
  },
170
171
  complementary: {
171
- selectors: [
172
- `aside:not(${scoped})`,
173
- 'aside[aria-label]',
174
- 'aside[aria-labelledby]',
175
- 'aside[title]',
176
- ],
172
+ selectors: ['aside'],
177
173
  },
178
174
  composite: {
179
175
  abstract: true,
@@ -246,7 +242,6 @@ export const roles = {
246
242
  'doc-tip': {},
247
243
  'doc-toc': {},
248
244
  document: {
249
- selectors: ['html'],
250
245
  childRoles: ['article', 'graphics-document'],
251
246
  },
252
247
  emphasis: {
@@ -258,13 +253,12 @@ export const roles = {
258
253
  childRoles: ['doc-example'],
259
254
  },
260
255
  form: {
261
- selectors: ['form[aria-label]', 'form[aria-labelledby]', 'form[title]'],
256
+ selectors: ['form'],
262
257
  },
263
258
  generic: {
264
259
  selectors: [
265
260
  'a:not([*|href])',
266
261
  'area:not([*|href])',
267
- `aside:not(${scoped}):not([aria-label]):not([aria-labelledby]):not([title])`,
268
262
  'b',
269
263
  'bdi',
270
264
  'bdo',
@@ -273,12 +267,12 @@ export const roles = {
273
267
  'div',
274
268
  // footer scoped
275
269
  // header scoped
270
+ 'html',
276
271
  'i',
277
272
  'li:not(ul > li):not(ol > li)',
278
273
  'pre',
279
274
  'q',
280
275
  'samp',
281
- 'section:not([aria-label]):not([aria-labelledby]):not([title])',
282
276
  'small',
283
277
  'span',
284
278
  'u',
@@ -500,7 +494,7 @@ export const roles = {
500
494
  childRoles: ['meter', 'progressbar', 'scrollbar', 'slider', 'spinbutton'],
501
495
  },
502
496
  region: {
503
- selectors: ['section[aria-label]', 'section[aria-labelledby]', 'section[title]'],
497
+ selectors: ['section'],
504
498
  },
505
499
  roletype: {
506
500
  abstract: true,
@@ -731,7 +725,7 @@ export const roles = {
731
725
  },
732
726
  };
733
727
 
734
- const getSubRoles = function(role) {
728
+ function getSubRoles(role) {
735
729
  const children = (roles[role]).childRoles || [];
736
730
  const descendents = children.map(getSubRoles);
737
731
 
@@ -746,7 +740,7 @@ const getSubRoles = function(role) {
746
740
  });
747
741
 
748
742
  return result;
749
- };
743
+ }
750
744
 
751
745
  export const attrsWithDefaults = [];
752
746
 
package/lib/name.js CHANGED
@@ -3,14 +3,14 @@ import * as atree from './atree.js';
3
3
  import * as attrs from './attrs.js';
4
4
  import * as query from './query.js';
5
5
 
6
- const addSpaces = function(text, el, pseudoSelector) {
6
+ function addSpaces(text, el, pseudoSelector) {
7
7
  // https://github.com/w3c/accname/issues/3
8
8
  const styles = window.getComputedStyle(el, pseudoSelector);
9
9
  const inline = styles.display === 'inline';
10
10
  return inline ? text : ` ${text} `;
11
- };
11
+ }
12
12
 
13
- const getPseudoContent = function(el, pseudoSelector) {
13
+ function getPseudoContent(el, pseudoSelector) {
14
14
  const styles = window.getComputedStyle(el, pseudoSelector);
15
15
  let tail = styles.getPropertyValue('content').trim();
16
16
  let ret = [];
@@ -37,22 +37,26 @@ const getPseudoContent = function(el, pseudoSelector) {
37
37
  }
38
38
 
39
39
  return addSpaces(ret.join(''), el, pseudoSelector);
40
- };
40
+ }
41
41
 
42
- const getContent = function(root, ongoingLabelledBy, visited) {
42
+ function getContent(root, ongoingLabelledBy, visited) {
43
43
  const children = atree.getChildNodes(root);
44
44
 
45
45
  let ret = '';
46
46
  for (let i = 0; i < children.length; i++) {
47
47
  const node = children[i];
48
48
  if (node.nodeType === node.TEXT_NODE) {
49
- const styles = window.getComputedStyle(node.parentElement);
50
- if (styles.textTransform === 'uppercase') {
51
- ret += node.textContent.toUpperCase();
52
- } else if (styles.textTransform === 'lowercase') {
53
- ret += node.textContent.toLowerCase();
54
- } else if (styles.textTransform === 'capitalize') {
55
- ret += node.textContent.replace(/\b\w/g, c => c.toUpperCase());
49
+ if (node.parentElement) {
50
+ const styles = window.getComputedStyle(node.parentElement);
51
+ if (styles.textTransform === 'uppercase') {
52
+ ret += node.textContent.toUpperCase();
53
+ } else if (styles.textTransform === 'lowercase') {
54
+ ret += node.textContent.toLowerCase();
55
+ } else if (styles.textTransform === 'capitalize') {
56
+ ret += node.textContent.replace(/\b\w/g, c => c.toUpperCase());
57
+ } else {
58
+ ret += node.textContent;
59
+ }
56
60
  } else {
57
61
  ret += node.textContent;
58
62
  }
@@ -66,16 +70,16 @@ const getContent = function(root, ongoingLabelledBy, visited) {
66
70
  }
67
71
 
68
72
  return ret;
69
- };
73
+ }
70
74
 
71
- const allowNameFromContent = function(el) {
75
+ function allowNameFromContent(el) {
72
76
  const role = attrs.getRole(el);
73
77
  if (role) {
74
78
  return constants.roles[role].nameFromContents;
75
79
  }
76
- };
80
+ }
77
81
 
78
- const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directReference) {
82
+ function getNameRaw(el, recursive, ongoingLabelledBy, visited, directReference) {
79
83
  let ret = '';
80
84
 
81
85
  visited = visited || [];
@@ -123,7 +127,7 @@ const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directRef
123
127
  }
124
128
 
125
129
  // D
126
- if (!ret.trim() && !recursive && el.labels) {
130
+ if (!ret.trim() && el.labels) {
127
131
  const strings = Array.prototype.map.call(el.labels, label => {
128
132
  return getNameRaw(label, true, ongoingLabelledBy, visited);
129
133
  });
@@ -190,16 +194,16 @@ const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directRef
190
194
  const before = getPseudoContent(el, ':before');
191
195
  const after = getPseudoContent(el, ':after');
192
196
  return addSpaces(before + ret + after, el);
193
- };
197
+ }
194
198
 
195
- export const getName = function(el) {
199
+ export function getName(el) {
196
200
  return getNameRaw(el)
197
201
  .replace(/[ \n\r\t\f]+/g, ' ')
198
202
  .replace(/^ /, '')
199
203
  .replace(/ $/, '');
200
- };
204
+ }
201
205
 
202
- export const getDescription = function(el) {
206
+ export function getDescription(el) {
203
207
  let ret = '';
204
208
 
205
209
  if (el.matches('[aria-describedby]')) {
@@ -230,4 +234,4 @@ export const getDescription = function(el) {
230
234
  }
231
235
 
232
236
  return ret;
233
- };
237
+ }
package/lib/query.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as attrs from './attrs.js';
2
2
  import * as atree from './atree.js';
3
3
 
4
- export const matches = function(el, selector) {
4
+ export function matches(el, selector) {
5
5
  if (selector.substr(0, 1) === ':') {
6
6
  const attr = selector.substr(1);
7
7
  return attrs.getAttribute(el, attr);
@@ -13,9 +13,9 @@ export const matches = function(el, selector) {
13
13
  } else {
14
14
  return attrs.hasRole(el, selector.split(','));
15
15
  }
16
- };
16
+ }
17
17
 
18
- const _querySelector = function(all) {
18
+ function _querySelector(all) {
19
19
  return function(root, selector) {
20
20
  const results = [];
21
21
  try {
@@ -37,15 +37,15 @@ const _querySelector = function(all) {
37
37
  }
38
38
  return all ? results : results[0];
39
39
  };
40
- };
40
+ }
41
41
 
42
- export const closest = function(el, selector) {
42
+ export function closest(el, selector) {
43
43
  return atree.searchUp(el, candidate => {
44
44
  if (candidate.nodeType === candidate.ELEMENT_NODE) {
45
45
  return matches(candidate, selector);
46
46
  }
47
47
  });
48
- };
48
+ }
49
49
 
50
50
  export const querySelector = _querySelector();
51
51
  export const querySelectorAll = _querySelector(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aria-api",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Access ARIA information from JavaScript",
5
5
  "main": "dist/aria.js",
6
6
  "module": "index.js",
@@ -13,9 +13,9 @@
13
13
  "license": "MIT",
14
14
  "devDependencies": {
15
15
  "expect": "^1.20.2",
16
- "mocha": "^10.2.0",
17
- "mocha-headless-chrome": "^4.0.0",
18
- "rollup": "^4.24.3"
16
+ "mocha": "^12.0.3",
17
+ "mocha-headless-chrome": "^5.1.0",
18
+ "rollup": "^4.68.2"
19
19
  },
20
20
  "files": [
21
21
  "index.js",