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.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/component_guide/accessibility-test.js +0 -6
  3. data/app/views/govuk_publishing_components/components/docs/machine_readable_metadata.yml +7 -0
  4. data/lib/govuk_publishing_components/presenters/machine_readable/dataset_schema.rb +34 -0
  5. data/lib/govuk_publishing_components/presenters/schema_org.rb +3 -0
  6. data/lib/govuk_publishing_components/version.rb +1 -1
  7. data/node_modules/axe-core/CHANGELOG.md +166 -2
  8. data/node_modules/axe-core/CONTRIBUTING.md +5 -5
  9. data/node_modules/axe-core/README.md +4 -4
  10. data/node_modules/axe-core/axe.d.ts +27 -11
  11. data/node_modules/axe-core/axe.js +9597 -2431
  12. data/node_modules/axe-core/axe.min.js +2 -2
  13. data/node_modules/axe-core/bower.json +1 -1
  14. data/node_modules/axe-core/doc/API.md +211 -128
  15. data/node_modules/axe-core/doc/accessibility-supported.md +1 -1
  16. data/node_modules/axe-core/doc/aria-supported.md +4 -13
  17. data/node_modules/axe-core/doc/backwards-compatibility-doc.md +93 -0
  18. data/node_modules/axe-core/doc/code-submission-guidelines.md +4 -4
  19. data/node_modules/axe-core/doc/developer-guide.md +27 -13
  20. data/node_modules/axe-core/doc/examples/chrome-debugging-protocol/package.json +5 -2
  21. data/node_modules/axe-core/doc/examples/jasmine/README.md +3 -5
  22. data/node_modules/axe-core/doc/examples/jasmine/karma.conf.js +29 -0
  23. data/node_modules/axe-core/doc/examples/jasmine/package.json +6 -5
  24. data/node_modules/axe-core/doc/examples/jest_react/README.md +1 -1
  25. data/node_modules/axe-core/doc/examples/jest_react/link.test.js +3 -3
  26. data/node_modules/axe-core/doc/examples/jest_react/package.json +9 -9
  27. data/node_modules/axe-core/doc/examples/jsdom/package.json +15 -0
  28. data/node_modules/axe-core/doc/examples/mocha/README.md +5 -7
  29. data/node_modules/axe-core/doc/examples/mocha/karma.conf.js +29 -0
  30. data/node_modules/axe-core/doc/examples/mocha/package.json +7 -6
  31. data/node_modules/axe-core/doc/examples/phantomjs/README.md +3 -3
  32. data/node_modules/axe-core/doc/examples/phantomjs/axe-phantom.js +4 -2
  33. data/node_modules/axe-core/doc/examples/phantomjs/package.json +3 -3
  34. data/node_modules/axe-core/doc/examples/puppeteer/package.json +5 -2
  35. data/node_modules/axe-core/doc/examples/qunit/README.md +2 -2
  36. data/node_modules/axe-core/doc/examples/qunit/package.json +2 -2
  37. data/node_modules/axe-core/doc/examples/test-examples.js +32 -0
  38. data/node_modules/axe-core/doc/plugins.md +10 -10
  39. data/node_modules/axe-core/doc/projects.md +12 -8
  40. data/node_modules/axe-core/doc/rule-descriptions.md +87 -79
  41. data/node_modules/axe-core/doc/rule-development.md +30 -2
  42. data/node_modules/axe-core/lib/checks/aria/allowed-attr.js +1 -1
  43. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.js +14 -0
  44. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.json +23 -0
  45. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.js +21 -0
  46. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.json +11 -0
  47. data/node_modules/axe-core/lib/checks/aria/required-attr.js +32 -7
  48. data/node_modules/axe-core/lib/checks/aria/required-children.js +40 -14
  49. data/node_modules/axe-core/lib/checks/aria/required-children.json +12 -1
  50. data/node_modules/axe-core/lib/checks/aria/required-parent.js +1 -1
  51. data/node_modules/axe-core/lib/checks/aria/unsupportedattr.js +1 -1
  52. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +50 -17
  53. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -1
  54. data/node_modules/axe-core/lib/checks/aria/valid-attr.js +1 -1
  55. data/node_modules/axe-core/lib/checks/color/color-contrast.js +2 -2
  56. data/node_modules/axe-core/lib/checks/forms/autocomplete-appropriate.js +4 -4
  57. data/node_modules/axe-core/lib/checks/forms/autocomplete-valid.js +1 -1
  58. data/node_modules/axe-core/lib/checks/forms/fieldset.json +1 -0
  59. data/node_modules/axe-core/lib/checks/forms/group-labelledby.json +1 -0
  60. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.js +16 -0
  61. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.json +11 -0
  62. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.js +12 -0
  63. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.json +11 -0
  64. data/node_modules/axe-core/lib/checks/keyboard/tabindex.js +6 -1
  65. data/node_modules/axe-core/lib/checks/label/alt-space-value.js +3 -2
  66. data/node_modules/axe-core/lib/checks/label/duplicate-img-label.js +18 -15
  67. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.js +13 -3
  68. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.json +4 -0
  69. data/node_modules/axe-core/lib/checks/label/multiple-label.js +22 -12
  70. data/node_modules/axe-core/lib/checks/label/multiple-label.json +1 -1
  71. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique-after.js +23 -0
  72. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.js +7 -0
  73. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.json +12 -0
  74. data/node_modules/axe-core/lib/checks/lists/listitem.js +1 -0
  75. data/node_modules/axe-core/lib/checks/lists/listitem.json +1 -1
  76. data/node_modules/axe-core/lib/checks/lists/only-listitems.js +0 -4
  77. data/node_modules/axe-core/lib/checks/mobile/css-orientation-lock.js +8 -6
  78. data/node_modules/axe-core/lib/checks/navigation/region.js +2 -19
  79. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.js +18 -0
  80. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.json +11 -0
  81. data/node_modules/axe-core/lib/checks/shared/exists.js +1 -1
  82. data/node_modules/axe-core/lib/checks/shared/exists.json +1 -1
  83. data/node_modules/axe-core/lib/checks/shared/has-alt.js +6 -4
  84. data/node_modules/axe-core/lib/checks/shared/non-empty-alt.js +1 -1
  85. data/node_modules/axe-core/lib/checks/tables/caption-faked.json +1 -1
  86. data/node_modules/axe-core/lib/checks/tables/td-has-header.js +5 -4
  87. data/node_modules/axe-core/lib/checks/tables/th-has-data-cells.js +19 -29
  88. data/node_modules/axe-core/lib/commons/aria/get-owned-virtual.js +1 -1
  89. data/node_modules/axe-core/lib/commons/aria/index.js +50 -46
  90. data/node_modules/axe-core/lib/commons/aria/is-accessible-ref.js +41 -37
  91. data/node_modules/axe-core/lib/commons/aria/label-virtual.js +2 -2
  92. data/node_modules/axe-core/lib/commons/aria/roles.js +1 -1
  93. data/node_modules/axe-core/lib/commons/aria/validate-attr-value.js +0 -1
  94. data/node_modules/axe-core/lib/commons/color/center-point-of-rect.js +30 -0
  95. data/node_modules/axe-core/lib/commons/color/contrast.js +7 -1
  96. data/node_modules/axe-core/lib/commons/color/element-has-image.js +36 -0
  97. data/node_modules/axe-core/lib/commons/color/get-background-color.js +332 -306
  98. data/node_modules/axe-core/lib/commons/color/get-foreground-color.js +35 -6
  99. data/node_modules/axe-core/lib/commons/color/get-own-background-color.js +22 -0
  100. data/node_modules/axe-core/lib/commons/dom/find-up.js +5 -5
  101. data/node_modules/axe-core/lib/commons/dom/get-scroll-offset.js +0 -1
  102. data/node_modules/axe-core/lib/commons/dom/get-tabbable-elements.js +1 -1
  103. data/node_modules/axe-core/lib/commons/dom/has-content-virtual.js +7 -5
  104. data/node_modules/axe-core/lib/commons/dom/is-focusable.js +8 -5
  105. data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +15 -2
  106. data/node_modules/axe-core/lib/commons/dom/is-in-text-block.js +1 -2
  107. data/node_modules/axe-core/lib/commons/dom/is-skip-link.js +45 -0
  108. data/node_modules/axe-core/lib/commons/dom/is-visible.js +43 -17
  109. data/node_modules/axe-core/lib/commons/dom/is-visual-content.js +0 -1
  110. data/node_modules/axe-core/lib/commons/dom/visually-contains.js +0 -1
  111. data/node_modules/axe-core/lib/commons/dom/visually-overlaps.js +0 -1
  112. data/node_modules/axe-core/lib/commons/forms/index.js +8 -0
  113. data/node_modules/axe-core/lib/commons/forms/is-aria-combobox.js +13 -0
  114. data/node_modules/axe-core/lib/commons/forms/is-aria-listbox.js +13 -0
  115. data/node_modules/axe-core/lib/commons/forms/is-aria-range.js +14 -0
  116. data/node_modules/axe-core/lib/commons/forms/is-aria-textbox.js +13 -0
  117. data/node_modules/axe-core/lib/commons/forms/is-native-select.js +13 -0
  118. data/node_modules/axe-core/lib/commons/forms/is-native-textbox.js +28 -0
  119. data/node_modules/axe-core/lib/commons/table/get-cell-position.js +2 -2
  120. data/node_modules/axe-core/lib/commons/table/get-headers.js +55 -11
  121. data/node_modules/axe-core/lib/commons/table/get-scope.js +1 -1
  122. data/node_modules/axe-core/lib/commons/table/is-data-table.js +0 -1
  123. data/node_modules/axe-core/lib/commons/table/to-grid.js +2 -2
  124. data/node_modules/axe-core/lib/commons/text/accessible-text-virtual.js +5 -5
  125. data/node_modules/axe-core/lib/commons/text/form-control-value.js +18 -30
  126. data/node_modules/axe-core/lib/commons/text/is-icon-ligature.js +210 -0
  127. data/node_modules/axe-core/lib/commons/text/is-valid-autocomplete.js +0 -1
  128. data/node_modules/axe-core/lib/commons/text/label-text.js +1 -1
  129. data/node_modules/axe-core/lib/commons/text/label-virtual.js +1 -1
  130. data/node_modules/axe-core/lib/commons/text/native-text-methods.js +1 -1
  131. data/node_modules/axe-core/lib/commons/text/subtree-text.js +3 -3
  132. data/node_modules/axe-core/lib/commons/text/unicode.js +15 -0
  133. data/node_modules/axe-core/lib/commons/text/visible-text-nodes.js +25 -0
  134. data/node_modules/axe-core/lib/commons/text/visible-virtual.js +1 -1
  135. data/node_modules/axe-core/lib/core/base/audit.js +90 -15
  136. data/node_modules/axe-core/lib/core/base/cache.js +33 -0
  137. data/node_modules/axe-core/lib/core/base/check.js +48 -1
  138. data/node_modules/axe-core/lib/core/base/context.js +15 -14
  139. data/node_modules/axe-core/lib/core/base/rule.js +223 -46
  140. data/node_modules/axe-core/lib/core/base/virtual-node/abstract-virtual-node.js +40 -0
  141. data/node_modules/axe-core/lib/core/base/virtual-node/serial-virtual-node.js +86 -0
  142. data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +85 -0
  143. data/node_modules/axe-core/lib/core/constants.js +10 -2
  144. data/node_modules/axe-core/lib/core/imports/index.js +28 -3
  145. data/node_modules/axe-core/lib/core/index.js +2 -4
  146. data/node_modules/axe-core/lib/core/public/configure.js +28 -1
  147. data/node_modules/axe-core/lib/core/public/run-rules.js +2 -0
  148. data/node_modules/axe-core/lib/core/public/run-virtual-rule.js +50 -0
  149. data/node_modules/axe-core/lib/core/public/run.js +13 -2
  150. data/node_modules/axe-core/lib/core/reporters/helpers/process-aggregate.js +1 -1
  151. data/node_modules/axe-core/lib/core/reporters/na.js +4 -0
  152. data/node_modules/axe-core/lib/core/reporters/raw-env.js +12 -0
  153. data/node_modules/axe-core/lib/core/reporters/raw.js +25 -1
  154. data/node_modules/axe-core/lib/core/utils/are-styles-set.js +4 -7
  155. data/node_modules/axe-core/lib/core/utils/assert.js +12 -0
  156. data/node_modules/axe-core/lib/core/utils/collect-results-from-frames.js +2 -2
  157. data/node_modules/axe-core/lib/core/utils/contains.js +27 -13
  158. data/node_modules/axe-core/lib/core/utils/css-parser.js +2 -0
  159. data/node_modules/axe-core/lib/core/utils/element-matches.js +5 -1
  160. data/node_modules/axe-core/lib/core/utils/escape-selector.js +1 -2
  161. data/node_modules/axe-core/lib/core/utils/flattened-tree.js +47 -61
  162. data/node_modules/axe-core/lib/core/utils/get-check-option.js +0 -1
  163. data/node_modules/axe-core/lib/core/utils/get-friendly-uri-end.js +1 -1
  164. data/node_modules/axe-core/lib/core/utils/get-node-attributes.js +21 -0
  165. data/node_modules/axe-core/lib/core/utils/get-scroll.js +39 -0
  166. data/node_modules/axe-core/lib/core/utils/get-selector.js +9 -6
  167. data/node_modules/axe-core/lib/core/utils/get-stylesheet-factory.js +51 -0
  168. data/node_modules/axe-core/lib/core/utils/get-xpath.js +0 -1
  169. data/node_modules/axe-core/lib/core/utils/is-hidden.js +16 -4
  170. data/node_modules/axe-core/lib/core/utils/is-html-element.js +5 -5
  171. data/node_modules/axe-core/lib/core/utils/is-shadow-root.js +3 -3
  172. data/node_modules/axe-core/lib/core/utils/memoize.js +17 -0
  173. data/node_modules/axe-core/lib/core/utils/parse-crossorigin-stylesheet.js +53 -0
  174. data/node_modules/axe-core/lib/core/utils/parse-sameorigin-stylesheet.js +96 -0
  175. data/node_modules/axe-core/lib/core/utils/parse-stylesheet.js +70 -0
  176. data/node_modules/axe-core/lib/core/utils/performance-timer.js +7 -2
  177. data/node_modules/axe-core/lib/core/utils/preload-cssom.js +77 -281
  178. data/node_modules/axe-core/lib/core/utils/preload.js +49 -23
  179. data/node_modules/axe-core/lib/core/utils/qsa.js +39 -50
  180. data/node_modules/axe-core/lib/core/utils/respondable.js +20 -3
  181. data/node_modules/axe-core/lib/core/utils/rule-should-run.js +0 -1
  182. data/node_modules/axe-core/lib/core/utils/scroll-state.js +12 -25
  183. data/node_modules/axe-core/lib/core/utils/select.js +12 -23
  184. data/node_modules/axe-core/lib/core/utils/uuid.js +1 -2
  185. data/node_modules/axe-core/lib/intro.stub +1 -1
  186. data/node_modules/axe-core/lib/misc/incomplete-fallback.json +1 -1
  187. data/node_modules/axe-core/lib/rules/aria-allowed-attr-matches.js +1 -1
  188. data/node_modules/axe-core/lib/rules/aria-form-field-name-matches.js +50 -0
  189. data/node_modules/axe-core/lib/rules/aria-has-attr-matches.js +1 -1
  190. data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +1 -1
  191. data/node_modules/axe-core/lib/rules/aria-input-field-name.json +13 -0
  192. data/node_modules/axe-core/lib/rules/aria-roledescription.json +12 -0
  193. data/node_modules/axe-core/lib/rules/aria-toggle-field-name.json +18 -0
  194. data/node_modules/axe-core/lib/rules/autocomplete-matches.js +14 -10
  195. data/node_modules/axe-core/lib/rules/avoid-inline-spacing.json +12 -0
  196. data/node_modules/axe-core/lib/rules/button-name.json +3 -5
  197. data/node_modules/axe-core/lib/rules/checkboxgroup.json +2 -1
  198. data/node_modules/axe-core/lib/rules/color-contrast-matches.js +37 -22
  199. data/node_modules/axe-core/lib/rules/duplicate-id-active-matches.js +1 -1
  200. data/node_modules/axe-core/lib/rules/duplicate-id-misc-matches.js +2 -2
  201. data/node_modules/axe-core/lib/rules/form-field-multiple-labels.json +1 -1
  202. data/node_modules/axe-core/lib/rules/html-has-lang.json +1 -0
  203. data/node_modules/axe-core/lib/rules/image-alt.json +1 -1
  204. data/node_modules/axe-core/lib/rules/img-redundant-alt.json +3 -3
  205. data/node_modules/axe-core/lib/rules/input-button-name.json +26 -0
  206. data/node_modules/axe-core/lib/rules/landmark-unique-matches.js +41 -0
  207. data/node_modules/axe-core/lib/rules/landmark-unique.json +13 -0
  208. data/node_modules/axe-core/lib/rules/link-name.json +5 -4
  209. data/node_modules/axe-core/lib/rules/meta-refresh.json +8 -1
  210. data/node_modules/axe-core/lib/rules/radiogroup.json +2 -1
  211. data/node_modules/axe-core/lib/rules/role-img-alt.json +18 -0
  212. data/node_modules/axe-core/lib/rules/scrollable-region-focusable-matches.js +30 -0
  213. data/node_modules/axe-core/lib/rules/scrollable-region-focusable.json +12 -0
  214. data/node_modules/axe-core/lib/rules/skip-link-matches.js +1 -1
  215. data/node_modules/axe-core/lib/rules/skip-link.json +1 -1
  216. data/node_modules/axe-core/lib/rules/video-description.json +3 -1
  217. data/node_modules/axe-core/locales/de.json +1 -5
  218. data/node_modules/axe-core/locales/es.json +773 -0
  219. data/node_modules/axe-core/locales/fr.json +15 -19
  220. data/node_modules/axe-core/locales/ja.json +65 -11
  221. data/node_modules/axe-core/locales/ko.json +777 -0
  222. data/node_modules/axe-core/locales/nl.json +35 -35
  223. data/node_modules/axe-core/locales/pt_BR.json +773 -0
  224. data/node_modules/axe-core/package.json +56 -52
  225. data/node_modules/axe-core/sri-history.json +20 -0
  226. data/node_modules/axe-core/typings/axe-core/axe-core-tests.ts +5 -15
  227. data/node_modules/govuk-frontend/package.json +10 -10
  228. metadata +62 -4
  229. data/node_modules/axe-core/doc/axelogo2018.png +0 -0
  230. 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(axe._tree[0], thing);
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(axe._tree[0], node);
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.attributes;
191
+ var nodeAttributes = axe.utils.getNodeAttributes(node);
192
192
  var ariaAttributes = [];
