- commit
- c8f3b94515ea3dd34c3599130f9d9620e2aa250f
- parent
- f329fa2407c271d5eb3cde33ab0b12617ce5e095
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2018-02-08 14:53
build
Diffstat
| M | babel.js | 3420 | ++++++++++++++++++++++++++++++------------------------------ |
1 files changed, 1710 insertions, 1710 deletions
diff --git a/babel.js b/babel.js
@@ -1,2004 +1,1641 @@ 1 1 (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){2 -1 /*!3 -1 calcNames 1.2, compute the Name and Description property values for a DOM node4 -1 Returns an object with 'name' and 'desc' properties.5 -1 Authored by Bryan Garaventa plus contrabutions by Tobias Bengfort6 -1 Distributed under the terms of the Open Source Initiative OSI - MIT License7 -1 */-1 2 // Copyright 2012 Google Inc. -1 3 // -1 4 // Licensed under the Apache License, Version 2.0 (the "License"); -1 5 // you may not use this file except in compliance with the License. -1 6 // You may obtain a copy of the License at -1 7 // -1 8 // http://www.apache.org/licenses/LICENSE-2.0 -1 9 // -1 10 // Unless required by applicable law or agreed to in writing, software -1 11 // distributed under the License is distributed on an "AS IS" BASIS, -1 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 13 // See the License for the specific language governing permissions and -1 14 // limitations under the License. 8 159 -1 var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {10 -1 if (!node || node.nodeType !== 1) {11 -1 return;12 -1 }-1 16 goog.require('axs.browserUtils'); -1 17 goog.require('axs.color'); -1 18 goog.require('axs.color.Color'); -1 19 goog.require('axs.constants'); -1 20 goog.require('axs.dom'); 13 2114 -1 var trim = function(str) {15 -1 if (typeof str !== 'string') {16 -1 return '';17 -1 }-1 22 goog.provide('axs.utils'); 18 2319 -1 return str.replace(/^\s+|\s+$/g, '');20 -1 };-1 24 /** -1 25 * @const -1 26 * @type {string} -1 27 */ -1 28 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR = -1 29 'input:not([type=hidden]):not([disabled]),' + -1 30 'select:not([disabled]),' + -1 31 'textarea:not([disabled]),' + -1 32 'button:not([disabled]),' + -1 33 'a[href],' + -1 34 'iframe,' + -1 35 '[tabindex]'; 21 3622 -1 var walkDOM = function(node, fn, refNode) {23 -1 if (!node) {24 -1 return;25 -1 }26 -1 fn(node);-1 37 /** -1 38 * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label -1 39 * @const -1 40 * @type {string} -1 41 */ -1 42 axs.utils.LABELABLE_ELEMENTS_SELECTOR = -1 43 'button,' + -1 44 'input:not([type=hidden]),' + -1 45 'keygen,' + -1 46 'meter,' + -1 47 'output,' + -1 48 'progress,' + -1 49 'select,' + -1 50 'textarea'; 27 5128 -1 if (!isException(node, refNode)) {29 -1 node = node.firstChild;30 5231 -1 while (node) {32 -1 walkDOM(node, fn, refNode);33 -1 node = node.nextSibling;34 -1 }35 -1 }36 -1 };-1 53 /** -1 54 * @param {Element} element -1 55 * @return {boolean} -1 56 */ -1 57 axs.utils.elementIsTransparent = function(element) { -1 58 return element.style.opacity == '0'; -1 59 }; 37 6038 -1 var isFocusable = function(node) {39 -1 var nodeName = node.nodeName.toLowerCase();-1 61 /** -1 62 * @param {Element} element -1 63 * @return {boolean} -1 64 */ -1 65 axs.utils.elementHasZeroArea = function(element) { -1 66 var rect = element.getBoundingClientRect(); -1 67 var width = rect.right - rect.left; -1 68 var height = rect.top - rect.bottom; -1 69 if (!width || !height) -1 70 return true; -1 71 return false; -1 72 }; 40 7341 -1 if (node.getAttribute('tabindex')) {42 -1 return true;43 -1 }44 -1 if (nodeName === 'a' && node.getAttribute('href')) {45 -1 return true;46 -1 }47 -1 if (['input', 'select', 'button'].indexOf(nodeName) !== -1 && node.getAttribute('type') !== 'hidden') {48 -1 return true;49 -1 }50 -1 return false;51 -1 };-1 74 /** -1 75 * @param {Element} element -1 76 * @return {boolean} -1 77 */ -1 78 axs.utils.elementIsOutsideScrollArea = function(element) { -1 79 var parent = axs.dom.parentElement(element); 52 8053 -1 var isException = function(node, refNode) {54 -1 if (!refNode || !node || refNode.nodeType !== 1 || node.nodeType !== 1) {55 -1 return false;56 -1 }-1 81 var defaultView = element.ownerDocument.defaultView; -1 82 while (parent != defaultView.document.body) { -1 83 if (axs.utils.isClippedBy(element, parent)) -1 84 return true; 57 8558 -1 var list1 = {59 -1 roles: ['link', 'button', 'checkbox', 'option', 'radio', 'switch', 'tab', 'treeitem', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'cell', 'columnheader', 'rowheader', 'tooltip', 'heading'],60 -1 tags: ['a', 'button', 'summary', 'input', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'menuitem', 'option', 'td', 'th']61 -1 };-1 86 if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent)) -1 87 return false; 62 8863 -1 var list2 = {64 -1 roles: ['application', 'alert', 'log', 'marquee', 'timer', 'alertdialog', 'dialog', 'banner', 'complementary', 'form', 'main', 'navigation', 'region', 'search', 'article', 'document', 'feed', 'figure', 'img', 'math', 'toolbar', 'menu', 'menubar', 'grid', 'listbox', 'radiogroup', 'textbox', 'searchbox', 'spinbutton', 'scrollbar', 'slider', 'tablist', 'tabpanel', 'tree', 'treegrid', 'separator'],65 -1 tags: ['article', 'aside', 'body', 'select', 'datalist', 'optgroup', 'dialog', 'figure', 'footer', 'form', 'header', 'hr', 'img', 'textarea', 'input', 'main', 'math', 'menu', 'nav', 'section']66 -1 };-1 89 parent = axs.dom.parentElement(parent); -1 90 } 67 9168 -1 var list3 = {69 -1 roles: ['combobox', 'term', 'definition', 'directory', 'list', 'group', 'note', 'status', 'table', 'rowgroup', 'row', 'contentinfo'],70 -1 tags: ['dl', 'ul', 'ol', 'dd', 'details', 'output', 'table', 'thead', 'tbody', 'tfoot', 'tr']71 -1 };-1 92 return !axs.utils.canScrollTo(element, defaultView.document.body); -1 93 }; 72 9473 -1 var inList = function(node, list) {74 -1 var role = node.getAttribute('role');75 -1 var tag = node.nodeName.toLowerCase();76 -1 return (77 -1 list.roles.indexOf(role) >= 0 ||78 -1 (!role && list2.tags.indexOf(tag) >= 0)79 -1 );80 -1 };-1 95 /** -1 96 * Checks whether it's possible to scroll to the given element within the given container. -1 97 * Assumes that |container| is an ancestor of |element|. -1 98 * If |container| cannot be scrolled, returns True if the element is within its bounding client -1 99 * rect. -1 100 * @param {Element} element -1 101 * @param {Element} container -1 102 * @return {boolean} True iff it's possible to scroll to |element| within |container|. -1 103 */ -1 104 axs.utils.canScrollTo = function(element, container) { -1 105 var rect = element.getBoundingClientRect(); -1 106 var containerRect = container.getBoundingClientRect(); -1 107 if (container == container.ownerDocument.body) { -1 108 var absoluteTop = containerRect.top; -1 109 var absoluteLeft = containerRect.left; -1 110 } else { -1 111 var absoluteTop = containerRect.top - container.scrollTop; -1 112 var absoluteLeft = containerRect.left - container.scrollLeft; -1 113 } -1 114 var containerScrollArea = -1 115 { top: absoluteTop, -1 116 bottom: absoluteTop + container.scrollHeight, -1 117 left: absoluteLeft, -1 118 right: absoluteLeft + container.scrollWidth }; 81 11982 -1 if (inList(node, list2)) {83 -1 return true;84 -1 } else if (inList(node, list3)) {85 -1 if (node === refNode) {86 -1 return !isFocusable(node);87 -1 } else {88 -1 return !inList(refNode, list1);89 -1 }90 -1 } else {91 -1 return false;92 -1 }93 -1 };-1 120 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top || -1 121 rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) { -1 122 return false; -1 123 } 94 12495 -1 var isHidden = function(node, refNode) {96 -1 if (node.nodeType !== 1 || node == refNode) {97 -1 return false;98 -1 }-1 125 var defaultView = element.ownerDocument.defaultView; -1 126 var style = defaultView.getComputedStyle(container); 99 127100 -1 if (node.getAttribute('aria-hidden') === 'true') {101 -1 return true;102 -1 }-1 128 if (rect.left > containerRect.right || rect.top > containerRect.bottom) { -1 129 return (style.overflow == 'scroll' || style.overflow == 'auto' || -1 130 container instanceof defaultView.HTMLBodyElement); -1 131 } 103 132104 -1 var style = {};105 -1 if (document.defaultView && document.defaultView.getComputedStyle) {106 -1 style = document.defaultView.getComputedStyle(node, '');107 -1 } else if (node.currentStyle) {108 -1 style = node.currentStyle;109 -1 }110 -1 if (style['display'] === 'none' || style['visibility'] === 'hidden') {111 -1 return true;112 -1 }-1 133 return true; -1 134 }; 113 135114 -1 return false;115 -1 };-1 136 /** -1 137 * Checks whether the given element is clipped by the given container. -1 138 * Assumes that |container| is an ancestor of |element|. -1 139 * @param {Element} element -1 140 * @param {Element} container -1 141 * @return {boolean} True iff |element| is clipped by |container|. -1 142 */ -1 143 axs.utils.isClippedBy = function(element, container) { -1 144 var rect = element.getBoundingClientRect(); -1 145 var containerRect = container.getBoundingClientRect(); -1 146 var containerTop = containerRect.top; -1 147 var containerLeft = containerRect.left; -1 148 var containerScrollArea = -1 149 { top: containerTop - container.scrollTop, -1 150 bottom: containerTop - container.scrollTop + container.scrollHeight, -1 151 left: containerLeft - container.scrollLeft, -1 152 right: containerLeft - container.scrollLeft + container.scrollWidth }; 116 153117 -1 var getCSSText = function(node, refNode) {118 -1 if (node.nodeType !== 1 || node == refNode || ['input', 'select', 'textarea', 'img', 'iframe'].indexOf(node.nodeName.toLowerCase()) !== -1) {119 -1 return {before: '', after: ''};120 -1 }-1 154 var defaultView = element.ownerDocument.defaultView; -1 155 var style = defaultView.getComputedStyle(container); 121 156122 -1 var getText = function(node, position) {123 -1 var text = document.defaultView.getComputedStyle(node, position).getPropertyValue('content').replace(/^\"|\"$/g, '');124 -1 if (!text || text === 'none') {125 -1 return '';126 -1 } else {127 -1 return text;128 -1 }129 -1 };-1 157 if ((rect.right < containerRect.left || rect.bottom < containerRect.top || -1 158 rect.left > containerRect.right || rect.top > containerRect.bottom) && -1 159 style.overflow == 'hidden') { -1 160 return true; -1 161 } 130 162131 -1 if (document.defaultView && document.defaultView.getComputedStyle) {132 -1 return {133 -1 before: getText(node, ':before'),134 -1 after: getText(node, ':after')135 -1 };136 -1 } else {137 -1 return {before: '', after: ''};138 -1 }139 -1 };-1 163 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top) -1 164 return (style.overflow != 'visible'); 140 165141 -1 var hasParentLabel = function(node, noLabel, refNode) {142 -1 while (node && node !== refNode) {143 -1 node = node.parentNode;-1 166 return false; -1 167 }; 144 168145 -1 if (node.getAttribute) {146 -1 if (['presentation', 'none'].indexOf(node.getAttribute('role')) === -1) {147 -1 if (!noLabel && node.getAttribute('aria-label')) {148 -1 return true;149 -1 }150 -1 if (isHidden(node, refNode)) {151 -1 return true;152 -1 }153 -1 }154 -1 }155 -1 }-1 169 /** -1 170 * @param {Node} ancestor A potential ancestor of |node|. -1 171 * @param {Node} node -1 172 * @return {boolean} true if |ancestor| is an ancestor of |node| (including -1 173 * |ancestor| === |node|). -1 174 */ -1 175 axs.utils.isAncestor = function(ancestor, node) { -1 176 if (node == null) -1 177 return false; -1 178 if (node === ancestor) -1 179 return true; 156 180157 -1 return false;158 -1 };-1 181 var parentNode = axs.dom.composedParentNode(node); -1 182 return axs.utils.isAncestor(ancestor, parentNode); -1 183 }; 159 184160 -1 var walk = function(refNode, stop, skip) {161 -1 var fullName = '';162 -1 var nodes = [];163 -1 var cssOP = {164 -1 before: '',165 -1 after: ''166 -1 };-1 185 /** -1 186 * @param {Element} element -1 187 * @return {Array.<Element>} An array of any non-transparent elements which -1 188 * overlap the given element. -1 189 */ -1 190 axs.utils.overlappingElements = function(element) { -1 191 if (axs.utils.elementHasZeroArea(element)) -1 192 return null; 167 193168 -1 if (nodes.indexOf(refNode) === -1) {169 -1 nodes.push(refNode);170 -1 cssOP = getCSSText(refNode, null);-1 194 var overlappingElements = []; -1 195 var clientRects = element.getClientRects(); -1 196 for (var i = 0; i < clientRects.length; i++) { -1 197 var rect = clientRects[i]; -1 198 var center_x = (rect.left + rect.right) / 2; -1 199 var center_y = (rect.top + rect.bottom) / 2; -1 200 var elementAtPoint = document.elementFromPoint(center_x, center_y); 171 201172 -1 // Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips173 -1 if (preventVisualARIASelfCSSRef) {174 -1 if (cssOP.before.indexOf(' [ARIA] ') !== -1 || cssOP.before.indexOf(' aria-') !== -1)175 -1 cssOP.before = '';176 -1 if (cssOP.after.indexOf(' [ARIA] ') !== -1 || cssOP.after.indexOf(' aria-') !== -1)177 -1 cssOP.after = '';178 -1 }179 -1 }-1 202 if (elementAtPoint == null || elementAtPoint == element || -1 203 axs.utils.isAncestor(elementAtPoint, element) || -1 204 axs.utils.isAncestor(element, elementAtPoint)) { -1 205 continue; -1 206 } 180 207181 -1 walkDOM(refNode, function(node) {182 -1 if (skip || !node || (isHidden(node, refNode))) {183 -1 return;184 -1 }-1 208 var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null); -1 209 if (!overlappingElementStyle) -1 210 continue; 185 211186 -1 var name = '';187 -1 var cssO = {188 -1 before: '',189 -1 after: ''190 -1 };-1 212 var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle, -1 213 elementAtPoint); -1 214 if (overlappingElementBg && overlappingElementBg.alpha > 0 && -1 215 overlappingElements.indexOf(elementAtPoint) < 0) { -1 216 overlappingElements.push(elementAtPoint); -1 217 } -1 218 } 191 219192 -1 var parent = refNode === node ? node : node.parentNode;193 -1 if (nodes.indexOf(parent) === -1) {194 -1 nodes.push(parent);195 -1 cssO = getCSSText(parent, refNode);-1 220 return overlappingElements; -1 221 }; 196 222197 -1 // Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips198 -1 if (preventVisualARIASelfCSSRef) {199 -1 if (cssO.before.indexOf(' [ARIA] ') !== -1 || cssO.before.indexOf(' aria-') !== -1)200 -1 cssO.before = '';201 -1 if (cssO.after.indexOf(' [ARIA] ') !== -1 || cssO.after.indexOf(' aria-') !== -1)202 -1 cssO.after = '';203 -1 }-1 223 /** -1 224 * @param {Element} element -1 225 * @return {boolean} -1 226 */ -1 227 axs.utils.elementIsHtmlControl = function(element) { -1 228 var defaultView = element.ownerDocument.defaultView; 204 229205 -1 }-1 230 // HTML control -1 231 if (element instanceof defaultView.HTMLButtonElement) -1 232 return true; -1 233 if (element instanceof defaultView.HTMLInputElement) -1 234 return true; -1 235 if (element instanceof defaultView.HTMLSelectElement) -1 236 return true; -1 237 if (element instanceof defaultView.HTMLTextAreaElement) -1 238 return true; 206 239207 -1 if (node.nodeType === 1) {208 -1 var aLabelledby = node.getAttribute('aria-labelledby') || '';209 -1 var aLabel = node.getAttribute('aria-label') || '';210 -1 var nTitle = node.getAttribute('title') || '';211 -1 var rolePresentation = ['presentation', 'none'].indexOf(node.getAttribute('role')) !== -1;-1 240 return false; -1 241 }; 212 242213 -1 if (!node.firstChild || (node == refNode && (aLabelledby || aLabel)) || (node.firstChild && node != refNode && aLabel)) {214 -1 if (!stop && node === refNode && aLabelledby) {215 -1 if (!rolePresentation) {216 -1 var ids = aLabelledby.split(/\s+/);217 -1 var parts = [];-1 243 /** -1 244 * @param {Element} element -1 245 * @return {boolean} -1 246 */ -1 247 axs.utils.elementIsAriaWidget = function(element) { -1 248 if (element.hasAttribute('role')) { -1 249 var roleValue = element.getAttribute('role'); -1 250 // TODO is this correct? -1 251 if (roleValue) { -1 252 var role = axs.constants.ARIA_ROLES[roleValue]; -1 253 if (role && 'widget' in role['allParentRolesSet']) -1 254 return true; -1 255 } -1 256 } -1 257 return false; -1 258 }; 218 259219 -1 for (var i = 0; i < ids.length; i++) {220 -1 var element = document.getElementById(ids[i]);221 -1 parts.push(walk(element, true, skip));222 -1 }223 -1 name = parts.join(' ');224 -1 }-1 260 /** -1 261 * @param {Element} element -1 262 * @return {boolean} -1 263 */ -1 264 axs.utils.elementIsVisible = function(element) { -1 265 if (axs.utils.elementIsTransparent(element)) -1 266 return false; -1 267 if (axs.utils.elementHasZeroArea(element)) -1 268 return false; -1 269 if (axs.utils.elementIsOutsideScrollArea(element)) -1 270 return false; 225 271226 -1 if (name || rolePresentation) {227 -1 skip = true;228 -1 }229 -1 }-1 272 var overlappingElements = axs.utils.overlappingElements(element); -1 273 if (overlappingElements.length) -1 274 return false; 230 275231 -1 /*!@ Add values of custom controls here if recursive controls with values */-1 276 return true; -1 277 }; 232 278233 -1 if (!name && !rolePresentation && aLabel) {234 -1 name = aLabel;-1 279 /** -1 280 * @param {CSSStyleDeclaration} style -1 281 * @return {boolean} -1 282 */ -1 283 axs.utils.isLargeFont = function(style) { -1 284 var fontSize = style.fontSize; -1 285 var bold = style.fontWeight == 'bold'; -1 286 var matches = fontSize.match(/(\d+)px/); -1 287 if (matches) { -1 288 var fontSizePx = parseInt(matches[1], 10); -1 289 var bodyStyle = window.getComputedStyle(document.body, null); -1 290 var bodyFontSize = bodyStyle.fontSize; -1 291 matches = bodyFontSize.match(/(\d+)px/); -1 292 if (matches) { -1 293 var bodyFontSizePx = parseInt(matches[1], 10); -1 294 var boldLarge = bodyFontSizePx * 1.2; -1 295 var large = bodyFontSizePx * 1.5; -1 296 } else { -1 297 var boldLarge = 19.2; -1 298 var large = 24; -1 299 } -1 300 return (bold && fontSizePx >= boldLarge || fontSizePx >= large); -1 301 } -1 302 matches = fontSize.match(/(\d+)em/); -1 303 if (matches) { -1 304 var fontSizeEm = parseInt(matches[1], 10); -1 305 if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5) -1 306 return true; -1 307 return false; -1 308 } -1 309 matches = fontSize.match(/(\d+)%/); -1 310 if (matches) { -1 311 var fontSizePercent = parseInt(matches[1], 10); -1 312 if (bold && fontSizePercent >= 120 || fontSizePercent >= 150) -1 313 return true; -1 314 return false; -1 315 } -1 316 matches = fontSize.match(/(\d+)pt/); -1 317 if (matches) { -1 318 var fontSizePt = parseInt(matches[1], 10); -1 319 if (bold && fontSizePt >= 14 || fontSizePt >= 18) -1 320 return true; -1 321 return false; -1 322 } -1 323 return false; -1 324 }; 235 325236 -1 if (name && node === refNode) {237 -1 skip = true;238 -1 }239 -1 }-1 326 /** -1 327 * @param {CSSStyleDeclaration} style -1 328 * @param {Element} element -1 329 * @return {?axs.color.Color} -1 330 */ -1 331 axs.utils.getBgColor = function(style, element) { -1 332 var bgColorString = style.backgroundColor; -1 333 var bgColor = axs.color.parseColor(bgColorString); -1 334 if (!bgColor) -1 335 return null; 240 336241 -1 if (!name && !rolePresentation && ['input', 'select', 'textarea'].indexOf(node.nodeName.toLowerCase()) !== -1 && node.id && document.querySelectorAll('label[for="' + node.id + '"]').length) {242 -1 var label = document.querySelector('label[for="' + node.id + '"]');243 -1 name = walk(label, true, skip);244 -1 }-1 337 if (style.opacity < 1) -1 338 bgColor.alpha = bgColor.alpha * style.opacity; 245 339246 -1 if (!name && !rolePresentation && node.nodeName.toLowerCase() == 'img' && node.getAttribute('alt')) {247 -1 name = node.getAttribute('alt');248 -1 }-1 340 if (bgColor.alpha < 1) { -1 341 var parentBg = axs.utils.getParentBgColor(element); -1 342 if (parentBg == null) -1 343 return null; 249 344250 -1 if (!name && !rolePresentation && nTitle) {251 -1 name = nTitle;252 -1 }253 -1 }254 -1 } else if (node.nodeType === 3) {255 -1 name = node.data;256 -1 }257 -1258 -1 name = cssO.before + name + cssO.after;259 -1260 -1 if (name && !hasParentLabel(node, false, refNode)) {261 -1 fullName += name;262 -1 }263 -1 }, refNode);264 -1265 -1 fullName = cssOP.before + fullName + cssOP.after;266 -1 return fullName;267 -1 };268 -1269 -1 if (isHidden(node, document.body) || hasParentLabel(node, true, document.body)) {270 -1 return;271 -1 }272 -1273 -1 var accName = walk(node, false);274 -1 var accDesc = '';275 -1276 -1 if (['presentation', 'none'].indexOf(node.getAttribute('role')) === -1) {277 -1 var desc = '';278 -1279 -1 var title = node.getAttribute('title') || '';280 -1 if (title) {281 -1 if (!accName) {282 -1 accName = title;283 -1 } else {284 -1 accDesc = title;285 -1 }286 -1 }287 -1288 -1 var describedby = node.getAttribute('aria-describedby') || '';289 -1 if (describedby) {290 -1 var ids = describedby.split(/\s+/);291 -1 var parts = [];292 -1293 -1 for (var j = 0; j < ids.length; j++) {294 -1 var element = document.getElementById(ids[j]);295 -1 parts.push(walk(element, true));296 -1 }297 -1298 -1 if (parts.length) {299 -1 accDesc = parts.join(' ');300 -1 }301 -1 }302 -1 }303 -1304 -1 accName = trim(accName.replace(/\s+/g, ' '));305 -1 accDesc = trim(accDesc.replace(/\s+/g, ' '));306 -1307 -1 if (accName === accDesc) {308 -1 accDesc = '';309 -1 }310 -1311 -1 var props = {312 -1 name: accName,313 -1 desc: accDesc314 -1 };315 -1316 -1 if (fnc && typeof fnc == 'function') {317 -1 return fnc.apply(node, [318 -1 node,319 -1 props320 -1 ]);321 -1 } else {322 -1 return props;323 -1 }-1 345 bgColor = axs.color.flattenColors(bgColor, parentBg); -1 346 } -1 347 return bgColor; 324 348 }; 325 349326 -1 // Customize returned string-1 350 /** -1 351 * Gets the effective background color of the parent of |element|. -1 352 * @param {Element} element -1 353 * @return {?axs.color.Color} -1 354 */ -1 355 axs.utils.getParentBgColor = function(element) { -1 356 /** @type {Element} */ var parent = element; -1 357 var bgStack = []; -1 358 var foundSolidColor = null; -1 359 while ((parent = axs.dom.parentElement(parent))) { -1 360 var computedStyle = window.getComputedStyle(parent, null); -1 361 if (!computedStyle) -1 362 continue; 327 363328 -1 var getNames = function(node) {329 -1 var props = calcNames(node);330 -1 return 'accName: "' + props.name + '"\n\naccDesc: "' + props.desc + '"';331 -1 };-1 364 var parentBg = axs.color.parseColor(computedStyle.backgroundColor); -1 365 if (!parentBg) -1 366 continue; 332 367333 -1 if (typeof module === 'object' && module.exports) {334 -1 module.exports = {335 -1 getNames: getNames,336 -1 calcNames: calcNames,337 -1 };338 -1 }339 -1 },{}],2:[function(require,module,exports){340 -1 (function (global){341 -1 global.goog = {342 -1 provide: function() {},343 -1 require: function() {},344 -1 };345 -1 global.axs = {346 -1 browserUtils: {},347 -1 color: {},348 -1 constants: {},349 -1 dom: {},350 -1 utils: {},351 -1 properties: {},352 -1 };-1 368 if (computedStyle.opacity < 1) -1 369 parentBg.alpha = parentBg.alpha * computedStyle.opacity; 353 370354 -1 require('accessibility-developer-tools/src/js/Constants');355 -1 require('accessibility-developer-tools/src/js/AccessibilityUtils');356 -1 require('accessibility-developer-tools/src/js/BrowserUtils');357 -1 require('accessibility-developer-tools/src/js/Color');358 -1 require('accessibility-developer-tools/src/js/DOMUtils');359 -1 require('accessibility-developer-tools/src/js/Properties');-1 371 if (parentBg.alpha == 0) -1 372 continue; 360 373361 -1 module.exports = global.axs;-1 374 bgStack.push(parentBg); 362 375363 -1 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})364 -1 },{"accessibility-developer-tools/src/js/AccessibilityUtils":3,"accessibility-developer-tools/src/js/BrowserUtils":4,"accessibility-developer-tools/src/js/Color":5,"accessibility-developer-tools/src/js/Constants":6,"accessibility-developer-tools/src/js/DOMUtils":7,"accessibility-developer-tools/src/js/Properties":8}],3:[function(require,module,exports){365 -1 // Copyright 2012 Google Inc.366 -1 //367 -1 // Licensed under the Apache License, Version 2.0 (the "License");368 -1 // you may not use this file except in compliance with the License.369 -1 // You may obtain a copy of the License at370 -1 //371 -1 // http://www.apache.org/licenses/LICENSE-2.0372 -1 //373 -1 // Unless required by applicable law or agreed to in writing, software374 -1 // distributed under the License is distributed on an "AS IS" BASIS,375 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.376 -1 // See the License for the specific language governing permissions and377 -1 // limitations under the License.-1 376 if (parentBg.alpha == 1) { -1 377 foundSolidColor = true; -1 378 break; -1 379 } -1 380 } 378 381379 -1 goog.require('axs.browserUtils');380 -1 goog.require('axs.color');381 -1 goog.require('axs.color.Color');382 -1 goog.require('axs.constants');383 -1 goog.require('axs.dom');-1 382 if (!foundSolidColor) -1 383 bgStack.push(new axs.color.Color(255, 255, 255, 1)); 384 384385 -1 goog.provide('axs.utils');-1 385 var bg = bgStack.pop(); -1 386 while (bgStack.length) { -1 387 var fg = bgStack.pop(); -1 388 bg = axs.color.flattenColors(fg, bg); -1 389 } -1 390 return bg; -1 391 }; 386 392 387 393 /**388 -1 * @const389 -1 * @type {string}-1 394 * @param {CSSStyleDeclaration} style -1 395 * @param {Element} element -1 396 * @param {axs.color.Color} bgColor The background color, which may come from -1 397 * another element (such as a parent element), for flattening into the -1 398 * foreground color. -1 399 * @return {?axs.color.Color} 390 400 */391 -1 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR =392 -1 'input:not([type=hidden]):not([disabled]),' +393 -1 'select:not([disabled]),' +394 -1 'textarea:not([disabled]),' +395 -1 'button:not([disabled]),' +396 -1 'a[href],' +397 -1 'iframe,' +398 -1 '[tabindex]';-1 401 axs.utils.getFgColor = function(style, element, bgColor) { -1 402 var fgColorString = style.color; -1 403 var fgColor = axs.color.parseColor(fgColorString); -1 404 if (!fgColor) -1 405 return null; 399 406400 -1 /**401 -1 * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label402 -1 * @const403 -1 * @type {string}404 -1 */405 -1 axs.utils.LABELABLE_ELEMENTS_SELECTOR =406 -1 'button,' +407 -1 'input:not([type=hidden]),' +408 -1 'keygen,' +409 -1 'meter,' +410 -1 'output,' +411 -1 'progress,' +412 -1 'select,' +413 -1 'textarea';-1 407 if (fgColor.alpha < 1) -1 408 fgColor = axs.color.flattenColors(fgColor, bgColor); 414 409 -1 410 if (style.opacity < 1) { -1 411 var parentBg = axs.utils.getParentBgColor(element); -1 412 fgColor.alpha = fgColor.alpha * style.opacity; -1 413 fgColor = axs.color.flattenColors(fgColor, parentBg); -1 414 } 415 415416 -1 /**417 -1 * @param {Element} element418 -1 * @return {boolean}419 -1 */420 -1 axs.utils.elementIsTransparent = function(element) {421 -1 return element.style.opacity == '0';-1 416 return fgColor; 422 417 }; 423 418 424 419 /** 425 420 * @param {Element} element426 -1 * @return {boolean}-1 421 * @return {?number} 427 422 */428 -1 axs.utils.elementHasZeroArea = function(element) {429 -1 var rect = element.getBoundingClientRect();430 -1 var width = rect.right - rect.left;431 -1 var height = rect.top - rect.bottom;432 -1 if (!width || !height)433 -1 return true;434 -1 return false;-1 423 axs.utils.getContrastRatioForElement = function(element) { -1 424 var style = window.getComputedStyle(element, null); -1 425 return axs.utils.getContrastRatioForElementWithComputedStyle(style, element); 435 426 }; 436 427 437 428 /** -1 429 * @param {CSSStyleDeclaration} style 438 430 * @param {Element} element439 -1 * @return {boolean}-1 431 * @return {?number} 440 432 */441 -1 axs.utils.elementIsOutsideScrollArea = function(element) {442 -1 var parent = axs.dom.parentElement(element);443 -1444 -1 var defaultView = element.ownerDocument.defaultView;445 -1 while (parent != defaultView.document.body) {446 -1 if (axs.utils.isClippedBy(element, parent))447 -1 return true;-1 433 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) { -1 434 if (axs.utils.isElementHidden(element)) -1 435 return null; 448 436449 -1 if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent))450 -1 return false;-1 437 var bgColor = axs.utils.getBgColor(style, element); -1 438 if (!bgColor) -1 439 return null; 451 440452 -1 parent = axs.dom.parentElement(parent);453 -1 }-1 441 var fgColor = axs.utils.getFgColor(style, element, bgColor); -1 442 if (!fgColor) -1 443 return null; 454 444455 -1 return !axs.utils.canScrollTo(element, defaultView.document.body);-1 445 return axs.color.calculateContrastRatio(fgColor, bgColor); 456 446 }; 457 447 458 448 /**459 -1 * Checks whether it's possible to scroll to the given element within the given container.460 -1 * Assumes that |container| is an ancestor of |element|.461 -1 * If |container| cannot be scrolled, returns True if the element is within its bounding client462 -1 * rect.463 449 * @param {Element} element464 -1 * @param {Element} container465 -1 * @return {boolean} True iff it's possible to scroll to |element| within |container|.-1 450 * @return {boolean} 466 451 */467 -1 axs.utils.canScrollTo = function(element, container) {468 -1 var rect = element.getBoundingClientRect();469 -1 var containerRect = container.getBoundingClientRect();470 -1 if (container == container.ownerDocument.body) {471 -1 var absoluteTop = containerRect.top;472 -1 var absoluteLeft = containerRect.left;473 -1 } else {474 -1 var absoluteTop = containerRect.top - container.scrollTop;475 -1 var absoluteLeft = containerRect.left - container.scrollLeft;476 -1 }477 -1 var containerScrollArea =478 -1 { top: absoluteTop,479 -1 bottom: absoluteTop + container.scrollHeight,480 -1 left: absoluteLeft,481 -1 right: absoluteLeft + container.scrollWidth };-1 452 axs.utils.isNativeTextElement = function(element) { -1 453 var tagName = element.tagName.toLowerCase(); -1 454 var type = element.type ? element.type.toLowerCase() : ''; -1 455 if (tagName == 'textarea') -1 456 return true; -1 457 if (tagName != 'input') -1 458 return false; 482 459483 -1 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top ||484 -1 rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) {-1 460 switch (type) { -1 461 case 'email': -1 462 case 'number': -1 463 case 'password': -1 464 case 'search': -1 465 case 'text': -1 466 case 'tel': -1 467 case 'url': -1 468 case '': -1 469 return true; -1 470 default: 485 471 return false; 486 472 } -1 473 }; 487 474488 -1 var defaultView = element.ownerDocument.defaultView;489 -1 var style = defaultView.getComputedStyle(container);490 -1491 -1 if (rect.left > containerRect.right || rect.top > containerRect.bottom) {492 -1 return (style.overflow == 'scroll' || style.overflow == 'auto' ||493 -1 container instanceof defaultView.HTMLBodyElement);-1 475 /** -1 476 * @param {number} contrastRatio -1 477 * @param {CSSStyleDeclaration} style -1 478 * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level -1 479 * @return {boolean} -1 480 */ -1 481 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) { -1 482 // Round to nearest 0.1 -1 483 var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10); -1 484 if (!opt_strict) { -1 485 return roundedContrastRatio < 3.0 || -1 486 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5); -1 487 } else { -1 488 return roundedContrastRatio < 4.5 || -1 489 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0); 494 490 }495 -1496 -1 return true;497 491 }; 498 492 499 493 /**500 -1 * Checks whether the given element is clipped by the given container.501 -1 * Assumes that |container| is an ancestor of |element|.502 494 * @param {Element} element503 -1 * @param {Element} container504 -1 * @return {boolean} True iff |element| is clipped by |container|.-1 495 * @return {boolean} 505 496 */506 -1 axs.utils.isClippedBy = function(element, container) {507 -1 var rect = element.getBoundingClientRect();508 -1 var containerRect = container.getBoundingClientRect();509 -1 var containerTop = containerRect.top;510 -1 var containerLeft = containerRect.left;511 -1 var containerScrollArea =512 -1 { top: containerTop - container.scrollTop,513 -1 bottom: containerTop - container.scrollTop + container.scrollHeight,514 -1 left: containerLeft - container.scrollLeft,515 -1 right: containerLeft - container.scrollLeft + container.scrollWidth };-1 497 axs.utils.hasLabel = function(element) { -1 498 var tagName = element.tagName.toLowerCase(); -1 499 var type = element.type ? element.type.toLowerCase() : ''; 516 500517 -1 var defaultView = element.ownerDocument.defaultView;518 -1 var style = defaultView.getComputedStyle(container);-1 501 if (element.hasAttribute('aria-label')) -1 502 return true; -1 503 if (element.hasAttribute('title')) -1 504 return true; -1 505 if (tagName == 'img' && element.hasAttribute('alt')) -1 506 return true; -1 507 if (tagName == 'input' && type == 'image' && element.hasAttribute('alt')) -1 508 return true; -1 509 if (tagName == 'input' && (type == 'submit' || type == 'reset')) -1 510 return true; 519 511520 -1 if ((rect.right < containerRect.left || rect.bottom < containerRect.top ||521 -1 rect.left > containerRect.right || rect.top > containerRect.bottom) &&522 -1 style.overflow == 'hidden') {-1 512 // There's a separate audit that makes sure this points to an actual element or elements. -1 513 if (element.hasAttribute('aria-labelledby')) 523 514 return true;524 -1 }525 515526 -1 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top)527 -1 return (style.overflow != 'visible');-1 516 if (element.hasAttribute('id')) { -1 517 var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]'); -1 518 if (labelsFor.length > 0) -1 519 return true; -1 520 } 528 521 -1 522 var parent = axs.dom.parentElement(element); -1 523 while (parent) { -1 524 if (parent.tagName.toLowerCase() == 'label') { -1 525 var parentLabel = /** HTMLLabelElement */ parent; -1 526 if (parentLabel.control == element) -1 527 return true; -1 528 } -1 529 parent = axs.dom.parentElement(parent); -1 530 } 529 531 return false; 530 532 }; 531 533 532 534 /**533 -1 * @param {Node} ancestor A potential ancestor of |node|.534 -1 * @param {Node} node535 -1 * @return {boolean} true if |ancestor| is an ancestor of |node| (including536 -1 * |ancestor| === |node|).-1 535 * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute. -1 536 * Disabled here means that the element should be considered disabled according to specification. -1 537 * This element may or may not be effectively disabled in practice as this is dependent on implementation. -1 538 * -1 539 * @param {Element} element An element to check. -1 540 * @return {boolean} true If the element supports being natively disabled. 537 541 */538 -1 axs.utils.isAncestor = function(ancestor, node) {539 -1 if (node == null)540 -1 return false;541 -1 if (node === ancestor)542 -1 return true;543 -1544 -1 var parentNode = axs.dom.composedParentNode(node);545 -1 return axs.utils.isAncestor(ancestor, parentNode);-1 542 axs.utils.isNativelyDisableable = function(element) { -1 543 var tagName = element.tagName.toUpperCase(); -1 544 return (tagName in axs.constants.NATIVELY_DISABLEABLE); 546 545 }; 547 546 548 547 /**549 -1 * @param {Element} element550 -1 * @return {Array.<Element>} An array of any non-transparent elements which551 -1 * overlap the given element.-1 548 * Determine if this element is disabled directly or indirectly by a disabled ancestor. -1 549 * Disabled here means that the element should be considered disabled according to specification. -1 550 * This element may or may not be effectively disabled in practice as this is dependent on implementation. -1 551 * -1 552 * @param {Element} element An element to check. -1 553 * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors. -1 554 * @return {boolean} true if the element or one of its ancestors is disabled. 552 555 */553 -1 axs.utils.overlappingElements = function(element) {554 -1 if (axs.utils.elementHasZeroArea(element))555 -1 return null;556 -1557 -1 var overlappingElements = [];558 -1 var clientRects = element.getClientRects();559 -1 for (var i = 0; i < clientRects.length; i++) {560 -1 var rect = clientRects[i];561 -1 var center_x = (rect.left + rect.right) / 2;562 -1 var center_y = (rect.top + rect.bottom) / 2;563 -1 var elementAtPoint = document.elementFromPoint(center_x, center_y);564 -1565 -1 if (elementAtPoint == null || elementAtPoint == element ||566 -1 axs.utils.isAncestor(elementAtPoint, element) ||567 -1 axs.utils.isAncestor(element, elementAtPoint)) {568 -1 continue;-1 556 axs.utils.isElementDisabled = function(element, ignoreAncestors) { -1 557 var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *'; -1 558 if (axs.browserUtils.matchSelector(element, selector)) { -1 559 return true; -1 560 } -1 561 if (!axs.utils.isNativelyDisableable(element) || -1 562 axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) { -1 563 return false; -1 564 } -1 565 for (var next = element; next !== null; next = axs.dom.parentElement(next)) { -1 566 if (next.hasAttribute('disabled')) { -1 567 return true; 569 568 }570 -1571 -1 var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null);572 -1 if (!overlappingElementStyle)573 -1 continue;574 -1575 -1 var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle,576 -1 elementAtPoint);577 -1 if (overlappingElementBg && overlappingElementBg.alpha > 0 &&578 -1 overlappingElements.indexOf(elementAtPoint) < 0) {579 -1 overlappingElements.push(elementAtPoint);-1 569 if (ignoreAncestors) { -1 570 return false; 580 571 } 581 572 }582 -1583 -1 return overlappingElements;-1 573 return false; 584 574 }; 585 575 586 576 /**587 -1 * @param {Element} element588 -1 * @return {boolean}-1 577 * @param {Element} element An element to check. -1 578 * @return {boolean} True if the element is hidden from accessibility. 589 579 */590 -1 axs.utils.elementIsHtmlControl = function(element) {591 -1 var defaultView = element.ownerDocument.defaultView;-1 580 axs.utils.isElementHidden = function(element) { -1 581 if (!(element instanceof element.ownerDocument.defaultView.HTMLElement)) -1 582 return false; 592 583593 -1 // HTML control594 -1 if (element instanceof defaultView.HTMLButtonElement)595 -1 return true;596 -1 if (element instanceof defaultView.HTMLInputElement)597 -1 return true;598 -1 if (element instanceof defaultView.HTMLSelectElement)599 -1 return true;600 -1 if (element instanceof defaultView.HTMLTextAreaElement)601 -1 return true;-1 584 if (element.hasAttribute('chromevoxignoreariahidden')) -1 585 var chromevoxignoreariahidden = true; 602 586603 -1 return false;604 -1 };-1 587 var style = window.getComputedStyle(element, null); -1 588 if (style.display == 'none' || style.visibility == 'hidden') -1 589 return true; 605 590606 -1 /**607 -1 * @param {Element} element608 -1 * @return {boolean}609 -1 */610 -1 axs.utils.elementIsAriaWidget = function(element) {611 -1 if (element.hasAttribute('role')) {612 -1 var roleValue = element.getAttribute('role');613 -1 // TODO is this correct?614 -1 if (roleValue) {615 -1 var role = axs.constants.ARIA_ROLES[roleValue];616 -1 if (role && 'widget' in role['allParentRolesSet'])617 -1 return true;618 -1 }-1 591 if (element.hasAttribute('aria-hidden') && -1 592 element.getAttribute('aria-hidden').toLowerCase() == 'true') { -1 593 return !chromevoxignoreariahidden; 619 594 } -1 595 620 596 return false; 621 597 }; 622 598 623 599 /**624 -1 * @param {Element} element625 -1 * @return {boolean}-1 600 * @param {Element} element An element to check. -1 601 * @return {boolean} True if the element or one of its ancestors is -1 602 * hidden from accessibility. 626 603 */627 -1 axs.utils.elementIsVisible = function(element) {628 -1 if (axs.utils.elementIsTransparent(element))629 -1 return false;630 -1 if (axs.utils.elementHasZeroArea(element))631 -1 return false;632 -1 if (axs.utils.elementIsOutsideScrollArea(element))633 -1 return false;-1 604 axs.utils.isElementOrAncestorHidden = function(element) { -1 605 if (axs.utils.isElementHidden(element)) -1 606 return true; 634 607635 -1 var overlappingElements = axs.utils.overlappingElements(element);636 -1 if (overlappingElements.length)-1 608 if (axs.dom.parentElement(element)) -1 609 return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element)); -1 610 else 637 611 return false;638 -1639 -1 return true;640 612 }; 641 613 642 614 /**643 -1 * @param {CSSStyleDeclaration} style644 -1 * @return {boolean}-1 615 * @param {Element} element An element to check -1 616 * @return {boolean} True if the given element is an inline element, false -1 617 * otherwise. 645 618 */646 -1 axs.utils.isLargeFont = function(style) {647 -1 var fontSize = style.fontSize;648 -1 var bold = style.fontWeight == 'bold';649 -1 var matches = fontSize.match(/(\d+)px/);650 -1 if (matches) {651 -1 var fontSizePx = parseInt(matches[1], 10);652 -1 var bodyStyle = window.getComputedStyle(document.body, null);653 -1 var bodyFontSize = bodyStyle.fontSize;654 -1 matches = bodyFontSize.match(/(\d+)px/);655 -1 if (matches) {656 -1 var bodyFontSizePx = parseInt(matches[1], 10);657 -1 var boldLarge = bodyFontSizePx * 1.2;658 -1 var large = bodyFontSizePx * 1.5;659 -1 } else {660 -1 var boldLarge = 19.2;661 -1 var large = 24;662 -1 }663 -1 return (bold && fontSizePx >= boldLarge || fontSizePx >= large);664 -1 }665 -1 matches = fontSize.match(/(\d+)em/);666 -1 if (matches) {667 -1 var fontSizeEm = parseInt(matches[1], 10);668 -1 if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5)669 -1 return true;670 -1 return false;671 -1 }672 -1 matches = fontSize.match(/(\d+)%/);673 -1 if (matches) {674 -1 var fontSizePercent = parseInt(matches[1], 10);675 -1 if (bold && fontSizePercent >= 120 || fontSizePercent >= 150)676 -1 return true;677 -1 return false;678 -1 }679 -1 matches = fontSize.match(/(\d+)pt/);680 -1 if (matches) {681 -1 var fontSizePt = parseInt(matches[1], 10);682 -1 if (bold && fontSizePt >= 14 || fontSizePt >= 18)683 -1 return true;684 -1 return false;685 -1 }686 -1 return false;-1 619 axs.utils.isInlineElement = function(element) { -1 620 var tagName = element.tagName.toUpperCase(); -1 621 return axs.constants.InlineElements[tagName]; 687 622 }; 688 623 689 624 /**690 -1 * @param {CSSStyleDeclaration} style691 -1 * @param {Element} element692 -1 * @return {?axs.color.Color}-1 625 * -1 626 * Gets role details from an element. -1 627 * @param {Element} element The DOM element whose role we want. -1 628 * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute. -1 629 * -1 630 * @return {Object} 693 631 */694 -1 axs.utils.getBgColor = function(style, element) {695 -1 var bgColorString = style.backgroundColor;696 -1 var bgColor = axs.color.parseColor(bgColorString);697 -1 if (!bgColor)-1 632 axs.utils.getRoles = function(element, implicit) { -1 633 if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit)) 698 634 return null;699 -1700 -1 if (style.opacity < 1)701 -1 bgColor.alpha = bgColor.alpha * style.opacity;702 -1703 -1 if (bgColor.alpha < 1) {704 -1 var parentBg = axs.utils.getParentBgColor(element);705 -1 if (parentBg == null)706 -1 return null;707 -1708 -1 bgColor = axs.color.flattenColors(bgColor, parentBg);-1 635 var roleValue = element.getAttribute('role'); -1 636 if (!roleValue && implicit) -1 637 roleValue = axs.properties.getImplicitRole(element); -1 638 if (!roleValue) // role='' or implicit role came up empty -1 639 return null; -1 640 var roleNames = roleValue.split(' '); -1 641 var result = { roles: [], valid: false }; -1 642 for (var i = 0; i < roleNames.length; i++) { -1 643 var role = roleNames[i]; -1 644 var ariaRole = axs.constants.ARIA_ROLES[role]; -1 645 var roleObject = { 'name': role }; -1 646 if (ariaRole && !ariaRole.abstract) { -1 647 roleObject.details = ariaRole; -1 648 if (!result.applied) { -1 649 result.applied = roleObject; -1 650 } -1 651 roleObject.valid = result.valid = true; -1 652 } else { -1 653 roleObject.valid = false; -1 654 } -1 655 result.roles.push(roleObject); 709 656 }710 -1 return bgColor;-1 657 -1 658 return result; 711 659 }; 712 660 713 661 /**714 -1 * Gets the effective background color of the parent of |element|.715 -1 * @param {Element} element716 -1 * @return {?axs.color.Color}-1 662 * @param {!string} propertyName -1 663 * @param {!string} value -1 664 * @param {!Element} element -1 665 * @return {!Object} 717 666 */718 -1 axs.utils.getParentBgColor = function(element) {719 -1 /** @type {Element} */ var parent = element;720 -1 var bgStack = [];721 -1 var foundSolidColor = null;722 -1 while ((parent = axs.dom.parentElement(parent))) {723 -1 var computedStyle = window.getComputedStyle(parent, null);724 -1 if (!computedStyle)725 -1 continue;726 -1727 -1 var parentBg = axs.color.parseColor(computedStyle.backgroundColor);728 -1 if (!parentBg)729 -1 continue;730 -1731 -1 if (computedStyle.opacity < 1)732 -1 parentBg.alpha = parentBg.alpha * computedStyle.opacity;733 -1734 -1 if (parentBg.alpha == 0)735 -1 continue;736 -1737 -1 bgStack.push(parentBg);738 -1739 -1 if (parentBg.alpha == 1) {740 -1 foundSolidColor = true;741 -1 break;742 -1 }-1 667 axs.utils.getAriaPropertyValue = function(propertyName, value, element) { -1 668 var propertyKey = propertyName.replace(/^aria-/, ''); -1 669 var property = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 670 var result = { 'name': propertyName, 'rawValue': value }; -1 671 if (!property) { -1 672 result.valid = false; -1 673 result.reason = '"' + propertyName + '" is not a valid ARIA property'; -1 674 return result; 743 675 } 744 676745 -1 if (!foundSolidColor)746 -1 bgStack.push(new axs.color.Color(255, 255, 255, 1));-1 677 var propertyType = property.valueType; -1 678 if (!propertyType) { -1 679 result.valid = false; -1 680 result.reason = '"' + propertyName + '" is not a valid ARIA property'; -1 681 return result; -1 682 } 747 683748 -1 var bg = bgStack.pop();749 -1 while (bgStack.length) {750 -1 var fg = bgStack.pop();751 -1 bg = axs.color.flattenColors(fg, bg);-1 684 switch (propertyType) { -1 685 case "idref": -1 686 var isValid = axs.utils.isValidIDRefValue(value, element); -1 687 result.valid = isValid.valid; -1 688 result.reason = isValid.reason; -1 689 result.idref = isValid.idref; -1 690 // falls through -1 691 case "idref_list": -1 692 var idrefValues = value.split(/\s+/); -1 693 result.valid = true; -1 694 for (var i = 0; i < idrefValues.length; i++) { -1 695 var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i], element); -1 696 if (!refIsValid.valid) -1 697 result.valid = false; -1 698 if (result.values) -1 699 result.values.push(refIsValid); -1 700 else -1 701 result.values = [refIsValid]; -1 702 } -1 703 return result; -1 704 case "integer": -1 705 var validNumber = axs.utils.isValidNumber(value); -1 706 if (!validNumber.valid) { -1 707 result.valid = false; -1 708 result.reason = validNumber.reason; -1 709 return result; -1 710 } -1 711 if (Math.floor(validNumber.value) !== validNumber.value) { -1 712 result.valid = false; -1 713 result.reason = '' + value + ' is not a whole integer'; -1 714 } else { -1 715 result.valid = true; -1 716 result.value = validNumber.value; -1 717 } -1 718 return result; -1 719 case "decimal": -1 720 case "number": -1 721 var validNumber = axs.utils.isValidNumber(value); -1 722 result.valid = validNumber.valid; -1 723 if (!validNumber.valid) { -1 724 result.reason = validNumber.reason; -1 725 return result; -1 726 } -1 727 result.value = validNumber.value; -1 728 return result; -1 729 case "string": -1 730 result.valid = true; -1 731 result.value = value; -1 732 return result; -1 733 case "token": -1 734 var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase()); -1 735 if (validTokenValue.valid) { -1 736 result.valid = true; -1 737 result.value = validTokenValue.value; -1 738 return result; -1 739 } else { -1 740 result.valid = false; -1 741 result.value = value; -1 742 result.reason = validTokenValue.reason; -1 743 return result; -1 744 } -1 745 // falls through -1 746 case "token_list": -1 747 var tokenValues = value.split(/\s+/); -1 748 result.valid = true; -1 749 for (var i = 0; i < tokenValues.length; i++) { -1 750 var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase()); -1 751 if (!validTokenValue.valid) { -1 752 result.valid = false; -1 753 if (result.reason) { -1 754 result.reason = [ result.reason ]; -1 755 result.reason.push(validTokenValue.reason); -1 756 } else { -1 757 result.reason = validTokenValue.reason; -1 758 result.possibleValues = validTokenValue.possibleValues; -1 759 } -1 760 } -1 761 // TODO (more structured result) -1 762 if (result.values) -1 763 result.values.push(validTokenValue.value); -1 764 else -1 765 result.values = [validTokenValue.value]; -1 766 } -1 767 return result; -1 768 case "tristate": -1 769 var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName); -1 770 if (validTristate.valid) { -1 771 result.valid = true; -1 772 result.value = validTristate.value; -1 773 } else { -1 774 result.valid = false; -1 775 result.value = value; -1 776 result.reason = validTristate.reason; -1 777 } -1 778 return result; -1 779 case "boolean": -1 780 var validBoolean = axs.utils.isValidBoolean(value); -1 781 if (validBoolean.valid) { -1 782 result.valid = true; -1 783 result.value = validBoolean.value; -1 784 } else { -1 785 result.valid = false; -1 786 result.value = value; -1 787 result.reason = validBoolean.reason; -1 788 } -1 789 return result; 752 790 }753 -1 return bg;-1 791 result.valid = false; -1 792 result.reason = 'Not a valid ARIA property'; -1 793 return result; 754 794 }; 755 795 756 796 /**757 -1 * @param {CSSStyleDeclaration} style758 -1 * @param {Element} element759 -1 * @param {axs.color.Color} bgColor The background color, which may come from760 -1 * another element (such as a parent element), for flattening into the761 -1 * foreground color.762 -1 * @return {?axs.color.Color}763 -1 */764 -1 axs.utils.getFgColor = function(style, element, bgColor) {765 -1 var fgColorString = style.color;766 -1 var fgColor = axs.color.parseColor(fgColorString);767 -1 if (!fgColor)768 -1 return null;769 -1770 -1 if (fgColor.alpha < 1)771 -1 fgColor = axs.color.flattenColors(fgColor, bgColor);772 -1773 -1 if (style.opacity < 1) {774 -1 var parentBg = axs.utils.getParentBgColor(element);775 -1 fgColor.alpha = fgColor.alpha * style.opacity;776 -1 fgColor = axs.color.flattenColors(fgColor, parentBg);777 -1 }778 -1779 -1 return fgColor;-1 797 * @param {string} propertyName The name of the property. -1 798 * @param {string} value The value to check. -1 799 * @return {!Object} -1 800 */ -1 801 axs.utils.isValidTokenValue = function(propertyName, value) { -1 802 var propertyKey = propertyName.replace(/^aria-/, ''); -1 803 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 804 var possibleValues = propertyDetails.valuesSet; -1 805 return axs.utils.isPossibleValue(value, possibleValues, propertyName); 780 806 }; 781 807 782 808 /**783 -1 * @param {Element} element784 -1 * @return {?number}-1 809 * @param {string} value -1 810 * @param {Object.<string, boolean>} possibleValues -1 811 * @param {string} propertyName The name of the property. -1 812 * @return {!Object} 785 813 */786 -1 axs.utils.getContrastRatioForElement = function(element) {787 -1 var style = window.getComputedStyle(element, null);788 -1 return axs.utils.getContrastRatioForElementWithComputedStyle(style, element);-1 814 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) { -1 815 if (!possibleValues[value]) -1 816 return { 'valid': false, -1 817 'value': value, -1 818 'reason': '"' + value + '" is not a valid value for ' + propertyName, -1 819 'possibleValues': Object.keys(possibleValues) }; -1 820 return { 'valid': true, 'value': value }; 789 821 }; 790 822 791 823 /**792 -1 * @param {CSSStyleDeclaration} style793 -1 * @param {Element} element794 -1 * @return {?number}-1 824 * @param {string} value -1 825 * @return {!Object} 795 826 */796 -1 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) {797 -1 if (axs.utils.isElementHidden(element))798 -1 return null;799 -1800 -1 var bgColor = axs.utils.getBgColor(style, element);801 -1 if (!bgColor)802 -1 return null;803 -1804 -1 var fgColor = axs.utils.getFgColor(style, element, bgColor);805 -1 if (!fgColor)806 -1 return null;807 -1808 -1 return axs.color.calculateContrastRatio(fgColor, bgColor);-1 827 axs.utils.isValidBoolean = function(value) { -1 828 try { -1 829 var parsedValue = JSON.parse(value); -1 830 } catch (e) { -1 831 parsedValue = ''; -1 832 } -1 833 if (typeof(parsedValue) != 'boolean') -1 834 return { 'valid': false, -1 835 'value': value, -1 836 'reason': '"' + value + '" is not a true/false value' }; -1 837 return { 'valid': true, 'value': parsedValue }; 809 838 }; 810 839 811 840 /**812 -1 * @param {Element} element813 -1 * @return {boolean}-1 841 * @param {string} value -1 842 * @param {!Element} element -1 843 * @return {!Object} 814 844 */815 -1 axs.utils.isNativeTextElement = function(element) {816 -1 var tagName = element.tagName.toLowerCase();817 -1 var type = element.type ? element.type.toLowerCase() : '';818 -1 if (tagName == 'textarea')819 -1 return true;820 -1 if (tagName != 'input')821 -1 return false;822 -1823 -1 switch (type) {824 -1 case 'email':825 -1 case 'number':826 -1 case 'password':827 -1 case 'search':828 -1 case 'text':829 -1 case 'tel':830 -1 case 'url':831 -1 case '':832 -1 return true;833 -1 default:834 -1 return false;835 -1 }-1 845 axs.utils.isValidIDRefValue = function(value, element) { -1 846 if (value.length == 0) -1 847 return { 'valid': true, 'idref': value }; -1 848 if (!element.ownerDocument.getElementById(value)) -1 849 return { 'valid': false, -1 850 'idref': value, -1 851 'reason': 'No element with ID "' + value + '"' }; -1 852 return { 'valid': true, 'idref': value }; 836 853 }; 837 854 838 855 /**839 -1 * @param {number} contrastRatio840 -1 * @param {CSSStyleDeclaration} style841 -1 * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level842 -1 * @return {boolean}-1 856 * Tests if a number is real number for a11y purposes. -1 857 * Must be a real, numerical, decimal value; heavily inspired by -1 858 * http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number -1 859 * @param {string} value -1 860 * @return {!Object} 843 861 */844 -1 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) {845 -1 // Round to nearest 0.1846 -1 var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10);847 -1 if (!opt_strict) {848 -1 return roundedContrastRatio < 3.0 ||849 -1 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5);850 -1 } else {851 -1 return roundedContrastRatio < 4.5 ||852 -1 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0);-1 862 axs.utils.isValidNumber = function(value) { -1 863 var failResult = { -1 864 'valid': false, -1 865 'value': value, -1 866 'reason': '"' + value + '" is not a number' -1 867 }; -1 868 if (!value) { -1 869 return failResult; -1 870 } -1 871 if (/^0x/i.test(value)) { -1 872 failResult.reason = '"' + value + '" is not a decimal number'; // hex is not accepted -1 873 return failResult; -1 874 } -1 875 var parsedValue = value * 1; -1 876 if (!isFinite(parsedValue)) { -1 877 return failResult; 853 878 } -1 879 return { 'valid': true, 'value': parsedValue }; 854 880 }; 855 881 856 882 /** 857 883 * @param {Element} element 858 884 * @return {boolean} 859 885 */860 -1 axs.utils.hasLabel = function(element) {861 -1 var tagName = element.tagName.toLowerCase();862 -1 var type = element.type ? element.type.toLowerCase() : '';863 -1864 -1 if (element.hasAttribute('aria-label'))865 -1 return true;866 -1 if (element.hasAttribute('title'))867 -1 return true;868 -1 if (tagName == 'img' && element.hasAttribute('alt'))869 -1 return true;870 -1 if (tagName == 'input' && type == 'image' && element.hasAttribute('alt'))871 -1 return true;872 -1 if (tagName == 'input' && (type == 'submit' || type == 'reset'))873 -1 return true;874 -1875 -1 // There's a separate audit that makes sure this points to an actual element or elements.876 -1 if (element.hasAttribute('aria-labelledby'))877 -1 return true;878 -1879 -1 if (element.hasAttribute('id')) {880 -1 var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]');881 -1 if (labelsFor.length > 0)882 -1 return true;883 -1 }-1 886 axs.utils.isElementImplicitlyFocusable = function(element) { -1 887 var defaultView = element.ownerDocument.defaultView; 884 888885 -1 var parent = axs.dom.parentElement(element);886 -1 while (parent) {887 -1 if (parent.tagName.toLowerCase() == 'label') {888 -1 var parentLabel = /** HTMLLabelElement */ parent;889 -1 if (parentLabel.control == element)890 -1 return true;891 -1 }892 -1 parent = axs.dom.parentElement(parent);893 -1 }-1 889 if (element instanceof defaultView.HTMLAnchorElement || -1 890 element instanceof defaultView.HTMLAreaElement) -1 891 return element.hasAttribute('href'); -1 892 if (element instanceof defaultView.HTMLInputElement || -1 893 element instanceof defaultView.HTMLSelectElement || -1 894 element instanceof defaultView.HTMLTextAreaElement || -1 895 element instanceof defaultView.HTMLButtonElement || -1 896 element instanceof defaultView.HTMLIFrameElement) -1 897 return !element.disabled; 894 898 return false; 895 899 }; 896 900 897 901 /**898 -1 * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute.899 -1 * Disabled here means that the element should be considered disabled according to specification.900 -1 * This element may or may not be effectively disabled in practice as this is dependent on implementation.901 -1 *902 -1 * @param {Element} element An element to check.903 -1 * @return {boolean} true If the element supports being natively disabled.-1 902 * Returns an array containing the values of the given JSON-compatible object. -1 903 * (Simply ignores any function values.) -1 904 * @param {Object} obj -1 905 * @return {Array} 904 906 */905 -1 axs.utils.isNativelyDisableable = function(element) {906 -1 var tagName = element.tagName.toUpperCase();907 -1 return (tagName in axs.constants.NATIVELY_DISABLEABLE);-1 907 axs.utils.values = function(obj) { -1 908 var values = []; -1 909 for (var key in obj) { -1 910 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function') -1 911 values.push(obj[key]); -1 912 } -1 913 return values; 908 914 }; 909 915 910 916 /**911 -1 * Determine if this element is disabled directly or indirectly by a disabled ancestor.912 -1 * Disabled here means that the element should be considered disabled according to specification.913 -1 * This element may or may not be effectively disabled in practice as this is dependent on implementation.914 -1 *915 -1 * @param {Element} element An element to check.916 -1 * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors.917 -1 * @return {boolean} true if the element or one of its ancestors is disabled.-1 917 * Returns an object containing the same keys and values as the given -1 918 * JSON-compatible object. (Simply ignores any function values.) -1 919 * @param {Object} obj -1 920 * @return {Object} 918 921 */919 -1 axs.utils.isElementDisabled = function(element, ignoreAncestors) {920 -1 var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *';921 -1 if (axs.browserUtils.matchSelector(element, selector)) {922 -1 return true;923 -1 }924 -1 if (!axs.utils.isNativelyDisableable(element) ||925 -1 axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) {926 -1 return false;927 -1 }928 -1 for (var next = element; next !== null; next = axs.dom.parentElement(next)) {929 -1 if (next.hasAttribute('disabled')) {930 -1 return true;931 -1 }932 -1 if (ignoreAncestors) {933 -1 return false;934 -1 }-1 922 axs.utils.namedValues = function(obj) { -1 923 var values = {}; -1 924 for (var key in obj) { -1 925 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function') -1 926 values[key] = obj[key]; 935 927 }936 -1 return false;-1 928 return values; 937 929 }; 938 930 939 931 /**940 -1 * @param {Element} element An element to check.941 -1 * @return {boolean} True if the element is hidden from accessibility.942 -1 */943 -1 axs.utils.isElementHidden = function(element) {944 -1 if (!(element instanceof element.ownerDocument.defaultView.HTMLElement))945 -1 return false;946 -1947 -1 if (element.hasAttribute('chromevoxignoreariahidden'))948 -1 var chromevoxignoreariahidden = true;-1 932 * Escapes a given ID to be used in a CSS selector -1 933 * -1 934 * @private -1 935 * @param {!string} id The ID to be escaped -1 936 * @return {string} The escaped ID -1 937 */ -1 938 function escapeId(id) { -1 939 return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; }); -1 940 } 949 941950 -1 var style = window.getComputedStyle(element, null);951 -1 if (style.display == 'none' || style.visibility == 'hidden')952 -1 return true;-1 942 /** Gets a CSS selector text for a DOM object. -1 943 * @param {Node} obj The DOM object. -1 944 * @return {string} CSS selector text for the DOM object. -1 945 */ -1 946 axs.utils.getQuerySelectorText = function(obj) { -1 947 if (obj == null || obj.tagName == 'HTML') { -1 948 return 'html'; -1 949 } else if (obj.tagName == 'BODY') { -1 950 return 'body'; -1 951 } 953 952954 -1 if (element.hasAttribute('aria-hidden') &&955 -1 element.getAttribute('aria-hidden').toLowerCase() == 'true') {956 -1 return !chromevoxignoreariahidden;-1 953 if (obj.hasAttribute) { -1 954 if (obj.id) { -1 955 return '#' + escapeId(obj.id); 957 956 } 958 957959 -1 return false;960 -1 };-1 958 if (obj.className) { -1 959 var selector = ''; -1 960 for (var i = 0; i < obj.classList.length; i++) -1 961 selector += '.' + obj.classList[i]; 961 962962 -1 /**963 -1 * @param {Element} element An element to check.964 -1 * @return {boolean} True if the element or one of its ancestors is965 -1 * hidden from accessibility.966 -1 */967 -1 axs.utils.isElementOrAncestorHidden = function(element) {968 -1 if (axs.utils.isElementHidden(element))969 -1 return true;-1 963 var total = 0; -1 964 if (obj.parentNode) { -1 965 for (i = 0; i < obj.parentNode.children.length; i++) { -1 966 var similar = obj.parentNode.children[i]; -1 967 if (axs.browserUtils.matchSelector(similar, selector)) -1 968 total++; -1 969 if (similar === obj) -1 970 break; -1 971 } -1 972 } else { -1 973 total = 1; -1 974 } 970 975971 -1 if (axs.dom.parentElement(element))972 -1 return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element));973 -1 else974 -1 return false;975 -1 };-1 976 if (total == 1) { -1 977 return axs.utils.getQuerySelectorText(obj.parentNode) + -1 978 ' > ' + selector; -1 979 } -1 980 } 976 981977 -1 /**978 -1 * @param {Element} element An element to check979 -1 * @return {boolean} True if the given element is an inline element, false980 -1 * otherwise.981 -1 */982 -1 axs.utils.isInlineElement = function(element) {983 -1 var tagName = element.tagName.toUpperCase();984 -1 return axs.constants.InlineElements[tagName];-1 982 if (obj.parentNode) { -1 983 var similarTags = obj.parentNode.children; -1 984 var total = 1; -1 985 var i = 0; -1 986 while (similarTags[i] !== obj) { -1 987 if (similarTags[i].tagName == obj.tagName) { -1 988 total++; -1 989 } -1 990 i++; -1 991 } -1 992 -1 993 var next = ''; -1 994 if (obj.parentNode.tagName != 'BODY') { -1 995 next = axs.utils.getQuerySelectorText(obj.parentNode) + -1 996 ' > '; -1 997 } -1 998 -1 999 if (total == 1) { -1 1000 return next + -1 1001 obj.tagName; -1 1002 } else { -1 1003 return next + -1 1004 obj.tagName + -1 1005 ':nth-of-type(' + total + ')'; -1 1006 } -1 1007 } -1 1008 -1 1009 } else if (obj.selectorText) { -1 1010 return obj.selectorText; -1 1011 } -1 1012 -1 1013 return ''; 985 1014 }; 986 1015 987 1016 /**988 -1 *989 -1 * Gets role details from an element.990 -1 * @param {Element} element The DOM element whose role we want.991 -1 * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute.992 -1 *993 -1 * @return {Object}-1 1017 * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or -1 1018 * single ID reference. -1 1019 * @param {Element} element a potential referent. -1 1020 * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'. -1 1021 * @return {NodeList} The elements that refer to this element or null. 994 1022 */995 -1 axs.utils.getRoles = function(element, implicit) {996 -1 if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit))-1 1023 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) { -1 1024 var propertyToSelector = function(propertyKey) { -1 1025 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 1026 if (propertyDetails) { -1 1027 if (propertyDetails.valueType === ('idref')) { -1 1028 return '[aria-' + propertyKey + '=\'' + id + '\']'; -1 1029 } else if (propertyDetails.valueType === ('idref_list')) { -1 1030 return '[aria-' + propertyKey + '~=\'' + id + '\']'; -1 1031 } -1 1032 } -1 1033 return ''; -1 1034 }; -1 1035 if (!element) 997 1036 return null;998 -1 var roleValue = element.getAttribute('role');999 -1 if (!roleValue && implicit)1000 -1 roleValue = axs.properties.getImplicitRole(element);1001 -1 if (!roleValue) // role='' or implicit role came up empty-1 1037 var id = element.id; -1 1038 if (!id) 1002 1039 return null;1003 -1 var roleNames = roleValue.split(' ');1004 -1 var result = { roles: [], valid: false };1005 -1 for (var i = 0; i < roleNames.length; i++) {1006 -1 var role = roleNames[i];1007 -1 var ariaRole = axs.constants.ARIA_ROLES[role];1008 -1 var roleObject = { 'name': role };1009 -1 if (ariaRole && !ariaRole.abstract) {1010 -1 roleObject.details = ariaRole;1011 -1 if (!result.applied) {1012 -1 result.applied = roleObject;-1 1040 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector -1 1041 -1 1042 if (opt_attributeName) { -1 1043 var propertyKey = opt_attributeName.replace(/^aria-/, ''); -1 1044 var referrerQuery = propertyToSelector(propertyKey); -1 1045 if (referrerQuery) { -1 1046 return element.ownerDocument.querySelectorAll(referrerQuery); -1 1047 } -1 1048 } else { -1 1049 var selectors = []; -1 1050 for (var propertyKey in axs.constants.ARIA_PROPERTIES) { -1 1051 var referrerQuery = propertyToSelector(propertyKey); -1 1052 if (referrerQuery) { -1 1053 selectors.push(referrerQuery); 1013 1054 }1014 -1 roleObject.valid = result.valid = true;1015 -1 } else {1016 -1 roleObject.valid = false;1017 1055 }1018 -1 result.roles.push(roleObject);-1 1056 return element.ownerDocument.querySelectorAll(selectors.join(',')); 1019 1057 }1020 -11021 -1 return result;-1 1058 return null; 1022 1059 }; 1023 1060 1024 1061 /**1025 -1 * @param {!string} propertyName1026 -1 * @param {!string} value1027 -1 * @param {!Element} element1028 -1 * @return {!Object}-1 1062 * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or -1 1063 * single ID reference. -1 1064 * @param {Element} element a potential referent. -1 1065 * @return {NodeList} The elements that refer to this element. 1029 1066 */1030 -1 axs.utils.getAriaPropertyValue = function(propertyName, value, element) {1031 -1 var propertyKey = propertyName.replace(/^aria-/, '');1032 -1 var property = axs.constants.ARIA_PROPERTIES[propertyKey];1033 -1 var result = { 'name': propertyName, 'rawValue': value };1034 -1 if (!property) {1035 -1 result.valid = false;1036 -1 result.reason = '"' + propertyName + '" is not a valid ARIA property';1037 -1 return result;1038 -1 }1039 -11040 -1 var propertyType = property.valueType;1041 -1 if (!propertyType) {1042 -1 result.valid = false;1043 -1 result.reason = '"' + propertyName + '" is not a valid ARIA property';1044 -1 return result;1045 -1 }-1 1067 axs.utils.getHtmlIdReferrers = function(element) { -1 1068 if (!element) -1 1069 return null; -1 1070 var id = element.id; -1 1071 if (!id) -1 1072 return null; -1 1073 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector -1 1074 var selectorTemplates = [ -1 1075 '[contextmenu=\'{id}\']', -1 1076 '[itemref~=\'{id}\']', -1 1077 'button[form=\'{id}\']', -1 1078 'button[menu=\'{id}\']', -1 1079 'fieldset[form=\'{id}\']', -1 1080 'input[form=\'{id}\']', -1 1081 'input[list=\'{id}\']', -1 1082 'keygen[form=\'{id}\']', -1 1083 'label[for=\'{id}\']', -1 1084 'label[form=\'{id}\']', -1 1085 'menuitem[command=\'{id}\']', -1 1086 'object[form=\'{id}\']', -1 1087 'output[for~=\'{id}\']', -1 1088 'output[form=\'{id}\']', -1 1089 'select[form=\'{id}\']', -1 1090 'td[headers~=\'{id}\']', -1 1091 'textarea[form=\'{id}\']', -1 1092 'tr[headers~=\'{id}\']']; -1 1093 var selectors = selectorTemplates.map(function(selector) { -1 1094 return selector.replace('\{id\}', id); -1 1095 }); -1 1096 return element.ownerDocument.querySelectorAll(selectors.join(',')); -1 1097 }; 1046 10981047 -1 switch (propertyType) {1048 -1 case "idref":1049 -1 var isValid = axs.utils.isValidIDRefValue(value, element);1050 -1 result.valid = isValid.valid;1051 -1 result.reason = isValid.reason;1052 -1 result.idref = isValid.idref;1053 -1 // falls through1054 -1 case "idref_list":1055 -1 var idrefValues = value.split(/\s+/);1056 -1 result.valid = true;1057 -1 for (var i = 0; i < idrefValues.length; i++) {1058 -1 var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i], element);1059 -1 if (!refIsValid.valid)1060 -1 result.valid = false;1061 -1 if (result.values)1062 -1 result.values.push(refIsValid);1063 -1 else1064 -1 result.values = [refIsValid];1065 -1 }1066 -1 return result;1067 -1 case "integer":1068 -1 var validNumber = axs.utils.isValidNumber(value);1069 -1 if (!validNumber.valid) {1070 -1 result.valid = false;1071 -1 result.reason = validNumber.reason;1072 -1 return result;1073 -1 }1074 -1 if (Math.floor(validNumber.value) !== validNumber.value) {1075 -1 result.valid = false;1076 -1 result.reason = '' + value + ' is not a whole integer';1077 -1 } else {1078 -1 result.valid = true;1079 -1 result.value = validNumber.value;1080 -1 }1081 -1 return result;1082 -1 case "decimal":1083 -1 case "number":1084 -1 var validNumber = axs.utils.isValidNumber(value);1085 -1 result.valid = validNumber.valid;1086 -1 if (!validNumber.valid) {1087 -1 result.reason = validNumber.reason;1088 -1 return result;1089 -1 }1090 -1 result.value = validNumber.value;1091 -1 return result;1092 -1 case "string":1093 -1 result.valid = true;1094 -1 result.value = value;1095 -1 return result;1096 -1 case "token":1097 -1 var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase());1098 -1 if (validTokenValue.valid) {1099 -1 result.valid = true;1100 -1 result.value = validTokenValue.value;1101 -1 return result;1102 -1 } else {1103 -1 result.valid = false;1104 -1 result.value = value;1105 -1 result.reason = validTokenValue.reason;1106 -1 return result;1107 -1 }1108 -1 // falls through1109 -1 case "token_list":1110 -1 var tokenValues = value.split(/\s+/);1111 -1 result.valid = true;1112 -1 for (var i = 0; i < tokenValues.length; i++) {1113 -1 var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase());1114 -1 if (!validTokenValue.valid) {1115 -1 result.valid = false;1116 -1 if (result.reason) {1117 -1 result.reason = [ result.reason ];1118 -1 result.reason.push(validTokenValue.reason);-1 1099 /** -1 1100 * Gets a list of all IDs this element references in either ARIA or HTML attributes. -1 1101 * -1 1102 * @param {Element} element The element to check for idref attributes. -1 1103 * @returns {Array.<string>} Any IDs this element references. -1 1104 */ -1 1105 axs.utils.getReferencedIds = function(element) { -1 1106 var result = []; -1 1107 var addResult = function(ids) { -1 1108 if (ids) { -1 1109 if (ids.indexOf(' ') > 0) { -1 1110 result = result.concat(attrib.value.split(' ')); 1119 1111 } else {1120 -1 result.reason = validTokenValue.reason;1121 -1 result.possibleValues = validTokenValue.possibleValues;-1 1112 result.push(ids); 1122 1113 } 1123 1114 }1124 -1 // TODO (more structured result)1125 -1 if (result.values)1126 -1 result.values.push(validTokenValue.value);1127 -1 else1128 -1 result.values = [validTokenValue.value];1129 -1 }1130 -1 return result;1131 -1 case "tristate":1132 -1 var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName);1133 -1 if (validTristate.valid) {1134 -1 result.valid = true;1135 -1 result.value = validTristate.value;1136 -1 } else {1137 -1 result.valid = false;1138 -1 result.value = value;1139 -1 result.reason = validTristate.reason;1140 -1 }1141 -1 return result;1142 -1 case "boolean":1143 -1 var validBoolean = axs.utils.isValidBoolean(value);1144 -1 if (validBoolean.valid) {1145 -1 result.valid = true;1146 -1 result.value = validBoolean.value;1147 -1 } else {1148 -1 result.valid = false;1149 -1 result.value = value;1150 -1 result.reason = validBoolean.reason;-1 1115 }; -1 1116 for (var i = 0; i < element.attributes.length; i++) { -1 1117 var tagName = element.tagName.toLowerCase(); -1 1118 var attrib = element.attributes[i]; -1 1119 if (attrib.specified) { -1 1120 var attribName = attrib.name; -1 1121 var ariaAttr = attribName.match(/aria-(.+)/); -1 1122 if (ariaAttr) { -1 1123 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]]; -1 1124 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) { -1 1125 addResult(attrib.value); -1 1126 } -1 1127 continue; -1 1128 } -1 1129 switch (attribName) { -1 1130 case 'contextmenu': -1 1131 case 'itemref': -1 1132 addResult(attrib.value); -1 1133 break; -1 1134 case 'form': -1 1135 if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' || -1 1136 tagName == 'keygen' || tagName == 'label' || tagName == 'object' || -1 1137 tagName == 'output' || tagName == 'select' || tagName == 'textarea') { -1 1138 addResult(attrib.value); -1 1139 } -1 1140 break; -1 1141 case 'for': -1 1142 if (tagName == 'label' || tagName == 'output') { -1 1143 addResult(attrib.value); -1 1144 } -1 1145 break; -1 1146 case 'menu': -1 1147 if (tagName == 'button') { -1 1148 addResult(attrib.value); -1 1149 } -1 1150 break; -1 1151 case 'list': -1 1152 if (tagName == 'input') { -1 1153 addResult(attrib.value); -1 1154 } -1 1155 break; -1 1156 case 'command': -1 1157 if (tagName == 'menuitem') { -1 1158 addResult(attrib.value); -1 1159 } -1 1160 break; -1 1161 case 'headers': -1 1162 if (tagName == 'td' || tagName == 'tr') { -1 1163 addResult(attrib.value); -1 1164 } -1 1165 break; -1 1166 } 1151 1167 }1152 -1 return result;1153 1168 }1154 -1 result.valid = false;1155 -1 result.reason = 'Not a valid ARIA property';1156 1169 return result; 1157 1170 }; 1158 1171 1159 1172 /**1160 -1 * @param {string} propertyName The name of the property.1161 -1 * @param {string} value The value to check.1162 -1 * @return {!Object}-1 1173 * Gets elements that refer to this element in an attribute that takes an ID reference list or -1 1174 * single ID reference. -1 1175 * @param {Element} element a potential referent. -1 1176 * @return {Array<Element>} The elements that refer to this element. 1163 1177 */1164 -1 axs.utils.isValidTokenValue = function(propertyName, value) {1165 -1 var propertyKey = propertyName.replace(/^aria-/, '');1166 -1 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];1167 -1 var possibleValues = propertyDetails.valuesSet;1168 -1 return axs.utils.isPossibleValue(value, possibleValues, propertyName);-1 1178 axs.utils.getIdReferrers = function(element) { -1 1179 var result = []; -1 1180 var referrers = axs.utils.getHtmlIdReferrers(element); -1 1181 if (referrers) { -1 1182 result = result.concat(Array.prototype.slice.call(referrers)); -1 1183 } -1 1184 referrers = axs.utils.getAriaIdReferrers(element); -1 1185 if (referrers) { -1 1186 result = result.concat(Array.prototype.slice.call(referrers)); -1 1187 } -1 1188 return result; 1169 1189 }; 1170 1190 1171 1191 /**1172 -1 * @param {string} value1173 -1 * @param {Object.<string, boolean>} possibleValues1174 -1 * @param {string} propertyName The name of the property.1175 -1 * @return {!Object}-1 1192 * Gets elements which this element refers to in the given attribute. -1 1193 * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'. -1 1194 * @param {Element} element The DOM element which has the ARIA attribute. -1 1195 * @return {!Array.<Element>} An array of elements that are referred to by this element. -1 1196 * @example -1 1197 * var owner = document.body.appendChild(document.createElement("div")); -1 1198 * var owned = document.body.appendChild(document.createElement("div")); -1 1199 * owner.setAttribute("aria-owns", "kungfu"); -1 1200 * owned.setAttribute("id", "kungfu"); -1 1201 * console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned); // This will log 'true' 1176 1202 */1177 -1 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) {1178 -1 if (!possibleValues[value])1179 -1 return { 'valid': false,1180 -1 'value': value,1181 -1 'reason': '"' + value + '" is not a valid value for ' + propertyName,1182 -1 'possibleValues': Object.keys(possibleValues) };1183 -1 return { 'valid': true, 'value': value };-1 1203 axs.utils.getIdReferents = function(attributeName, element) { -1 1204 var result = []; -1 1205 var propertyKey = attributeName.replace(/^aria-/, ''); -1 1206 var property = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 1207 if (!property || !element.hasAttribute(attributeName)) -1 1208 return result; -1 1209 var propertyType = property.valueType; -1 1210 if (propertyType === 'idref_list' || propertyType === 'idref') { -1 1211 var ownerDocument = element.ownerDocument; -1 1212 var ids = element.getAttribute(attributeName); -1 1213 ids = ids.split(/\s+/); -1 1214 for (var i = 0, len = ids.length; i < len; i++) { -1 1215 var next = ownerDocument.getElementById(ids[i]); -1 1216 if (next) { -1 1217 result[result.length] = next; -1 1218 } -1 1219 } -1 1220 } -1 1221 return result; 1184 1222 }; 1185 1223 1186 1224 /**1187 -1 * @param {string} value1188 -1 * @return {!Object}-1 1225 * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'. -1 1226 * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list']. -1 1227 * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match. 1189 1228 */1190 -1 axs.utils.isValidBoolean = function(value) {1191 -1 try {1192 -1 var parsedValue = JSON.parse(value);1193 -1 } catch (e) {1194 -1 parsedValue = '';-1 1229 axs.utils.getAriaPropertiesByValueType = function(valueTypes) { -1 1230 var result = {}; -1 1231 for (var propertyName in axs.constants.ARIA_PROPERTIES) { -1 1232 var property = axs.constants.ARIA_PROPERTIES[propertyName]; -1 1233 if (property && valueTypes.indexOf(property.valueType) >= 0) { -1 1234 result[propertyName] = property; -1 1235 } 1195 1236 }1196 -1 if (typeof(parsedValue) != 'boolean')1197 -1 return { 'valid': false,1198 -1 'value': value,1199 -1 'reason': '"' + value + '" is not a true/false value' };1200 -1 return { 'valid': true, 'value': parsedValue };-1 1237 return result; 1201 1238 }; 1202 1239 1203 1240 /**1204 -1 * @param {string} value1205 -1 * @param {!Element} element1206 -1 * @return {!Object}-1 1241 * Builds a selector that matches an element with any of these ARIA properties. -1 1242 * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES -1 1243 * @return {!string} The selector. 1207 1244 */1208 -1 axs.utils.isValidIDRefValue = function(value, element) {1209 -1 if (value.length == 0)1210 -1 return { 'valid': true, 'idref': value };1211 -1 if (!element.ownerDocument.getElementById(value))1212 -1 return { 'valid': false,1213 -1 'idref': value,1214 -1 'reason': 'No element with ID "' + value + '"' };1215 -1 return { 'valid': true, 'idref': value };-1 1245 axs.utils.getSelectorForAriaProperties = function(ariaProperties) { -1 1246 var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties)); -1 1247 var result = propertyNames.map(function(propertyName) { -1 1248 return '[aria-' + propertyName + ']'; -1 1249 }); -1 1250 result.sort(); // facilitates reading long selectors and unit testing -1 1251 return result.join(','); 1216 1252 }; 1217 1253 1218 1254 /**1219 -1 * Tests if a number is real number for a11y purposes.1220 -1 * Must be a real, numerical, decimal value; heavily inspired by1221 -1 * http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number1222 -1 * @param {string} value1223 -1 * @return {!Object}-1 1255 * Finds descendants of this element which implement the given ARIA role. -1 1256 * Will look for descendants with implicit or explicit role. -1 1257 * @param {Element} element an HTML DOM element. -1 1258 * @param {string} role The role you seek. -1 1259 * @return {!Array.<Element>} An array of matching elements. -1 1260 * @example -1 1261 * var container = document.createElement("div"); -1 1262 * var button = document.createElement("button"); -1 1263 * var span = document.createElement("span"); -1 1264 * span.setAttribute("role", "button"); -1 1265 * container.appendChild(button); -1 1266 * container.appendChild(span); -1 1267 * var result = axs.utils.findDescendantsWithRole(container, "button"); // result is an array containing both 'button' and 'span' 1224 1268 */1225 -1 axs.utils.isValidNumber = function(value) {1226 -1 var failResult = {1227 -1 'valid': false,1228 -1 'value': value,1229 -1 'reason': '"' + value + '" is not a number'1230 -1 };1231 -1 if (!value) {1232 -1 return failResult;1233 -1 }1234 -1 if (/^0x/i.test(value)) {1235 -1 failResult.reason = '"' + value + '" is not a decimal number'; // hex is not accepted1236 -1 return failResult;1237 -1 }1238 -1 var parsedValue = value * 1;1239 -1 if (!isFinite(parsedValue)) {1240 -1 return failResult;-1 1269 axs.utils.findDescendantsWithRole = function(element, role) { -1 1270 if (!(element && role)) -1 1271 return []; -1 1272 var selector = axs.properties.getSelectorForRole(role); -1 1273 if (!selector) -1 1274 return []; -1 1275 var result = element.querySelectorAll(selector); -1 1276 if (result) { // Convert NodeList to Array; methinks 80/20 that's what callers want. -1 1277 result = Array.prototype.map.call(result, function(item) { return item; }); -1 1278 } else { -1 1279 return []; 1241 1280 }1242 -1 return { 'valid': true, 'value': parsedValue };-1 1281 return result; 1243 1282 }; 1244 1283 -1 1284 },{}],2:[function(require,module,exports){ -1 1285 // Copyright 2013 Google Inc. -1 1286 // -1 1287 // Licensed under the Apache License, Version 2.0 (the "License"); -1 1288 // you may not use this file except in compliance with the License. -1 1289 // You may obtain a copy of the License at -1 1290 // -1 1291 // http://www.apache.org/licenses/LICENSE-2.0 -1 1292 // -1 1293 // Unless required by applicable law or agreed to in writing, software -1 1294 // distributed under the License is distributed on an "AS IS" BASIS, -1 1295 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 1296 // See the License for the specific language governing permissions and -1 1297 // limitations under the License. -1 1298 -1 1299 goog.provide('axs.browserUtils'); -1 1300 1245 1301 /** -1 1302 * Use Webkit matcher when matches() is not supported. -1 1303 * Use Firefox matcher when Webkit is not supported. -1 1304 * Use IE matcher when neither webkit nor Firefox supported. 1246 1305 * @param {Element} element1247 -1 * @return {boolean}-1 1306 * @param {string} selector -1 1307 * @return {boolean} true if the element matches the selector 1248 1308 */1249 -1 axs.utils.isElementImplicitlyFocusable = function(element) {1250 -1 var defaultView = element.ownerDocument.defaultView;1251 -11252 -1 if (element instanceof defaultView.HTMLAnchorElement ||1253 -1 element instanceof defaultView.HTMLAreaElement)1254 -1 return element.hasAttribute('href');1255 -1 if (element instanceof defaultView.HTMLInputElement ||1256 -1 element instanceof defaultView.HTMLSelectElement ||1257 -1 element instanceof defaultView.HTMLTextAreaElement ||1258 -1 element instanceof defaultView.HTMLButtonElement ||1259 -1 element instanceof defaultView.HTMLIFrameElement)1260 -1 return !element.disabled;-1 1309 axs.browserUtils.matchSelector = function(element, selector) { -1 1310 if (element.matches) -1 1311 return element.matches(selector); -1 1312 if (element.webkitMatchesSelector) -1 1313 return element.webkitMatchesSelector(selector); -1 1314 if (element.mozMatchesSelector) -1 1315 return element.mozMatchesSelector(selector); -1 1316 if (element.msMatchesSelector) -1 1317 return element.msMatchesSelector(selector); 1261 1318 return false; 1262 1319 }; 1263 13201264 -1 /**1265 -1 * Returns an array containing the values of the given JSON-compatible object.1266 -1 * (Simply ignores any function values.)1267 -1 * @param {Object} obj1268 -1 * @return {Array}1269 -1 */1270 -1 axs.utils.values = function(obj) {1271 -1 var values = [];1272 -1 for (var key in obj) {1273 -1 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')1274 -1 values.push(obj[key]);1275 -1 }1276 -1 return values;1277 -1 };-1 1321 },{}],3:[function(require,module,exports){ -1 1322 // Copyright 2015 Google Inc. -1 1323 // -1 1324 // Licensed under the Apache License, Version 2.0 (the "License"); -1 1325 // you may not use this file except in compliance with the License. -1 1326 // You may obtain a copy of the License at -1 1327 // -1 1328 // http://www.apache.org/licenses/LICENSE-2.0 -1 1329 // -1 1330 // Unless required by applicable law or agreed to in writing, software -1 1331 // distributed under the License is distributed on an "AS IS" BASIS, -1 1332 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 1333 // See the License for the specific language governing permissions and -1 1334 // limitations under the License. -1 1335 -1 1336 goog.provide('axs.color'); -1 1337 goog.provide('axs.color.Color'); 1278 1338 1279 1339 /**1280 -1 * Returns an object containing the same keys and values as the given1281 -1 * JSON-compatible object. (Simply ignores any function values.)1282 -1 * @param {Object} obj1283 -1 * @return {Object}-1 1340 * @constructor -1 1341 * @param {number} red -1 1342 * @param {number} green -1 1343 * @param {number} blue -1 1344 * @param {number} alpha 1284 1345 */1285 -1 axs.utils.namedValues = function(obj) {1286 -1 var values = {};1287 -1 for (var key in obj) {1288 -1 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')1289 -1 values[key] = obj[key];1290 -1 }1291 -1 return values;-1 1346 axs.color.Color = function(red, green, blue, alpha) { -1 1347 /** @type {number} */ -1 1348 this.red = red; -1 1349 -1 1350 /** @type {number} */ -1 1351 this.green = green; -1 1352 -1 1353 /** @type {number} */ -1 1354 this.blue = blue; -1 1355 -1 1356 /** @type {number} */ -1 1357 this.alpha = alpha; 1292 1358 }; 1293 1359 1294 1360 /**1295 -1 * Escapes a given ID to be used in a CSS selector1296 -1 *1297 -1 * @private1298 -1 * @param {!string} id The ID to be escaped1299 -1 * @return {string} The escaped ID1300 -1 */1301 -1 function escapeId(id) {1302 -1 return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; });1303 -1 }1304 -11305 -1 /** Gets a CSS selector text for a DOM object.1306 -1 * @param {Node} obj The DOM object.1307 -1 * @return {string} CSS selector text for the DOM object.-1 1361 * @constructor -1 1362 * See https://en.wikipedia.org/wiki/YCbCr for more information. -1 1363 * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr]. -1 1364 * All numbers are in the range [0, 1]. 1308 1365 */1309 -1 axs.utils.getQuerySelectorText = function(obj) {1310 -1 if (obj == null || obj.tagName == 'HTML') {1311 -1 return 'html';1312 -1 } else if (obj.tagName == 'BODY') {1313 -1 return 'body';1314 -1 }-1 1366 axs.color.YCbCr = function(coords) { -1 1367 /** @type {number} */ -1 1368 this.luma = this.z = coords[0]; 1315 13691316 -1 if (obj.hasAttribute) {1317 -1 if (obj.id) {1318 -1 return '#' + escapeId(obj.id);1319 -1 }-1 1370 /** @type {number} */ -1 1371 this.Cb = this.x = coords[1]; 1320 13721321 -1 if (obj.className) {1322 -1 var selector = '';1323 -1 for (var i = 0; i < obj.classList.length; i++)1324 -1 selector += '.' + obj.classList[i];-1 1373 /** @type {number} */ -1 1374 this.Cr = this.y = coords[2]; -1 1375 }; 1325 13761326 -1 var total = 0;1327 -1 if (obj.parentNode) {1328 -1 for (i = 0; i < obj.parentNode.children.length; i++) {1329 -1 var similar = obj.parentNode.children[i];1330 -1 if (axs.browserUtils.matchSelector(similar, selector))1331 -1 total++;1332 -1 if (similar === obj)1333 -1 break;1334 -1 }1335 -1 } else {1336 -1 total = 1;1337 -1 }-1 1377 axs.color.YCbCr.prototype = { -1 1378 /** -1 1379 * @param {number} scalar -1 1380 * @return {axs.color.YCbCr} This color multiplied by the given scalar -1 1381 */ -1 1382 multiply: function(scalar) { -1 1383 var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ]; -1 1384 return new axs.color.YCbCr(result); -1 1385 }, 1338 13861339 -1 if (total == 1) {1340 -1 return axs.utils.getQuerySelectorText(obj.parentNode) +1341 -1 ' > ' + selector;1342 -1 }1343 -1 }-1 1387 /** -1 1388 * @param {axs.color.YCbCr} other -1 1389 * @return {axs.color.YCbCr} This plus other -1 1390 */ -1 1391 add: function(other) { -1 1392 var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ]; -1 1393 return new axs.color.YCbCr(result); -1 1394 }, 1344 13951345 -1 if (obj.parentNode) {1346 -1 var similarTags = obj.parentNode.children;1347 -1 var total = 1;1348 -1 var i = 0;1349 -1 while (similarTags[i] !== obj) {1350 -1 if (similarTags[i].tagName == obj.tagName) {1351 -1 total++;1352 -1 }1353 -1 i++;1354 -1 }-1 1396 /** -1 1397 * @param {axs.color.YCbCr} other -1 1398 * @return {axs.color.YCbCr} This minus other -1 1399 */ -1 1400 subtract: function(other) { -1 1401 var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ]; -1 1402 return new axs.color.YCbCr(result); -1 1403 } 1355 14041356 -1 var next = '';1357 -1 if (obj.parentNode.tagName != 'BODY') {1358 -1 next = axs.utils.getQuerySelectorText(obj.parentNode) +1359 -1 ' > ';1360 -1 }-1 1405 }; 1361 14061362 -1 if (total == 1) {1363 -1 return next +1364 -1 obj.tagName;1365 -1 } else {1366 -1 return next +1367 -1 obj.tagName +1368 -1 ':nth-of-type(' + total + ')';1369 -1 }1370 -1 }1371 14071372 -1 } else if (obj.selectorText) {1373 -1 return obj.selectorText;1374 -1 }-1 1408 /** -1 1409 * Calculate the contrast ratio between the two given colors. Returns the ratio -1 1410 * to 1, for example for two two colors with a contrast ratio of 21:1, this -1 1411 * function will return 21. -1 1412 * @param {axs.color.Color} fgColor -1 1413 * @param {axs.color.Color} bgColor -1 1414 * @return {!number} -1 1415 */ -1 1416 axs.color.calculateContrastRatio = function(fgColor, bgColor) { -1 1417 if (fgColor.alpha < 1) -1 1418 fgColor = axs.color.flattenColors(fgColor, bgColor); 1375 14191376 -1 return '';-1 1420 var fgLuminance = axs.color.calculateLuminance(fgColor); -1 1421 var bgLuminance = axs.color.calculateLuminance(bgColor); -1 1422 var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) / -1 1423 (Math.min(fgLuminance, bgLuminance) + 0.05); -1 1424 return contrastRatio; 1377 1425 }; 1378 1426 1379 1427 /**1380 -1 * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or1381 -1 * single ID reference.1382 -1 * @param {Element} element a potential referent.1383 -1 * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'.1384 -1 * @return {NodeList} The elements that refer to this element or null.-1 1428 * Calculate the luminance of the given color using the WCAG algorithm. -1 1429 * @param {axs.color.Color} color -1 1430 * @return {number} 1385 1431 */1386 -1 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) {1387 -1 var propertyToSelector = function(propertyKey) {1388 -1 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];1389 -1 if (propertyDetails) {1390 -1 if (propertyDetails.valueType === ('idref')) {1391 -1 return '[aria-' + propertyKey + '=\'' + id + '\']';1392 -1 } else if (propertyDetails.valueType === ('idref_list')) {1393 -1 return '[aria-' + propertyKey + '~=\'' + id + '\']';1394 -1 }1395 -1 }1396 -1 return '';1397 -1 };1398 -1 if (!element)1399 -1 return null;1400 -1 var id = element.id;1401 -1 if (!id)1402 -1 return null;1403 -1 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector-1 1432 axs.color.calculateLuminance = function(color) { -1 1433 /* var rSRGB = color.red / 255; -1 1434 var gSRGB = color.green / 255; -1 1435 var bSRGB = color.blue / 255; 1404 14361405 -1 if (opt_attributeName) {1406 -1 var propertyKey = opt_attributeName.replace(/^aria-/, '');1407 -1 var referrerQuery = propertyToSelector(propertyKey);1408 -1 if (referrerQuery) {1409 -1 return element.ownerDocument.querySelectorAll(referrerQuery);1410 -1 }1411 -1 } else {1412 -1 var selectors = [];1413 -1 for (var propertyKey in axs.constants.ARIA_PROPERTIES) {1414 -1 var referrerQuery = propertyToSelector(propertyKey);1415 -1 if (referrerQuery) {1416 -1 selectors.push(referrerQuery);1417 -1 }1418 -1 }1419 -1 return element.ownerDocument.querySelectorAll(selectors.join(','));1420 -1 }1421 -1 return null;-1 1437 var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4); -1 1438 var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4); -1 1439 var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4); -1 1440 -1 1441 return 0.2126 * r + 0.7152 * g + 0.0722 * b; */ -1 1442 var ycc = axs.color.toYCbCr(color); -1 1443 return ycc.luma; 1422 1444 }; 1423 1445 1424 1446 /**1425 -1 * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or1426 -1 * single ID reference.1427 -1 * @param {Element} element a potential referent.1428 -1 * @return {NodeList} The elements that refer to this element.-1 1447 * Compute the luminance ratio between two luminance values. -1 1448 * @param {number} luminance1 -1 1449 * @param {number} luminance2 1429 1450 */1430 -1 axs.utils.getHtmlIdReferrers = function(element) {1431 -1 if (!element)1432 -1 return null;1433 -1 var id = element.id;1434 -1 if (!id)1435 -1 return null;1436 -1 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector1437 -1 var selectorTemplates = [1438 -1 '[contextmenu=\'{id}\']',1439 -1 '[itemref~=\'{id}\']',1440 -1 'button[form=\'{id}\']',1441 -1 'button[menu=\'{id}\']',1442 -1 'fieldset[form=\'{id}\']',1443 -1 'input[form=\'{id}\']',1444 -1 'input[list=\'{id}\']',1445 -1 'keygen[form=\'{id}\']',1446 -1 'label[for=\'{id}\']',1447 -1 'label[form=\'{id}\']',1448 -1 'menuitem[command=\'{id}\']',1449 -1 'object[form=\'{id}\']',1450 -1 'output[for~=\'{id}\']',1451 -1 'output[form=\'{id}\']',1452 -1 'select[form=\'{id}\']',1453 -1 'td[headers~=\'{id}\']',1454 -1 'textarea[form=\'{id}\']',1455 -1 'tr[headers~=\'{id}\']'];1456 -1 var selectors = selectorTemplates.map(function(selector) {1457 -1 return selector.replace('\{id\}', id);1458 -1 });1459 -1 return element.ownerDocument.querySelectorAll(selectors.join(','));-1 1451 axs.color.luminanceRatio = function(luminance1, luminance2) { -1 1452 return (Math.max(luminance1, luminance2) + 0.05) / -1 1453 (Math.min(luminance1, luminance2) + 0.05); 1460 1454 }; 1461 1455 1462 1456 /**1463 -1 * Gets a list of all IDs this element references in either ARIA or HTML attributes.1464 -1 *1465 -1 * @param {Element} element The element to check for idref attributes.1466 -1 * @returns {Array.<string>} Any IDs this element references.-1 1457 * @param {string} colorString The color string from CSS. -1 1458 * @return {?axs.color.Color} 1467 1459 */1468 -1 axs.utils.getReferencedIds = function(element) {1469 -1 var result = [];1470 -1 var addResult = function(ids) {1471 -1 if (ids) {1472 -1 if (ids.indexOf(' ') > 0) {1473 -1 result = result.concat(attrib.value.split(' '));1474 -1 } else {1475 -1 result.push(ids);1476 -1 }1477 -1 }1478 -1 };1479 -1 for (var i = 0; i < element.attributes.length; i++) {1480 -1 var tagName = element.tagName.toLowerCase();1481 -1 var attrib = element.attributes[i];1482 -1 if (attrib.specified) {1483 -1 var attribName = attrib.name;1484 -1 var ariaAttr = attribName.match(/aria-(.+)/);1485 -1 if (ariaAttr) {1486 -1 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]];1487 -1 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) {1488 -1 addResult(attrib.value);1489 -1 }1490 -1 continue;1491 -1 }1492 -1 switch (attribName) {1493 -1 case 'contextmenu':1494 -1 case 'itemref':1495 -1 addResult(attrib.value);1496 -1 break;1497 -1 case 'form':1498 -1 if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' ||1499 -1 tagName == 'keygen' || tagName == 'label' || tagName == 'object' ||1500 -1 tagName == 'output' || tagName == 'select' || tagName == 'textarea') {1501 -1 addResult(attrib.value);1502 -1 }1503 -1 break;1504 -1 case 'for':1505 -1 if (tagName == 'label' || tagName == 'output') {1506 -1 addResult(attrib.value);1507 -1 }1508 -1 break;1509 -1 case 'menu':1510 -1 if (tagName == 'button') {1511 -1 addResult(attrib.value);1512 -1 }1513 -1 break;1514 -1 case 'list':1515 -1 if (tagName == 'input') {1516 -1 addResult(attrib.value);1517 -1 }1518 -1 break;1519 -1 case 'command':1520 -1 if (tagName == 'menuitem') {1521 -1 addResult(attrib.value);1522 -1 }1523 -1 break;1524 -1 case 'headers':1525 -1 if (tagName == 'td' || tagName == 'tr') {1526 -1 addResult(attrib.value);1527 -1 }1528 -1 break;1529 -1 }1530 -1 }-1 1460 axs.color.parseColor = function(colorString) { -1 1461 if (colorString === "transparent") { -1 1462 return new axs.color.Color(0, 0, 0, 0); 1531 1463 }1532 -1 return result;1533 -1 };-1 1464 var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/; -1 1465 var match = colorString.match(rgbRegex); 1534 14661535 -1 /**1536 -1 * Gets elements that refer to this element in an attribute that takes an ID reference list or1537 -1 * single ID reference.1538 -1 * @param {Element} element a potential referent.1539 -1 * @return {Array<Element>} The elements that refer to this element.1540 -1 */1541 -1 axs.utils.getIdReferrers = function(element) {1542 -1 var result = [];1543 -1 var referrers = axs.utils.getHtmlIdReferrers(element);1544 -1 if (referrers) {1545 -1 result = result.concat(Array.prototype.slice.call(referrers));-1 1467 if (match) { -1 1468 var r = parseInt(match[1], 10); -1 1469 var g = parseInt(match[2], 10); -1 1470 var b = parseInt(match[3], 10); -1 1471 var a = 1; -1 1472 return new axs.color.Color(r, g, b, a); 1546 1473 }1547 -1 referrers = axs.utils.getAriaIdReferrers(element);1548 -1 if (referrers) {1549 -1 result = result.concat(Array.prototype.slice.call(referrers));-1 1474 -1 1475 var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/; -1 1476 match = colorString.match(rgbaRegex); -1 1477 if (match) { -1 1478 var r = parseInt(match[1], 10); -1 1479 var g = parseInt(match[2], 10); -1 1480 var b = parseInt(match[3], 10); -1 1481 var a = parseFloat(match[4]); -1 1482 return new axs.color.Color(r, g, b, a); 1550 1483 }1551 -1 return result;-1 1484 -1 1485 return null; 1552 1486 }; 1553 1487 1554 1488 /**1555 -1 * Gets elements which this element refers to in the given attribute.1556 -1 * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'.1557 -1 * @param {Element} element The DOM element which has the ARIA attribute.1558 -1 * @return {!Array.<Element>} An array of elements that are referred to by this element.1559 -1 * @example1560 -1 * var owner = document.body.appendChild(document.createElement("div"));1561 -1 * var owned = document.body.appendChild(document.createElement("div"));1562 -1 * owner.setAttribute("aria-owns", "kungfu");1563 -1 * owned.setAttribute("id", "kungfu");1564 -1 * console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned); // This will log 'true'-1 1489 * @param {number} value The value of a color channel, 0 <= value <= 0xFF -1 1490 * @return {!string} 1565 1491 */1566 -1 axs.utils.getIdReferents = function(attributeName, element) {1567 -1 var result = [];1568 -1 var propertyKey = attributeName.replace(/^aria-/, '');1569 -1 var property = axs.constants.ARIA_PROPERTIES[propertyKey];1570 -1 if (!property || !element.hasAttribute(attributeName))1571 -1 return result;1572 -1 var propertyType = property.valueType;1573 -1 if (propertyType === 'idref_list' || propertyType === 'idref') {1574 -1 var ownerDocument = element.ownerDocument;1575 -1 var ids = element.getAttribute(attributeName);1576 -1 ids = ids.split(/\s+/);1577 -1 for (var i = 0, len = ids.length; i < len; i++) {1578 -1 var next = ownerDocument.getElementById(ids[i]);1579 -1 if (next) {1580 -1 result[result.length] = next;1581 -1 }1582 -1 }1583 -1 }1584 -1 return result;-1 1492 axs.color.colorChannelToString = function(value) { -1 1493 value = Math.round(value); -1 1494 if (value <= 0xF) -1 1495 return '0' + value.toString(16); -1 1496 return value.toString(16); 1585 1497 }; 1586 1498 1587 1499 /**1588 -1 * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'.1589 -1 * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list'].1590 -1 * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match.-1 1500 * @param {axs.color.Color} color -1 1501 * @return {!string} 1591 1502 */1592 -1 axs.utils.getAriaPropertiesByValueType = function(valueTypes) {1593 -1 var result = {};1594 -1 for (var propertyName in axs.constants.ARIA_PROPERTIES) {1595 -1 var property = axs.constants.ARIA_PROPERTIES[propertyName];1596 -1 if (property && valueTypes.indexOf(property.valueType) >= 0) {1597 -1 result[propertyName] = property;1598 -1 }-1 1503 axs.color.colorToString = function(color) { -1 1504 if (color.alpha == 1) { -1 1505 return '#' + axs.color.colorChannelToString(color.red) + -1 1506 axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue); 1599 1507 }1600 -1 return result;-1 1508 else -1 1509 return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')'; 1601 1510 }; 1602 1511 1603 1512 /**1604 -1 * Builds a selector that matches an element with any of these ARIA properties.1605 -1 * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES1606 -1 * @return {!string} The selector.1607 -1 */1608 -1 axs.utils.getSelectorForAriaProperties = function(ariaProperties) {1609 -1 var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties));1610 -1 var result = propertyNames.map(function(propertyName) {1611 -1 return '[aria-' + propertyName + ']';1612 -1 });1613 -1 result.sort(); // facilitates reading long selectors and unit testing1614 -1 return result.join(',');1615 -1 };1616 -11617 -1 /**1618 -1 * Finds descendants of this element which implement the given ARIA role.1619 -1 * Will look for descendants with implicit or explicit role.1620 -1 * @param {Element} element an HTML DOM element.1621 -1 * @param {string} role The role you seek.1622 -1 * @return {!Array.<Element>} An array of matching elements.1623 -1 * @example1624 -1 * var container = document.createElement("div");1625 -1 * var button = document.createElement("button");1626 -1 * var span = document.createElement("span");1627 -1 * span.setAttribute("role", "button");1628 -1 * container.appendChild(button);1629 -1 * container.appendChild(span);1630 -1 * var result = axs.utils.findDescendantsWithRole(container, "button"); // result is an array containing both 'button' and 'span'-1 1513 * Compute a desired luminance given a given luminance and a desired contrast ratio. -1 1514 * @param {number} luminance The given luminance. -1 1515 * @param {number} contrast The desired contrast ratio. -1 1516 * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance. -1 1517 * @return {number} The desired luminance. 1631 1518 */1632 -1 axs.utils.findDescendantsWithRole = function(element, role) {1633 -1 if (!(element && role))1634 -1 return [];1635 -1 var selector = axs.properties.getSelectorForRole(role);1636 -1 if (!selector)1637 -1 return [];1638 -1 var result = element.querySelectorAll(selector);1639 -1 if (result) { // Convert NodeList to Array; methinks 80/20 that's what callers want.1640 -1 result = Array.prototype.map.call(result, function(item) { return item; });-1 1519 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) { -1 1520 if (higher) { -1 1521 var newLuminance = (luminance + 0.05) * contrast - 0.05; -1 1522 return newLuminance; 1641 1523 } else {1642 -1 return [];-1 1524 var newLuminance = (luminance + 0.05) / contrast - 0.05; -1 1525 return newLuminance; 1643 1526 }1644 -1 return result;1645 1527 }; 1646 15281647 -1 },{}],4:[function(require,module,exports){1648 -1 // Copyright 2013 Google Inc.1649 -1 //1650 -1 // Licensed under the Apache License, Version 2.0 (the "License");1651 -1 // you may not use this file except in compliance with the License.1652 -1 // You may obtain a copy of the License at1653 -1 //1654 -1 // http://www.apache.org/licenses/LICENSE-2.01655 -1 //1656 -1 // Unless required by applicable law or agreed to in writing, software1657 -1 // distributed under the License is distributed on an "AS IS" BASIS,1658 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.1659 -1 // See the License for the specific language governing permissions and1660 -1 // limitations under the License.1661 -11662 -1 goog.provide('axs.browserUtils');1663 -11664 1529 /**1665 -1 * Use Webkit matcher when matches() is not supported.1666 -1 * Use Firefox matcher when Webkit is not supported.1667 -1 * Use IE matcher when neither webkit nor Firefox supported.1668 -1 * @param {Element} element1669 -1 * @param {string} selector1670 -1 * @return {boolean} true if the element matches the selector-1 1530 * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is -1 1531 * as close as possible to the original color. -1 1532 * @param {axs.color.YCbCr} ycc The original color in YCbCr form. -1 1533 * @param {number} luma The desired luminance -1 1534 * @return {!axs.color.Color} A new color in RGB. 1671 1535 */1672 -1 axs.browserUtils.matchSelector = function(element, selector) {1673 -1 if (element.matches)1674 -1 return element.matches(selector);1675 -1 if (element.webkitMatchesSelector)1676 -1 return element.webkitMatchesSelector(selector);1677 -1 if (element.mozMatchesSelector)1678 -1 return element.mozMatchesSelector(selector);1679 -1 if (element.msMatchesSelector)1680 -1 return element.msMatchesSelector(selector);1681 -1 return false;1682 -1 };1683 -11684 -1 },{}],5:[function(require,module,exports){1685 -1 // Copyright 2015 Google Inc.1686 -1 //1687 -1 // Licensed under the Apache License, Version 2.0 (the "License");1688 -1 // you may not use this file except in compliance with the License.1689 -1 // You may obtain a copy of the License at1690 -1 //1691 -1 // http://www.apache.org/licenses/LICENSE-2.01692 -1 //1693 -1 // Unless required by applicable law or agreed to in writing, software1694 -1 // distributed under the License is distributed on an "AS IS" BASIS,1695 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.1696 -1 // See the License for the specific language governing permissions and1697 -1 // limitations under the License.-1 1536 axs.color.translateColor = function(ycc, luma) { -1 1537 var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC; -1 1538 var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE -1 1539 : axs.color.YCC_CUBE_FACES_BLACK; 1698 15401699 -1 goog.provide('axs.color');1700 -1 goog.provide('axs.color.Color');-1 1541 var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]); -1 1542 var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]); -1 1543 var line = { a: a, b: b }; 1701 15441702 -1 /**1703 -1 * @constructor1704 -1 * @param {number} red1705 -1 * @param {number} green1706 -1 * @param {number} blue1707 -1 * @param {number} alpha1708 -1 */1709 -1 axs.color.Color = function(red, green, blue, alpha) {1710 -1 /** @type {number} */1711 -1 this.red = red;-1 1545 var intersection = null; -1 1546 for (var i = 0; i < cubeFaces.length; i++) { -1 1547 var cubeFace = cubeFaces[i]; -1 1548 intersection = axs.color.findIntersection(line, cubeFace); -1 1549 // If intersection within [0, 1] in Z axis, it is within the cube. -1 1550 if (intersection.z >= 0 && intersection.z <= 1) -1 1551 break; -1 1552 } -1 1553 if (!intersection) { -1 1554 // Should never happen -1 1555 throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + "."; -1 1556 } -1 1557 if (intersection.x != ycc.x || intersection.y != ycc.y) { -1 1558 // Should never happen -1 1559 throw "Intersection has wrong Cb/Cr values."; -1 1560 } 1712 15611713 -1 /** @type {number} */1714 -1 this.green = green;-1 1562 // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube -1 1563 // and we can immediately return new value. -1 1564 if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) { -1 1565 var translatedColor = [luma, ycc.Cb, ycc.Cr]; -1 1566 return axs.color.fromYCbCrArray(translatedColor); -1 1567 } 1715 15681716 -1 /** @type {number} */1717 -1 this.blue = blue;-1 1569 // Otherwise, translate from intersection towards white/black such that luma is correct. -1 1570 var dLuma = luma - intersection.luma; -1 1571 var scale = dLuma / (endpoint.luma - intersection.luma); -1 1572 var translatedColor = [ luma, -1 1573 intersection.Cb - (intersection.Cb * scale), -1 1574 intersection.Cr - (intersection.Cr * scale) ]; 1718 15751719 -1 /** @type {number} */1720 -1 this.alpha = alpha;-1 1576 return axs.color.fromYCbCrArray(translatedColor); 1721 1577 }; 1722 1578 -1 1579 /** @typedef {{fg: string, bg: string, contrast: string}} */ -1 1580 axs.color.SuggestedColors; -1 1581 1723 1582 /**1724 -1 * @constructor1725 -1 * See https://en.wikipedia.org/wiki/YCbCr for more information.1726 -1 * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr].1727 -1 * All numbers are in the range [0, 1].-1 1583 * @param {axs.color.Color} bgColor -1 1584 * @param {axs.color.Color} fgColor -1 1585 * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio. -1 1586 * @return {Object.<string, axs.color.SuggestedColors>} 1728 1587 */1729 -1 axs.color.YCbCr = function(coords) {1730 -1 /** @type {number} */1731 -1 this.luma = this.z = coords[0];1732 -11733 -1 /** @type {number} */1734 -1 this.Cb = this.x = coords[1];1735 -11736 -1 /** @type {number} */1737 -1 this.Cr = this.y = coords[2];1738 -1 };-1 1588 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) { -1 1589 var colors = {}; -1 1590 var bgLuminance = axs.color.calculateLuminance(bgColor); -1 1591 var fgLuminance = axs.color.calculateLuminance(fgColor); 1739 15921740 -1 axs.color.YCbCr.prototype = {1741 -1 /**1742 -1 * @param {number} scalar1743 -1 * @return {axs.color.YCbCr} This color multiplied by the given scalar1744 -1 */1745 -1 multiply: function(scalar) {1746 -1 var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ];1747 -1 return new axs.color.YCbCr(result);1748 -1 },-1 1593 var fgLuminanceIsHigher = fgLuminance > bgLuminance; -1 1594 var fgYCbCr = axs.color.toYCbCr(fgColor); -1 1595 var bgYCbCr = axs.color.toYCbCr(bgColor); -1 1596 for (var desiredLabel in desiredContrastRatios) { -1 1597 var desiredContrast = desiredContrastRatios[desiredLabel]; 1749 15981750 -1 /**1751 -1 * @param {axs.color.YCbCr} other1752 -1 * @return {axs.color.YCbCr} This plus other1753 -1 */1754 -1 add: function(other) {1755 -1 var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ];1756 -1 return new axs.color.YCbCr(result);1757 -1 },-1 1599 var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher); -1 1600 if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) { -1 1601 var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance); -1 1602 var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor); -1 1603 var suggestedColors = {}; -1 1604 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor)); -1 1605 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor)); -1 1606 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2)); -1 1607 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors); -1 1608 continue; -1 1609 } 1758 16101759 -1 /**1760 -1 * @param {axs.color.YCbCr} other1761 -1 * @return {axs.color.YCbCr} This minus other1762 -1 */1763 -1 subtract: function(other) {1764 -1 var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ];1765 -1 return new axs.color.YCbCr(result);-1 1611 var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher); -1 1612 if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) { -1 1613 var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance); -1 1614 var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor); -1 1615 var suggestedColors = {}; -1 1616 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor)); -1 1617 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor)); -1 1618 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2)); -1 1619 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors); -1 1620 } 1766 1621 }1767 -1-1 1622 return colors; 1768 1623 }; 1769 16241770 -11771 1625 /**1772 -1 * Calculate the contrast ratio between the two given colors. Returns the ratio1773 -1 * to 1, for example for two two colors with a contrast ratio of 21:1, this1774 -1 * function will return 21.-1 1626 * Combine the two given color according to alpha blending. 1775 1627 * @param {axs.color.Color} fgColor 1776 1628 * @param {axs.color.Color} bgColor1777 -1 * @return {!number}-1 1629 * @return {axs.color.Color} 1778 1630 */1779 -1 axs.color.calculateContrastRatio = function(fgColor, bgColor) {1780 -1 if (fgColor.alpha < 1)1781 -1 fgColor = axs.color.flattenColors(fgColor, bgColor);-1 1631 axs.color.flattenColors = function(fgColor, bgColor) { -1 1632 var alpha = fgColor.alpha; -1 1633 var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red); -1 1634 var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green); -1 1635 var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue); -1 1636 var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha)); 1782 16371783 -1 var fgLuminance = axs.color.calculateLuminance(fgColor);1784 -1 var bgLuminance = axs.color.calculateLuminance(bgColor);1785 -1 var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) /1786 -1 (Math.min(fgLuminance, bgLuminance) + 0.05);1787 -1 return contrastRatio;1788 -1 };1789 -11790 -1 /**1791 -1 * Calculate the luminance of the given color using the WCAG algorithm.1792 -1 * @param {axs.color.Color} color1793 -1 * @return {number}1794 -1 */1795 -1 axs.color.calculateLuminance = function(color) {1796 -1 /* var rSRGB = color.red / 255;1797 -1 var gSRGB = color.green / 255;1798 -1 var bSRGB = color.blue / 255;1799 -11800 -1 var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);1801 -1 var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);1802 -1 var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);1803 -11804 -1 return 0.2126 * r + 0.7152 * g + 0.0722 * b; */1805 -1 var ycc = axs.color.toYCbCr(color);1806 -1 return ycc.luma;1807 -1 };1808 -11809 -1 /**1810 -1 * Compute the luminance ratio between two luminance values.1811 -1 * @param {number} luminance11812 -1 * @param {number} luminance21813 -1 */1814 -1 axs.color.luminanceRatio = function(luminance1, luminance2) {1815 -1 return (Math.max(luminance1, luminance2) + 0.05) /1816 -1 (Math.min(luminance1, luminance2) + 0.05);1817 -1 };1818 -11819 -1 /**1820 -1 * @param {string} colorString The color string from CSS.1821 -1 * @return {?axs.color.Color}1822 -1 */1823 -1 axs.color.parseColor = function(colorString) {1824 -1 if (colorString === "transparent") {1825 -1 return new axs.color.Color(0, 0, 0, 0);1826 -1 }1827 -1 var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/;1828 -1 var match = colorString.match(rgbRegex);1829 -11830 -1 if (match) {1831 -1 var r = parseInt(match[1], 10);1832 -1 var g = parseInt(match[2], 10);1833 -1 var b = parseInt(match[3], 10);1834 -1 var a = 1;1835 -1 return new axs.color.Color(r, g, b, a);1836 -1 }1837 -11838 -1 var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;1839 -1 match = colorString.match(rgbaRegex);1840 -1 if (match) {1841 -1 var r = parseInt(match[1], 10);1842 -1 var g = parseInt(match[2], 10);1843 -1 var b = parseInt(match[3], 10);1844 -1 var a = parseFloat(match[4]);1845 -1 return new axs.color.Color(r, g, b, a);1846 -1 }1847 -11848 -1 return null;1849 -1 };1850 -11851 -1 /**1852 -1 * @param {number} value The value of a color channel, 0 <= value <= 0xFF1853 -1 * @return {!string}1854 -1 */1855 -1 axs.color.colorChannelToString = function(value) {1856 -1 value = Math.round(value);1857 -1 if (value <= 0xF)1858 -1 return '0' + value.toString(16);1859 -1 return value.toString(16);1860 -1 };1861 -11862 -1 /**1863 -1 * @param {axs.color.Color} color1864 -1 * @return {!string}1865 -1 */1866 -1 axs.color.colorToString = function(color) {1867 -1 if (color.alpha == 1) {1868 -1 return '#' + axs.color.colorChannelToString(color.red) +1869 -1 axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue);1870 -1 }1871 -1 else1872 -1 return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')';1873 -1 };1874 -11875 -1 /**1876 -1 * Compute a desired luminance given a given luminance and a desired contrast ratio.1877 -1 * @param {number} luminance The given luminance.1878 -1 * @param {number} contrast The desired contrast ratio.1879 -1 * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance.1880 -1 * @return {number} The desired luminance.1881 -1 */1882 -1 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) {1883 -1 if (higher) {1884 -1 var newLuminance = (luminance + 0.05) * contrast - 0.05;1885 -1 return newLuminance;1886 -1 } else {1887 -1 var newLuminance = (luminance + 0.05) / contrast - 0.05;1888 -1 return newLuminance;1889 -1 }1890 -1 };1891 -11892 -1 /**1893 -1 * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is1894 -1 * as close as possible to the original color.1895 -1 * @param {axs.color.YCbCr} ycc The original color in YCbCr form.1896 -1 * @param {number} luma The desired luminance1897 -1 * @return {!axs.color.Color} A new color in RGB.1898 -1 */1899 -1 axs.color.translateColor = function(ycc, luma) {1900 -1 var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC;1901 -1 var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE1902 -1 : axs.color.YCC_CUBE_FACES_BLACK;1903 -11904 -1 var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]);1905 -1 var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]);1906 -1 var line = { a: a, b: b };1907 -11908 -1 var intersection = null;1909 -1 for (var i = 0; i < cubeFaces.length; i++) {1910 -1 var cubeFace = cubeFaces[i];1911 -1 intersection = axs.color.findIntersection(line, cubeFace);1912 -1 // If intersection within [0, 1] in Z axis, it is within the cube.1913 -1 if (intersection.z >= 0 && intersection.z <= 1)1914 -1 break;1915 -1 }1916 -1 if (!intersection) {1917 -1 // Should never happen1918 -1 throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + ".";1919 -1 }1920 -1 if (intersection.x != ycc.x || intersection.y != ycc.y) {1921 -1 // Should never happen1922 -1 throw "Intersection has wrong Cb/Cr values.";1923 -1 }1924 -11925 -1 // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube1926 -1 // and we can immediately return new value.1927 -1 if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) {1928 -1 var translatedColor = [luma, ycc.Cb, ycc.Cr];1929 -1 return axs.color.fromYCbCrArray(translatedColor);1930 -1 }1931 -11932 -1 // Otherwise, translate from intersection towards white/black such that luma is correct.1933 -1 var dLuma = luma - intersection.luma;1934 -1 var scale = dLuma / (endpoint.luma - intersection.luma);1935 -1 var translatedColor = [ luma,1936 -1 intersection.Cb - (intersection.Cb * scale),1937 -1 intersection.Cr - (intersection.Cr * scale) ];1938 -11939 -1 return axs.color.fromYCbCrArray(translatedColor);1940 -1 };1941 -11942 -1 /** @typedef {{fg: string, bg: string, contrast: string}} */1943 -1 axs.color.SuggestedColors;1944 -11945 -1 /**1946 -1 * @param {axs.color.Color} bgColor1947 -1 * @param {axs.color.Color} fgColor1948 -1 * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio.1949 -1 * @return {Object.<string, axs.color.SuggestedColors>}1950 -1 */1951 -1 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) {1952 -1 var colors = {};1953 -1 var bgLuminance = axs.color.calculateLuminance(bgColor);1954 -1 var fgLuminance = axs.color.calculateLuminance(fgColor);1955 -11956 -1 var fgLuminanceIsHigher = fgLuminance > bgLuminance;1957 -1 var fgYCbCr = axs.color.toYCbCr(fgColor);1958 -1 var bgYCbCr = axs.color.toYCbCr(bgColor);1959 -1 for (var desiredLabel in desiredContrastRatios) {1960 -1 var desiredContrast = desiredContrastRatios[desiredLabel];1961 -11962 -1 var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher);1963 -1 if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) {1964 -1 var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance);1965 -1 var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor);1966 -1 var suggestedColors = {};1967 -1 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor));1968 -1 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor));1969 -1 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));1970 -1 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);1971 -1 continue;1972 -1 }1973 -11974 -1 var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher);1975 -1 if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) {1976 -1 var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance);1977 -1 var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor);1978 -1 var suggestedColors = {};1979 -1 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor));1980 -1 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor));1981 -1 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));1982 -1 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);1983 -1 }1984 -1 }1985 -1 return colors;1986 -1 };1987 -11988 -1 /**1989 -1 * Combine the two given color according to alpha blending.1990 -1 * @param {axs.color.Color} fgColor1991 -1 * @param {axs.color.Color} bgColor1992 -1 * @return {axs.color.Color}1993 -1 */1994 -1 axs.color.flattenColors = function(fgColor, bgColor) {1995 -1 var alpha = fgColor.alpha;1996 -1 var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red);1997 -1 var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green);1998 -1 var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue);1999 -1 var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha));2000 -12001 -1 return new axs.color.Color(r, g, b, a);-1 1638 return new axs.color.Color(r, g, b, a); 2002 1639 }; 2003 1640 2004 1641 /** @@ -2223,7 +1860,7 @@ axs.color.YCC_CUBE_FACES_WHITE = [ { p0: axs.color.WHITE_YCC, p1: axs.color.CYAN 2223 1860 { p0: axs.color.WHITE_YCC, p1: axs.color.MAGENTA_YCC, p2: axs.color.YELLOW_YCC }, 2224 1861 { p0: axs.color.WHITE_YCC, p1: axs.color.YELLOW_YCC, p2: axs.color.CYAN_YCC } ]; 2225 18622226 -1 },{}],6:[function(require,module,exports){-1 1863 },{}],4:[function(require,module,exports){ 2227 1864 // Copyright 2012 Google Inc. 2228 1865 // 2229 1866 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -3907,7 +3544,7 @@ axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO = { 3907 3544 }] 3908 3545 }; 3909 35463910 -1 },{}],7:[function(require,module,exports){-1 3547 },{}],5:[function(require,module,exports){ 3911 3548 // Copyright 2015 Google Inc. 3912 3549 // 3913 3550 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -4121,7 +3758,7 @@ axs.dom.composedTreeSearch = function(node, end, callbacks, parentFlags, opt_sha 4121 3758 return found; 4122 3759 }; 4123 37604124 -1 },{}],8:[function(require,module,exports){-1 3761 },{}],6:[function(require,module,exports){ 4125 3762 // Copyright 2012 Google Inc. 4126 3763 // 4127 3764 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -5050,7 +4687,7 @@ axs.properties.getNativelySupportedAttributes = function(element) { 5050 4687 }; 5051 4688 })(); 5052 46895053 -1 },{}],9:[function(require,module,exports){-1 4690 },{}],7:[function(require,module,exports){ 5054 4691 var query = require('./lib/query.js'); 5055 4692 var name = require('./lib/name.js'); 5056 4693 @@ -5066,7 +4703,7 @@ module.exports = { 5066 4703 closest: query.closest, 5067 4704 }; 5068 47055069 -1 },{"./lib/name.js":11,"./lib/query.js":12}],10:[function(require,module,exports){-1 4706 },{"./lib/name.js":9,"./lib/query.js":10}],8:[function(require,module,exports){ 5070 4707 exports.attributes = { 5071 4708 // widget 5072 4709 'autocomplete': 'token', @@ -5370,7 +5007,7 @@ exports.labelable = [ 5370 5007 'textarea', 5371 5008 ]; 5372 50095373 -1 },{}],11:[function(require,module,exports){-1 5010 },{}],9:[function(require,module,exports){ 5374 5011 var constants = require('./constants.js'); 5375 5012 var query = require('./query.js'); 5376 5013 @@ -5514,7 +5151,7 @@ module.exports = { 5514 5151 getDescription: getDescription, 5515 5152 }; 5516 51535517 -1 },{"./constants.js":10,"./query.js":12}],12:[function(require,module,exports){-1 5154 },{"./constants.js":8,"./query.js":10}],10:[function(require,module,exports){ 5518 5155 var constants = require('./constants.js'); 5519 5156 var util = require('./util.js'); 5520 5157 @@ -5661,7 +5298,7 @@ module.exports = { 5661 5298 closest: closest, 5662 5299 }; 5663 53005664 -1 },{"./constants.js":10,"./util.js":13}],13:[function(require,module,exports){-1 5301 },{"./constants.js":8,"./util.js":11}],11:[function(require,module,exports){ 5665 5302 var walkDOM = function(root, fn) { 5666 5303 if (fn(root) === false) { 5667 5304 return false; @@ -5691,7 +5328,7 @@ module.exports = { 5691 5328 searchUp: searchUp, 5692 5329 }; 5693 53305694 -1 },{}],14:[function(require,module,exports){-1 5331 },{}],12:[function(require,module,exports){ 5695 5332 /*! aXe v2.6.1 5696 5333 * Copyright (c) 2017 Deque Systems, Inc. 5697 5334 * @@ -14551,11 +14188,374 @@ module.exports = { 14551 14188 }() 14552 14189 }); 14553 14190 })(typeof window === 'object' ? window : this);14554 -1 },{}],15:[function(require,module,exports){-1 14191 },{}],13:[function(require,module,exports){ -1 14192 /*! -1 14193 calcNames 1.2, compute the Name and Description property values for a DOM node -1 14194 Returns an object with 'name' and 'desc' properties. -1 14195 Authored by Bryan Garaventa plus contrabutions by Tobias Bengfort -1 14196 Distributed under the terms of the Open Source Initiative OSI - MIT License -1 14197 */ -1 14198 -1 14199 var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) { -1 14200 if (!node || node.nodeType !== 1) { -1 14201 return; -1 14202 } -1 14203 -1 14204 var trim = function(str) { -1 14205 if (typeof str !== 'string') { -1 14206 return ''; -1 14207 } -1 14208 -1 14209 return str.replace(/^\s+|\s+$/g, ''); -1 14210 }; -1 14211 -1 14212 var walkDOM = function(node, fn, refNode) { -1 14213 if (!node) { -1 14214 return; -1 14215 } -1 14216 fn(node); -1 14217 -1 14218 if (!isException(node, refNode)) { -1 14219 node = node.firstChild; -1 14220 -1 14221 while (node) { -1 14222 walkDOM(node, fn, refNode); -1 14223 node = node.nextSibling; -1 14224 } -1 14225 } -1 14226 }; -1 14227 -1 14228 var isFocusable = function(node) { -1 14229 var nodeName = node.nodeName.toLowerCase(); -1 14230 -1 14231 if (node.getAttribute('tabindex')) { -1 14232 return true; -1 14233 } -1 14234 if (nodeName === 'a' && node.getAttribute('href')) { -1 14235 return true; -1 14236 } -1 14237 if (['input', 'select', 'button'].indexOf(nodeName) !== -1 && node.getAttribute('type') !== 'hidden') { -1 14238 return true; -1 14239 } -1 14240 return false; -1 14241 }; -1 14242 -1 14243 var isException = function(node, refNode) { -1 14244 if (!refNode || !node || refNode.nodeType !== 1 || node.nodeType !== 1) { -1 14245 return false; -1 14246 } -1 14247 -1 14248 var list1 = { -1 14249 roles: ['link', 'button', 'checkbox', 'option', 'radio', 'switch', 'tab', 'treeitem', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'cell', 'columnheader', 'rowheader', 'tooltip', 'heading'], -1 14250 tags: ['a', 'button', 'summary', 'input', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'menuitem', 'option', 'td', 'th'] -1 14251 }; -1 14252 -1 14253 var list2 = { -1 14254 roles: ['application', 'alert', 'log', 'marquee', 'timer', 'alertdialog', 'dialog', 'banner', 'complementary', 'form', 'main', 'navigation', 'region', 'search', 'article', 'document', 'feed', 'figure', 'img', 'math', 'toolbar', 'menu', 'menubar', 'grid', 'listbox', 'radiogroup', 'textbox', 'searchbox', 'spinbutton', 'scrollbar', 'slider', 'tablist', 'tabpanel', 'tree', 'treegrid', 'separator'], -1 14255 tags: ['article', 'aside', 'body', 'select', 'datalist', 'optgroup', 'dialog', 'figure', 'footer', 'form', 'header', 'hr', 'img', 'textarea', 'input', 'main', 'math', 'menu', 'nav', 'section'] -1 14256 }; -1 14257 -1 14258 var list3 = { -1 14259 roles: ['combobox', 'term', 'definition', 'directory', 'list', 'group', 'note', 'status', 'table', 'rowgroup', 'row', 'contentinfo'], -1 14260 tags: ['dl', 'ul', 'ol', 'dd', 'details', 'output', 'table', 'thead', 'tbody', 'tfoot', 'tr'] -1 14261 }; -1 14262 -1 14263 var inList = function(node, list) { -1 14264 var role = node.getAttribute('role'); -1 14265 var tag = node.nodeName.toLowerCase(); -1 14266 return ( -1 14267 list.roles.indexOf(role) >= 0 || -1 14268 (!role && list2.tags.indexOf(tag) >= 0) -1 14269 ); -1 14270 }; -1 14271 -1 14272 if (inList(node, list2)) { -1 14273 return true; -1 14274 } else if (inList(node, list3)) { -1 14275 if (node === refNode) { -1 14276 return !isFocusable(node); -1 14277 } else { -1 14278 return !inList(refNode, list1); -1 14279 } -1 14280 } else { -1 14281 return false; -1 14282 } -1 14283 }; -1 14284 -1 14285 var isHidden = function(node, refNode) { -1 14286 if (node.nodeType !== 1 || node == refNode) { -1 14287 return false; -1 14288 } -1 14289 -1 14290 if (node.getAttribute('aria-hidden') === 'true') { -1 14291 return true; -1 14292 } -1 14293 -1 14294 var style = {}; -1 14295 if (document.defaultView && document.defaultView.getComputedStyle) { -1 14296 style = document.defaultView.getComputedStyle(node, ''); -1 14297 } else if (node.currentStyle) { -1 14298 style = node.currentStyle; -1 14299 } -1 14300 if (style['display'] === 'none' || style['visibility'] === 'hidden') { -1 14301 return true; -1 14302 } -1 14303 -1 14304 return false; -1 14305 }; -1 14306 -1 14307 var getCSSText = function(node, refNode) { -1 14308 if (node.nodeType !== 1 || node == refNode || ['input', 'select', 'textarea', 'img', 'iframe'].indexOf(node.nodeName.toLowerCase()) !== -1) { -1 14309 return {before: '', after: ''}; -1 14310 } -1 14311 -1 14312 var getText = function(node, position) { -1 14313 var text = document.defaultView.getComputedStyle(node, position).getPropertyValue('content').replace(/^\"|\"$/g, ''); -1 14314 if (!text || text === 'none') { -1 14315 return ''; -1 14316 } else { -1 14317 return text; -1 14318 } -1 14319 }; -1 14320 -1 14321 if (document.defaultView && document.defaultView.getComputedStyle) { -1 14322 return { -1 14323 before: getText(node, ':before'), -1 14324 after: getText(node, ':after') -1 14325 }; -1 14326 } else { -1 14327 return {before: '', after: ''}; -1 14328 } -1 14329 }; -1 14330 -1 14331 var hasParentLabel = function(node, noLabel, refNode) { -1 14332 while (node && node !== refNode) { -1 14333 node = node.parentNode; -1 14334 -1 14335 if (node.getAttribute) { -1 14336 if (['presentation', 'none'].indexOf(node.getAttribute('role')) === -1) { -1 14337 if (!noLabel && node.getAttribute('aria-label')) { -1 14338 return true; -1 14339 } -1 14340 if (isHidden(node, refNode)) { -1 14341 return true; -1 14342 } -1 14343 } -1 14344 } -1 14345 } -1 14346 -1 14347 return false; -1 14348 }; -1 14349 -1 14350 var walk = function(refNode, stop, skip) { -1 14351 var fullName = ''; -1 14352 var nodes = []; -1 14353 var cssOP = { -1 14354 before: '', -1 14355 after: '' -1 14356 }; -1 14357 -1 14358 if (nodes.indexOf(refNode) === -1) { -1 14359 nodes.push(refNode); -1 14360 cssOP = getCSSText(refNode, null); -1 14361 -1 14362 // Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips -1 14363 if (preventVisualARIASelfCSSRef) { -1 14364 if (cssOP.before.indexOf(' [ARIA] ') !== -1 || cssOP.before.indexOf(' aria-') !== -1) -1 14365 cssOP.before = ''; -1 14366 if (cssOP.after.indexOf(' [ARIA] ') !== -1 || cssOP.after.indexOf(' aria-') !== -1) -1 14367 cssOP.after = ''; -1 14368 } -1 14369 } -1 14370 -1 14371 walkDOM(refNode, function(node) { -1 14372 if (skip || !node || (isHidden(node, refNode))) { -1 14373 return; -1 14374 } -1 14375 -1 14376 var name = ''; -1 14377 var cssO = { -1 14378 before: '', -1 14379 after: '' -1 14380 }; -1 14381 -1 14382 var parent = refNode === node ? node : node.parentNode; -1 14383 if (nodes.indexOf(parent) === -1) { -1 14384 nodes.push(parent); -1 14385 cssO = getCSSText(parent, refNode); -1 14386 -1 14387 // Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips -1 14388 if (preventVisualARIASelfCSSRef) { -1 14389 if (cssO.before.indexOf(' [ARIA] ') !== -1 || cssO.before.indexOf(' aria-') !== -1) -1 14390 cssO.before = ''; -1 14391 if (cssO.after.indexOf(' [ARIA] ') !== -1 || cssO.after.indexOf(' aria-') !== -1) -1 14392 cssO.after = ''; -1 14393 } -1 14394 -1 14395 } -1 14396 -1 14397 if (node.nodeType === 1) { -1 14398 var aLabelledby = node.getAttribute('aria-labelledby') || ''; -1 14399 var aLabel = node.getAttribute('aria-label') || ''; -1 14400 var nTitle = node.getAttribute('title') || ''; -1 14401 var rolePresentation = ['presentation', 'none'].indexOf(node.getAttribute('role')) !== -1; -1 14402 -1 14403 if (!node.firstChild || (node == refNode && (aLabelledby || aLabel)) || (node.firstChild && node != refNode && aLabel)) { -1 14404 if (!stop && node === refNode && aLabelledby) { -1 14405 if (!rolePresentation) { -1 14406 var ids = aLabelledby.split(/\s+/); -1 14407 var parts = []; -1 14408 -1 14409 for (var i = 0; i < ids.length; i++) { -1 14410 var element = document.getElementById(ids[i]); -1 14411 parts.push(walk(element, true, skip)); -1 14412 } -1 14413 name = parts.join(' '); -1 14414 } -1 14415 -1 14416 if (name || rolePresentation) { -1 14417 skip = true; -1 14418 } -1 14419 } -1 14420 -1 14421 /*!@ Add values of custom controls here if recursive controls with values */ -1 14422 -1 14423 if (!name && !rolePresentation && aLabel) { -1 14424 name = aLabel; -1 14425 -1 14426 if (name && node === refNode) { -1 14427 skip = true; -1 14428 } -1 14429 } -1 14430 -1 14431 if (!name && !rolePresentation && ['input', 'select', 'textarea'].indexOf(node.nodeName.toLowerCase()) !== -1 && node.id && document.querySelectorAll('label[for="' + node.id + '"]').length) { -1 14432 var label = document.querySelector('label[for="' + node.id + '"]'); -1 14433 name = walk(label, true, skip); -1 14434 } -1 14435 -1 14436 if (!name && !rolePresentation && node.nodeName.toLowerCase() == 'img' && node.getAttribute('alt')) { -1 14437 name = node.getAttribute('alt'); -1 14438 } -1 14439 -1 14440 if (!name && !rolePresentation && nTitle) { -1 14441 name = nTitle; -1 14442 } -1 14443 } -1 14444 } else if (node.nodeType === 3) { -1 14445 name = node.data; -1 14446 } -1 14447 -1 14448 name = cssO.before + name + cssO.after; -1 14449 -1 14450 if (name && !hasParentLabel(node, false, refNode)) { -1 14451 fullName += name; -1 14452 } -1 14453 }, refNode); -1 14454 -1 14455 fullName = cssOP.before + fullName + cssOP.after; -1 14456 return fullName; -1 14457 }; -1 14458 -1 14459 if (isHidden(node, document.body) || hasParentLabel(node, true, document.body)) { -1 14460 return; -1 14461 } -1 14462 -1 14463 var accName = walk(node, false); -1 14464 var accDesc = ''; -1 14465 -1 14466 if (['presentation', 'none'].indexOf(node.getAttribute('role')) === -1) { -1 14467 var desc = ''; -1 14468 -1 14469 var title = node.getAttribute('title') || ''; -1 14470 if (title) { -1 14471 if (!accName) { -1 14472 accName = title; -1 14473 } else { -1 14474 accDesc = title; -1 14475 } -1 14476 } -1 14477 -1 14478 var describedby = node.getAttribute('aria-describedby') || ''; -1 14479 if (describedby) { -1 14480 var ids = describedby.split(/\s+/); -1 14481 var parts = []; -1 14482 -1 14483 for (var j = 0; j < ids.length; j++) { -1 14484 var element = document.getElementById(ids[j]); -1 14485 parts.push(walk(element, true)); -1 14486 } -1 14487 -1 14488 if (parts.length) { -1 14489 accDesc = parts.join(' '); -1 14490 } -1 14491 } -1 14492 } -1 14493 -1 14494 accName = trim(accName.replace(/\s+/g, ' ')); -1 14495 accDesc = trim(accDesc.replace(/\s+/g, ' ')); -1 14496 -1 14497 if (accName === accDesc) { -1 14498 accDesc = ''; -1 14499 } -1 14500 -1 14501 var props = { -1 14502 name: accName, -1 14503 desc: accDesc -1 14504 }; -1 14505 -1 14506 if (fnc && typeof fnc == 'function') { -1 14507 return fnc.apply(node, [ -1 14508 node, -1 14509 props -1 14510 ]); -1 14511 } else { -1 14512 return props; -1 14513 } -1 14514 }; -1 14515 -1 14516 // Customize returned string -1 14517 -1 14518 var getNames = function(node) { -1 14519 var props = calcNames(node); -1 14520 return 'accName: "' + props.name + '"\n\naccDesc: "' + props.desc + '"'; -1 14521 }; -1 14522 -1 14523 if (typeof module === 'object' && module.exports) { -1 14524 module.exports = { -1 14525 getNames: getNames, -1 14526 calcNames: calcNames, -1 14527 }; -1 14528 } -1 14529 },{}],14:[function(require,module,exports){ -1 14530 (function (global){ -1 14531 global.goog = { -1 14532 provide: function() {}, -1 14533 require: function() {}, -1 14534 }; -1 14535 global.axs = { -1 14536 browserUtils: {}, -1 14537 color: {}, -1 14538 constants: {}, -1 14539 dom: {}, -1 14540 utils: {}, -1 14541 properties: {}, -1 14542 }; -1 14543 -1 14544 require('accessibility-developer-tools/src/js/Constants'); -1 14545 require('accessibility-developer-tools/src/js/AccessibilityUtils'); -1 14546 require('accessibility-developer-tools/src/js/BrowserUtils'); -1 14547 require('accessibility-developer-tools/src/js/Color'); -1 14548 require('accessibility-developer-tools/src/js/DOMUtils'); -1 14549 require('accessibility-developer-tools/src/js/Properties'); -1 14550 -1 14551 module.exports = global.axs; -1 14552 -1 14553 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -1 14554 },{"accessibility-developer-tools/src/js/AccessibilityUtils":1,"accessibility-developer-tools/src/js/BrowserUtils":2,"accessibility-developer-tools/src/js/Color":3,"accessibility-developer-tools/src/js/Constants":4,"accessibility-developer-tools/src/js/DOMUtils":5,"accessibility-developer-tools/src/js/Properties":6}],15:[function(require,module,exports){ 14555 14555 var ariaApi = require('aria-api');14556 -1 var accdc = require('../lib/accdc');-1 14556 var accdc = require('w3c-alternative-text-computation'); 14557 14557 var axe = require('axe-core');14558 -1 var axs = require('../lib/axs');-1 14558 var axs = require('./axs'); 14559 14559 14560 14560 var form = document.querySelector('#ba-form'); 14561 14561 var preview = document.querySelector('#ba-preview'); @@ -14610,7 +14610,7 @@ var run = function(html) { 14610 14610 results.innerHTML = ''; 14611 14611 14612 14612 return Promise.all(Object.keys(implementations).map(function(key) {14613 -1 var p = implementations[key](preview.children[0] || preview);-1 14613 var p = implementations[key](preview.querySelector('#test') || preview.children[0] || preview); 14614 14614 14615 14615 return Promise.resolve(p).then(function(result) { 14616 14616 var tr = document.createElement('tr'); @@ -14649,4 +14649,4 @@ try { 14649 14649 }); 14650 14650 } 14651 1465114652 -1 },{"../lib/accdc":1,"../lib/axs":2,"aria-api":9,"axe-core":14}]},{},[15]);-1 14652 },{"./axs":14,"aria-api":7,"axe-core":12,"w3c-alternative-text-computation":13}]},{},[15]);