govuk_publishing_components 21.22.1 → 21.22.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/app/assets/stylesheets/govuk_publishing_components/components/_breadcrumbs.scss +34 -0
  4. data/app/assets/stylesheets/govuk_publishing_components/components/_document-list.scss +2 -2
  5. data/app/views/govuk_publishing_components/components/_breadcrumbs.html.erb +0 -3
  6. data/app/views/govuk_publishing_components/components/_contextual_breadcrumbs.html.erb +0 -2
  7. data/lib/govuk_publishing_components/version.rb +1 -1
  8. data/node_modules/axe-core/CHANGELOG.md +70 -10
  9. data/node_modules/axe-core/axe.js +1295 -632
  10. data/node_modules/axe-core/axe.min.js +2 -2
  11. data/node_modules/axe-core/bower.json +1 -1
  12. data/node_modules/axe-core/doc/API.md +1 -0
  13. data/node_modules/axe-core/doc/aria-supported.md +0 -1
  14. data/node_modules/axe-core/doc/developer-guide.md +2 -2
  15. data/node_modules/axe-core/doc/rule-descriptions.md +91 -87
  16. data/node_modules/axe-core/lib/checks/aria/abstractrole.js +10 -1
  17. data/node_modules/axe-core/lib/checks/aria/abstractrole.json +4 -1
  18. data/node_modules/axe-core/lib/checks/aria/fallbackrole.js +1 -0
  19. data/node_modules/axe-core/lib/checks/aria/fallbackrole.json +11 -0
  20. data/node_modules/axe-core/lib/checks/aria/invalidrole.js +14 -3
  21. data/node_modules/axe-core/lib/checks/aria/invalidrole.json +4 -1
  22. data/node_modules/axe-core/lib/checks/aria/required-children.js +41 -30
  23. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +24 -9
  24. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -2
  25. data/node_modules/axe-core/lib/checks/color/color-contrast.js +23 -7
  26. data/node_modules/axe-core/lib/checks/color/color-contrast.json +7 -1
  27. data/node_modules/axe-core/lib/checks/keyboard/focusable-disabled.js +5 -0
  28. data/node_modules/axe-core/lib/checks/keyboard/focusable-modal-open.js +14 -0
  29. data/node_modules/axe-core/lib/checks/keyboard/focusable-modal-open.json +11 -0
  30. data/node_modules/axe-core/lib/checks/keyboard/focusable-not-tabbable.js +5 -0
  31. data/node_modules/axe-core/lib/checks/keyboard/page-has-elm.js +5 -1
  32. data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-after.js +2 -0
  33. data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-banner.json +1 -0
  34. data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-contentinfo.json +1 -0
  35. data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-main.json +1 -0
  36. data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate.js +14 -2
  37. data/node_modules/axe-core/lib/checks/lists/only-listitems.js +43 -49
  38. data/node_modules/axe-core/lib/checks/lists/only-listitems.json +4 -1
  39. data/node_modules/axe-core/lib/checks/media/no-autoplay-audio.js +93 -0
  40. data/node_modules/axe-core/lib/checks/media/no-autoplay-audio.json +15 -0
  41. data/node_modules/axe-core/lib/checks/navigation/identical-links-same-purpose-after.js +100 -0
  42. data/node_modules/axe-core/lib/checks/navigation/identical-links-same-purpose.js +31 -0
  43. data/node_modules/axe-core/lib/checks/navigation/identical-links-same-purpose.json +12 -0
  44. data/node_modules/axe-core/lib/checks/navigation/region.js +42 -8
  45. data/node_modules/axe-core/lib/checks/navigation/region.json +0 -1
  46. data/node_modules/axe-core/lib/checks/shared/svg-non-empty-title.js +4 -0
  47. data/node_modules/axe-core/lib/checks/shared/svg-non-empty-title.json +11 -0
  48. data/node_modules/axe-core/lib/commons/aria/index.js +12 -4
  49. data/node_modules/axe-core/lib/commons/color/get-background-color.js +5 -157
  50. data/node_modules/axe-core/lib/commons/dom/get-element-stack.js +272 -174
  51. data/node_modules/axe-core/lib/commons/dom/is-focusable.js +3 -1
  52. data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +2 -2
  53. data/node_modules/axe-core/lib/commons/dom/is-modal-open.js +98 -0
  54. data/node_modules/axe-core/lib/commons/dom/is-visible.js +57 -2
  55. data/node_modules/axe-core/lib/commons/dom/url-props-from-attribute.js +143 -0
  56. data/node_modules/axe-core/lib/commons/dom/visually-contains.js +62 -12
  57. data/node_modules/axe-core/lib/commons/matches/attributes.js +12 -8
  58. data/node_modules/axe-core/lib/commons/matches/from-definition.js +15 -10
  59. data/node_modules/axe-core/lib/commons/matches/index.js +11 -9
  60. data/node_modules/axe-core/lib/commons/matches/node-name.js +11 -21
  61. data/node_modules/axe-core/lib/commons/matches/properties.js +12 -9
  62. data/node_modules/axe-core/lib/commons/text/unicode.js +27 -24
  63. data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +11 -3
  64. data/node_modules/axe-core/lib/core/constants.js +1 -1
  65. data/node_modules/axe-core/lib/core/reporters/v1.js +6 -3
  66. data/node_modules/axe-core/lib/core/utils/contains.js +1 -1
  67. data/node_modules/axe-core/lib/core/utils/is-hidden.js +6 -6
  68. data/node_modules/axe-core/lib/core/utils/matches.js +263 -0
  69. data/node_modules/axe-core/lib/core/utils/preload-media.js +65 -0
  70. data/node_modules/axe-core/lib/core/utils/preload.js +33 -24
  71. data/node_modules/axe-core/lib/core/utils/qsa.js +7 -208
  72. data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +5 -1
  73. data/node_modules/axe-core/lib/rules/aria-roles.json +1 -1
  74. data/node_modules/axe-core/lib/rules/button-name.json +1 -1
  75. data/node_modules/axe-core/lib/rules/color-contrast-matches.js +1 -1
  76. data/node_modules/axe-core/lib/rules/color-contrast.json +0 -3
  77. data/node_modules/axe-core/lib/rules/html-namespace-matches.js +1 -0
  78. data/node_modules/axe-core/lib/rules/identical-links-same-purpose-matches.js +13 -0
  79. data/node_modules/axe-core/lib/rules/identical-links-same-purpose.json +14 -0
  80. data/node_modules/axe-core/lib/rules/landmark-no-duplicate-banner.json +1 -1
  81. data/node_modules/axe-core/lib/rules/landmark-no-duplicate-contentinfo.json +1 -1
  82. data/node_modules/axe-core/lib/rules/landmark-no-duplicate-main.json +12 -0
  83. data/node_modules/axe-core/lib/rules/landmark-one-main.json +2 -2
  84. data/node_modules/axe-core/lib/rules/link-name.json +2 -4
  85. data/node_modules/axe-core/lib/rules/meta-viewport.json +1 -1
  86. data/node_modules/axe-core/lib/rules/no-autoplay-audio-matches.js +18 -0
  87. data/node_modules/axe-core/lib/rules/no-autoplay-audio.json +15 -0
  88. data/node_modules/axe-core/lib/rules/region.json +1 -2
  89. data/node_modules/axe-core/lib/rules/role-img-alt.json +2 -1
  90. data/node_modules/axe-core/lib/rules/svg-img-alt.json +24 -0
  91. data/node_modules/axe-core/lib/rules/svg-namespace-matches.js +1 -0
  92. data/node_modules/axe-core/locales/da.json +782 -0
  93. data/node_modules/axe-core/locales/fr.json +221 -42
  94. data/node_modules/axe-core/locales/ja.json +124 -24
  95. data/node_modules/axe-core/package.json +29 -26
  96. data/node_modules/axe-core/sri-history.json +26 -10
  97. metadata +27 -9
  98. data/node_modules/axe-core/doc/examples/jasmine/package-lock.json +0 -1489
  99. data/node_modules/axe-core/doc/examples/jest_react/package-lock.json +0 -7525
  100. data/node_modules/axe-core/doc/examples/mocha/package-lock.json +0 -1671
  101. data/node_modules/axe-core/doc/examples/phantomjs/package-lock.json +0 -862
  102. data/node_modules/axe-core/doc/examples/qunit/package-lock.json +0 -2951
  103. data/node_modules/axe-core/lib/checks/navigation/region-after.js +0 -1
  104. data/node_modules/axe-core/typings/axe-core/axe-core-tests.js +0 -151
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Skip, as no results to curate
3
+ */
4
+ if (results.length < 2) {
5
+ return results;
6
+ }
7
+
8
+ /**
9
+ * Filter results for which `result` is undefined & thus `data`, `relatedNodes` are undefined
10
+ */
11
+ const incompleteResults = results.filter(({ result }) => result !== undefined);
12
+
13
+ /**
14
+ * for each result
15
+ * - get other results with matching accessible name
16
+ * - check if same purpose is served
17
+ * - if not change `result` to `undefined`
18
+ * - construct a list of unique results with relatedNodes to return
19
+ */
20
+ const uniqueResults = [];
21
+ const nameMap = {};
22
+
23
+ for (let index = 0; index < incompleteResults.length; index++) {
24
+ const currentResult = incompleteResults[index];
25
+
26
+ const { name, urlProps } = currentResult.data;
27
+ /**
28
+ * This is to avoid duplications in the `nodeMap`
29
+ */
30
+ if (nameMap[name]) {
31
+ continue;
32
+ }
33
+
34
+ const sameNameResults = incompleteResults.filter(
35
+ ({ data }, resultNum) => data.name === name && resultNum !== index
36
+ );
37
+ const isSameUrl = sameNameResults.every(({ data }) =>
38
+ isIdenticalObject(data.urlProps, urlProps)
39
+ );
40
+
41
+ /**
42
+ * when identical nodes exists but do not resolve to same url, flag result as `incomplete`
43
+ */
44
+ if (sameNameResults.length && !isSameUrl) {
45
+ currentResult.result = undefined;
46
+ }
47
+
48
+ /**
49
+ * -> deduplicate results (for both `pass` or `incomplete`) and add `relatedNodes` if any
50
+ */
51
+ currentResult.relatedNodes = [];
52
+ currentResult.relatedNodes.push(
53
+ ...sameNameResults.map(node => node.relatedNodes[0])
54
+ );
55
+
56
+ /**
57
+ * Update `nodeMap` with `sameNameResults`
58
+ */
59
+ nameMap[name] = sameNameResults;
60
+
61
+ uniqueResults.push(currentResult);
62
+ }
63
+
64
+ return uniqueResults;
65
+
66
+ /**
67
+ * Check if two given objects are the same (Note: this fn is not extensive in terms of depth equality)
68
+ * @param {Object} a object a, to compare
69
+ * @param {*} b object b, to compare
70
+ * @returns {Boolean}
71
+ */
72
+ function isIdenticalObject(a, b) {
73
+ if (!a || !b) {
74
+ return false;
75
+ }
76
+
77
+ const aProps = Object.getOwnPropertyNames(a);
78
+ const bProps = Object.getOwnPropertyNames(b);
79
+
80
+ if (aProps.length !== bProps.length) {
81
+ return false;
82
+ }
83
+
84
+ const result = aProps.every(propName => {
85
+ const aValue = a[propName];
86
+ const bValue = b[propName];
87
+
88
+ if (typeof aValue !== typeof bValue) {
89
+ return false;
90
+ }
91
+
92
+ if (typeof aValue === `object` || typeof bValue === `object`) {
93
+ return isIdenticalObject(aValue, bValue);
94
+ }
95
+
96
+ return aValue === bValue;
97
+ });
98
+
99
+ return result;
100
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Note: `identical-links-same-purpose-after` fn, helps reconcile the results
3
+ */
4
+ const { dom, text } = axe.commons;
5
+
6
+ const accText = text.accessibleTextVirtual(virtualNode);
7
+ const name = text
8
+ .sanitize(
9
+ text.removeUnicode(accText, {
10
+ emoji: true,
11
+ nonBmp: true,
12
+ punctuations: true
13
+ })
14
+ )
15
+ .toLowerCase();
16
+
17
+ if (!name) {
18
+ return undefined;
19
+ }
20
+
21
+ /**
22
+ * Set `data` and `relatedNodes` for use in `after` fn
23
+ */
24
+ const afterData = {
25
+ name,
26
+ urlProps: dom.urlPropsFromAttribute(node, 'href')
27
+ };
28
+ this.data(afterData);
29
+ this.relatedNodes([node]);
30
+
31
+ return true;
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "identical-links-same-purpose",
3
+ "evaluate": "identical-links-same-purpose.js",
4
+ "after": "identical-links-same-purpose-after.js",
5
+ "metadata": {
6
+ "impact": "minor",
7
+ "messages": {
8
+ "pass": "There are no other links with the same name, that go to a different URL",
9
+ "incomplete": "Check that links have the same purpose, or are intentionally ambiguous."
10
+ }
11
+ }
12
+ }
@@ -1,5 +1,11 @@
1
1
  const { dom, aria } = axe.commons;