193
193
 
194
194
  /* Get all aria-attributes defined for this node */
@@ -10,7 +10,6 @@
10
10
  * @return {Boolean}
11
11
  */
12
12
  aria.validateAttrValue = function validateAttrValue(node, attr) {
13
- /*eslint complexity: ["error",17]*/
14
13
  'use strict';
15
14
  var matches,
16
15
  list,
@@ -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
- this.alpha = parseFloat(match[4]);
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
- * Reports if an element has a background image or gradient
6
- * @private
7
- * @param {Element} elm
8
- * @param {Object|null} style
9
- * @return {Boolean}
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 elmHasImage(elm, style) {
12
- var nodeName = elm.nodeName.toUpperCase();
13
- if (graphicNodes.includes(nodeName)) {
14
- axe.commons.color.incompleteData.set('bgColor', 'imgNode');
15
- return true;
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
- style = style || window.getComputedStyle(elm);
19
- var bgImageStyle = style.getPropertyValue('background-image');
20
- var hasBgImage = bgImageStyle !== 'none';
21
- if (hasBgImage) {
22
- var hasGradient = /gradient/.test(bgImageStyle);
23
- axe.commons.color.incompleteData.set(
24
- 'bgColor',
25
- hasGradient ? 'bgGradient' : 'bgImage'
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
- * Returns the non-alpha-blended background color of an element
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
- let bgColor = new color.Color();
41
- bgColor.parseRgbString(elmStyle.getPropertyValue('background-color'));
48
+ // Search the stack until we have an alpha === 1 background
49
+ (elmStack || []).some(bgElm => {
50
+ const bgElmStyle = window.getComputedStyle(bgElm);
42
51
 
43
- if (bgColor.alpha !== 0) {
44
- let opacity = elmStyle.getPropertyValue('opacity');
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
- * Determines overlap of node's content with a bgNode. Used for inline elements
52
- * @private
53
- * @param {Element} targetElement
54
- * @param {Element} bgNode
55
- * @return {Boolean}
56
- */
57
- function contentOverlapping(targetElement, bgNode) {
58
- // get content box of target element
59
- // check to see if the current bgNode is overlapping
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
- if (elmIndex > 0) {
89
- // there are elements above our element, check if they contribute to the background
90
- for (var i = elmIndex - 1; i >= 0; i--) {
91
- let bgElm = elmStack[i];
92
- let bgElmStyle = window.getComputedStyle(bgElm);
93
- let bgColor = getBgColor(bgElm, bgElmStyle);
94
- if (bgColor.alpha && contentOverlapping(originalElm, bgElm)) {
95
- totalAlpha += bgColor.alpha;
96
- } else {
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
- return totalAlpha;
103
- }
85
+
86
+ return null;
87
+ };
88
+
104
89
  /**
105
- * Determine if element is partially overlapped, triggering a Can't Tell result
106
- * @private
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
- * @param {Element} bgElm
109
- * @param {Object} bgColor
110
- * @return {Boolean}
96
+ * @return {Array}
111
97
  */
112
- function elmPartiallyObscured(elm, bgElm, bgColor) {
113
- var obscured =
114
- elm !== bgElm && !dom.visuallyContains(elm, bgElm) && bgColor.alpha !== 0;
115
- if (obscured) {
116
- axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscured');
98
+ color.getBackgroundStack = function getBackgroundStack(elm) {
99
+ let elmStack = color.filteredRectStack(elm);
100
+
101
+ if (elmStack === null) {
102
+ return null;
117
103
  }
118
- return obscured;
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
- * Include nodes missing from initial gathering because
123
- * document.elementsFromPoint misses some elements we need
124
- * i.e. TR is missing from table elementStack and leaves out bgColor
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 includeMissingElements(elmStack, elm) {
132
- /*eslint max-depth:["error",7]*/
133
- const elementMap = {
134
- TD: ['TR', 'TBODY'],
135
- TH: ['TR', 'THEAD'],
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
- * Look at document and body elements for relevant background information
180
- * @private
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
- let bgNodes = elmStack;
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
- if (
189
- // Check that the body background is the page's background
190
- bodyIndex > 1 && // only if there are negative z-index elements
191
- !elmHasImage(document.documentElement) &&
192
- getBgColor(document.documentElement).alpha === 0
193
- ) {
194
- // Remove body and html from it's current place
195
- bgNodes.splice(bodyIndex, 1);
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
- // Put the body background as the lowest element
199
- bgNodes.push(document.body);
200
- }
201
- return bgNodes;
202
- }
203
- /**
204
- * Get coordinates for an element's client rects or bounding client rect
205
- * @method getCoords
206
- * @memberof axe.commons.color
207
- * @instance
208
- * @param {DOMRect} rect
209
- * @return {Object}
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
- return { x, y };
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
- let boundingCoords = color.getCoords(elm.getBoundingClientRect());
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
- let coords = color.getCoords(rect);
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
- * Get filtered stack of block and inline elements, excluding line breaks
273
- * @method filteredRectStack
274
- * @memberof axe.commons.color
275
- * @instance
276
- * @param {Element} elm
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
- color.filteredRectStack = function(elm) {
280
- let rectStack = color.getRectStack(elm);
281
- if (rectStack && rectStack.length === 1) {
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
- // if elements in clientRects are the same
297
- // or the boundingClientRect contains the differing element, pass it
298
- isSame =
299
- rectA.every(function(element, elementIndex) {
300
- return element === rectB[elementIndex];
301
- }) || boundingStack.includes(elm);
302
- });
303
- if (!isSame) {
304
- axe.commons.color.incompleteData.set('bgColor', 'elmPartiallyObscuring');
305
- return null;
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
- // pass the first stack if it wasn't partially covered
308
- return rectStack[0];
309
- } else {
310
- // rect outside of viewport
311
- axe.commons.color.incompleteData.set('bgColor', 'outsideViewport');
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
- * Get all elements rendered underneath the current element, In the order they are displayed (front to back)
317
- * @method getBackgroundStack
318
- * @memberof axe.commons.color
319
- * @instance
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
- * @return {Array}
274
+ * @returns {Array}
322
275
  */
323
- color.getBackgroundStack = function(elm) {
324
- let elmStack = color.filteredRectStack(elm);
325
- if (elmStack === null) {
326
- return null;
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
- elmStack = includeMissingElements(elmStack, elm);
329
- elmStack = dom.reduceToElementsBelowFloating(elmStack, elm);
330
- elmStack = sortPageBackground(elmStack);
323
+ return bgNodes;
324
+ }
331
325
 
332
- // Return all elements BELOW the current element, null if the element is undefined
333
- let elmIndex = elmStack.indexOf(elm);
334
- if (calculateObscuringAlpha(elmIndex, elmStack, elm) >= 0.99) {
335
- // if the total of the elements above our element results in total obscuring, return null
336
- axe.commons.color.incompleteData.set('bgColor', 'bgOverlap');
337
- return null;
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 elmIndex !== -1 ? elmStack : null;
340
- };
340
+ return obscured;
341
+ }
341
342
 
342
343
  /**
343
- * Returns background color for element
344
- * Uses color.getBackgroundStack() to get all elements rendered underneath the current element to
345
- * help determine the background color.
346
- * @param {Element} elm Element to determine background color
347
- * @param {Array} [bgElms=[]] [description]
348
- * @param {Boolean} [noScroll=false] [description]
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
- color.getBackgroundColor = function(elm, bgElms = [], noScroll = false) {
352
- if (noScroll !== true) {
353
- // Avoid scrolling overflow:hidden containers, by only aligning to top
354
- // when not doing so would move the center point above the viewport top.
355
- const clientHeight = elm.getBoundingClientRect().height;
356
- const alignToTop = clientHeight - 2 >= window.innerHeight * 2;
357
- elm.scrollIntoView(alignToTop);
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
- if (
370
- // abort if a node is partially obscured and obscuring element has a background
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
- return true;
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
- if (bgColor.alpha !== 0) {
382
- // store elements contributing to the br color.
383
- bgElms.push(bgElm);
384
- bgColors.push(bgColor);
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
- // Exit if the background is opaque
387
- return bgColor.alpha === 1;
388
- } else {
389
- return false;
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 null;
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
- let style = window.getComputedStyle(node);
410
- return elmHasImage(node, style) || getBgColor(node, style).alpha === 1;
432
+ const style = window.getComputedStyle(node);
433
+ return (
434
+ color.elementHasImage(node, style) ||
435
+ color.getOwnBackgroundColor(style).alpha === 1
436
+ );
411
437
  };