govuk_publishing_components 21.16.3 → 21.17.0
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/component_guide/accessibility-test.js +0 -6
- data/app/views/govuk_publishing_components/components/docs/machine_readable_metadata.yml +7 -0
- data/lib/govuk_publishing_components/presenters/machine_readable/dataset_schema.rb +34 -0
- data/lib/govuk_publishing_components/presenters/schema_org.rb +3 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/axe-core/CHANGELOG.md +166 -2
- data/node_modules/axe-core/CONTRIBUTING.md +5 -5
- data/node_modules/axe-core/README.md +4 -4
- data/node_modules/axe-core/axe.d.ts +27 -11
- data/node_modules/axe-core/axe.js +9597 -2431
- 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 +211 -128
- data/node_modules/axe-core/doc/accessibility-supported.md +1 -1
- data/node_modules/axe-core/doc/aria-supported.md +4 -13
- data/node_modules/axe-core/doc/backwards-compatibility-doc.md +93 -0
- data/node_modules/axe-core/doc/code-submission-guidelines.md +4 -4
- data/node_modules/axe-core/doc/developer-guide.md +27 -13
- data/node_modules/axe-core/doc/examples/chrome-debugging-protocol/package.json +5 -2
- data/node_modules/axe-core/doc/examples/jasmine/README.md +3 -5
- data/node_modules/axe-core/doc/examples/jasmine/karma.conf.js +29 -0
- data/node_modules/axe-core/doc/examples/jasmine/package.json +6 -5
- data/node_modules/axe-core/doc/examples/jest_react/README.md +1 -1
- data/node_modules/axe-core/doc/examples/jest_react/link.test.js +3 -3
- data/node_modules/axe-core/doc/examples/jest_react/package.json +9 -9
- data/node_modules/axe-core/doc/examples/jsdom/package.json +15 -0
- data/node_modules/axe-core/doc/examples/mocha/README.md +5 -7
- data/node_modules/axe-core/doc/examples/mocha/karma.conf.js +29 -0
- data/node_modules/axe-core/doc/examples/mocha/package.json +7 -6
- data/node_modules/axe-core/doc/examples/phantomjs/README.md +3 -3
- data/node_modules/axe-core/doc/examples/phantomjs/axe-phantom.js +4 -2
- data/node_modules/axe-core/doc/examples/phantomjs/package.json +3 -3
- data/node_modules/axe-core/doc/examples/puppeteer/package.json +5 -2
- data/node_modules/axe-core/doc/examples/qunit/README.md +2 -2
- data/node_modules/axe-core/doc/examples/qunit/package.json +2 -2
- data/node_modules/axe-core/doc/examples/test-examples.js +32 -0
- data/node_modules/axe-core/doc/plugins.md +10 -10
- data/node_modules/axe-core/doc/projects.md +12 -8
- data/node_modules/axe-core/doc/rule-descriptions.md +87 -79
- data/node_modules/axe-core/doc/rule-development.md +30 -2
- data/node_modules/axe-core/lib/checks/aria/allowed-attr.js +1 -1
- data/node_modules/axe-core/lib/checks/aria/aria-roledescription.js +14 -0
- data/node_modules/axe-core/lib/checks/aria/aria-roledescription.json +23 -0
- data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.js +21 -0
- data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.json +11 -0
- data/node_modules/axe-core/lib/checks/aria/required-attr.js +32 -7
- data/node_modules/axe-core/lib/checks/aria/required-children.js +40 -14
- data/node_modules/axe-core/lib/checks/aria/required-children.json +12 -1
- data/node_modules/axe-core/lib/checks/aria/required-parent.js +1 -1
- data/node_modules/axe-core/lib/checks/aria/unsupportedattr.js +1 -1
- data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +50 -17
- data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -1
- data/node_modules/axe-core/lib/checks/aria/valid-attr.js +1 -1
- data/node_modules/axe-core/lib/checks/color/color-contrast.js +2 -2
- data/node_modules/axe-core/lib/checks/forms/autocomplete-appropriate.js +4 -4
- data/node_modules/axe-core/lib/checks/forms/autocomplete-valid.js +1 -1
- data/node_modules/axe-core/lib/checks/forms/fieldset.json +1 -0
- data/node_modules/axe-core/lib/checks/forms/group-labelledby.json +1 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-content.js +16 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-content.json +11 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-element.js +12 -0
- data/node_modules/axe-core/lib/checks/keyboard/focusable-element.json +11 -0
- data/node_modules/axe-core/lib/checks/keyboard/tabindex.js +6 -1
- data/node_modules/axe-core/lib/checks/label/alt-space-value.js +3 -2
- data/node_modules/axe-core/lib/checks/label/duplicate-img-label.js +18 -15
- data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.js +13 -3
- data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.json +4 -0
- data/node_modules/axe-core/lib/checks/label/multiple-label.js +22 -12
- data/node_modules/axe-core/lib/checks/label/multiple-label.json +1 -1
- data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique-after.js +23 -0
- data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.js +7 -0
- data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.json +12 -0
- data/node_modules/axe-core/lib/checks/lists/listitem.js +1 -0
- data/node_modules/axe-core/lib/checks/lists/listitem.json +1 -1
- data/node_modules/axe-core/lib/checks/lists/only-listitems.js +0 -4
- data/node_modules/axe-core/lib/checks/mobile/css-orientation-lock.js +8 -6
- data/node_modules/axe-core/lib/checks/navigation/region.js +2 -19
- data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.js +18 -0
- data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.json +11 -0
- data/node_modules/axe-core/lib/checks/shared/exists.js +1 -1
- data/node_modules/axe-core/lib/checks/shared/exists.json +1 -1
- data/node_modules/axe-core/lib/checks/shared/has-alt.js +6 -4
- data/node_modules/axe-core/lib/checks/shared/non-empty-alt.js +1 -1
- data/node_modules/axe-core/lib/checks/tables/caption-faked.json +1 -1
- data/node_modules/axe-core/lib/checks/tables/td-has-header.js +5 -4
- data/node_modules/axe-core/lib/checks/tables/th-has-data-cells.js +19 -29
- data/node_modules/axe-core/lib/commons/aria/get-owned-virtual.js +1 -1
- data/node_modules/axe-core/lib/commons/aria/index.js +50 -46
- data/node_modules/axe-core/lib/commons/aria/is-accessible-ref.js +41 -37
- data/node_modules/axe-core/lib/commons/aria/label-virtual.js +2 -2
- data/node_modules/axe-core/lib/commons/aria/roles.js +1 -1
- data/node_modules/axe-core/lib/commons/aria/validate-attr-value.js +0 -1
- data/node_modules/axe-core/lib/commons/color/center-point-of-rect.js +30 -0
- data/node_modules/axe-core/lib/commons/color/contrast.js +7 -1
- data/node_modules/axe-core/lib/commons/color/element-has-image.js +36 -0
- data/node_modules/axe-core/lib/commons/color/get-background-color.js +332 -306
- data/node_modules/axe-core/lib/commons/color/get-foreground-color.js +35 -6
- data/node_modules/axe-core/lib/commons/color/get-own-background-color.js +22 -0
- data/node_modules/axe-core/lib/commons/dom/find-up.js +5 -5
- data/node_modules/axe-core/lib/commons/dom/get-scroll-offset.js +0 -1
- data/node_modules/axe-core/lib/commons/dom/get-tabbable-elements.js +1 -1
- data/node_modules/axe-core/lib/commons/dom/has-content-virtual.js +7 -5
- data/node_modules/axe-core/lib/commons/dom/is-focusable.js +8 -5
- data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +15 -2
- data/node_modules/axe-core/lib/commons/dom/is-in-text-block.js +1 -2
- data/node_modules/axe-core/lib/commons/dom/is-skip-link.js +45 -0
- data/node_modules/axe-core/lib/commons/dom/is-visible.js +43 -17
- data/node_modules/axe-core/lib/commons/dom/is-visual-content.js +0 -1
- data/node_modules/axe-core/lib/commons/dom/visually-contains.js +0 -1
- data/node_modules/axe-core/lib/commons/dom/visually-overlaps.js +0 -1
- data/node_modules/axe-core/lib/commons/forms/index.js +8 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-combobox.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-listbox.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-range.js +14 -0
- data/node_modules/axe-core/lib/commons/forms/is-aria-textbox.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-native-select.js +13 -0
- data/node_modules/axe-core/lib/commons/forms/is-native-textbox.js +28 -0
- data/node_modules/axe-core/lib/commons/table/get-cell-position.js +2 -2
- data/node_modules/axe-core/lib/commons/table/get-headers.js +55 -11
- data/node_modules/axe-core/lib/commons/table/get-scope.js +1 -1
- data/node_modules/axe-core/lib/commons/table/is-data-table.js +0 -1
- data/node_modules/axe-core/lib/commons/table/to-grid.js +2 -2
- data/node_modules/axe-core/lib/commons/text/accessible-text-virtual.js +5 -5
- data/node_modules/axe-core/lib/commons/text/form-control-value.js +18 -30
- data/node_modules/axe-core/lib/commons/text/is-icon-ligature.js +210 -0
- data/node_modules/axe-core/lib/commons/text/is-valid-autocomplete.js +0 -1
- data/node_modules/axe-core/lib/commons/text/label-text.js +1 -1
- data/node_modules/axe-core/lib/commons/text/label-virtual.js +1 -1
- data/node_modules/axe-core/lib/commons/text/native-text-methods.js +1 -1
- data/node_modules/axe-core/lib/commons/text/subtree-text.js +3 -3
- data/node_modules/axe-core/lib/commons/text/unicode.js +15 -0
- data/node_modules/axe-core/lib/commons/text/visible-text-nodes.js +25 -0
- data/node_modules/axe-core/lib/commons/text/visible-virtual.js +1 -1
- data/node_modules/axe-core/lib/core/base/audit.js +90 -15
- data/node_modules/axe-core/lib/core/base/cache.js +33 -0
- data/node_modules/axe-core/lib/core/base/check.js +48 -1
- data/node_modules/axe-core/lib/core/base/context.js +15 -14
- data/node_modules/axe-core/lib/core/base/rule.js +223 -46
- data/node_modules/axe-core/lib/core/base/virtual-node/abstract-virtual-node.js +40 -0
- data/node_modules/axe-core/lib/core/base/virtual-node/serial-virtual-node.js +86 -0
- data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +85 -0
- data/node_modules/axe-core/lib/core/constants.js +10 -2
- data/node_modules/axe-core/lib/core/imports/index.js +28 -3
- data/node_modules/axe-core/lib/core/index.js +2 -4
- data/node_modules/axe-core/lib/core/public/configure.js +28 -1
- data/node_modules/axe-core/lib/core/public/run-rules.js +2 -0
- data/node_modules/axe-core/lib/core/public/run-virtual-rule.js +50 -0
- data/node_modules/axe-core/lib/core/public/run.js +13 -2
- data/node_modules/axe-core/lib/core/reporters/helpers/process-aggregate.js +1 -1
- data/node_modules/axe-core/lib/core/reporters/na.js +4 -0
- data/node_modules/axe-core/lib/core/reporters/raw-env.js +12 -0
- data/node_modules/axe-core/lib/core/reporters/raw.js +25 -1
- data/node_modules/axe-core/lib/core/utils/are-styles-set.js +4 -7
- data/node_modules/axe-core/lib/core/utils/assert.js +12 -0
- data/node_modules/axe-core/lib/core/utils/collect-results-from-frames.js +2 -2
- data/node_modules/axe-core/lib/core/utils/contains.js +27 -13
- data/node_modules/axe-core/lib/core/utils/css-parser.js +2 -0
- data/node_modules/axe-core/lib/core/utils/element-matches.js +5 -1
- data/node_modules/axe-core/lib/core/utils/escape-selector.js +1 -2
- data/node_modules/axe-core/lib/core/utils/flattened-tree.js +47 -61
- data/node_modules/axe-core/lib/core/utils/get-check-option.js +0 -1
- data/node_modules/axe-core/lib/core/utils/get-friendly-uri-end.js +1 -1
- data/node_modules/axe-core/lib/core/utils/get-node-attributes.js +21 -0
- data/node_modules/axe-core/lib/core/utils/get-scroll.js +39 -0
- data/node_modules/axe-core/lib/core/utils/get-selector.js +9 -6
- data/node_modules/axe-core/lib/core/utils/get-stylesheet-factory.js +51 -0
- data/node_modules/axe-core/lib/core/utils/get-xpath.js +0 -1
- data/node_modules/axe-core/lib/core/utils/is-hidden.js +16 -4
- data/node_modules/axe-core/lib/core/utils/is-html-element.js +5 -5
- data/node_modules/axe-core/lib/core/utils/is-shadow-root.js +3 -3
- data/node_modules/axe-core/lib/core/utils/memoize.js +17 -0
- data/node_modules/axe-core/lib/core/utils/parse-crossorigin-stylesheet.js +53 -0
- data/node_modules/axe-core/lib/core/utils/parse-sameorigin-stylesheet.js +96 -0
- data/node_modules/axe-core/lib/core/utils/parse-stylesheet.js +70 -0
- data/node_modules/axe-core/lib/core/utils/performance-timer.js +7 -2
- data/node_modules/axe-core/lib/core/utils/preload-cssom.js +77 -281
- data/node_modules/axe-core/lib/core/utils/preload.js +49 -23
- data/node_modules/axe-core/lib/core/utils/qsa.js +39 -50
- data/node_modules/axe-core/lib/core/utils/respondable.js +20 -3
- data/node_modules/axe-core/lib/core/utils/rule-should-run.js +0 -1
- data/node_modules/axe-core/lib/core/utils/scroll-state.js +12 -25
- data/node_modules/axe-core/lib/core/utils/select.js +12 -23
- data/node_modules/axe-core/lib/core/utils/uuid.js +1 -2
- data/node_modules/axe-core/lib/intro.stub +1 -1
- data/node_modules/axe-core/lib/misc/incomplete-fallback.json +1 -1
- data/node_modules/axe-core/lib/rules/aria-allowed-attr-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/aria-form-field-name-matches.js +50 -0
- data/node_modules/axe-core/lib/rules/aria-has-attr-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +1 -1
- data/node_modules/axe-core/lib/rules/aria-input-field-name.json +13 -0
- data/node_modules/axe-core/lib/rules/aria-roledescription.json +12 -0
- data/node_modules/axe-core/lib/rules/aria-toggle-field-name.json +18 -0
- data/node_modules/axe-core/lib/rules/autocomplete-matches.js +14 -10
- data/node_modules/axe-core/lib/rules/avoid-inline-spacing.json +12 -0
- data/node_modules/axe-core/lib/rules/button-name.json +3 -5
- data/node_modules/axe-core/lib/rules/checkboxgroup.json +2 -1
- data/node_modules/axe-core/lib/rules/color-contrast-matches.js +37 -22
- data/node_modules/axe-core/lib/rules/duplicate-id-active-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/duplicate-id-misc-matches.js +2 -2
- data/node_modules/axe-core/lib/rules/form-field-multiple-labels.json +1 -1
- data/node_modules/axe-core/lib/rules/html-has-lang.json +1 -0
- data/node_modules/axe-core/lib/rules/image-alt.json +1 -1
- data/node_modules/axe-core/lib/rules/img-redundant-alt.json +3 -3
- data/node_modules/axe-core/lib/rules/input-button-name.json +26 -0
- data/node_modules/axe-core/lib/rules/landmark-unique-matches.js +41 -0
- data/node_modules/axe-core/lib/rules/landmark-unique.json +13 -0
- data/node_modules/axe-core/lib/rules/link-name.json +5 -4
- data/node_modules/axe-core/lib/rules/meta-refresh.json +8 -1
- data/node_modules/axe-core/lib/rules/radiogroup.json +2 -1
- data/node_modules/axe-core/lib/rules/role-img-alt.json +18 -0
- data/node_modules/axe-core/lib/rules/scrollable-region-focusable-matches.js +30 -0
- data/node_modules/axe-core/lib/rules/scrollable-region-focusable.json +12 -0
- data/node_modules/axe-core/lib/rules/skip-link-matches.js +1 -1
- data/node_modules/axe-core/lib/rules/skip-link.json +1 -1
- data/node_modules/axe-core/lib/rules/video-description.json +3 -1
- data/node_modules/axe-core/locales/de.json +1 -5
- data/node_modules/axe-core/locales/es.json +773 -0
- data/node_modules/axe-core/locales/fr.json +15 -19
- data/node_modules/axe-core/locales/ja.json +65 -11
- data/node_modules/axe-core/locales/ko.json +777 -0
- data/node_modules/axe-core/locales/nl.json +35 -35
- data/node_modules/axe-core/locales/pt_BR.json +773 -0
- data/node_modules/axe-core/package.json +56 -52
- data/node_modules/axe-core/sri-history.json +20 -0
- data/node_modules/axe-core/typings/axe-core/axe-core-tests.ts +5 -15
- data/node_modules/govuk-frontend/package.json +10 -10
- metadata +62 -4
- data/node_modules/axe-core/doc/axelogo2018.png +0 -0
- data/node_modules/axe-core/lib/rules/role-not-button-matches.js +0 -1
|
@@ -17,7 +17,7 @@ aria.labelVirtual = function({ actualNode }) {
|
|
|
17
17
|
ref = dom.idrefs(actualNode, 'aria-labelledby');
|
|
18
18
|
candidate = ref
|
|
19
19
|
.map(function(thing) {
|
|
20
|
-
const vNode = axe.utils.getNodeFromTree(
|
|
20
|
+
const vNode = axe.utils.getNodeFromTree(thing);
|
|
21
21
|
return vNode ? text.visibleVirtual(vNode, true) : '';
|
|
22
22
|
})
|
|
23
23
|
.join(' ')
|
|
@@ -49,6 +49,6 @@ aria.labelVirtual = function({ actualNode }) {
|
|
|
49
49
|
* @return {Mixed} String of visible text, or `null` if no label is found
|
|
50
50
|
*/
|
|
51
51
|
aria.label = function(node) {
|
|
52
|
-
node = axe.utils.getNodeFromTree(
|
|
52
|
+
node = axe.utils.getNodeFromTree(node);
|
|
53
53
|
return aria.labelVirtual(node);
|
|
54
54
|
};
|
|
@@ -188,7 +188,7 @@ aria.implicitRole = function(node) {
|
|
|
188
188
|
return null;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
var nodeAttributes = node
|
|
191
|
+
var nodeAttributes = axe.utils.getNodeAttributes(node);
|
|
192
192
|
var ariaAttributes = [];
|
|
193
193
|
|
|
194
194
|
/* Get all aria-attributes defined for this node */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* global color */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get coordinates for an element's client rects or bounding client rect
|
|
5
|
+
*
|
|
6
|
+
* @method centerPointOfRect
|
|
7
|
+
* @memberof axe.commons.color
|
|
8
|
+
* @param {DOMRect} rect
|
|
9
|
+
* @returns {Object | undefined}
|
|
10
|
+
*/
|
|
11
|
+
color.centerPointOfRect = function centerPointOfRect(rect) {
|
|
12
|
+
if (rect.left > window.innerWidth) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (rect.top > window.innerHeight) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const x = Math.min(
|
|
21
|
+
Math.ceil(rect.left + rect.width / 2),
|
|
22
|
+
window.innerWidth - 1
|
|
23
|
+
);
|
|
24
|
+
const y = Math.min(
|
|
25
|
+
Math.ceil(rect.top + rect.height / 2),
|
|
26
|
+
window.innerHeight - 1
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return { x, y };
|
|
30
|
+
};
|
|
@@ -74,7 +74,13 @@ color.Color = function(red, green, blue, alpha) {
|
|
|
74
74
|
this.red = parseInt(match[1], 10);
|
|
75
75
|
this.green = parseInt(match[2], 10);
|
|
76
76
|
this.blue = parseInt(match[3], 10);
|
|
77
|
-
|
|
77
|
+
|
|
78
|
+
// alpha values can be between 0 and 1, with browsers having
|
|
79
|
+
// different floating point precision. for example,
|
|
80
|
+
// 'rgba(0,0,0,0.5)' results in 'rgba(0,0,0,0.498039)' in Safari
|
|
81
|
+
// when getting the computed style background-color property. to
|
|
82
|
+
// fix this, we'll round all alpha values to 2 decimal points.
|
|
83
|
+
this.alpha = Math.round(parseFloat(match[4]) * 100) / 100;
|
|
78
84
|
return;
|
|
79
85
|
}
|
|
80
86
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* global color */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reports if an element has a background image or gradient
|
|
5
|
+
*
|
|
6
|
+
* @method elementHasImage
|
|
7
|
+
* @memberof axe.commons.color
|
|
8
|
+
* @private
|
|
9
|
+
* @param {Element} elm
|
|
10
|
+
* @param {Object|null} style
|
|
11
|
+
* @return {Boolean}
|
|
12
|
+
*/
|
|
13
|
+
color.elementHasImage = function elementHasImage(elm, style) {
|
|
14
|
+
const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG'];
|
|
15
|
+
const nodeName = elm.nodeName.toUpperCase();
|
|
16
|
+
|
|
17
|
+
if (graphicNodes.includes(nodeName)) {
|
|
18
|
+
axe.commons.color.incompleteData.set('bgColor', 'imgNode');
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
style = style || window.getComputedStyle(elm);
|
|
23
|
+
|
|
24
|
+
const bgImageStyle = style.getPropertyValue('background-image');
|
|
25
|
+
const hasBgImage = bgImageStyle !== 'none';
|
|
26
|
+
|
|
27
|
+
if (hasBgImage) {
|
|
28
|
+
const hasGradient = /gradient/.test(bgImageStyle);
|
|
29
|
+
axe.commons.color.incompleteData.set(
|
|
30
|
+
'bgColor',
|
|
31
|
+
hasGradient ? 'bgGradient' : 'bgImage'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return hasBgImage;
|
|
36
|
+
};
|
|
@@ -1,236 +1,188 @@
|
|
|
1
1
|
/* global axe, color, dom */
|
|
2
|
-
const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG'];
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @
|
|
4
|
+
* Returns background color for element
|
|
5
|
+
* Uses getBackgroundStack() to get all elements rendered underneath the current element,
|
|
6
|
+
* to help determine the composite background color.
|
|
7
|
+
*
|
|
8
|
+
* @method getBackgroundColor
|
|
9
|
+
* @memberof axe.commons.color
|
|
10
|
+
* @param {Element} elm Element to determine background color
|
|
11
|
+
* @param {Array} [bgElms=[]] elements to inspect
|
|
12
|
+
* @param {Boolean} [noScroll=false] should scroll
|
|
13
|
+
* @returns {Color}
|
|
10
14
|
*/
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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();
|
|
17
33
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
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);
|
|
27
43
|
}
|
|
28
|
-
return hasBgImage;
|
|
29
|
-
}
|
|
30
44
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* @private
|
|
34
|
-
* @param {Element} elm
|
|
35
|
-
* @return {Color}
|
|
36
|
-
*/
|
|
37
|
-
function getBgColor(elm, elmStyle) {
|
|
38
|
-
elmStyle = elmStyle || window.getComputedStyle(elm);
|
|
45
|
+
let bgColors = [];
|
|
46
|
+
let elmStack = color.getBackgroundStack(elm);
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
// Search the stack until we have an alpha === 1 background
|
|
49
|
+
(elmStack || []).some(bgElm => {
|
|
50
|
+
const bgElmStyle = window.getComputedStyle(bgElm);
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
let
|
|
45
|
-
bgColor.alpha = bgColor.alpha * opacity;
|
|
46
|
-
}
|
|
47
|
-
return bgColor;
|
|
48
|
-
}
|
|
52
|
+
// Get the background color
|
|
53
|
+
let bgColor = color.getOwnBackgroundColor(bgElmStyle);
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
var targetRect = targetElement.getClientRects()[0];
|
|
61
|
-
var obscuringElements = dom.shadowElementsFromPoint(
|
|
62
|
-
targetRect.left,
|
|
63
|
-
targetRect.top
|
|
64
|
-
);
|
|
65
|
-
if (obscuringElements) {
|
|
66
|
-
for (var i = 0; i < obscuringElements.length; i++) {
|
|
67
|
-
if (
|
|
68
|
-
obscuringElements[i] !== targetElement &&
|
|
69
|
-
obscuringElements[i] === bgNode
|
|
70
|
-
) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
55
|
+
if (
|
|
56
|
+
// abort if a node is partially obscured and obscuring element has a background
|
|
57
|
+
elmPartiallyObscured(elm, bgElm, bgColor) ||
|
|
58
|
+
// OR if the background elm is a graphic
|
|
59
|
+
color.elementHasImage(bgElm, bgElmStyle)
|
|
60
|
+
) {
|
|
61
|
+
bgColors = null;
|
|
62
|
+
bgElms.push(bgElm);
|
|
63
|
+
|
|
64
|
+
return true;
|
|
73
65
|
}
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Calculate alpha transparency of a background element obscuring the current node
|
|
79
|
-
* @private
|
|
80
|
-
* @param {Number} elmIndex
|
|
81
|
-
* @param {Array} elmStack
|
|
82
|
-
* @param {Element} originalElm
|
|
83
|
-
* @return {Number|undefined}
|
|
84
|
-
*/
|
|
85
|
-
function calculateObscuringAlpha(elmIndex, elmStack, originalElm) {
|
|
86
|
-
var totalAlpha = 0;
|
|
87
66
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// remove elements not contributing to the background
|
|
98
|
-
elmStack.splice(i, 1);
|
|
99
|
-
}
|
|
67
|
+
if (bgColor.alpha !== 0) {
|
|
68
|
+
// store elements contributing to the br color.
|
|
69
|
+
bgElms.push(bgElm);
|
|
70
|
+
bgColors.push(bgColor);
|
|
71
|
+
|
|
72
|
+
// Exit if the background is opaque
|
|
73
|
+
return bgColor.alpha === 1;
|
|
74
|
+
} else {
|
|
75
|
+
return false;
|
|
100
76
|
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (bgColors !== null && elmStack !== null) {
|
|
80
|
+
// Mix the colors together, on top of a default white
|
|
81
|
+
bgColors.push(new color.Color(255, 255, 255, 1));
|
|
82
|
+
var colors = bgColors.reduce(color.flattenColors);
|
|
83
|
+
return colors;
|
|
101
84
|
}
|
|
102
|
-
|
|
103
|
-
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
|
|
104
89
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
90
|
+
* Get all elements rendered underneath the current element,
|
|
91
|
+
* In the order they are displayed (front to back)
|
|
92
|
+
*
|
|
93
|
+
* @method getBackgroundStack
|
|
94
|
+
* @memberof axe.commons.color
|
|
107
95
|
* @param {Element} elm
|
|
108
|
-
* @
|
|
109
|
-
* @param {Object} bgColor
|
|
110
|
-
* @return {Boolean}
|
|
96
|
+
* @return {Array}
|
|
111
97
|
*/
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
98
|
+
color.getBackgroundStack = function getBackgroundStack(elm) {
|
|
99
|
+
let elmStack = color.filteredRectStack(elm);
|
|
100
|
+
|
|
101
|
+
if (elmStack === null) {
|
|
102
|
+
return null;
|
|
117
103
|
}
|
|
118
|
-
|
|
119
|
-
|
|
104
|
+
elmStack = includeMissingElements(elmStack, elm);
|
|
105
|
+
elmStack = dom.reduceToElementsBelowFloating(elmStack, elm);
|
|
106
|
+
elmStack = sortPageBackground(elmStack);
|
|
107
|
+
|
|
108
|
+
// Return all elements BELOW the current element, null if the element is undefined
|
|
109
|
+
let elmIndex = elmStack.indexOf(elm);
|
|
110
|
+
if (calculateObscuringElement(elmIndex, elmStack, elm)) {
|
|
111
|
+
// if the total of the elements above our element results in total obscuring, return null
|
|
112
|
+
axe.commons.color.incompleteData.set('bgColor', 'bgOverlap');
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return elmIndex !== -1 ? elmStack : null;
|
|
116
|
+
};
|
|
120
117
|
|
|
121
118
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* https://github.com/dequelabs/axe-core/issues/273
|
|
126
|
-
*
|
|
127
|
-
* @private
|
|
128
|
-
* @param {Array} elmStack
|
|
119
|
+
* Get filtered stack of block and inline elements, excluding line breaks
|
|
120
|
+
* @method filteredRectStack
|
|
121
|
+
* @memberof axe.commons.color
|
|
129
122
|
* @param {Element} elm
|
|
123
|
+
* @return {Array}
|
|
130
124
|
*/
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
INPUT: ['LABEL']
|
|
137
|
-
};
|
|
138
|
-
const tagArray = elmStack.map(elm => {
|
|
139
|
-
return elm.nodeName;
|
|
140
|
-
});
|
|
141
|
-
let bgNodes = elmStack;
|
|
142
|
-
for (let candidate in elementMap) {
|
|
143
|
-
// check that TR or LABEL has paired nodeName from elementMap, but don't expect elm to be that candidate
|
|
144
|
-
if (tagArray.includes(candidate)) {
|
|
145
|
-
for (let candidateIndex in elementMap[candidate]) {
|
|
146
|
-
if (candidate.hasOwnProperty(candidateIndex)) {
|
|
147
|
-
// look up the tree for a matching candidate
|
|
148
|
-
let ancestorMatch = axe.commons.dom.findUp(
|
|
149
|
-
elm,
|
|
150
|
-
elementMap[candidate][candidateIndex]
|
|
151
|
-
);
|
|
152
|
-
if (ancestorMatch && elmStack.indexOf(ancestorMatch) === -1) {
|
|
153
|
-
// found an ancestor not in elmStack, and it overlaps
|
|
154
|
-
let overlaps = axe.commons.dom.visuallyOverlaps(
|
|
155
|
-
elm.getBoundingClientRect(),
|
|
156
|
-
ancestorMatch
|
|
157
|
-
);
|
|
158
|
-
if (overlaps) {
|
|
159
|
-
// if target is in the elementMap, use its position.
|
|
160
|
-
bgNodes.splice(tagArray.indexOf(candidate) + 1, 0, ancestorMatch);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// nodeName matches value
|
|
164
|
-
// (such as LABEL, when matching itself. It should be in the list, but Phantom skips it)
|
|
165
|
-
if (
|
|
166
|
-
elm.nodeName === elementMap[candidate][candidateIndex] &&
|
|
167
|
-
tagArray.indexOf(elm.nodeName) === -1
|
|
168
|
-
) {
|
|
169
|
-
bgNodes.splice(tagArray.indexOf(candidate) + 1, 0, elm);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
125
|
+
color.filteredRectStack = function filteredRectStack(elm) {
|
|
126
|
+
const rectStack = color.getRectStack(elm);
|
|
127
|
+
|
|
128
|
+
if (rectStack && rectStack.length === 1) {
|
|
129
|
+
return rectStack[0];
|
|
174
130
|
}
|
|
175
|
-
return bgNodes;
|
|
176
|
-
}
|
|
177
131
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* @param {Array} elmStack
|
|
182
|
-
*/
|
|
183
|
-
function sortPageBackground(elmStack) {
|
|
184
|
-
let bodyIndex = elmStack.indexOf(document.body);
|
|
132
|
+
if (rectStack && rectStack.length > 1) {
|
|
133
|
+
const boundingStack = rectStack.shift();
|
|
134
|
+
let isSame;
|
|
185
135
|
|
|
186
|
-
|
|
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);
|
|
187
144
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
bgNodes.splice(elmStack.indexOf(document.documentElement), 1);
|
|
145
|
+
// iterating over arrays of DOMRects
|
|
146
|
+
rectStack.forEach((rectList, index) => {
|
|
147
|
+
if (index === 0) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// if the stacks are the same, use the first one. otherwise, return null.
|
|
151
|
+
let rectA = rectStack[index - 1],
|
|
152
|
+
rectB = rectStack[index];
|
|
197
153
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
color.getCoords = function(rect) {
|
|
212
|
-
let x, y;
|
|
213
|
-
if (rect.left > window.innerWidth) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (rect.top > window.innerHeight) {
|
|
217
|
-
return;
|
|
154
|
+
// if elements in clientRects are the same
|
|
155
|
+
// or the boundingClientRect contains the differing element, pass it
|
|
156
|
+
isSame =
|
|
157
|
+
rectA.every(
|
|
158
|
+
(element, elementIndex) => element === rectB[elementIndex]
|
|
159
|
+
) || boundingStack.includes(elm);
|
|
160
|
+
});
|
|
161
|
+
if (!isSame) {
|
|
162
|
+
axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscuring');
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
// pass the first stack if it wasn't partially covered
|
|
166
|
+
return rectStack[0];
|
|
218
167
|
}
|
|
219
|
-
x = Math.min(Math.ceil(rect.left + rect.width / 2), window.innerWidth - 1);
|
|
220
|
-
y = Math.min(Math.ceil(rect.top + rect.height / 2), window.innerHeight - 1);
|
|
221
168
|
|
|
222
|
-
|
|
169
|
+
// rect outside of viewport
|
|
170
|
+
axe.commons.color.incompleteData.set('bgColor', 'outsideViewport');
|
|
171
|
+
return null;
|
|
223
172
|
};
|
|
173
|
+
|
|
224
174
|
/**
|
|
225
175
|
* Get relevant stacks of block and inline elements, excluding line breaks
|
|
226
176
|
* @method getRectStack
|
|
227
177
|
* @memberof axe.commons.color
|
|
228
|
-
* @instance
|
|
229
178
|
* @param {Element} elm
|
|
230
179
|
* @return {Array}
|
|
231
180
|
*/
|
|
232
181
|
color.getRectStack = function(elm) {
|
|
233
|
-
|
|
182
|
+
const boundingCoords = axe.commons.color.centerPointOfRect(
|
|
183
|
+
elm.getBoundingClientRect()
|
|
184
|
+
);
|
|
185
|
+
|
|
234
186
|
if (!boundingCoords) {
|
|
235
187
|
return null;
|
|
236
188
|
}
|
|
@@ -253,7 +205,7 @@ color.getRectStack = function(elm) {
|
|
|
253
205
|
return rect.width && rect.width > 0;
|
|
254
206
|
})
|
|
255
207
|
.map(rect => {
|
|
256
|
-
|
|
208
|
+
const coords = axe.commons.color.centerPointOfRect(rect);
|
|
257
209
|
if (coords) {
|
|
258
210
|
return dom.shadowElementsFromPoint(coords.x, coords.y);
|
|
259
211
|
}
|
|
@@ -268,137 +220,208 @@ color.getRectStack = function(elm) {
|
|
|
268
220
|
filteredArr.splice(0, 0, boundingStack);
|
|
269
221
|
return filteredArr;
|
|
270
222
|
};
|
|
223
|
+
|
|
271
224
|
/**
|
|
272
|
-
*
|
|
273
|
-
* @method
|
|
274
|
-
* @
|
|
275
|
-
* @
|
|
276
|
-
* @
|
|
277
|
-
* @return {Array}
|
|
225
|
+
* Look at document and body elements for relevant background information
|
|
226
|
+
* @method sortPageBackground
|
|
227
|
+
* @private
|
|
228
|
+
* @param {Array} elmStack
|
|
229
|
+
* @returns {Array}
|
|
278
230
|
*/
|
|
279
|
-
|
|
280
|
-
let
|
|
281
|
-
|
|
282
|
-
// default case, elm.getBoundingClientRect()
|
|
283
|
-
return rectStack[0];
|
|
284
|
-
} else if (rectStack && rectStack.length > 1) {
|
|
285
|
-
let boundingStack = rectStack.shift();
|
|
286
|
-
let isSame;
|
|
287
|
-
// iterating over arrays of DOMRects
|
|
288
|
-
rectStack.forEach((rectList, index) => {
|
|
289
|
-
if (index === 0) {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
// if the stacks are the same, use the first one. otherwise, return null.
|
|
293
|
-
let rectA = rectStack[index - 1],
|
|
294
|
-
rectB = rectStack[index];
|
|
231
|
+
function sortPageBackground(elmStack) {
|
|
232
|
+
let bodyIndex = elmStack.indexOf(document.body);
|
|
233
|
+
let bgNodes = elmStack;
|
|
295
234
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
235
|
+
// Body can sometimes appear out of order in the stack:
|
|
236
|
+
// 1) Body is not the first element due to negative z-index elements
|
|
237
|
+
// 2) Elements are positioned outside of body's rect coordinates
|
|
238
|
+
// (see https://github.com/dequelabs/axe-core/issues/1456)
|
|
239
|
+
// In those instances we want to reinsert body back into the element stack
|
|
240
|
+
// when not using the root document element as the html canvas for bgcolor
|
|
241
|
+
// prettier-ignore
|
|
242
|
+
let sortBodyElement =
|
|
243
|
+
bodyIndex > 1 || // negative z-index elements
|
|
244
|
+
bodyIndex === -1; // element does not intersect with body
|
|
245
|
+
|
|
246
|
+
if (
|
|
247
|
+
sortBodyElement &&
|
|
248
|
+
!color.elementHasImage(document.documentElement) &&
|
|
249
|
+
color.getOwnBackgroundColor(
|
|
250
|
+
window.getComputedStyle(document.documentElement)
|
|
251
|
+
).alpha === 0
|
|
252
|
+
) {
|
|
253
|
+
// Only remove document.body if it was originally contained within the element stack
|
|
254
|
+
if (bodyIndex > 1) {
|
|
255
|
+
bgNodes.splice(bodyIndex, 1);
|
|
306
256
|
}
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
return null;
|
|
257
|
+
// Remove document element since body will be used for bgcolor
|
|
258
|
+
bgNodes.splice(elmStack.indexOf(document.documentElement), 1);
|
|
259
|
+
|
|
260
|
+
// Put the body background as the lowest element
|
|
261
|
+
bgNodes.push(document.body);
|
|
313
262
|
}
|
|
314
|
-
|
|
263
|
+
return bgNodes;
|
|
264
|
+
}
|
|
265
|
+
|
|
315
266
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
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
|
|
320
273
|
* @param {Element} elm
|
|
321
|
-
* @
|
|
274
|
+
* @returns {Array}
|
|
322
275
|
*/
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
+
}
|
|
327
322
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
elmStack = sortPageBackground(elmStack);
|
|
323
|
+
return bgNodes;
|
|
324
|
+
}
|
|
331
325
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Determine if element is partially overlapped, triggering a Can't Tell result
|
|
328
|
+
* @private
|
|
329
|
+
* @param {Element} elm
|
|
330
|
+
* @param {Element} bgElm
|
|
331
|
+
* @param {Object} bgColor
|
|
332
|
+
* @return {Boolean}
|
|
333
|
+
*/
|
|
334
|
+
function elmPartiallyObscured(elm, bgElm, bgColor) {
|
|
335
|
+
var obscured =
|
|
336
|
+
elm !== bgElm && !dom.visuallyContains(elm, bgElm) && bgColor.alpha !== 0;
|
|
337
|
+
if (obscured) {
|
|
338
|
+
axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscured');
|
|
338
339
|
}
|
|
339
|
-
return
|
|
340
|
-
}
|
|
340
|
+
return obscured;
|
|
341
|
+
}
|
|
341
342
|
|
|
342
343
|
/**
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
* @param
|
|
347
|
-
* @param
|
|
348
|
-
* @
|
|
349
|
-
* @return {Color} [description]
|
|
344
|
+
* Calculate alpha transparency of a background element obscuring the current node
|
|
345
|
+
* @private
|
|
346
|
+
* @param {Number} elmIndex
|
|
347
|
+
* @param {Array} elmStack
|
|
348
|
+
* @param {Element} originalElm
|
|
349
|
+
* @return {Number|undefined}
|
|
350
350
|
*/
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
351
|
+
function calculateObscuringElement(elmIndex, elmStack, originalElm) {
|
|
352
|
+
if (elmIndex > 0) {
|
|
353
|
+
// there are elements above our element, check if they contribute to the background
|
|
354
|
+
for (var i = elmIndex - 1; i >= 0; i--) {
|
|
355
|
+
let bgElm = elmStack[i];
|
|
356
|
+
if (contentOverlapping(originalElm, bgElm)) {
|
|
357
|
+
return true;
|
|
358
|
+
} else {
|
|
359
|
+
// remove elements not contributing to the background
|
|
360
|
+
elmStack.splice(i, 1);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
358
363
|
}
|
|
359
|
-
let bgColors = [];
|
|
360
|
-
let elmStack = color.getBackgroundStack(elm);
|
|
361
|
-
|
|
362
|
-
// Search the stack until we have an alpha === 1 background
|
|
363
|
-
(elmStack || []).some(bgElm => {
|
|
364
|
-
let bgElmStyle = window.getComputedStyle(bgElm);
|
|
365
|
-
|
|
366
|
-
// Get the background color
|
|
367
|
-
let bgColor = getBgColor(bgElm, bgElmStyle);
|
|
368
364
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
elmPartiallyObscured(elm, bgElm, bgColor) ||
|
|
372
|
-
// OR if the background elm is a graphic
|
|
373
|
-
elmHasImage(bgElm, bgElmStyle)
|
|
374
|
-
) {
|
|
375
|
-
bgColors = null;
|
|
376
|
-
bgElms.push(bgElm);
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
377
367
|
|
|
378
|
-
|
|
368
|
+
/**
|
|
369
|
+
* Determines overlap of node's content with a bgNode. Used for inline elements
|
|
370
|
+
* @private
|
|
371
|
+
* @param {Element} targetElement
|
|
372
|
+
* @param {Element} bgNode
|
|
373
|
+
* @return {Boolean}
|
|
374
|
+
*/
|
|
375
|
+
function contentOverlapping(targetElement, bgNode) {
|
|
376
|
+
// get content box of target element
|
|
377
|
+
// check to see if the current bgNode is overlapping
|
|
378
|
+
var targetRect = targetElement.getClientRects()[0];
|
|
379
|
+
var obscuringElements = dom.shadowElementsFromPoint(
|
|
380
|
+
targetRect.left,
|
|
381
|
+
targetRect.top
|
|
382
|
+
);
|
|
383
|
+
if (obscuringElements) {
|
|
384
|
+
for (var i = 0; i < obscuringElements.length; i++) {
|
|
385
|
+
if (
|
|
386
|
+
obscuringElements[i] !== targetElement &&
|
|
387
|
+
obscuringElements[i] === bgNode
|
|
388
|
+
) {
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
379
391
|
}
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
380
395
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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)/;
|
|
385
407
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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;
|
|
390
420
|
}
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
if (bgColors !== null && elmStack !== null) {
|
|
394
|
-
// Mix the colors together, on top of a default white
|
|
395
|
-
bgColors.push(new color.Color(255, 255, 255, 1));
|
|
396
|
-
var colors = bgColors.reduce(color.flattenColors);
|
|
397
|
-
return colors;
|
|
398
421
|
}
|
|
399
422
|
|
|
400
|
-
return
|
|
401
|
-
}
|
|
423
|
+
return document.documentElement;
|
|
424
|
+
}
|
|
402
425
|
|
|
403
426
|
/**
|
|
404
427
|
* Determines whether an element has a fully opaque background, whether solid color or an image
|
|
@@ -406,6 +429,9 @@ color.getBackgroundColor = function(elm, bgElms = [], noScroll = false) {
|
|
|
406
429
|
* @return {Boolean} false if the background is transparent, true otherwise
|
|
407
430
|
*/
|
|
408
431
|
dom.isOpaque = function(node) {
|
|
409
|
-
|
|
410
|
-
return
|
|
432
|
+
const style = window.getComputedStyle(node);
|
|
433
|
+
return (
|
|
434
|
+
color.elementHasImage(node, style) ||
|
|
435
|
+
color.getOwnBackgroundColor(style).alpha === 1
|
|
436
|
+
);
|
|
411
437
|
};
|