govuk_publishing_components 21.16.3 → 21.17.0

Sign up to get free protection for your applications and to get access to all the features.
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
  };