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
@@ -1,23 +1,27 @@
|
|
1
1
|
/* global matches */
|
2
2
|
/**
|
3
|
-
* Check if a node matches some attribute(s)
|
3
|
+
* Check if a virtual node matches some attribute(s)
|
4
4
|
*
|
5
|
-
* Note: matches.attributes(
|
6
|
-
* matches(
|
5
|
+
* Note: matches.attributes(vNode, matcher) can be indirectly used through
|
6
|
+
* matches(vNode, { attributes: matcher })
|
7
7
|
*
|
8
8
|
* Example:
|
9
9
|
* ```js
|
10
|
-
* matches.attributes(
|
10
|
+
* matches.attributes(vNode, {
|
11
11
|
* 'aria-live': 'assertive', // Simple string match
|
12
12
|
* 'aria-expanded': /true|false/i, // either boolean, case insensitive
|
13
13
|
* })
|
14
14
|
* ```
|
15
15
|
*
|
16
|
-
* @
|
16
|
+
* @deprecated HTMLElement is deprecated, use VirtualNode instead
|
17
|
+
*
|
18
|
+
* @param {HTMLElement|VirtualNode} vNode
|
17
19
|
* @param {Object} Attribute matcher
|
18
20
|
* @returns {Boolean}
|
19
21
|
*/
|
20
|
-
matches.attributes = function matchesAttributes(
|
21
|
-
|
22
|
-
|
22
|
+
matches.attributes = function matchesAttributes(vNode, matcher) {
|
23
|
+
if (!(vNode instanceof axe.AbstractVirtualNode)) {
|
24
|
+
vNode = axe.utils.getNodeFromTree(vNode);
|
25
|
+
}
|
26
|
+
return matches.fromFunction(attrName => vNode.attr(attrName), matcher);
|
23
27
|
};
|
@@ -2,14 +2,14 @@
|
|
2
2
|
const matchers = ['nodeName', 'attributes', 'properties', 'condition'];
|
3
3
|
|
4
4
|
/**
|
5
|
-
* Check if a node matches some definition
|
5
|
+
* Check if a virtual node matches some definition
|
6
6
|
*
|
7
|
-
* Note: matches.fromDefinition(
|
8
|
-
* matches(
|
7
|
+
* Note: matches.fromDefinition(vNode, definition) can be indirectly used through
|
8
|
+
* matches(vNode, definition)
|
9
9
|
*
|
10
10
|
* Example:
|
11
11
|
* ```js
|
12
|
-
* matches.fromDefinition(
|
12
|
+
* matches.fromDefinition(vNode, {
|
13
13
|
* nodeName: ['div', 'span']
|
14
14
|
* attributes: {
|
15
15
|
* 'aria-live': 'assertive'
|
@@ -17,18 +17,23 @@ const matchers = ['nodeName', 'attributes', 'properties', 'condition'];
|
|
17
17
|
* })
|
18
18
|
* ```
|
19
19
|
*
|
20
|
+
* @deprecated HTMLElement is deprecated, use VirtualNode instead
|
21
|
+
*
|
20
22
|
* @private
|
21
|
-
* @param {HTMLElement|VirtualNode}
|
23
|
+
* @param {HTMLElement|VirtualNode} vNode
|
22
24
|
* @param {Object|Array<Object>} definition
|
23
25
|
* @returns {Boolean}
|
24
26
|
*/
|
25
|
-
matches.fromDefinition = function matchFromDefinition(
|
26
|
-
|
27
|
+
matches.fromDefinition = function matchFromDefinition(vNode, definition) {
|
28
|
+
if (!(vNode instanceof axe.AbstractVirtualNode)) {
|
29
|
+
vNode = axe.utils.getNodeFromTree(vNode);
|
30
|
+
}
|
31
|
+
|
27
32
|
if (Array.isArray(definition)) {
|
28
|
-
return definition.some(definitionItem => matches(
|
33
|
+
return definition.some(definitionItem => matches(vNode, definitionItem));
|
29
34
|
}
|
30
35
|
if (typeof definition === 'string') {
|
31
|
-
return axe.utils.
|
36
|
+
return axe.utils.matches(vNode, definition);
|
32
37
|
}
|
33
38
|
|
34
39
|
return Object.keys(definition).every(matcherName => {
|
@@ -42,6 +47,6 @@ matches.fromDefinition = function matchFromDefinition(node, definition) {
|
|
42
47
|
// Find the matcher that goes into the matches method.
|
43
48
|
// 'div', /^div$/, (str) => str === 'div', etc.
|
44
49
|
const matcher = definition[matcherName];
|
45
|
-
return matchMethod(
|
50
|
+
return matchMethod(vNode, matcher);
|
46
51
|
});
|
47
52
|
};
|
@@ -1,37 +1,39 @@
|
|
1
1
|
/* exported matches */
|
2
2
|
|
3
3
|
/**
|
4
|
-
* Check if a
|
4
|
+
* Check if a virtual node matches a definition
|
5
5
|
*
|
6
6
|
* Example:
|
7
7
|
* ```js
|
8
8
|
* // Match a single nodeName:
|
9
|
-
* axe.commons.matches(
|
9
|
+
* axe.commons.matches(vNode, 'div')
|
10
10
|
*
|
11
11
|
* // Match one of multiple nodeNames:
|
12
|
-
* axe.commons.matches(
|
12
|
+
* axe.commons.matches(vNode, ['ul', 'ol'])
|
13
13
|
*
|
14
14
|
* // Match a node with nodeName 'button' and with aria-hidden: true:
|
15
|
-
* axe.commons.matches(
|
15
|
+
* axe.commons.matches(vNode, {
|
16
16
|
* nodeName: 'button',
|
17
17
|
* attributes: { 'aria-hidden': 'true' }
|
18
18
|
* })
|
19
19
|
*
|
20
20
|
* // Mixed input. Match button nodeName, input[type=button] and input[type=reset]
|
21
|
-
* axe.commons.matches(
|
21
|
+
* axe.commons.matches(vNode, ['button', {
|
22
22
|
* nodeName: 'input', // nodeName match isn't case sensitive
|
23
23
|
* properties: { type: ['button', 'reset'] }
|
24
24
|
* }])
|
25
25
|
* ```
|
26
26
|
*
|
27
|
+
* @deprecated HTMLElement is deprecated, use VirtualNode instead
|
28
|
+
*
|
27
29
|
* @namespace matches
|
28
30
|
* @memberof axe.commons
|
29
|
-
* @param {HTMLElement|VirtualNode}
|
31
|
+
* @param {HTMLElement|VirtualNode} vNode virtual node to verify attributes against constraints
|
30
32
|
* @param {Array<ElementDefinition>|String|Object|Function|Regex} definition
|
31
|
-
* @return {Boolean} true/ false based on if
|
33
|
+
* @return {Boolean} true/ false based on if vNode passes the constraints expected
|
32
34
|
*/
|
33
|
-
function matches(
|
34
|
-
return matches.fromDefinition(
|
35
|
+
function matches(vNode, definition) {
|
36
|
+
return matches.fromDefinition(vNode, definition);
|
35
37
|
}
|
36
38
|
|
37
39
|
commons.matches = matches;
|
@@ -1,34 +1,24 @@
|
|
1
1
|
/* global matches */
|
2
|
-
let isXHTMLGlobal;
|
3
2
|
/**
|
4
|
-
* Check if the nodeName of a node matches some value
|
3
|
+
* Check if the nodeName of a virtual node matches some value.
|
5
4
|
*
|
6
|
-
* Note: matches.nodeName(
|
7
|
-
* matches(
|
5
|
+
* Note: matches.nodeName(vNode, matcher) can be indirectly used through
|
6
|
+
* matches(vNode, { nodeName: matcher })
|
8
7
|
*
|
9
8
|
* Example:
|
10
9
|
* ```js
|
11
|
-
* matches.nodeName(
|
10
|
+
* matches.nodeName(vNode, ['div', 'span'])
|
12
11
|
* ```
|
13
12
|
*
|
14
|
-
* @
|
13
|
+
* @deprecated HTMLElement is deprecated, use VirtualNode instead
|
14
|
+
*
|
15
|
+
* @param {HTMLElement|VirtualNode} vNode
|
15
16
|
* @param {Object} Attribute matcher
|
16
17
|
* @returns {Boolean}
|
17
18
|
*/
|
18
|
-
matches.nodeName = function matchNodeName(
|
19
|
-
|
20
|
-
|
21
|
-
// When the matcher is a string, use native .matches() function:
|
22
|
-
if (typeof matcher === 'string') {
|
23
|
-
return axe.utils.matchesSelector(node, matcher);
|
24
|
-
}
|
25
|
-
|
26
|
-
if (typeof isXHTMLGlobal === 'undefined') {
|
27
|
-
isXHTMLGlobal = axe.utils.isXHTML(node.ownerDocument);
|
28
|
-
}
|
29
|
-
isXHTML = isXHTMLGlobal;
|
19
|
+
matches.nodeName = function matchNodeName(vNode, matcher) {
|
20
|
+
if (!(vNode instanceof axe.AbstractVirtualNode)) {
|
21
|
+
vNode = axe.utils.getNodeFromTree(vNode);
|
30
22
|
}
|
31
|
-
|
32
|
-
const nodeName = isXHTML ? node.nodeName : node.nodeName.toLowerCase();
|
33
|
-
return matches.fromPrimative(nodeName, matcher);
|
23
|
+
return matches.fromPrimative(vNode.props.nodeName, matcher);
|
34
24
|
};
|
@@ -1,25 +1,28 @@
|
|
1
1
|
/* global matches */
|
2
2
|
|
3
3
|
/**
|
4
|
-
* Check if a node matches some attribute(s)
|
4
|
+
* Check if a virtual node matches some attribute(s)
|
5
5
|
*
|
6
|
-
* Note: matches.properties(
|
7
|
-
* matches(
|
6
|
+
* Note: matches.properties(vNode, matcher) can be indirectly used through
|
7
|
+
* matches(vNode, { properties: matcher })
|
8
8
|
*
|
9
9
|
* Example:
|
10
10
|
* ```js
|
11
|
-
* matches.properties(
|
11
|
+
* matches.properties(vNode, {
|
12
12
|
* type: 'text', // Simple string match
|
13
13
|
* value: value => value.trim() !== '', // None-empty value, using a function matcher
|
14
14
|
* })
|
15
15
|
* ```
|
16
16
|
*
|
17
|
-
* @
|
17
|
+
* @deprecated HTMLElement is deprecated, use VirtualNode instead
|
18
|
+
*
|
19
|
+
* @param {HTMLElement|VirtualNode} vNode
|
18
20
|
* @param {Object} matcher
|
19
21
|
* @returns {Boolean}
|
20
22
|
*/
|
21
|
-
matches.properties = function matchesProperties(
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
matches.properties = function matchesProperties(vNode, matcher) {
|
24
|
+
if (!(vNode instanceof axe.AbstractVirtualNode)) {
|
25
|
+
vNode = axe.utils.getNodeFromTree(vNode);
|
26
|
+
}
|
27
|
+
return matches.fromFunction(propName => vNode.props[propName], matcher);
|
25
28
|
};
|
@@ -71,30 +71,33 @@ function getUnicodeNonBmpRegExp() {
|
|
71
71
|
* Regex for matching astral plane unicode
|
72
72
|
* - http://kourge.net/projects/regexp-unicode-block
|
73
73
|
*/
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Notes on various unicode planes being used in the regex below:
|
77
|
+
* '\u1D00-\u1D7F' Phonetic Extensions
|
78
|
+
* '\u1D80-\u1DBF' Phonetic Extensions Supplement
|
79
|
+
* '\u1DC0-\u1DFF' Combining Diacritical Marks Supplement
|
80
|
+
* '\u20A0-\u20CF' Currency symbols
|
81
|
+
* '\u20D0-\u20FF' Combining Diacritical Marks for Symbols
|
82
|
+
* '\u2100-\u214F' Letter like symbols
|
83
|
+
* '\u2150-\u218F' Number forms (eg: Roman numbers)
|
84
|
+
* '\u2190-\u21FF' Arrows
|
85
|
+
* '\u2200-\u22FF' Mathematical operators
|
86
|
+
* '\u2300-\u23FF' Misc Technical
|
87
|
+
* '\u2400-\u243F' Control pictures
|
88
|
+
* '\u2440-\u245F' OCR
|
89
|
+
* '\u2460-\u24FF' Enclosed alpha numerics
|
90
|
+
* '\u2500-\u257F' Box Drawing
|
91
|
+
* '\u2580-\u259F' Block Elements
|
92
|
+
* '\u25A0-\u25FF' Geometric Shapes
|
93
|
+
* '\u2600-\u26FF' Misc Symbols
|
94
|
+
* '\u2700-\u27BF' Dingbats
|
95
|
+
* '\uE000-\uF8FF' Private Use
|
96
|
+
*
|
97
|
+
* Note: plane '\u2000-\u206F' used for General punctuation is excluded as it is handled in -> getPunctuationRegExp
|
98
|
+
*/
|
99
|
+
|
100
|
+
return /[\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF\u20A0-\u20CF\u20D0-\u20FF\u2100-\u214F\u2150-\u218F\u2190-\u21FF\u2200-\u22FF\u2300-\u23FF\u2400-\u243F\u2440-\u245F\u2460-\u24FF\u2500-\u257F\u2580-\u259F\u25A0-\u25FF\u2600-\u26FF\u2700-\u27BF\uE000-\uF8FF]/g;
|
98
101
|
}
|
99
102
|
|
100
103
|
/**
|
@@ -1,3 +1,5 @@
|
|
1
|
+
let isXHTMLGlobal;
|
2
|
+
|
1
3
|
// class is unused in the file...
|
2
4
|
// eslint-disable-next-line no-unused-vars
|
3
5
|
class VirtualNode extends axe.AbstractVirtualNode {
|
@@ -17,6 +19,11 @@ class VirtualNode extends axe.AbstractVirtualNode {
|
|
17
19
|
this._isHidden = null; // will be populated by axe.utils.isHidden
|
18
20
|
this._cache = {};
|
19
21
|
|
22
|
+
if (typeof isXHTMLGlobal === 'undefined') {
|
23
|
+
isXHTMLGlobal = axe.utils.isXHTML(node.ownerDocument);
|
24
|
+
}
|
25
|
+
this._isXHTML = isXHTMLGlobal;
|
26
|
+
|
20
27
|
if (axe._cache.get('nodeMap')) {
|
21
28
|
axe._cache.get('nodeMap').set(node, this);
|
22
29
|
}
|
@@ -25,13 +32,14 @@ class VirtualNode extends axe.AbstractVirtualNode {
|
|
25
32
|
// abstract Node properties so we can run axe in DOM-less environments.
|
26
33
|
// add to the prototype so memory is shared across all virtual nodes
|
27
34
|
get props() {
|
28
|
-
const { nodeType, nodeName, id, type } = this.actualNode;
|
35
|
+
const { nodeType, nodeName, id, type, multiple } = this.actualNode;
|
29
36
|
|
30
37
|
return {
|
31
38
|
nodeType,
|
32
|
-
nodeName: nodeName.toLowerCase(),
|
39
|
+
nodeName: this._isXHTML ? nodeName : nodeName.toLowerCase(),
|
33
40
|
id,
|
34
|
-
type
|
41
|
+
type,
|
42
|
+
multiple
|
35
43
|
};
|
36
44
|
}
|
37
45
|
|
@@ -9,11 +9,14 @@ axe.addReporter('v1', function(results, options, callback) {
|
|
9
9
|
}
|
10
10
|
var out = helpers.processAggregate(results, options);
|
11
11
|
|
12
|
-
|
12
|
+
const addFailureSummaries = result => {
|
13
13
|
result.nodes.forEach(nodeResult => {
|
14
14
|
nodeResult.failureSummary = helpers.failureSummary(nodeResult);
|
15
|
-
})
|
16
|
-
|
15
|
+
});
|
16
|
+
};
|
17
|
+
|
18
|
+
out.incomplete.forEach(addFailureSummaries);
|
19
|
+
out.violations.forEach(addFailureSummaries);
|
17
20
|
|
18
21
|
callback({
|
19
22
|
...helpers.getEnvironmentData(),
|
@@ -28,12 +28,12 @@ axe.utils.isHidden = function isHidden(el, recursed) {
|
|
28
28
|
|
29
29
|
if (
|
30
30
|
!style ||
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
!el.parentNode ||
|
32
|
+
style.getPropertyValue('display') === 'none' ||
|
33
|
+
(!recursed &&
|
34
|
+
// visibility is only accurate on the first element
|
35
|
+
style.getPropertyValue('visibility') === 'hidden') ||
|
36
|
+
el.getAttribute('aria-hidden') === 'true'
|
37
37
|
) {
|
38
38
|
return true;
|
39
39
|
}
|
@@ -0,0 +1,263 @@
|
|
1
|
+
function matchesTag(vNode, exp) {
|
2
|
+
return (
|
3
|
+
vNode.props.nodeType === 1 &&
|
4
|
+
(exp.tag === '*' || vNode.props.nodeName === exp.tag)
|
5
|
+
);
|
6
|
+
}
|
7
|
+
|
8
|
+
function matchesClasses(vNode, exp) {
|
9
|
+
return !exp.classes || exp.classes.every(cl => vNode.hasClass(cl.value));
|
10
|
+
}
|
11
|
+
|
12
|
+
function matchesAttributes(vNode, exp) {
|
13
|
+
return (
|
14
|
+
!exp.attributes ||
|
15
|
+
exp.attributes.every(att => {
|
16
|
+
var nodeAtt = vNode.attr(att.key);
|
17
|
+
return nodeAtt !== null && (!att.value || att.test(nodeAtt));
|
18
|
+
})
|
19
|
+
);
|
20
|
+
}
|
21
|
+
|
22
|
+
function matchesId(vNode, exp) {
|
23
|
+
return !exp.id || vNode.props.id === exp.id;
|
24
|
+
}
|
25
|
+
|
26
|
+
function matchesPseudos(target, exp) {
|
27
|
+
if (
|
28
|
+
!exp.pseudos ||
|
29
|
+
exp.pseudos.every(pseudo => {
|
30
|
+
if (pseudo.name === 'not') {
|
31
|
+
return !axe.utils.matchesExpression(target, pseudo.expressions[0]);
|
32
|
+
}
|
33
|
+
throw new Error(
|
34
|
+
'the pseudo selector ' + pseudo.name + ' has not yet been implemented'
|
35
|
+
);
|
36
|
+
})
|
37
|
+
) {
|
38
|
+
return true;
|
39
|
+
}
|
40
|
+
return false;
|
41
|
+
}
|
42
|
+
|
43
|
+
function matchExpression(vNode, expression) {
|
44
|
+
return (
|
45
|
+
matchesTag(vNode, expression) &&
|
46
|
+
matchesClasses(vNode, expression) &&
|
47
|
+
matchesAttributes(vNode, expression) &&
|
48
|
+
matchesId(vNode, expression) &&
|
49
|
+
matchesPseudos(vNode, expression)
|
50
|
+
);
|
51
|
+
}
|
52
|
+
|
53
|
+
var escapeRegExp = (function() {
|
54
|
+
/*! Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License */
|
55
|
+
var from = /(?=[\-\[\]{}()*+?.\\\^$|,#\s])/g;
|
56
|
+
var to = '\\';
|
57
|
+
return function(string) {
|
58
|
+
return string.replace(from, to);
|
59
|
+
};
|
60
|
+
})();
|
61
|
+
|
62
|
+
const reUnescape = /\\/g;
|
63
|
+
function convertAttributes(atts) {
|
64
|
+
/*! Credit Mootools Copyright Mootools, MIT License */
|
65
|
+
if (!atts) {
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
return atts.map(att => {
|
69
|
+
const attributeKey = att.name.replace(reUnescape, '');
|
70
|
+
const attributeValue = (att.value || '').replace(reUnescape, '');
|
71
|
+
let test, regexp;
|
72
|
+
|
73
|
+
switch (att.operator) {
|
74
|
+
case '^=':
|
75
|
+
regexp = new RegExp('^' + escapeRegExp(attributeValue));
|
76
|
+
break;
|
77
|
+
case '$=':
|
78
|
+
regexp = new RegExp(escapeRegExp(attributeValue) + '$');
|
79
|
+
break;
|
80
|
+
case '~=':
|
81
|
+
regexp = new RegExp(
|
82
|
+
'(^|\\s)' + escapeRegExp(attributeValue) + '(\\s|$)'
|
83
|
+
);
|
84
|
+
break;
|
85
|
+
case '|=':
|
86
|
+
regexp = new RegExp('^' + escapeRegExp(attributeValue) + '(-|$)');
|
87
|
+
break;
|
88
|
+
case '=':
|
89
|
+
test = function(value) {
|
90
|
+
return attributeValue === value;
|
91
|
+
};
|
92
|
+
break;
|
93
|
+
case '*=':
|
94
|
+
test = function(value) {
|
95
|
+
return value && value.includes(attributeValue);
|
96
|
+
};
|
97
|
+
break;
|
98
|
+
case '!=':
|
99
|
+
test = function(value) {
|
100
|
+
return attributeValue !== value;
|
101
|
+
};
|
102
|
+
break;
|
103
|
+
default:
|
104
|
+
test = function(value) {
|
105
|
+
return !!value;
|
106
|
+
};
|
107
|
+
}
|
108
|
+
|
109
|
+
if (attributeValue === '' && /^[*$^]=$/.test(att.operator)) {
|
110
|
+
test = function() {
|
111
|
+
return false;
|
112
|
+
};
|
113
|
+
}
|
114
|
+
|
115
|
+
if (!test) {
|
116
|
+
test = function(value) {
|
117
|
+
return value && regexp.test(value);
|
118
|
+
};
|
119
|
+
}
|
120
|
+
return {
|
121
|
+
key: attributeKey,
|
122
|
+
value: attributeValue,
|
123
|
+
test: test
|
124
|
+
};
|
125
|
+
});
|
126
|
+
}
|
127
|
+
|
128
|
+
function convertClasses(classes) {
|
129
|
+
if (!classes) {
|
130
|
+
return;
|
131
|
+
}
|
132
|
+
return classes.map(className => {
|
133
|
+
className = className.replace(reUnescape, '');
|
134
|
+
|
135
|
+
return {
|
136
|
+
value: className,
|
137
|
+
regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
|
138
|
+
};
|
139
|
+
});
|
140
|
+
}
|
141
|
+
|
142
|
+
function convertPseudos(pseudos) {
|
143
|
+
if (!pseudos) {
|
144
|
+
return;
|
145
|
+
}
|
146
|
+
return pseudos.map(p => {
|
147
|
+
var expressions;
|
148
|
+
|
149
|
+
if (p.name === 'not') {
|
150
|
+
expressions = p.value;
|
151
|
+
expressions = expressions.selectors
|
152
|
+
? expressions.selectors
|
153
|
+
: [expressions];
|
154
|
+
expressions = convertExpressions(expressions);
|
155
|
+
}
|
156
|
+
return {
|
157
|
+
name: p.name,
|
158
|
+
expressions: expressions,
|
159
|
+
value: p.value
|
160
|
+
};
|
161
|
+
});
|
162
|
+
}
|
163
|
+
|
164
|
+
/**
|
165
|
+
* convert the css-selector-parser format into the Slick format
|
166
|
+
* @private
|
167
|
+
* @param Array {Object} expressions
|
168
|
+
* @return Array {Object}
|
169
|
+
*
|
170
|
+
*/
|
171
|
+
function convertExpressions(expressions) {
|
172
|
+
return expressions.map(exp => {
|
173
|
+
var newExp = [];
|
174
|
+
var rule = exp.rule;
|
175
|
+
while (rule) {
|
176
|
+
/* eslint no-restricted-syntax: 0 */
|
177
|
+
// `.tagName` is a property coming from the `CSSSelectorParser` library
|
178
|
+
newExp.push({
|
179
|
+
tag: rule.tagName ? rule.tagName.toLowerCase() : '*',
|
180
|
+
combinator: rule.nestingOperator ? rule.nestingOperator : ' ',
|
181
|
+
id: rule.id,
|
182
|
+
attributes: convertAttributes(rule.attrs),
|
183
|
+
classes: convertClasses(rule.classNames),
|
184
|
+
pseudos: convertPseudos(rule.pseudos)
|
185
|
+
});
|
186
|
+
rule = rule.rule;
|
187
|
+
}
|
188
|
+
return newExp;
|
189
|
+
});
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* Convert a CSS selector to the Slick format expression
|
194
|
+
*
|
195
|
+
* @private
|
196
|
+
* @param {String} selector CSS selector to convert
|
197
|
+
* @returns {Object[]} Array of Slick format expressions
|
198
|
+
*/
|
199
|
+
axe.utils.convertSelector = function convertSelector(selector) {
|
200
|
+
var expressions = axe.utils.cssParser.parse(selector);
|
201
|
+
expressions = expressions.selectors ? expressions.selectors : [expressions];
|
202
|
+
return convertExpressions(expressions);
|
203
|
+
};
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Determine if a virtual node matches a Slick format CSS expression
|
207
|
+
*
|
208
|
+
* @private
|
209
|
+
* @method matchesExpression
|
210
|
+
* @memberof axe.utils
|
211
|
+
* @param {VirtualNode} vNode VirtualNode to match
|
212
|
+
* @param {Object|Object[]} expressions CSS selector expression or array of expressions
|
213
|
+
* @returns {Boolean}
|
214
|
+
*/
|
215
|
+
axe.utils.matchesExpression = function matchesExpression(
|
216
|
+
vNode,
|
217
|
+
expressions,
|
218
|
+
matchAnyParent
|
219
|
+
) {
|
220
|
+
let exps = [].concat(expressions);
|
221
|
+
let expression = exps.pop();
|
222
|
+
let matches = matchExpression(vNode, expression);
|
223
|
+
|
224
|
+
while (!matches && matchAnyParent && vNode.parent) {
|
225
|
+
vNode = vNode.parent;
|
226
|
+
matches = matchExpression(vNode, expression);
|
227
|
+
}
|
228
|
+
|
229
|
+
if (exps.length) {
|
230
|
+
if ([' ', '>'].includes(expression.combinator) === false) {
|
231
|
+
throw new Error(
|
232
|
+
'axe.utils.matchesExpression does not support the combinator: ' +
|
233
|
+
expression.combinator
|
234
|
+
);
|
235
|
+
}
|
236
|
+
|
237
|
+
matches =
|
238
|
+
matches &&
|
239
|
+
axe.utils.matchesExpression(
|
240
|
+
vNode.parent,
|
241
|
+
exps,
|
242
|
+
expression.combinator === ' '
|
243
|
+
);
|
244
|
+
}
|
245
|
+
|
246
|
+
return matches;
|
247
|
+
};
|
248
|
+
|
249
|
+
/**
|
250
|
+
* matches implementation that operates on a VirtualNode
|
251
|
+
*
|
252
|
+
* @method matches
|
253
|
+
* @memberof axe.utils
|
254
|
+
* @param {VirtualNode} vNode VirtualNode to match
|
255
|
+
* @param {String} selector CSS selector string
|
256
|
+
* @return {Boolean}
|
257
|
+
*/
|
258
|
+
axe.utils.matches = function matches(vNode, selector) {
|
259
|
+
let expressions = axe.utils.convertSelector(selector);
|
260
|
+
return expressions.some(expression =>
|
261
|
+
axe.utils.matchesExpression(vNode, expression)
|
262
|
+
);
|
263
|
+
};
|