2
2
  const landmarkRoles = aria.getRolesByType('landmark');
3
+ const implicitAriaLiveRoles = ['alert', 'log', 'status'];
4
+
5
+ let regionlessNodes = axe._cache.get('regionlessNodes');
6
+ if (regionlessNodes) {
7
+ return !regionlessNodes.includes(virtualNode);
8
+ }
3
9
 
4
10
  // Create a list of nodeNames that have a landmark as an implicit role
5
11
  const implicitLandmarks = landmarkRoles
@@ -12,14 +18,18 @@ function isRegion(virtualNode) {
12
18
  const explicitRole = axe.commons.aria.getRole(node, { noImplicit: true });
13
19
  const ariaLive = (node.getAttribute('aria-live') || '').toLowerCase().trim();
14
20
 
15
- if (explicitRole) {
16
- return explicitRole === 'dialog' || landmarkRoles.includes(explicitRole);
17
- }
18
21
  // Ignore content inside of aria-live
19
- if (['assertive', 'polite'].includes(ariaLive)) {
22
+ if (
23
+ ['assertive', 'polite'].includes(ariaLive) ||
24
+ implicitAriaLiveRoles.includes(explicitRole)
25
+ ) {
20
26
  return true;
21
27
  }
22
28
 
29
+ if (explicitRole) {
30
+ return explicitRole === 'dialog' || landmarkRoles.includes(explicitRole);
31
+ }
32
+
23
33
  // Check if the node matches any of the CSS selectors of implicit landmarks
24
34
  return implicitLandmarks.some(implicitSelector => {
25
35
  let matches = axe.utils.matchesSelector(node, implicitSelector);
@@ -47,11 +57,18 @@ function findRegionlessElms(virtualNode) {
47
57
  dom.getElementByReference(virtualNode.actualNode, 'href')) ||
48
58
  !dom.isVisible(node, true)
49
59
  ) {
60
+ // Mark each parent node as having region descendant
61
+ let vNode = virtualNode;
62
+ while (vNode) {
63
+ vNode._hasRegionDescendant = true;
64
+ vNode = vNode.parent;
65
+ }
66
+
50
67
  return [];
51
68
 
52
69
  // Return the node is a content element
53
70
  } else if (dom.hasContent(node, /* noRecursion: */ true)) {
54
- return [node];
71
+ return [virtualNode];
55
72
 
56
73
  // Recursively look at all child elements
57
74
  } else {
@@ -62,7 +79,24 @@ function findRegionlessElms(virtualNode) {
62
79
  }
63
80
  }
64
81
 
65
- var regionlessNodes = findRegionlessElms(virtualNode);
66
- this.relatedNodes(regionlessNodes);
82
+ regionlessNodes = findRegionlessElms(axe._tree[0])
83
+ // Find first parent marked as having region descendant (or body) and
84
+ // return the node right before it as the "outer" element
85
+ .map(vNode => {
86
+ while (
87
+ vNode.parent &&
88
+ !vNode.parent._hasRegionDescendant &&
89
+ vNode.parent.actualNode !== document.body
90
+ ) {
91
+ vNode = vNode.parent;
92
+ }
93
+
94
+ return vNode;
95
+ })
96
+ // Remove duplicate containers
97
+ .filter((vNode, index, array) => {
98
+ return array.indexOf(vNode) === index;
99
+ });
100
+ axe._cache.set('regionlessNodes', regionlessNodes);
67
101
 
68
- return regionlessNodes.length === 0;
102
+ return !regionlessNodes.includes(virtualNode);
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "id": "region",
3
3
  "evaluate": "region.js",
4
- "after": "region-after.js",
5
4
  "metadata": {
6
5
  "impact": "moderate",
7
6
  "messages": {
@@ -0,0 +1,4 @@
1
+ const titleNode = virtualNode.children.find(({ props }) => {
2
+ return props.nodeName === 'title';
3
+ });
4
+ return !!titleNode && titleNode.actualNode.textContent.trim() !== '';
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "svg-non-empty-title",
3
+ "evaluate": "svg-non-empty-title.js",
4
+ "metadata": {
5
+ "impact": "serious",
6
+ "messages": {
7
+ "pass": "element has a child that is a title",
8
+ "fail": "element has no child that is a title"
9
+ }
10
+ }
11
+ }
@@ -68,7 +68,9 @@ lookupTable.attributes = {
68
68
  unstandardized: true
69
69
  },
70
70
  'aria-details': {
71
- unsupported: true
71
+ type: 'idref',
72
+ allowEmpty: true,
73
+ unsupported: false
72
74
  },
73
75
  'aria-disabled': {
74
76
  type: 'boolean',
@@ -250,6 +252,7 @@ lookupTable.globalAttributes = [
250
252
  'aria-controls',
251
253
  'aria-current',
252
254
  'aria-describedby',
255
+ 'aria-details',
253
256
  'aria-disabled',
254
257
  'aria-dropeffect',
255
258
  'aria-flowto',
@@ -433,7 +436,7 @@ lookupTable.role = {
433
436
  required: ['aria-expanded']
434
437
  },
435
438
  owned: {
436
- all: ['listbox', 'textbox']
439
+ all: ['listbox', 'tree', 'grid', 'dialog', 'textbox']
437
440
  },
438
441
  nameFrom: ['author'],
439
442
  context: null,
@@ -2166,8 +2169,12 @@ lookupTable.elementsAllowedNoRole = [
2166
2169
  },
2167
2170
  {
2168
2171
  nodeName: 'select',
2169
- condition: node => {
2170
- return Number(node.getAttribute('size')) > 1;
2172
+ condition: vNode => {
2173
+ if (!(vNode instanceof axe.AbstractVirtualNode)) {
2174
+ vNode = axe.utils.getNodeFromTree(vNode);
2175
+ }
2176
+
2177
+ return Number(vNode.attr('size')) > 1;
2171
2178
  },
2172
2179
  properties: {
2173
2180
  multiple: true
@@ -2406,6 +2413,7 @@ lookupTable.rolesOfType = {
2406
2413
  'progressbar',
2407
2414
  'radio',
2408
2415
  'scrollbar',
2416
+ 'searchbox',
2409
2417
  'slider',
2410
2418
  'spinbutton',
2411
2419
  'status',
@@ -9,39 +9,9 @@
9
9
  * @memberof axe.commons.color
10
10
  * @param {Element} elm Element to determine background color
11
11
  * @param {Array} [bgElms=[]] elements to inspect
12
- * @param {Boolean} [noScroll=false] should scroll
13
12
  * @returns {Color}
14
13
  */
15
- color.getBackgroundColor = function getBackgroundColor(
16
- elm,
17
- bgElms = [],
18
- noScroll = false
19
- ) {
20
- if (noScroll !== true) {
21
- /**
22
- * Avoid scrolling overflow:hidden containers, by only aligning to top,
23
- * when not doing so would move the center point above the viewport top.
24
- */
25
- const clientHeight = elm.getBoundingClientRect().height;
26
- const alignToTop = clientHeight - 2 >= window.innerHeight * 2;
27
- elm.scrollIntoView(alignToTop);
28
-
29
- // ensure element is scrolled into view horizontally
30
- let center, scrollParent;
31
- do {
32
- const rect = elm.getBoundingClientRect();
33
-
34
- // 'x' does not exist in IE11
35
- const x = 'x' in rect ? rect.x : rect.left;
36
- center = x + rect.width / 2;
37
-
38
- if (center < 0) {
39
- scrollParent = getScrollParent(elm);
40
- scrollParent.scrollLeft = 0;
41
- }
42
- } while (center < 0 && scrollParent !== document.documentElement);
43
- }
44
-
14
+ color.getBackgroundColor = function getBackgroundColor(elm, bgElms = []) {
45
15
  let bgColors = [];
46
16
  let elmStack = color.getBackgroundStack(elm);
47
17
 
@@ -101,7 +71,6 @@ color.getBackgroundStack = function getBackgroundStack(elm) {
101
71
  if (elmStack === null) {
102
72
  return null;
103
73
  }
104
- elmStack = includeMissingElements(elmStack, elm);
105
74
  elmStack = dom.reduceToElementsBelowFloating(elmStack, elm);
106
75
  elmStack = sortPageBackground(elmStack);
107
76
 
@@ -133,15 +102,6 @@ color.filteredRectStack = function filteredRectStack(elm) {
133
102
  const boundingStack = rectStack.shift();
134
103
  let isSame;
135
104
 
136
- // Safari v12.1 does not include labels as part of elementsFromPoint()
137
- // if they wrap an input element (UNLESS the label has a background
138
- // color). this results in two different rectStacks (since
139
- // elm.getClientRects() returns two rects for the element) which
140
- // returns null as isSame is false. we can fix this by adding in the
141
- // missing label to the boundingStack before checking for isSame
142
- // @see https://bugs.webkit.org/show_bug.cgi?id=197743
143
- includeMissingElements(boundingStack, elm);
144
-
145
105
  // iterating over arrays of DOMRects
146
106
  rectStack.forEach((rectList, index) => {
147
107
  if (index === 0) {
@@ -179,38 +139,16 @@ color.filteredRectStack = function filteredRectStack(elm) {
179
139
  * @return {Array}
180
140
  */
181
141
  color.getRectStack = function(elm) {
182
- const boundingCoords = axe.commons.color.centerPointOfRect(
183
- elm.getBoundingClientRect()
184
- );
142
+ const boundingStack = axe.commons.dom.getElementStack(elm);
185
143
 
186
- if (!boundingCoords) {
187
- return null;
188
- }
189
-
190
- let boundingStack = dom.shadowElementsFromPoint(
191
- boundingCoords.x,
192
- boundingCoords.y
193
- );
144
+ // Handle inline elements spanning multiple lines to be evaluated
145
+ const filteredArr = axe.commons.dom.getTextElementStack(elm);
194
146
 
195
- let rects = Array.from(elm.getClientRects());
196
147
  // If the element does not have multiple rects, like for display:block, return a single stack
197
- if (!rects || rects.length <= 1) {
148
+ if (!filteredArr || filteredArr.length <= 1) {
198
149
  return [boundingStack];
199
150
  }
200
151
 
201
- // Handle inline elements spanning multiple lines to be evaluated
202
- let filteredArr = rects
203
- .filter(rect => {
204
- // exclude manual line breaks in Chrome/Safari
205
- return rect.width && rect.width > 0;
206
- })
207
- .map(rect => {
208
- const coords = axe.commons.color.centerPointOfRect(rect);
209
- if (coords) {
210
- return dom.shadowElementsFromPoint(coords.x, coords.y);
211
- }
212
- });
213
-
214
152
  if (filteredArr.some(stack => stack === undefined)) {
215
153
  // Can be happen when one or more of the rects sits outside the viewport
216
154
  return null;
@@ -263,66 +201,6 @@ function sortPageBackground(elmStack) {
263
201
  return bgNodes;
264
202
  }
265
203
 
266
- /**
267
- * Include nodes missing from initial gathering because
268
- * document.elementsFromPoint misses some elements we need
269
- * i.e. TR is missing from table elementStack and leaves out bgColor
270
- * https://github.com/dequelabs/axe-core/issues/273
271
- * @private
272
- * @param {Array} elmStack
273
- * @param {Element} elm
274
- * @returns {Array}
275
- */
276
- function includeMissingElements(elmStack, elm) {
277
- /*eslint max-depth:["error",7]*/
278
- const nodeName = elm.nodeName.toUpperCase();
279
- const elementMap = {
280
- TD: ['TR', 'THEAD', 'TBODY', 'TFOOT'],
281
- TH: ['TR', 'THEAD', 'TBODY', 'TFOOT'],
282
- INPUT: ['LABEL']
283
- };
284
- const tagArray = elmStack.map(elm => {
285
- return elm.nodeName.toUpperCase();
286
- });
287
- let bgNodes = elmStack;
288
- for (let candidate in elementMap) {
289
- // check that TR or LABEL has paired nodeName from elementMap, but don't expect elm to be that candidate
290
- if (tagArray.includes(candidate)) {
291
- for (
292
- let candidateIndex = 0;
293
- candidateIndex < elementMap[candidate].length;
294
- candidateIndex++
295
- ) {
296
- // look up the tree for a matching candidate
297
- let ancestorMatch = axe.commons.dom.findUp(
298
- elm,
299
- elementMap[candidate][candidateIndex]
300
- );
301
- if (ancestorMatch && elmStack.indexOf(ancestorMatch) === -1) {
302
- // found an ancestor not in elmStack, and it overlaps
303
- let overlaps = axe.commons.dom.visuallyOverlaps(
304
- elm.getBoundingClientRect(),
305
- ancestorMatch
306
- );
307
- if (overlaps) {
308
- // if target is in the elementMap, use its position.
309
- bgNodes.splice(tagArray.indexOf(candidate) + 1, 0, ancestorMatch);
310
- }
311
- }
312
- // nodeName matches value
313
- // (such as LABEL, when matching itself. It should be in the list, but Phantom skips it)
314
- if (
315
- nodeName === elementMap[candidate][candidateIndex] &&
316
- tagArray.indexOf(nodeName) === -1
317
- ) {
318
- bgNodes.splice(tagArray.indexOf(candidate) + 1, 0, elm);
319
- }
320
- }
321
- }
322
- }
323
- return bgNodes;
324
- }
325
-
326
204
  /**
327
205
  * Determine if element is partially overlapped, triggering a Can't Tell result
328
206
  * @private
@@ -393,36 +271,6 @@ function contentOverlapping(targetElement, bgNode) {
393
271
  return false;
394
272
  }
395
273
 
396
- /**
397
- * Return the scrolling parent element
398
- * @see https://stackoverflow.com/questions/35939886/find-first-scrollable-parent#42543908
399
- * @param {Element} element
400
- * @param {Boolean} includeHidden
401
- * @return {Element}
402
- */
403
- function getScrollParent(element, includeHidden) {
404
- var style = getComputedStyle(element);
405
- var excludeStaticParent = style.position === 'absolute';
406
- var overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
407
-
408
- if (style.position === 'fixed') {
409
- return document.documentElement;
410
- }
411
- for (var parent = element; (parent = parent.parentElement); ) {
412
- style = getComputedStyle(parent);
413
- if (excludeStaticParent && style.position === 'static') {
414
- continue;
415
- }
416
- if (
417
- overflowRegex.test(style.overflow + style.overflowY + style.overflowX)
418
- ) {
419
- return parent;
420
- }
421
- }
422
-
423
- return document.documentElement;
424
- }
425
-
426
274
  /**
427
275
  * Determines whether an element has a fully opaque background, whether solid color or an image
428
276
  * @param {Element} node