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.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/app/assets/stylesheets/govuk_publishing_components/components/_breadcrumbs.scss +34 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_document-list.scss +2 -2
- data/app/views/govuk_publishing_components/components/_breadcrumbs.html.erb +0 -3
- data/app/views/govuk_publishing_components/components/_contextual_breadcrumbs.html.erb +0 -2
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/axe-core/CHANGELOG.md +70 -10
- data/node_modules/axe-core/axe.js +1295 -632
- data/node_modules/axe-core/axe.min.js +2 -2
- data/node_modules/axe-core/bower.json +1 -1
- data/node_modules/axe-core/doc/API.md +1 -0
- data/node_modules/axe-core/doc/aria-supported.md +0 -1
- data/node_modules/axe-core/doc/developer-guide.md +2 -2
- data/node_modules/axe-core/doc/rule-descriptions.md +91 -87
- data/node_modules/axe-core/lib/checks/aria/abstractrole.js +10 -1
- data/node_modules/axe-core/lib/checks/aria/abstractrole.json +4 -1
- data/node_modules/axe-core/lib/checks/aria/fallbackrole.js +1 -0
- data/node_modules/axe-core/lib/checks/aria/fallbackrole.json +11 -0
- data/node_modules/axe-core/lib/checks/aria/invalidrole.js +14 -3
- data/node_modules/axe-core/lib/checks/aria/invalidrole.json +4 -1
- data/node_modules/axe-core/lib/checks/aria/required-children.js +41 -30
- data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +24 -9
- data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -2
- data/node_modules/axe-core/lib/checks/color/color-contrast.js +23 -7
- data/node_modules/axe-core/lib/checks/color/color-contrast.json +7 -1
- data/node_modules/axe-core/lib/checks/keyboard/focusable-disabled.js +5 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-modal-open.js +14 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-modal-open.json +11 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-not-tabbable.js +5 -0
- data/node_modules/axe-core/lib/checks/keyboard/page-has-elm.js +5 -1
- data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-after.js +2 -0
- data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-banner.json +1 -0
- data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-contentinfo.json +1 -0
- data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate-main.json +1 -0
- data/node_modules/axe-core/lib/checks/keyboard/page-no-duplicate.js +14 -2
- data/node_modules/axe-core/lib/checks/lists/only-listitems.js +43 -49
- data/node_modules/axe-core/lib/checks/lists/only-listitems.json +4 -1
- data/node_modules/axe-core/lib/checks/media/no-autoplay-audio.js +93 -0
- data/node_modules/axe-core/lib/checks/media/no-autoplay-audio.json +15 -0
- data/node_modules/axe-core/lib/checks/navigation/identical-links-same-purpose-after.js +100 -0
- data/node_modules/axe-core/lib/checks/navigation/identical-links-same-purpose.js +31 -0
- data/node_modules/axe-core/lib/checks/navigation/identical-links-same-purpose.json +12 -0
- data/node_modules/axe-core/lib/checks/navigation/region.js +42 -8
- data/node_modules/axe-core/lib/checks/navigation/region.json +0 -1
- data/node_modules/axe-core/lib/checks/shared/svg-non-empty-title.js +4 -0
- data/node_modules/axe-core/lib/checks/shared/svg-non-empty-title.json +11 -0
- data/node_modules/axe-core/lib/commons/aria/index.js +12 -4
- data/node_modules/axe-core/lib/commons/color/get-background-color.js +5 -157
- data/node_modules/axe-core/lib/commons/dom/get-element-stack.js +272 -174
- data/node_modules/axe-core/lib/commons/dom/is-focusable.js +3 -1
- data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +2 -2
- data/node_modules/axe-core/lib/commons/dom/is-modal-open.js +98 -0
- data/node_modules/axe-core/lib/commons/dom/is-visible.js +57 -2
- data/node_modules/axe-core/lib/commons/dom/url-props-from-attribute.js +143 -0
- data/node_modules/axe-core/lib/commons/dom/visually-contains.js +62 -12
- data/node_modules/axe-core/lib/commons/matches/attributes.js +12 -8
- data/node_modules/axe-core/lib/commons/matches/from-definition.js +15 -10
- data/node_modules/axe-core/lib/commons/matches/index.js +11 -9
- data/node_modules/axe-core/lib/commons/matches/node-name.js +11 -21
- data/node_modules/axe-core/lib/commons/matches/properties.js +12 -9
- data/node_modules/axe-core/lib/commons/text/unicode.js +27 -24
- data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +11 -3
- data/node_modules/axe-core/lib/core/constants.js +1 -1
- data/node_modules/axe-core/lib/core/reporters/v1.js +6 -3
- data/node_modules/axe-core/lib/core/utils/contains.js +1 -1
- data/node_modules/axe-core/lib/core/utils/is-hidden.js +6 -6
- data/node_modules/axe-core/lib/core/utils/matches.js +263 -0
- data/node_modules/axe-core/lib/core/utils/preload-media.js +65 -0
- data/node_modules/axe-core/lib/core/utils/preload.js +33 -24
- data/node_modules/axe-core/lib/core/utils/qsa.js +7 -208
- data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +5 -1
- data/node_modules/axe-core/lib/rules/aria-roles.json +1 -1
- data/node_modules/axe-core/lib/rules/button-name.json +1 -1
- data/node_modules/axe-core/lib/rules/color-contrast-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/color-contrast.json +0 -3
- data/node_modules/axe-core/lib/rules/html-namespace-matches.js +1 -0
- data/node_modules/axe-core/lib/rules/identical-links-same-purpose-matches.js +13 -0
- data/node_modules/axe-core/lib/rules/identical-links-same-purpose.json +14 -0
- data/node_modules/axe-core/lib/rules/landmark-no-duplicate-banner.json +1 -1
- data/node_modules/axe-core/lib/rules/landmark-no-duplicate-contentinfo.json +1 -1
- data/node_modules/axe-core/lib/rules/landmark-no-duplicate-main.json +12 -0
- data/node_modules/axe-core/lib/rules/landmark-one-main.json +2 -2
- data/node_modules/axe-core/lib/rules/link-name.json +2 -4
- data/node_modules/axe-core/lib/rules/meta-viewport.json +1 -1
- data/node_modules/axe-core/lib/rules/no-autoplay-audio-matches.js +18 -0
- data/node_modules/axe-core/lib/rules/no-autoplay-audio.json +15 -0
- data/node_modules/axe-core/lib/rules/region.json +1 -2
- data/node_modules/axe-core/lib/rules/role-img-alt.json +2 -1
- data/node_modules/axe-core/lib/rules/svg-img-alt.json +24 -0
- data/node_modules/axe-core/lib/rules/svg-namespace-matches.js +1 -0
- data/node_modules/axe-core/locales/da.json +782 -0
- data/node_modules/axe-core/locales/fr.json +221 -42
- data/node_modules/axe-core/locales/ja.json +124 -24
- data/node_modules/axe-core/package.json +29 -26
- data/node_modules/axe-core/sri-history.json +26 -10
- metadata +27 -9
- data/node_modules/axe-core/doc/examples/jasmine/package-lock.json +0 -1489
- data/node_modules/axe-core/doc/examples/jest_react/package-lock.json +0 -7525
- data/node_modules/axe-core/doc/examples/mocha/package-lock.json +0 -1671
- data/node_modules/axe-core/doc/examples/phantomjs/package-lock.json +0 -862
- data/node_modules/axe-core/doc/examples/qunit/package-lock.json +0 -2951
- data/node_modules/axe-core/lib/checks/navigation/region-after.js +0 -1
- 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 (
|
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 [
|
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
|
-
|
66
|
-
|
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.
|
102
|
+
return !regionlessNodes.includes(virtualNode);
|
@@ -68,7 +68,9 @@ lookupTable.attributes = {
|
|
68
68
|
unstandardized: true
|
69
69
|
},
|
70
70
|
'aria-details': {
|
71
|
-
|
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:
|
2170
|
-
|
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
|
183
|
-
elm.getBoundingClientRect()
|
184
|
-
);
|
142
|
+
const boundingStack = axe.commons.dom.getElementStack(elm);
|
185
143
|
|
186
|
-
|
187
|
-
|
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 (!
|
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
|