- commit
- b00dfb587103535e651db71e753e19de153265af
- parent
- c8f3b94515ea3dd34c3599130f9d9620e2aa250f
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2018-02-08 14:55
update aria-api
Diffstat
| M | babel.js | 9633 | +++++++++++++++++++++++++++++++------------------------------ |
1 files changed, 4821 insertions, 4812 deletions
diff --git a/babel.js b/babel.js
@@ -1,3764 +1,2516 @@ 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 // Copyright 2012 Google Inc.3 -1 //4 -1 // Licensed under the Apache License, Version 2.0 (the "License");5 -1 // you may not use this file except in compliance with the License.6 -1 // You may obtain a copy of the License at7 -1 //8 -1 // http://www.apache.org/licenses/LICENSE-2.09 -1 //10 -1 // Unless required by applicable law or agreed to in writing, software11 -1 // distributed under the License is distributed on an "AS IS" BASIS,12 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 -1 // See the License for the specific language governing permissions and14 -1 // limitations under the License.15 -116 -1 goog.require('axs.browserUtils');17 -1 goog.require('axs.color');18 -1 goog.require('axs.color.Color');19 -1 goog.require('axs.constants');20 -1 goog.require('axs.dom');21 -122 -1 goog.provide('axs.utils');23 -124 -1 /**25 -1 * @const26 -1 * @type {string}27 -1 */28 -1 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR =29 -1 'input:not([type=hidden]):not([disabled]),' +30 -1 'select:not([disabled]),' +31 -1 'textarea:not([disabled]),' +32 -1 'button:not([disabled]),' +33 -1 'a[href],' +34 -1 'iframe,' +35 -1 '[tabindex]';36 -137 -1 /**38 -1 * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label39 -1 * @const40 -1 * @type {string}41 -1 */42 -1 axs.utils.LABELABLE_ELEMENTS_SELECTOR =43 -1 'button,' +44 -1 'input:not([type=hidden]),' +45 -1 'keygen,' +46 -1 'meter,' +47 -1 'output,' +48 -1 'progress,' +49 -1 'select,' +50 -1 'textarea';51 -1-1 2 var query = require('./lib/query.js'); -1 3 var name = require('./lib/name.js'); 52 453 -1 /**54 -1 * @param {Element} element55 -1 * @return {boolean}56 -1 */57 -1 axs.utils.elementIsTransparent = function(element) {58 -1 return element.style.opacity == '0';59 -1 };-1 5 module.exports = { -1 6 getRole: query.getRole, -1 7 getAttribute: query.getAttribute, -1 8 getName: name.getName, -1 9 getDescription: name.getDescription, 60 1061 -1 /**62 -1 * @param {Element} element63 -1 * @return {boolean}64 -1 */65 -1 axs.utils.elementHasZeroArea = function(element) {66 -1 var rect = element.getBoundingClientRect();67 -1 var width = rect.right - rect.left;68 -1 var height = rect.top - rect.bottom;69 -1 if (!width || !height)70 -1 return true;71 -1 return false;-1 11 matches: query.matches, -1 12 querySelector: query.querySelector, -1 13 querySelectorAll: query.querySelectorAll, -1 14 closest: query.closest, 72 15 }; 73 1674 -1 /**75 -1 * @param {Element} element76 -1 * @return {boolean}77 -1 */78 -1 axs.utils.elementIsOutsideScrollArea = function(element) {79 -1 var parent = axs.dom.parentElement(element);80 -181 -1 var defaultView = element.ownerDocument.defaultView;82 -1 while (parent != defaultView.document.body) {83 -1 if (axs.utils.isClippedBy(element, parent))84 -1 return true;-1 17 },{"./lib/name.js":3,"./lib/query.js":4}],2:[function(require,module,exports){ -1 18 exports.attributes = { -1 19 // widget -1 20 'autocomplete': 'token', -1 21 'checked': 'tristate', -1 22 'current': 'token', -1 23 'disabled': 'bool', -1 24 'expanded': 'bool-undefined', -1 25 'haspopup': 'token', -1 26 'hidden': 'bool', // ! -1 27 'invalid': 'token', -1 28 'keyshortcuts': 'string', -1 29 'label': 'string', -1 30 'level': 'int', -1 31 'modal': 'bool', -1 32 'multiline': 'bool', -1 33 'multiselectable': 'bool', -1 34 'orientation': 'token', -1 35 'placeholder': 'string', -1 36 'pressed': 'tristate', -1 37 'readonly': 'bool', -1 38 'required': 'bool', -1 39 'roledescription': 'string', -1 40 'selected': 'bool-undefined', -1 41 'valuemax': 'number', -1 42 'valuemin': 'number', -1 43 'valuenow': 'number', -1 44 'valuetext': 'string', 85 4586 -1 if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent))87 -1 return false;-1 46 // live -1 47 'atomic': 'bool', -1 48 'busy': 'bool', -1 49 'live': 'token', -1 50 'relevant': 'token-list', 88 5189 -1 parent = axs.dom.parentElement(parent);90 -1 }-1 52 // dragndrop -1 53 'dropeffect': 'token-list', -1 54 'grabbed': 'bool-undefined', 91 5592 -1 return !axs.utils.canScrollTo(element, defaultView.document.body);-1 56 // relationship -1 57 'activedescendant': 'id', -1 58 'colcount': 'int', -1 59 'colindex': 'int', -1 60 'colspan': 'int', -1 61 'controls': 'id-list', -1 62 'describedby': 'id-list', -1 63 'details': 'id', -1 64 'errormessage': 'id', -1 65 'flowto': 'id-list', -1 66 'labelledby': 'id-list', -1 67 'owns': 'id-list', -1 68 'posinset': 'int', -1 69 'rowcount': 'int', -1 70 'rowindex': 'int', -1 71 'rowspan': 'int', -1 72 'setsize': 'int', -1 73 'sort': 'token', 93 74 }; 94 7595 -1 /**96 -1 * Checks whether it's possible to scroll to the given element within the given container.97 -1 * Assumes that |container| is an ancestor of |element|.98 -1 * If |container| cannot be scrolled, returns True if the element is within its bounding client99 -1 * rect.100 -1 * @param {Element} element101 -1 * @param {Element} container102 -1 * @return {boolean} True iff it's possible to scroll to |element| within |container|.103 -1 */104 -1 axs.utils.canScrollTo = function(element, container) {105 -1 var rect = element.getBoundingClientRect();106 -1 var containerRect = container.getBoundingClientRect();107 -1 if (container == container.ownerDocument.body) {108 -1 var absoluteTop = containerRect.top;109 -1 var absoluteLeft = containerRect.left;110 -1 } else {111 -1 var absoluteTop = containerRect.top - container.scrollTop;112 -1 var absoluteLeft = containerRect.left - container.scrollLeft;113 -1 }114 -1 var containerScrollArea =115 -1 { top: absoluteTop,116 -1 bottom: absoluteTop + container.scrollHeight,117 -1 left: absoluteLeft,118 -1 right: absoluteLeft + container.scrollWidth };119 -1120 -1 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top ||121 -1 rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) {122 -1 return false;123 -1 }124 -1125 -1 var defaultView = element.ownerDocument.defaultView;126 -1 var style = defaultView.getComputedStyle(container);127 -1128 -1 if (rect.left > containerRect.right || rect.top > containerRect.bottom) {129 -1 return (style.overflow == 'scroll' || style.overflow == 'auto' ||130 -1 container instanceof defaultView.HTMLBodyElement);131 -1 }-1 76 // https://www.w3.org/TR/html-aria/#docconformance -1 77 exports.extraSelectors = { -1 78 article: ['article'], -1 79 button: [ -1 80 'button', -1 81 'input[type="button"]', -1 82 'input[type="image"]', -1 83 'input[type="reset"]', -1 84 'input[type="submit"]', -1 85 'summary', -1 86 ], -1 87 cell: ['td'], -1 88 checkbox: ['input[type="checkbox"]'], -1 89 combobox: [ -1 90 'input:not([type])[list]', -1 91 'input[type="email"][list]', -1 92 'input[type="search"][list]', -1 93 'input[type="tel"][list]', -1 94 'input[type="text"][list]', -1 95 'input[type="url"][list]', -1 96 'select:not([multiple])', -1 97 ], -1 98 complementary: ['aside'], -1 99 definition: ['dd'], -1 100 dialog: ['dialog'], -1 101 document: ['body'], -1 102 figure: ['figure'], -1 103 form: ['form[aria-label]', 'form[aria-labelledby]'], -1 104 group: ['details', 'optgroup'], -1 105 heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], -1 106 img: ['img:not([alt=""])'], -1 107 link: ['a[href]', 'area[href]', 'link[href]'], -1 108 list: ['dl', 'ol', 'ul'], -1 109 listbox: ['select[multiple]'], -1 110 listitem: ['dt', 'ul > li', 'ol > li'], -1 111 main: ['main'], -1 112 math: ['math'], -1 113 menuitemcheckbox: ['menuitem[type="checkbox"]'], -1 114 menuitem: ['menuitem[type="command"]'], -1 115 menuitemradio: ['menuitem[type="radio"]'], -1 116 menu: ['menu[type="context"]'], -1 117 navigation: ['nav'], -1 118 option: ['option'], -1 119 progressbar: ['progress'], -1 120 radio: ['input[type="radio"]'], -1 121 region: ['section'], -1 122 rowgroup: ['tbody', 'thead', 'tfoot'], -1 123 row: ['tr'], -1 124 searchbox: ['input[type="search"]:not([list])'], -1 125 separator: ['hr'], -1 126 slider: ['input[type="range"]'], -1 127 spinbutton: ['input[type="number"]'], -1 128 status: ['output'], -1 129 table: ['table'], -1 130 textbox: [ -1 131 'input:not([type]):not([list])', -1 132 'input[type="email"]:not([list])', -1 133 'input[type="tel"]:not([list])', -1 134 'input[type="text"]:not([list])', -1 135 'input[type="url"]:not([list])', -1 136 'textarea', -1 137 ], 132 138133 -1 return true;-1 139 // if scope is missing, it is calculated automatically -1 140 rowheader: ['th[scope="row"]'], -1 141 columnheader: ['th[scope="col"]'], 134 142 }; 135 143136 -1 /**137 -1 * Checks whether the given element is clipped by the given container.138 -1 * Assumes that |container| is an ancestor of |element|.139 -1 * @param {Element} element140 -1 * @param {Element} container141 -1 * @return {boolean} True iff |element| is clipped by |container|.142 -1 */143 -1 axs.utils.isClippedBy = function(element, container) {144 -1 var rect = element.getBoundingClientRect();145 -1 var containerRect = container.getBoundingClientRect();146 -1 var containerTop = containerRect.top;147 -1 var containerLeft = containerRect.left;148 -1 var containerScrollArea =149 -1 { top: containerTop - container.scrollTop,150 -1 bottom: containerTop - container.scrollTop + container.scrollHeight,151 -1 left: containerLeft - container.scrollLeft,152 -1 right: containerLeft - container.scrollLeft + container.scrollWidth };153 -1154 -1 var defaultView = element.ownerDocument.defaultView;155 -1 var style = defaultView.getComputedStyle(container);-1 144 exports.scoped = [ -1 145 'article *', 'aside *', 'main *', 'nav *', 'section *', -1 146 ].join(','); 156 147157 -1 if ((rect.right < containerRect.left || rect.bottom < containerRect.top ||158 -1 rect.left > containerRect.right || rect.top > containerRect.bottom) &&159 -1 style.overflow == 'hidden') {160 -1 return true;161 -1 }-1 148 // https://www.w3.org/TR/wai-aria/roles -1 149 var subRoles = { -1 150 cell: ['gridcell', 'rowheader'], -1 151 command: ['button', 'link', 'menuitem'], -1 152 composite: ['grid', 'select', 'spinbutton', 'tablist'], -1 153 img: ['doc-cover'], -1 154 input: ['checkbox', 'option', 'radio', 'slider', 'spinbutton', 'textbox'], -1 155 landmark: [ -1 156 'banner', -1 157 'complementary', -1 158 'contentinfo', -1 159 'doc-acknowledgments', -1 160 'doc-afterword', -1 161 'doc-appendix', -1 162 'doc-bibliography', -1 163 'doc-chapter', -1 164 'doc-conclusion', -1 165 'doc-credits', -1 166 'doc-endnotes', -1 167 'doc-epilogue', -1 168 'doc-errata', -1 169 'doc-foreword', -1 170 'doc-glossary', -1 171 'doc-introduction', -1 172 'doc-part', -1 173 'doc-preface', -1 174 'doc-prologue', -1 175 'form', -1 176 'main', -1 177 'navigation', -1 178 'region', -1 179 'search', -1 180 ], -1 181 range: ['progressbar', 'scrollbar', 'slider', 'spinbutton'], -1 182 roletype: ['structure', 'widget', 'window'], -1 183 section: [ -1 184 'alert', -1 185 'cell', -1 186 'definition', -1 187 'doc-abstract', -1 188 'doc-colophon', -1 189 'doc-credit', -1 190 'doc-dedication', -1 191 'doc-epigraph', -1 192 'doc-example', -1 193 'doc-footnote', -1 194 'doc-qna', -1 195 'figure', -1 196 'group', -1 197 'img', -1 198 'landmark', -1 199 'list', -1 200 'listitem', -1 201 'log', -1 202 'marquee', -1 203 'math', -1 204 'note', -1 205 'status', -1 206 'table', -1 207 'tabpanel', -1 208 'term', -1 209 'tooltip', -1 210 ], -1 211 sectionhead: [ -1 212 'columnheader', -1 213 'doc-subtitle', -1 214 'heading', -1 215 'rowheader', -1 216 'tab', -1 217 ], -1 218 select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'], -1 219 separator: ['doc-pagebreak'], -1 220 structure: [ -1 221 'application', -1 222 'document', -1 223 'none', -1 224 'presentation', -1 225 'rowgroup', -1 226 'section', -1 227 'sectionhead', -1 228 'separator', -1 229 ], -1 230 table: ['grid'], -1 231 textbox: ['searchbox'], -1 232 widget: [ -1 233 'command', -1 234 'composite', -1 235 'gridcell', -1 236 'input', -1 237 'range', -1 238 'row', -1 239 'separator', -1 240 'tab', -1 241 ], -1 242 window: ['dialog'], -1 243 alert: ['alertdialog'], -1 244 checkbox: ['menuitemcheckbox', 'switch'], -1 245 dialog: ['alertdialog'], -1 246 gridcell: ['columnheader', 'rowheader'], -1 247 menuitem: ['menuitemcheckbox'], -1 248 menuitemcheckbox: ['menuitemradio'], -1 249 option: ['treeitem'], -1 250 radio: ['menuitemradio'], -1 251 status: ['timer'], -1 252 grid: ['treegrid'], -1 253 menu: ['menubar'], -1 254 tree: ['treegrid'], -1 255 document: ['article'], -1 256 group: ['row', 'select', 'toolbar'], -1 257 link: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'], -1 258 list: ['directory', 'feed'], -1 259 listitem: ['doc-biblioentry', 'doc-endnote', 'treeitem'], -1 260 navigation: ['doc-index', 'doc-pagelist', 'doc-toc'], -1 261 note: ['doc-notice', 'doc-tip'], -1 262 }; 162 263163 -1 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top)164 -1 return (style.overflow != 'visible');-1 264 var getSubRoles = function(role) { -1 265 var children = subRoles[role] || []; -1 266 var descendents = children.map(getSubRoles); 165 267166 -1 return false;167 -1 };-1 268 var result = [role]; 168 269169 -1 /**170 -1 * @param {Node} ancestor A potential ancestor of |node|.171 -1 * @param {Node} node172 -1 * @return {boolean} true if |ancestor| is an ancestor of |node| (including173 -1 * |ancestor| === |node|).174 -1 */175 -1 axs.utils.isAncestor = function(ancestor, node) {176 -1 if (node == null)177 -1 return false;178 -1 if (node === ancestor)179 -1 return true;-1 270 descendents.forEach(function(list) { -1 271 list.forEach(function(r) { -1 272 if (result.indexOf(r) === -1) { -1 273 result.push(r); -1 274 } -1 275 }); -1 276 }); 180 277181 -1 var parentNode = axs.dom.composedParentNode(node);182 -1 return axs.utils.isAncestor(ancestor, parentNode);-1 278 return result; 183 279 }; 184 280185 -1 /**186 -1 * @param {Element} element187 -1 * @return {Array.<Element>} An array of any non-transparent elements which188 -1 * overlap the given element.189 -1 */190 -1 axs.utils.overlappingElements = function(element) {191 -1 if (axs.utils.elementHasZeroArea(element))192 -1 return null;193 -1194 -1 var overlappingElements = [];195 -1 var clientRects = element.getClientRects();196 -1 for (var i = 0; i < clientRects.length; i++) {197 -1 var rect = clientRects[i];198 -1 var center_x = (rect.left + rect.right) / 2;199 -1 var center_y = (rect.top + rect.bottom) / 2;200 -1 var elementAtPoint = document.elementFromPoint(center_x, center_y);-1 281 exports.subRoles = {}; -1 282 for (var role in subRoles) { -1 283 exports.subRoles[role] = getSubRoles(role); -1 284 } -1 285 exports.subRoles['none'] = ['none', 'presentation']; -1 286 exports.subRoles['presentation'] = ['presentation', 'none']; 201 287202 -1 if (elementAtPoint == null || elementAtPoint == element ||203 -1 axs.utils.isAncestor(elementAtPoint, element) ||204 -1 axs.utils.isAncestor(element, elementAtPoint)) {205 -1 continue;206 -1 }-1 288 exports.nameFromContents = [ -1 289 'button', -1 290 'checkbox', -1 291 'columnheader', -1 292 'doc-backlink', -1 293 'doc-biblioref', -1 294 'doc-glossref', -1 295 'doc-noteref', -1 296 'gridcell', -1 297 'heading', -1 298 'link', -1 299 'menuitem', -1 300 'menuitemcheckbox', -1 301 'menuitemradio', -1 302 'option', -1 303 'radio', -1 304 'row', -1 305 'rowgroup', -1 306 'rowheader', -1 307 'sectionhead', -1 308 'tab', -1 309 'tooltip', -1 310 'treeitem', -1 311 'switch', -1 312 ]; 207 313208 -1 var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null);209 -1 if (!overlappingElementStyle)210 -1 continue;-1 314 exports.labelable = [ -1 315 'button', -1 316 'input:not([type="hidden"])', -1 317 'keygen', -1 318 'meter', -1 319 'output', -1 320 'progress', -1 321 'select', -1 322 'textarea', -1 323 ]; 211 324212 -1 var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle,213 -1 elementAtPoint);214 -1 if (overlappingElementBg && overlappingElementBg.alpha > 0 &&215 -1 overlappingElements.indexOf(elementAtPoint) < 0) {216 -1 overlappingElements.push(elementAtPoint);217 -1 }218 -1 }-1 325 },{}],3:[function(require,module,exports){ -1 326 var constants = require('./constants.js'); -1 327 var query = require('./query.js'); -1 328 var util = require('./util.js'); 219 329220 -1 return overlappingElements;-1 330 var getPseudoContent = function(node, selector) { -1 331 var styles = window.getComputedStyle(node, selector); -1 332 var ret = styles.getPropertyValue('content'); -1 333 if (ret === 'none' || ret.substr(0, 4) === '-moz') { -1 334 return ''; -1 335 } else { -1 336 return ret -1 337 .replace(/^["']/, '') -1 338 .replace(/["']$/, ''); -1 339 } 221 340 }; 222 341223 -1 /**224 -1 * @param {Element} element225 -1 * @return {boolean}226 -1 */227 -1 axs.utils.elementIsHtmlControl = function(element) {228 -1 var defaultView = element.ownerDocument.defaultView;229 -1230 -1 // HTML control231 -1 if (element instanceof defaultView.HTMLButtonElement)232 -1 return true;233 -1 if (element instanceof defaultView.HTMLInputElement)234 -1 return true;235 -1 if (element instanceof defaultView.HTMLSelectElement)236 -1 return true;237 -1 if (element instanceof defaultView.HTMLTextAreaElement)238 -1 return true;-1 342 var getContent = function(root, referenced) { -1 343 var ret = getPseudoContent(root, ':before'); -1 344 var node = root.firstChild; -1 345 while (node) { -1 346 if (node.nodeType === node.TEXT_NODE) { -1 347 ret += node.textContent; -1 348 } else if (node.nodeType === node.ELEMENT_NODE) { -1 349 ret += getName(node, true, referenced); -1 350 } -1 351 node = node.nextSibling; -1 352 } -1 353 ret += getPseudoContent(root, ':after'); -1 354 return ret; -1 355 }; 239 356240 -1 return false;-1 357 var allowNameFromContent = function(el) { -1 358 var role = query.getRole(el); -1 359 return !role || constants.nameFromContents.indexOf(role) !== -1; 241 360 }; 242 361243 -1 /**244 -1 * @param {Element} element245 -1 * @return {boolean}246 -1 */247 -1 axs.utils.elementIsAriaWidget = function(element) {248 -1 if (element.hasAttribute('role')) {249 -1 var roleValue = element.getAttribute('role');250 -1 // TODO is this correct?251 -1 if (roleValue) {252 -1 var role = axs.constants.ARIA_ROLES[roleValue];253 -1 if (role && 'widget' in role['allParentRolesSet'])254 -1 return true;255 -1 }256 -1 }257 -1 return false;-1 362 var isLabelable = function(el) { -1 363 var selector = constants.labelable.join(','); -1 364 return el.matches(selector); 258 365 }; 259 366260 -1 /**261 -1 * @param {Element} element262 -1 * @return {boolean}263 -1 */264 -1 axs.utils.elementIsVisible = function(element) {265 -1 if (axs.utils.elementIsTransparent(element))266 -1 return false;267 -1 if (axs.utils.elementHasZeroArea(element))268 -1 return false;269 -1 if (axs.utils.elementIsOutsideScrollArea(element))270 -1 return false;-1 367 // Control.labels is part of the standard, but not supported in most browsers -1 368 var getLabelNodes = function(element) { -1 369 var labels = []; -1 370 var labelable = constants.labelable.join(','); -1 371 util.walkDOM(document.body, function(node) { -1 372 if (node.tagName && node.tagName.toLowerCase() === 'label') { -1 373 if (node.getAttribute('for')) { -1 374 if (element.id && node.getAttribute('for') === element.id) { -1 375 labels.push(node); -1 376 } -1 377 } else if (node.querySelector(labelable) === element) { -1 378 labels.push(node); -1 379 } -1 380 } -1 381 }); -1 382 return labels; -1 383 }; 271 384272 -1 var overlappingElements = axs.utils.overlappingElements(element);273 -1 if (overlappingElements.length)274 -1 return false;-1 385 // http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/ -1 386 // https://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te -1 387 var getName = function(el, recursive, referenced) { -1 388 var ret; 275 389276 -1 return true;277 -1 };-1 390 if (query.getAttribute(el, 'hidden', referenced)) { -1 391 return ''; -1 392 } -1 393 if (query.matches(el, 'presentation')) { -1 394 return getContent(el, referenced); -1 395 } -1 396 if (!recursive && el.matches('[aria-labelledby]')) { -1 397 var ids = el.getAttribute('aria-labelledby').split(/\s+/); -1 398 var strings = ids.map(function(id) { -1 399 var label = document.getElementById(id); -1 400 return getName(label, true, label); -1 401 }); -1 402 ret = strings.join(' '); -1 403 } -1 404 if (!ret && el.matches('[aria-label]')) { -1 405 ret = el.getAttribute('aria-label'); -1 406 } -1 407 if (!query.matches(el, 'presentation')) { -1 408 if (!ret && !recursive && isLabelable(el)) { -1 409 var strings = getLabelNodes(el).map(function(label) { -1 410 return getName(label, true, label); -1 411 }); -1 412 ret = strings.join(' '); -1 413 } -1 414 if (!ret) { -1 415 ret = el.getAttribute('placeholder'); -1 416 } -1 417 if (!ret) { -1 418 ret = el.getAttribute('alt'); -1 419 } -1 420 if (!ret && el.matches('abbr,acronym') && el.title) { -1 421 ret = el.title; -1 422 } -1 423 // figcaption -1 424 // caption -1 425 // table -1 426 } -1 427 // FIXME only if this is embedded in a label -1 428 if (!ret && query.matches(el, 'textbox,button,combobox,range')) { -1 429 if (query.matches(el, 'textbox,button')) { -1 430 ret = el.value || el.textContent; -1 431 } else if (query.matches(el, 'combobox')) { -1 432 var selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option'); -1 433 if (selected) { -1 434 ret = getName(selected, recursive, referenced); -1 435 } -1 436 } else if (query.matches(el, 'range')) { -1 437 ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value); -1 438 } -1 439 } -1 440 if (!ret && (recursive || allowNameFromContent(el))) { -1 441 ret = getContent(el, referenced); -1 442 } -1 443 if (!ret) { -1 444 ret = el.getAttribute('title'); -1 445 } 278 446279 -1 /**280 -1 * @param {CSSStyleDeclaration} style281 -1 * @return {boolean}282 -1 */283 -1 axs.utils.isLargeFont = function(style) {284 -1 var fontSize = style.fontSize;285 -1 var bold = style.fontWeight == 'bold';286 -1 var matches = fontSize.match(/(\d+)px/);287 -1 if (matches) {288 -1 var fontSizePx = parseInt(matches[1], 10);289 -1 var bodyStyle = window.getComputedStyle(document.body, null);290 -1 var bodyFontSize = bodyStyle.fontSize;291 -1 matches = bodyFontSize.match(/(\d+)px/);292 -1 if (matches) {293 -1 var bodyFontSizePx = parseInt(matches[1], 10);294 -1 var boldLarge = bodyFontSizePx * 1.2;295 -1 var large = bodyFontSizePx * 1.5;296 -1 } else {297 -1 var boldLarge = 19.2;298 -1 var large = 24;299 -1 }300 -1 return (bold && fontSizePx >= boldLarge || fontSizePx >= large);301 -1 }302 -1 matches = fontSize.match(/(\d+)em/);303 -1 if (matches) {304 -1 var fontSizeEm = parseInt(matches[1], 10);305 -1 if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5)306 -1 return true;307 -1 return false;308 -1 }309 -1 matches = fontSize.match(/(\d+)%/);310 -1 if (matches) {311 -1 var fontSizePercent = parseInt(matches[1], 10);312 -1 if (bold && fontSizePercent >= 120 || fontSizePercent >= 150)313 -1 return true;314 -1 return false;315 -1 }316 -1 matches = fontSize.match(/(\d+)pt/);317 -1 if (matches) {318 -1 var fontSizePt = parseInt(matches[1], 10);319 -1 if (bold && fontSizePt >= 14 || fontSizePt >= 18)320 -1 return true;321 -1 return false;322 -1 }323 -1 return false;-1 447 return (ret || '').trim().replace(/\s+/g, ' '); 324 448 }; 325 449326 -1 /**327 -1 * @param {CSSStyleDeclaration} style328 -1 * @param {Element} element329 -1 * @return {?axs.color.Color}330 -1 */331 -1 axs.utils.getBgColor = function(style, element) {332 -1 var bgColorString = style.backgroundColor;333 -1 var bgColor = axs.color.parseColor(bgColorString);334 -1 if (!bgColor)335 -1 return null;-1 450 var getDescription = function(el) { -1 451 var ret = ''; 336 452337 -1 if (style.opacity < 1)338 -1 bgColor.alpha = bgColor.alpha * style.opacity;-1 453 if (el.matches('[aria-describedby]')) { -1 454 var ids = el.getAttribute('aria-describedby').split(/\s+/); -1 455 var strings = ids.map(function(id) { -1 456 var label = document.getElementById(id); -1 457 return getName(label, true, label); -1 458 }); -1 459 ret = strings.join(' '); -1 460 } else if (el.title) { -1 461 ret = el.title; -1 462 } else if (el.placeholder) { -1 463 ret = el.placeholder; -1 464 } 339 465340 -1 if (bgColor.alpha < 1) {341 -1 var parentBg = axs.utils.getParentBgColor(element);342 -1 if (parentBg == null)343 -1 return null;-1 466 return (ret || '').trim().replace(/\s+/g, ' '); -1 467 }; 344 468345 -1 bgColor = axs.color.flattenColors(bgColor, parentBg);346 -1 }347 -1 return bgColor;-1 469 module.exports = { -1 470 getName: getName, -1 471 getDescription: getDescription, 348 472 }; 349 473350 -1 /**351 -1 * Gets the effective background color of the parent of |element|.352 -1 * @param {Element} element353 -1 * @return {?axs.color.Color}354 -1 */355 -1 axs.utils.getParentBgColor = function(element) {356 -1 /** @type {Element} */ var parent = element;357 -1 var bgStack = [];358 -1 var foundSolidColor = null;359 -1 while ((parent = axs.dom.parentElement(parent))) {360 -1 var computedStyle = window.getComputedStyle(parent, null);361 -1 if (!computedStyle)362 -1 continue;-1 474 },{"./constants.js":2,"./query.js":4,"./util.js":5}],4:[function(require,module,exports){ -1 475 var constants = require('./constants.js'); -1 476 var util = require('./util.js'); 363 477364 -1 var parentBg = axs.color.parseColor(computedStyle.backgroundColor);365 -1 if (!parentBg)366 -1 continue;-1 478 var getSubRoles = function(roles) { -1 479 return [].concat.apply([], roles.map(function(role) { -1 480 return constants.subRoles[role] || [role]; -1 481 })); -1 482 }; 367 483368 -1 if (computedStyle.opacity < 1)369 -1 parentBg.alpha = parentBg.alpha * computedStyle.opacity;-1 484 // candidates can be passed for performance optimization -1 485 var _getRole = function(el, candidates) { -1 486 if (el.hasAttribute('role')) { -1 487 return el.getAttribute('role'); -1 488 } -1 489 for (var role in constants.extraSelectors) { -1 490 var selector = constants.extraSelectors[role].join(','); -1 491 if ((!candidates || candidates.indexOf(role) !== -1) && el.matches(selector)) { -1 492 return role; -1 493 } -1 494 } 370 495371 -1 if (parentBg.alpha == 0)372 -1 continue;-1 496 if (!candidates || -1 497 candidates.indexOf('banner') !== -1 || -1 498 candidates.indexOf('contentinfo') !== -1) { -1 499 var scoped = el.matches(constants.scoped); 373 500374 -1 bgStack.push(parentBg);-1 501 if (el.matches('header') && !scoped) { -1 502 return 'banner'; -1 503 } -1 504 if (el.matches('footer') && !scoped) { -1 505 return 'contentinfo'; -1 506 } -1 507 } -1 508 }; 375 509376 -1 if (parentBg.alpha == 1) {377 -1 foundSolidColor = true;378 -1 break;379 -1 }380 -1 }-1 510 var getAttribute = function(el, key, _hiddenRoot) { -1 511 if (key === 'hidden' && el === _hiddenRoot) { // used for name calculation -1 512 return false; -1 513 } 381 514382 -1 if (!foundSolidColor)383 -1 bgStack.push(new axs.color.Color(255, 255, 255, 1));-1 515 var type = constants.attributes[key]; -1 516 var raw = el.getAttribute('aria-' + key); 384 517385 -1 var bg = bgStack.pop();386 -1 while (bgStack.length) {387 -1 var fg = bgStack.pop();388 -1 bg = axs.color.flattenColors(fg, bg);389 -1 }390 -1 return bg;-1 518 if (raw) { -1 519 if (type === 'bool') { -1 520 return raw === 'true'; -1 521 } else if (type === 'tristate') { -1 522 return raw === 'true' ? true : raw === 'false' ? false : 'mixed'; -1 523 } else if (type === 'bool-undefined') { -1 524 return raw === 'true' ? true : raw === 'false' ? false : undefined; -1 525 } else if (type === 'id-list') { -1 526 return raw.split(/\s+/); -1 527 } else if (type === 'integer') { -1 528 return parseInt(raw); -1 529 } else if (type === 'number') { -1 530 return parseFloat(raw); -1 531 } else if (type === 'token-list') { -1 532 return raw.split(/\s+/); -1 533 } else { -1 534 return raw; -1 535 } -1 536 } -1 537 -1 538 if (key === 'level') { -1 539 for (var i = 1; i <= 6; i++) { -1 540 if (el.tagName.toLowerCase() === 'h' + i) { -1 541 return i; -1 542 } -1 543 } -1 544 } else if (key === 'disabled') { -1 545 return el.disabled; -1 546 } else if (key === 'placeholder') { -1 547 return el.placeholder; -1 548 } else if (key === 'required') { -1 549 return el.required; -1 550 } else if (key === 'readonly') { -1 551 return el.readOnly && !el.isContentEditable; -1 552 } else if (key === 'hidden') { -1 553 var style = window.getComputedStyle(el); -1 554 if (el.hidden || style.display === 'none' || style.visibility === 'hidden') { -1 555 return true; -1 556 } else if (el.clientHeight === 0) { // rough check for performance -1 557 return el.parentNode && getAttribute(el.parentNode, 'hidden', _hiddenRoot); -1 558 } -1 559 } else if (key === 'invalid' && el.checkValidity) { -1 560 return el.checkValidity(); -1 561 } -1 562 -1 563 if (type === 'bool' || type === 'tristate') { -1 564 return false; -1 565 } -1 566 }; -1 567 -1 568 var matches = function(el, selector) { -1 569 var actual; -1 570 -1 571 if (selector.substr(0, 1) === ':') { -1 572 var attr = selector.substr(1); -1 573 return getAttribute(el, attr); -1 574 } else if (selector.substr(0, 1) === '[') { -1 575 var match = /\[([a-z]+)="(.*)"\]/.exec(selector); -1 576 actual = getAttribute(el, match[1]); -1 577 var rawValue = match[2]; -1 578 return actual.toString() == rawValue; -1 579 } else { -1 580 var candidates = getSubRoles(selector.split(',')); -1 581 actual = _getRole(el, candidates); -1 582 return candidates.indexOf(actual) !== -1; -1 583 } -1 584 }; -1 585 -1 586 var _querySelector = function(all) { -1 587 return function(root, role) { -1 588 var results = []; -1 589 util.walkDOM(root, function(node) { -1 590 if (node.nodeType === node.ELEMENT_NODE) { -1 591 // FIXME: skip hidden elements -1 592 if (matches(node, role)) { -1 593 results.push(node); -1 594 if (!all) { -1 595 return false; -1 596 } -1 597 } -1 598 } -1 599 }); -1 600 return all ? results : results[0]; -1 601 }; -1 602 }; -1 603 -1 604 var closest = function(el, selector) { -1 605 return util.searchUp(el, function(candidate) { -1 606 return matches(candidate, selector); -1 607 }); -1 608 }; -1 609 -1 610 module.exports = { -1 611 getRole: function(el) { -1 612 return _getRole(el); -1 613 }, -1 614 getAttribute: getAttribute, -1 615 matches: matches, -1 616 querySelector: _querySelector(), -1 617 querySelectorAll: _querySelector(true), -1 618 closest: closest, -1 619 }; -1 620 -1 621 },{"./constants.js":2,"./util.js":5}],5:[function(require,module,exports){ -1 622 var walkDOM = function(root, fn) { -1 623 if (fn(root) === false) { -1 624 return false; -1 625 } -1 626 var node = root.firstChild; -1 627 while (node) { -1 628 if (walkDOM(node, fn) === false) { -1 629 return false; -1 630 } -1 631 node = node.nextSibling; -1 632 } -1 633 }; -1 634 -1 635 var searchUp = function(el, test) { -1 636 var candidate = el.parentElement; -1 637 if (candidate) { -1 638 if (test(candidate)) { -1 639 return candidate; -1 640 } else { -1 641 return searchUp(candidate, test); -1 642 } -1 643 } -1 644 }; -1 645 -1 646 module.exports = { -1 647 walkDOM: walkDOM, -1 648 searchUp: searchUp, 391 649 }; 392 650 -1 651 },{}],6:[function(require,module,exports){ -1 652 // Copyright 2012 Google Inc. -1 653 // -1 654 // Licensed under the Apache License, Version 2.0 (the "License"); -1 655 // you may not use this file except in compliance with the License. -1 656 // You may obtain a copy of the License at -1 657 // -1 658 // http://www.apache.org/licenses/LICENSE-2.0 -1 659 // -1 660 // Unless required by applicable law or agreed to in writing, software -1 661 // distributed under the License is distributed on an "AS IS" BASIS, -1 662 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 663 // See the License for the specific language governing permissions and -1 664 // limitations under the License. -1 665 -1 666 goog.require('axs.browserUtils'); -1 667 goog.require('axs.color'); -1 668 goog.require('axs.color.Color'); -1 669 goog.require('axs.constants'); -1 670 goog.require('axs.dom'); -1 671 -1 672 goog.provide('axs.utils'); -1 673 393 674 /**394 -1 * @param {CSSStyleDeclaration} style395 -1 * @param {Element} element396 -1 * @param {axs.color.Color} bgColor The background color, which may come from397 -1 * another element (such as a parent element), for flattening into the398 -1 * foreground color.399 -1 * @return {?axs.color.Color}-1 675 * @const -1 676 * @type {string} 400 677 */401 -1 axs.utils.getFgColor = function(style, element, bgColor) {402 -1 var fgColorString = style.color;403 -1 var fgColor = axs.color.parseColor(fgColorString);404 -1 if (!fgColor)405 -1 return null;-1 678 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR = -1 679 'input:not([type=hidden]):not([disabled]),' + -1 680 'select:not([disabled]),' + -1 681 'textarea:not([disabled]),' + -1 682 'button:not([disabled]),' + -1 683 'a[href],' + -1 684 'iframe,' + -1 685 '[tabindex]'; 406 686407 -1 if (fgColor.alpha < 1)408 -1 fgColor = axs.color.flattenColors(fgColor, bgColor);-1 687 /** -1 688 * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label -1 689 * @const -1 690 * @type {string} -1 691 */ -1 692 axs.utils.LABELABLE_ELEMENTS_SELECTOR = -1 693 'button,' + -1 694 'input:not([type=hidden]),' + -1 695 'keygen,' + -1 696 'meter,' + -1 697 'output,' + -1 698 'progress,' + -1 699 'select,' + -1 700 'textarea'; 409 701410 -1 if (style.opacity < 1) {411 -1 var parentBg = axs.utils.getParentBgColor(element);412 -1 fgColor.alpha = fgColor.alpha * style.opacity;413 -1 fgColor = axs.color.flattenColors(fgColor, parentBg);414 -1 }415 702416 -1 return fgColor;-1 703 /** -1 704 * @param {Element} element -1 705 * @return {boolean} -1 706 */ -1 707 axs.utils.elementIsTransparent = function(element) { -1 708 return element.style.opacity == '0'; 417 709 }; 418 710 419 711 /** 420 712 * @param {Element} element421 -1 * @return {?number}-1 713 * @return {boolean} 422 714 */423 -1 axs.utils.getContrastRatioForElement = function(element) {424 -1 var style = window.getComputedStyle(element, null);425 -1 return axs.utils.getContrastRatioForElementWithComputedStyle(style, element);-1 715 axs.utils.elementHasZeroArea = function(element) { -1 716 var rect = element.getBoundingClientRect(); -1 717 var width = rect.right - rect.left; -1 718 var height = rect.top - rect.bottom; -1 719 if (!width || !height) -1 720 return true; -1 721 return false; 426 722 }; 427 723 428 724 /**429 -1 * @param {CSSStyleDeclaration} style430 725 * @param {Element} element431 -1 * @return {?number}-1 726 * @return {boolean} 432 727 */433 -1 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) {434 -1 if (axs.utils.isElementHidden(element))435 -1 return null;-1 728 axs.utils.elementIsOutsideScrollArea = function(element) { -1 729 var parent = axs.dom.parentElement(element); 436 730437 -1 var bgColor = axs.utils.getBgColor(style, element);438 -1 if (!bgColor)439 -1 return null;-1 731 var defaultView = element.ownerDocument.defaultView; -1 732 while (parent != defaultView.document.body) { -1 733 if (axs.utils.isClippedBy(element, parent)) -1 734 return true; 440 735441 -1 var fgColor = axs.utils.getFgColor(style, element, bgColor);442 -1 if (!fgColor)443 -1 return null;-1 736 if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent)) -1 737 return false; 444 738445 -1 return axs.color.calculateContrastRatio(fgColor, bgColor);-1 739 parent = axs.dom.parentElement(parent); -1 740 } -1 741 -1 742 return !axs.utils.canScrollTo(element, defaultView.document.body); 446 743 }; 447 744 448 745 /** -1 746 * Checks whether it's possible to scroll to the given element within the given container. -1 747 * Assumes that |container| is an ancestor of |element|. -1 748 * If |container| cannot be scrolled, returns True if the element is within its bounding client -1 749 * rect. 449 750 * @param {Element} element450 -1 * @return {boolean}-1 751 * @param {Element} container -1 752 * @return {boolean} True iff it's possible to scroll to |element| within |container|. 451 753 */452 -1 axs.utils.isNativeTextElement = function(element) {453 -1 var tagName = element.tagName.toLowerCase();454 -1 var type = element.type ? element.type.toLowerCase() : '';455 -1 if (tagName == 'textarea')456 -1 return true;457 -1 if (tagName != 'input')458 -1 return false;-1 754 axs.utils.canScrollTo = function(element, container) { -1 755 var rect = element.getBoundingClientRect(); -1 756 var containerRect = container.getBoundingClientRect(); -1 757 if (container == container.ownerDocument.body) { -1 758 var absoluteTop = containerRect.top; -1 759 var absoluteLeft = containerRect.left; -1 760 } else { -1 761 var absoluteTop = containerRect.top - container.scrollTop; -1 762 var absoluteLeft = containerRect.left - container.scrollLeft; -1 763 } -1 764 var containerScrollArea = -1 765 { top: absoluteTop, -1 766 bottom: absoluteTop + container.scrollHeight, -1 767 left: absoluteLeft, -1 768 right: absoluteLeft + container.scrollWidth }; 459 769460 -1 switch (type) {461 -1 case 'email':462 -1 case 'number':463 -1 case 'password':464 -1 case 'search':465 -1 case 'text':466 -1 case 'tel':467 -1 case 'url':468 -1 case '':469 -1 return true;470 -1 default:-1 770 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top || -1 771 rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) { 471 772 return false; 472 773 }473 -1 };474 774475 -1 /**476 -1 * @param {number} contrastRatio477 -1 * @param {CSSStyleDeclaration} style478 -1 * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level479 -1 * @return {boolean}480 -1 */481 -1 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) {482 -1 // Round to nearest 0.1483 -1 var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10);484 -1 if (!opt_strict) {485 -1 return roundedContrastRatio < 3.0 ||486 -1 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5);487 -1 } else {488 -1 return roundedContrastRatio < 4.5 ||489 -1 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0);-1 775 var defaultView = element.ownerDocument.defaultView; -1 776 var style = defaultView.getComputedStyle(container); -1 777 -1 778 if (rect.left > containerRect.right || rect.top > containerRect.bottom) { -1 779 return (style.overflow == 'scroll' || style.overflow == 'auto' || -1 780 container instanceof defaultView.HTMLBodyElement); 490 781 } -1 782 -1 783 return true; 491 784 }; 492 785 493 786 /** -1 787 * Checks whether the given element is clipped by the given container. -1 788 * Assumes that |container| is an ancestor of |element|. 494 789 * @param {Element} element495 -1 * @return {boolean}-1 790 * @param {Element} container -1 791 * @return {boolean} True iff |element| is clipped by |container|. 496 792 */497 -1 axs.utils.hasLabel = function(element) {498 -1 var tagName = element.tagName.toLowerCase();499 -1 var type = element.type ? element.type.toLowerCase() : '';-1 793 axs.utils.isClippedBy = function(element, container) { -1 794 var rect = element.getBoundingClientRect(); -1 795 var containerRect = container.getBoundingClientRect(); -1 796 var containerTop = containerRect.top; -1 797 var containerLeft = containerRect.left; -1 798 var containerScrollArea = -1 799 { top: containerTop - container.scrollTop, -1 800 bottom: containerTop - container.scrollTop + container.scrollHeight, -1 801 left: containerLeft - container.scrollLeft, -1 802 right: containerLeft - container.scrollLeft + container.scrollWidth }; 500 803501 -1 if (element.hasAttribute('aria-label'))502 -1 return true;503 -1 if (element.hasAttribute('title'))504 -1 return true;505 -1 if (tagName == 'img' && element.hasAttribute('alt'))506 -1 return true;507 -1 if (tagName == 'input' && type == 'image' && element.hasAttribute('alt'))508 -1 return true;509 -1 if (tagName == 'input' && (type == 'submit' || type == 'reset'))510 -1 return true;-1 804 var defaultView = element.ownerDocument.defaultView; -1 805 var style = defaultView.getComputedStyle(container); 511 806512 -1 // There's a separate audit that makes sure this points to an actual element or elements.513 -1 if (element.hasAttribute('aria-labelledby'))-1 807 if ((rect.right < containerRect.left || rect.bottom < containerRect.top || -1 808 rect.left > containerRect.right || rect.top > containerRect.bottom) && -1 809 style.overflow == 'hidden') { 514 810 return true;515 -1516 -1 if (element.hasAttribute('id')) {517 -1 var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]');518 -1 if (labelsFor.length > 0)519 -1 return true;520 811 } 521 812522 -1 var parent = axs.dom.parentElement(element);523 -1 while (parent) {524 -1 if (parent.tagName.toLowerCase() == 'label') {525 -1 var parentLabel = /** HTMLLabelElement */ parent;526 -1 if (parentLabel.control == element)527 -1 return true;528 -1 }529 -1 parent = axs.dom.parentElement(parent);530 -1 }-1 813 if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top) -1 814 return (style.overflow != 'visible'); -1 815 531 816 return false; 532 817 }; 533 818 534 819 /**535 -1 * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute.536 -1 * Disabled here means that the element should be considered disabled according to specification.537 -1 * This element may or may not be effectively disabled in practice as this is dependent on implementation.538 -1 *539 -1 * @param {Element} element An element to check.540 -1 * @return {boolean} true If the element supports being natively disabled.-1 820 * @param {Node} ancestor A potential ancestor of |node|. -1 821 * @param {Node} node -1 822 * @return {boolean} true if |ancestor| is an ancestor of |node| (including -1 823 * |ancestor| === |node|). 541 824 */542 -1 axs.utils.isNativelyDisableable = function(element) {543 -1 var tagName = element.tagName.toUpperCase();544 -1 return (tagName in axs.constants.NATIVELY_DISABLEABLE);-1 825 axs.utils.isAncestor = function(ancestor, node) { -1 826 if (node == null) -1 827 return false; -1 828 if (node === ancestor) -1 829 return true; -1 830 -1 831 var parentNode = axs.dom.composedParentNode(node); -1 832 return axs.utils.isAncestor(ancestor, parentNode); 545 833 }; 546 834 547 835 /**548 -1 * Determine if this element is disabled directly or indirectly by a disabled ancestor.549 -1 * Disabled here means that the element should be considered disabled according to specification.550 -1 * This element may or may not be effectively disabled in practice as this is dependent on implementation.551 -1 *552 -1 * @param {Element} element An element to check.553 -1 * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors.554 -1 * @return {boolean} true if the element or one of its ancestors is disabled.-1 836 * @param {Element} element -1 837 * @return {Array.<Element>} An array of any non-transparent elements which -1 838 * overlap the given element. 555 839 */556 -1 axs.utils.isElementDisabled = function(element, ignoreAncestors) {557 -1 var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *';558 -1 if (axs.browserUtils.matchSelector(element, selector)) {559 -1 return true;560 -1 }561 -1 if (!axs.utils.isNativelyDisableable(element) ||562 -1 axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) {563 -1 return false;564 -1 }565 -1 for (var next = element; next !== null; next = axs.dom.parentElement(next)) {566 -1 if (next.hasAttribute('disabled')) {567 -1 return true;-1 840 axs.utils.overlappingElements = function(element) { -1 841 if (axs.utils.elementHasZeroArea(element)) -1 842 return null; -1 843 -1 844 var overlappingElements = []; -1 845 var clientRects = element.getClientRects(); -1 846 for (var i = 0; i < clientRects.length; i++) { -1 847 var rect = clientRects[i]; -1 848 var center_x = (rect.left + rect.right) / 2; -1 849 var center_y = (rect.top + rect.bottom) / 2; -1 850 var elementAtPoint = document.elementFromPoint(center_x, center_y); -1 851 -1 852 if (elementAtPoint == null || elementAtPoint == element || -1 853 axs.utils.isAncestor(elementAtPoint, element) || -1 854 axs.utils.isAncestor(element, elementAtPoint)) { -1 855 continue; 568 856 }569 -1 if (ignoreAncestors) {570 -1 return false;-1 857 -1 858 var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null); -1 859 if (!overlappingElementStyle) -1 860 continue; -1 861 -1 862 var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle, -1 863 elementAtPoint); -1 864 if (overlappingElementBg && overlappingElementBg.alpha > 0 && -1 865 overlappingElements.indexOf(elementAtPoint) < 0) { -1 866 overlappingElements.push(elementAtPoint); 571 867 } 572 868 }573 -1 return false;-1 869 -1 870 return overlappingElements; 574 871 }; 575 872 576 873 /**577 -1 * @param {Element} element An element to check.578 -1 * @return {boolean} True if the element is hidden from accessibility.-1 874 * @param {Element} element -1 875 * @return {boolean} 579 876 */580 -1 axs.utils.isElementHidden = function(element) {581 -1 if (!(element instanceof element.ownerDocument.defaultView.HTMLElement))582 -1 return false;583 -1584 -1 if (element.hasAttribute('chromevoxignoreariahidden'))585 -1 var chromevoxignoreariahidden = true;-1 877 axs.utils.elementIsHtmlControl = function(element) { -1 878 var defaultView = element.ownerDocument.defaultView; 586 879587 -1 var style = window.getComputedStyle(element, null);588 -1 if (style.display == 'none' || style.visibility == 'hidden')-1 880 // HTML control -1 881 if (element instanceof defaultView.HTMLButtonElement) -1 882 return true; -1 883 if (element instanceof defaultView.HTMLInputElement) -1 884 return true; -1 885 if (element instanceof defaultView.HTMLSelectElement) -1 886 return true; -1 887 if (element instanceof defaultView.HTMLTextAreaElement) 589 888 return true; 590 889591 -1 if (element.hasAttribute('aria-hidden') &&592 -1 element.getAttribute('aria-hidden').toLowerCase() == 'true') {593 -1 return !chromevoxignoreariahidden;594 -1 }-1 890 return false; -1 891 }; 595 892 -1 893 /** -1 894 * @param {Element} element -1 895 * @return {boolean} -1 896 */ -1 897 axs.utils.elementIsAriaWidget = function(element) { -1 898 if (element.hasAttribute('role')) { -1 899 var roleValue = element.getAttribute('role'); -1 900 // TODO is this correct? -1 901 if (roleValue) { -1 902 var role = axs.constants.ARIA_ROLES[roleValue]; -1 903 if (role && 'widget' in role['allParentRolesSet']) -1 904 return true; -1 905 } -1 906 } 596 907 return false; 597 908 }; 598 909 599 910 /**600 -1 * @param {Element} element An element to check.601 -1 * @return {boolean} True if the element or one of its ancestors is602 -1 * hidden from accessibility.-1 911 * @param {Element} element -1 912 * @return {boolean} 603 913 */604 -1 axs.utils.isElementOrAncestorHidden = function(element) {605 -1 if (axs.utils.isElementHidden(element))606 -1 return true;-1 914 axs.utils.elementIsVisible = function(element) { -1 915 if (axs.utils.elementIsTransparent(element)) -1 916 return false; -1 917 if (axs.utils.elementHasZeroArea(element)) -1 918 return false; -1 919 if (axs.utils.elementIsOutsideScrollArea(element)) -1 920 return false; 607 921608 -1 if (axs.dom.parentElement(element))609 -1 return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element));610 -1 else-1 922 var overlappingElements = axs.utils.overlappingElements(element); -1 923 if (overlappingElements.length) 611 924 return false; -1 925 -1 926 return true; 612 927 }; 613 928 614 929 /**615 -1 * @param {Element} element An element to check616 -1 * @return {boolean} True if the given element is an inline element, false617 -1 * otherwise.-1 930 * @param {CSSStyleDeclaration} style -1 931 * @return {boolean} 618 932 */619 -1 axs.utils.isInlineElement = function(element) {620 -1 var tagName = element.tagName.toUpperCase();621 -1 return axs.constants.InlineElements[tagName];-1 933 axs.utils.isLargeFont = function(style) { -1 934 var fontSize = style.fontSize; -1 935 var bold = style.fontWeight == 'bold'; -1 936 var matches = fontSize.match(/(\d+)px/); -1 937 if (matches) { -1 938 var fontSizePx = parseInt(matches[1], 10); -1 939 var bodyStyle = window.getComputedStyle(document.body, null); -1 940 var bodyFontSize = bodyStyle.fontSize; -1 941 matches = bodyFontSize.match(/(\d+)px/); -1 942 if (matches) { -1 943 var bodyFontSizePx = parseInt(matches[1], 10); -1 944 var boldLarge = bodyFontSizePx * 1.2; -1 945 var large = bodyFontSizePx * 1.5; -1 946 } else { -1 947 var boldLarge = 19.2; -1 948 var large = 24; -1 949 } -1 950 return (bold && fontSizePx >= boldLarge || fontSizePx >= large); -1 951 } -1 952 matches = fontSize.match(/(\d+)em/); -1 953 if (matches) { -1 954 var fontSizeEm = parseInt(matches[1], 10); -1 955 if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5) -1 956 return true; -1 957 return false; -1 958 } -1 959 matches = fontSize.match(/(\d+)%/); -1 960 if (matches) { -1 961 var fontSizePercent = parseInt(matches[1], 10); -1 962 if (bold && fontSizePercent >= 120 || fontSizePercent >= 150) -1 963 return true; -1 964 return false; -1 965 } -1 966 matches = fontSize.match(/(\d+)pt/); -1 967 if (matches) { -1 968 var fontSizePt = parseInt(matches[1], 10); -1 969 if (bold && fontSizePt >= 14 || fontSizePt >= 18) -1 970 return true; -1 971 return false; -1 972 } -1 973 return false; 622 974 }; 623 975 624 976 /**625 -1 *626 -1 * Gets role details from an element.627 -1 * @param {Element} element The DOM element whose role we want.628 -1 * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute.629 -1 *630 -1 * @return {Object}-1 977 * @param {CSSStyleDeclaration} style -1 978 * @param {Element} element -1 979 * @return {?axs.color.Color} 631 980 */632 -1 axs.utils.getRoles = function(element, implicit) {633 -1 if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit))634 -1 return null;635 -1 var roleValue = element.getAttribute('role');636 -1 if (!roleValue && implicit)637 -1 roleValue = axs.properties.getImplicitRole(element);638 -1 if (!roleValue) // role='' or implicit role came up empty-1 981 axs.utils.getBgColor = function(style, element) { -1 982 var bgColorString = style.backgroundColor; -1 983 var bgColor = axs.color.parseColor(bgColorString); -1 984 if (!bgColor) 639 985 return null;640 -1 var roleNames = roleValue.split(' ');641 -1 var result = { roles: [], valid: false };642 -1 for (var i = 0; i < roleNames.length; i++) {643 -1 var role = roleNames[i];644 -1 var ariaRole = axs.constants.ARIA_ROLES[role];645 -1 var roleObject = { 'name': role };646 -1 if (ariaRole && !ariaRole.abstract) {647 -1 roleObject.details = ariaRole;648 -1 if (!result.applied) {649 -1 result.applied = roleObject;650 -1 }651 -1 roleObject.valid = result.valid = true;652 -1 } else {653 -1 roleObject.valid = false;654 -1 }655 -1 result.roles.push(roleObject);656 -1 }657 986658 -1 return result;-1 987 if (style.opacity < 1) -1 988 bgColor.alpha = bgColor.alpha * style.opacity; -1 989 -1 990 if (bgColor.alpha < 1) { -1 991 var parentBg = axs.utils.getParentBgColor(element); -1 992 if (parentBg == null) -1 993 return null; -1 994 -1 995 bgColor = axs.color.flattenColors(bgColor, parentBg); -1 996 } -1 997 return bgColor; 659 998 }; 660 999 661 1000 /**662 -1 * @param {!string} propertyName663 -1 * @param {!string} value664 -1 * @param {!Element} element665 -1 * @return {!Object}-1 1001 * Gets the effective background color of the parent of |element|. -1 1002 * @param {Element} element -1 1003 * @return {?axs.color.Color} 666 1004 */667 -1 axs.utils.getAriaPropertyValue = function(propertyName, value, element) {668 -1 var propertyKey = propertyName.replace(/^aria-/, '');669 -1 var property = axs.constants.ARIA_PROPERTIES[propertyKey];670 -1 var result = { 'name': propertyName, 'rawValue': value };671 -1 if (!property) {672 -1 result.valid = false;673 -1 result.reason = '"' + propertyName + '" is not a valid ARIA property';674 -1 return result;675 -1 }-1 1005 axs.utils.getParentBgColor = function(element) { -1 1006 /** @type {Element} */ var parent = element; -1 1007 var bgStack = []; -1 1008 var foundSolidColor = null; -1 1009 while ((parent = axs.dom.parentElement(parent))) { -1 1010 var computedStyle = window.getComputedStyle(parent, null); -1 1011 if (!computedStyle) -1 1012 continue; 676 1013677 -1 var propertyType = property.valueType;678 -1 if (!propertyType) {679 -1 result.valid = false;680 -1 result.reason = '"' + propertyName + '" is not a valid ARIA property';681 -1 return result;682 -1 }-1 1014 var parentBg = axs.color.parseColor(computedStyle.backgroundColor); -1 1015 if (!parentBg) -1 1016 continue; 683 1017684 -1 switch (propertyType) {685 -1 case "idref":686 -1 var isValid = axs.utils.isValidIDRefValue(value, element);687 -1 result.valid = isValid.valid;688 -1 result.reason = isValid.reason;689 -1 result.idref = isValid.idref;690 -1 // falls through691 -1 case "idref_list":692 -1 var idrefValues = value.split(/\s+/);693 -1 result.valid = true;694 -1 for (var i = 0; i < idrefValues.length; i++) {695 -1 var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i], element);696 -1 if (!refIsValid.valid)697 -1 result.valid = false;698 -1 if (result.values)699 -1 result.values.push(refIsValid);700 -1 else701 -1 result.values = [refIsValid];702 -1 }703 -1 return result;704 -1 case "integer":705 -1 var validNumber = axs.utils.isValidNumber(value);706 -1 if (!validNumber.valid) {707 -1 result.valid = false;708 -1 result.reason = validNumber.reason;709 -1 return result;710 -1 }711 -1 if (Math.floor(validNumber.value) !== validNumber.value) {712 -1 result.valid = false;713 -1 result.reason = '' + value + ' is not a whole integer';714 -1 } else {715 -1 result.valid = true;716 -1 result.value = validNumber.value;717 -1 }718 -1 return result;719 -1 case "decimal":720 -1 case "number":721 -1 var validNumber = axs.utils.isValidNumber(value);722 -1 result.valid = validNumber.valid;723 -1 if (!validNumber.valid) {724 -1 result.reason = validNumber.reason;725 -1 return result;726 -1 }727 -1 result.value = validNumber.value;728 -1 return result;729 -1 case "string":730 -1 result.valid = true;731 -1 result.value = value;732 -1 return result;733 -1 case "token":734 -1 var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase());735 -1 if (validTokenValue.valid) {736 -1 result.valid = true;737 -1 result.value = validTokenValue.value;738 -1 return result;739 -1 } else {740 -1 result.valid = false;741 -1 result.value = value;742 -1 result.reason = validTokenValue.reason;743 -1 return result;744 -1 }745 -1 // falls through746 -1 case "token_list":747 -1 var tokenValues = value.split(/\s+/);748 -1 result.valid = true;749 -1 for (var i = 0; i < tokenValues.length; i++) {750 -1 var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase());751 -1 if (!validTokenValue.valid) {752 -1 result.valid = false;753 -1 if (result.reason) {754 -1 result.reason = [ result.reason ];755 -1 result.reason.push(validTokenValue.reason);756 -1 } else {757 -1 result.reason = validTokenValue.reason;758 -1 result.possibleValues = validTokenValue.possibleValues;759 -1 }760 -1 }761 -1 // TODO (more structured result)762 -1 if (result.values)763 -1 result.values.push(validTokenValue.value);764 -1 else765 -1 result.values = [validTokenValue.value];766 -1 }767 -1 return result;768 -1 case "tristate":769 -1 var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName);770 -1 if (validTristate.valid) {771 -1 result.valid = true;772 -1 result.value = validTristate.value;773 -1 } else {774 -1 result.valid = false;775 -1 result.value = value;776 -1 result.reason = validTristate.reason;777 -1 }778 -1 return result;779 -1 case "boolean":780 -1 var validBoolean = axs.utils.isValidBoolean(value);781 -1 if (validBoolean.valid) {782 -1 result.valid = true;783 -1 result.value = validBoolean.value;784 -1 } else {785 -1 result.valid = false;786 -1 result.value = value;787 -1 result.reason = validBoolean.reason;-1 1018 if (computedStyle.opacity < 1) -1 1019 parentBg.alpha = parentBg.alpha * computedStyle.opacity; -1 1020 -1 1021 if (parentBg.alpha == 0) -1 1022 continue; -1 1023 -1 1024 bgStack.push(parentBg); -1 1025 -1 1026 if (parentBg.alpha == 1) { -1 1027 foundSolidColor = true; -1 1028 break; 788 1029 }789 -1 return result;790 1030 }791 -1 result.valid = false;792 -1 result.reason = 'Not a valid ARIA property';793 -1 return result;-1 1031 -1 1032 if (!foundSolidColor) -1 1033 bgStack.push(new axs.color.Color(255, 255, 255, 1)); -1 1034 -1 1035 var bg = bgStack.pop(); -1 1036 while (bgStack.length) { -1 1037 var fg = bgStack.pop(); -1 1038 bg = axs.color.flattenColors(fg, bg); -1 1039 } -1 1040 return bg; 794 1041 }; 795 1042 796 1043 /**797 -1 * @param {string} propertyName The name of the property.798 -1 * @param {string} value The value to check.799 -1 * @return {!Object}-1 1044 * @param {CSSStyleDeclaration} style -1 1045 * @param {Element} element -1 1046 * @param {axs.color.Color} bgColor The background color, which may come from -1 1047 * another element (such as a parent element), for flattening into the -1 1048 * foreground color. -1 1049 * @return {?axs.color.Color} 800 1050 */801 -1 axs.utils.isValidTokenValue = function(propertyName, value) {802 -1 var propertyKey = propertyName.replace(/^aria-/, '');803 -1 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];804 -1 var possibleValues = propertyDetails.valuesSet;805 -1 return axs.utils.isPossibleValue(value, possibleValues, propertyName);-1 1051 axs.utils.getFgColor = function(style, element, bgColor) { -1 1052 var fgColorString = style.color; -1 1053 var fgColor = axs.color.parseColor(fgColorString); -1 1054 if (!fgColor) -1 1055 return null; -1 1056 -1 1057 if (fgColor.alpha < 1) -1 1058 fgColor = axs.color.flattenColors(fgColor, bgColor); -1 1059 -1 1060 if (style.opacity < 1) { -1 1061 var parentBg = axs.utils.getParentBgColor(element); -1 1062 fgColor.alpha = fgColor.alpha * style.opacity; -1 1063 fgColor = axs.color.flattenColors(fgColor, parentBg); -1 1064 } -1 1065 -1 1066 return fgColor; 806 1067 }; 807 1068 808 1069 /**809 -1 * @param {string} value810 -1 * @param {Object.<string, boolean>} possibleValues811 -1 * @param {string} propertyName The name of the property.812 -1 * @return {!Object}-1 1070 * @param {Element} element -1 1071 * @return {?number} 813 1072 */814 -1 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) {815 -1 if (!possibleValues[value])816 -1 return { 'valid': false,817 -1 'value': value,818 -1 'reason': '"' + value + '" is not a valid value for ' + propertyName,819 -1 'possibleValues': Object.keys(possibleValues) };820 -1 return { 'valid': true, 'value': value };-1 1073 axs.utils.getContrastRatioForElement = function(element) { -1 1074 var style = window.getComputedStyle(element, null); -1 1075 return axs.utils.getContrastRatioForElementWithComputedStyle(style, element); 821 1076 }; 822 1077 823 1078 /**824 -1 * @param {string} value825 -1 * @return {!Object}-1 1079 * @param {CSSStyleDeclaration} style -1 1080 * @param {Element} element -1 1081 * @return {?number} 826 1082 */827 -1 axs.utils.isValidBoolean = function(value) {828 -1 try {829 -1 var parsedValue = JSON.parse(value);830 -1 } catch (e) {831 -1 parsedValue = '';832 -1 }833 -1 if (typeof(parsedValue) != 'boolean')834 -1 return { 'valid': false,835 -1 'value': value,836 -1 'reason': '"' + value + '" is not a true/false value' };837 -1 return { 'valid': true, 'value': parsedValue };-1 1083 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) { -1 1084 if (axs.utils.isElementHidden(element)) -1 1085 return null; -1 1086 -1 1087 var bgColor = axs.utils.getBgColor(style, element); -1 1088 if (!bgColor) -1 1089 return null; -1 1090 -1 1091 var fgColor = axs.utils.getFgColor(style, element, bgColor); -1 1092 if (!fgColor) -1 1093 return null; -1 1094 -1 1095 return axs.color.calculateContrastRatio(fgColor, bgColor); 838 1096 }; 839 1097 840 1098 /**841 -1 * @param {string} value842 -1 * @param {!Element} element843 -1 * @return {!Object}-1 1099 * @param {Element} element -1 1100 * @return {boolean} 844 1101 */845 -1 axs.utils.isValidIDRefValue = function(value, element) {846 -1 if (value.length == 0)847 -1 return { 'valid': true, 'idref': value };848 -1 if (!element.ownerDocument.getElementById(value))849 -1 return { 'valid': false,850 -1 'idref': value,851 -1 'reason': 'No element with ID "' + value + '"' };852 -1 return { 'valid': true, 'idref': value };-1 1102 axs.utils.isNativeTextElement = function(element) { -1 1103 var tagName = element.tagName.toLowerCase(); -1 1104 var type = element.type ? element.type.toLowerCase() : ''; -1 1105 if (tagName == 'textarea') -1 1106 return true; -1 1107 if (tagName != 'input') -1 1108 return false; -1 1109 -1 1110 switch (type) { -1 1111 case 'email': -1 1112 case 'number': -1 1113 case 'password': -1 1114 case 'search': -1 1115 case 'text': -1 1116 case 'tel': -1 1117 case 'url': -1 1118 case '': -1 1119 return true; -1 1120 default: -1 1121 return false; -1 1122 } 853 1123 }; 854 1124 855 1125 /**856 -1 * Tests if a number is real number for a11y purposes.857 -1 * Must be a real, numerical, decimal value; heavily inspired by858 -1 * http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number859 -1 * @param {string} value860 -1 * @return {!Object}-1 1126 * @param {number} contrastRatio -1 1127 * @param {CSSStyleDeclaration} style -1 1128 * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level -1 1129 * @return {boolean} 861 1130 */862 -1 axs.utils.isValidNumber = function(value) {863 -1 var failResult = {864 -1 'valid': false,865 -1 'value': value,866 -1 'reason': '"' + value + '" is not a number'867 -1 };868 -1 if (!value) {869 -1 return failResult;870 -1 }871 -1 if (/^0x/i.test(value)) {872 -1 failResult.reason = '"' + value + '" is not a decimal number'; // hex is not accepted873 -1 return failResult;874 -1 }875 -1 var parsedValue = value * 1;876 -1 if (!isFinite(parsedValue)) {877 -1 return failResult;-1 1131 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) { -1 1132 // Round to nearest 0.1 -1 1133 var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10); -1 1134 if (!opt_strict) { -1 1135 return roundedContrastRatio < 3.0 || -1 1136 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5); -1 1137 } else { -1 1138 return roundedContrastRatio < 4.5 || -1 1139 (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0); 878 1140 }879 -1 return { 'valid': true, 'value': parsedValue };880 1141 }; 881 1142 882 1143 /** 883 1144 * @param {Element} element 884 1145 * @return {boolean} 885 1146 */886 -1 axs.utils.isElementImplicitlyFocusable = function(element) {887 -1 var defaultView = element.ownerDocument.defaultView;-1 1147 axs.utils.hasLabel = function(element) { -1 1148 var tagName = element.tagName.toLowerCase(); -1 1149 var type = element.type ? element.type.toLowerCase() : ''; 888 1150889 -1 if (element instanceof defaultView.HTMLAnchorElement ||890 -1 element instanceof defaultView.HTMLAreaElement)891 -1 return element.hasAttribute('href');892 -1 if (element instanceof defaultView.HTMLInputElement ||893 -1 element instanceof defaultView.HTMLSelectElement ||894 -1 element instanceof defaultView.HTMLTextAreaElement ||895 -1 element instanceof defaultView.HTMLButtonElement ||896 -1 element instanceof defaultView.HTMLIFrameElement)897 -1 return !element.disabled;-1 1151 if (element.hasAttribute('aria-label')) -1 1152 return true; -1 1153 if (element.hasAttribute('title')) -1 1154 return true; -1 1155 if (tagName == 'img' && element.hasAttribute('alt')) -1 1156 return true; -1 1157 if (tagName == 'input' && type == 'image' && element.hasAttribute('alt')) -1 1158 return true; -1 1159 if (tagName == 'input' && (type == 'submit' || type == 'reset')) -1 1160 return true; -1 1161 -1 1162 // There's a separate audit that makes sure this points to an actual element or elements. -1 1163 if (element.hasAttribute('aria-labelledby')) -1 1164 return true; -1 1165 -1 1166 if (element.hasAttribute('id')) { -1 1167 var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]'); -1 1168 if (labelsFor.length > 0) -1 1169 return true; -1 1170 } -1 1171 -1 1172 var parent = axs.dom.parentElement(element); -1 1173 while (parent) { -1 1174 if (parent.tagName.toLowerCase() == 'label') { -1 1175 var parentLabel = /** HTMLLabelElement */ parent; -1 1176 if (parentLabel.control == element) -1 1177 return true; -1 1178 } -1 1179 parent = axs.dom.parentElement(parent); -1 1180 } 898 1181 return false; 899 1182 }; 900 1183 901 1184 /**902 -1 * Returns an array containing the values of the given JSON-compatible object.903 -1 * (Simply ignores any function values.)904 -1 * @param {Object} obj905 -1 * @return {Array}-1 1185 * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute. -1 1186 * Disabled here means that the element should be considered disabled according to specification. -1 1187 * This element may or may not be effectively disabled in practice as this is dependent on implementation. -1 1188 * -1 1189 * @param {Element} element An element to check. -1 1190 * @return {boolean} true If the element supports being natively disabled. 906 1191 */907 -1 axs.utils.values = function(obj) {908 -1 var values = [];909 -1 for (var key in obj) {910 -1 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')911 -1 values.push(obj[key]);912 -1 }913 -1 return values;-1 1192 axs.utils.isNativelyDisableable = function(element) { -1 1193 var tagName = element.tagName.toUpperCase(); -1 1194 return (tagName in axs.constants.NATIVELY_DISABLEABLE); 914 1195 }; 915 1196 916 1197 /**917 -1 * Returns an object containing the same keys and values as the given918 -1 * JSON-compatible object. (Simply ignores any function values.)919 -1 * @param {Object} obj920 -1 * @return {Object}-1 1198 * Determine if this element is disabled directly or indirectly by a disabled ancestor. -1 1199 * Disabled here means that the element should be considered disabled according to specification. -1 1200 * This element may or may not be effectively disabled in practice as this is dependent on implementation. -1 1201 * -1 1202 * @param {Element} element An element to check. -1 1203 * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors. -1 1204 * @return {boolean} true if the element or one of its ancestors is disabled. 921 1205 */922 -1 axs.utils.namedValues = function(obj) {923 -1 var values = {};924 -1 for (var key in obj) {925 -1 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')926 -1 values[key] = obj[key];-1 1206 axs.utils.isElementDisabled = function(element, ignoreAncestors) { -1 1207 var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *'; -1 1208 if (axs.browserUtils.matchSelector(element, selector)) { -1 1209 return true; 927 1210 }928 -1 return values;-1 1211 if (!axs.utils.isNativelyDisableable(element) || -1 1212 axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) { -1 1213 return false; -1 1214 } -1 1215 for (var next = element; next !== null; next = axs.dom.parentElement(next)) { -1 1216 if (next.hasAttribute('disabled')) { -1 1217 return true; -1 1218 } -1 1219 if (ignoreAncestors) { -1 1220 return false; -1 1221 } -1 1222 } -1 1223 return false; 929 1224 }; 930 1225 931 1226 /**932 -1 * Escapes a given ID to be used in a CSS selector933 -1 *934 -1 * @private935 -1 * @param {!string} id The ID to be escaped936 -1 * @return {string} The escaped ID937 -1 */938 -1 function escapeId(id) {939 -1 return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; });940 -1 }941 -1942 -1 /** Gets a CSS selector text for a DOM object.943 -1 * @param {Node} obj The DOM object.944 -1 * @return {string} CSS selector text for the DOM object.-1 1227 * @param {Element} element An element to check. -1 1228 * @return {boolean} True if the element is hidden from accessibility. 945 1229 */946 -1 axs.utils.getQuerySelectorText = function(obj) {947 -1 if (obj == null || obj.tagName == 'HTML') {948 -1 return 'html';949 -1 } else if (obj.tagName == 'BODY') {950 -1 return 'body';951 -1 }952 -1953 -1 if (obj.hasAttribute) {954 -1 if (obj.id) {955 -1 return '#' + escapeId(obj.id);956 -1 }957 -1958 -1 if (obj.className) {959 -1 var selector = '';960 -1 for (var i = 0; i < obj.classList.length; i++)961 -1 selector += '.' + obj.classList[i];962 -1963 -1 var total = 0;964 -1 if (obj.parentNode) {965 -1 for (i = 0; i < obj.parentNode.children.length; i++) {966 -1 var similar = obj.parentNode.children[i];967 -1 if (axs.browserUtils.matchSelector(similar, selector))968 -1 total++;969 -1 if (similar === obj)970 -1 break;971 -1 }972 -1 } else {973 -1 total = 1;974 -1 }975 -1976 -1 if (total == 1) {977 -1 return axs.utils.getQuerySelectorText(obj.parentNode) +978 -1 ' > ' + selector;979 -1 }980 -1 }-1 1230 axs.utils.isElementHidden = function(element) { -1 1231 if (!(element instanceof element.ownerDocument.defaultView.HTMLElement)) -1 1232 return false; 981 1233982 -1 if (obj.parentNode) {983 -1 var similarTags = obj.parentNode.children;984 -1 var total = 1;985 -1 var i = 0;986 -1 while (similarTags[i] !== obj) {987 -1 if (similarTags[i].tagName == obj.tagName) {988 -1 total++;989 -1 }990 -1 i++;991 -1 }-1 1234 if (element.hasAttribute('chromevoxignoreariahidden')) -1 1235 var chromevoxignoreariahidden = true; 992 1236993 -1 var next = '';994 -1 if (obj.parentNode.tagName != 'BODY') {995 -1 next = axs.utils.getQuerySelectorText(obj.parentNode) +996 -1 ' > ';997 -1 }-1 1237 var style = window.getComputedStyle(element, null); -1 1238 if (style.display == 'none' || style.visibility == 'hidden') -1 1239 return true; 998 1240999 -1 if (total == 1) {1000 -1 return next +1001 -1 obj.tagName;1002 -1 } else {1003 -1 return next +1004 -1 obj.tagName +1005 -1 ':nth-of-type(' + total + ')';1006 -1 }-1 1241 if (element.hasAttribute('aria-hidden') && -1 1242 element.getAttribute('aria-hidden').toLowerCase() == 'true') { -1 1243 return !chromevoxignoreariahidden; 1007 1244 } 1008 12451009 -1 } else if (obj.selectorText) {1010 -1 return obj.selectorText;1011 -1 }1012 -11013 -1 return '';-1 1246 return false; 1014 1247 }; 1015 1248 1016 1249 /**1017 -1 * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or1018 -1 * single ID reference.1019 -1 * @param {Element} element a potential referent.1020 -1 * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'.1021 -1 * @return {NodeList} The elements that refer to this element or null.-1 1250 * @param {Element} element An element to check. -1 1251 * @return {boolean} True if the element or one of its ancestors is -1 1252 * hidden from accessibility. 1022 1253 */1023 -1 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) {1024 -1 var propertyToSelector = function(propertyKey) {1025 -1 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];1026 -1 if (propertyDetails) {1027 -1 if (propertyDetails.valueType === ('idref')) {1028 -1 return '[aria-' + propertyKey + '=\'' + id + '\']';1029 -1 } else if (propertyDetails.valueType === ('idref_list')) {1030 -1 return '[aria-' + propertyKey + '~=\'' + id + '\']';1031 -1 }1032 -1 }1033 -1 return '';1034 -1 };1035 -1 if (!element)1036 -1 return null;1037 -1 var id = element.id;1038 -1 if (!id)1039 -1 return null;1040 -1 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector-1 1254 axs.utils.isElementOrAncestorHidden = function(element) { -1 1255 if (axs.utils.isElementHidden(element)) -1 1256 return true; 1041 12571042 -1 if (opt_attributeName) {1043 -1 var propertyKey = opt_attributeName.replace(/^aria-/, '');1044 -1 var referrerQuery = propertyToSelector(propertyKey);1045 -1 if (referrerQuery) {1046 -1 return element.ownerDocument.querySelectorAll(referrerQuery);1047 -1 }1048 -1 } else {1049 -1 var selectors = [];1050 -1 for (var propertyKey in axs.constants.ARIA_PROPERTIES) {1051 -1 var referrerQuery = propertyToSelector(propertyKey);1052 -1 if (referrerQuery) {1053 -1 selectors.push(referrerQuery);1054 -1 }1055 -1 }1056 -1 return element.ownerDocument.querySelectorAll(selectors.join(','));1057 -1 }1058 -1 return null;-1 1258 if (axs.dom.parentElement(element)) -1 1259 return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element)); -1 1260 else -1 1261 return false; 1059 1262 }; 1060 1263 1061 1264 /**1062 -1 * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or1063 -1 * single ID reference.1064 -1 * @param {Element} element a potential referent.1065 -1 * @return {NodeList} The elements that refer to this element.-1 1265 * @param {Element} element An element to check -1 1266 * @return {boolean} True if the given element is an inline element, false -1 1267 * otherwise. 1066 1268 */1067 -1 axs.utils.getHtmlIdReferrers = function(element) {1068 -1 if (!element)1069 -1 return null;1070 -1 var id = element.id;1071 -1 if (!id)1072 -1 return null;1073 -1 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector1074 -1 var selectorTemplates = [1075 -1 '[contextmenu=\'{id}\']',1076 -1 '[itemref~=\'{id}\']',1077 -1 'button[form=\'{id}\']',1078 -1 'button[menu=\'{id}\']',1079 -1 'fieldset[form=\'{id}\']',1080 -1 'input[form=\'{id}\']',1081 -1 'input[list=\'{id}\']',1082 -1 'keygen[form=\'{id}\']',1083 -1 'label[for=\'{id}\']',1084 -1 'label[form=\'{id}\']',1085 -1 'menuitem[command=\'{id}\']',1086 -1 'object[form=\'{id}\']',1087 -1 'output[for~=\'{id}\']',1088 -1 'output[form=\'{id}\']',1089 -1 'select[form=\'{id}\']',1090 -1 'td[headers~=\'{id}\']',1091 -1 'textarea[form=\'{id}\']',1092 -1 'tr[headers~=\'{id}\']'];1093 -1 var selectors = selectorTemplates.map(function(selector) {1094 -1 return selector.replace('\{id\}', id);1095 -1 });1096 -1 return element.ownerDocument.querySelectorAll(selectors.join(','));-1 1269 axs.utils.isInlineElement = function(element) { -1 1270 var tagName = element.tagName.toUpperCase(); -1 1271 return axs.constants.InlineElements[tagName]; 1097 1272 }; 1098 1273 1099 1274 /**1100 -1 * Gets a list of all IDs this element references in either ARIA or HTML attributes.1101 1275 *1102 -1 * @param {Element} element The element to check for idref attributes.1103 -1 * @returns {Array.<string>} Any IDs this element references.-1 1276 * Gets role details from an element. -1 1277 * @param {Element} element The DOM element whose role we want. -1 1278 * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute. -1 1279 * -1 1280 * @return {Object} 1104 1281 */1105 -1 axs.utils.getReferencedIds = function(element) {1106 -1 var result = [];1107 -1 var addResult = function(ids) {1108 -1 if (ids) {1109 -1 if (ids.indexOf(' ') > 0) {1110 -1 result = result.concat(attrib.value.split(' '));1111 -1 } else {1112 -1 result.push(ids);1113 -1 }1114 -1 }1115 -1 };1116 -1 for (var i = 0; i < element.attributes.length; i++) {1117 -1 var tagName = element.tagName.toLowerCase();1118 -1 var attrib = element.attributes[i];1119 -1 if (attrib.specified) {1120 -1 var attribName = attrib.name;1121 -1 var ariaAttr = attribName.match(/aria-(.+)/);1122 -1 if (ariaAttr) {1123 -1 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]];1124 -1 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) {1125 -1 addResult(attrib.value);1126 -1 }1127 -1 continue;1128 -1 }1129 -1 switch (attribName) {1130 -1 case 'contextmenu':1131 -1 case 'itemref':1132 -1 addResult(attrib.value);1133 -1 break;1134 -1 case 'form':1135 -1 if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' ||1136 -1 tagName == 'keygen' || tagName == 'label' || tagName == 'object' ||1137 -1 tagName == 'output' || tagName == 'select' || tagName == 'textarea') {1138 -1 addResult(attrib.value);1139 -1 }1140 -1 break;1141 -1 case 'for':1142 -1 if (tagName == 'label' || tagName == 'output') {1143 -1 addResult(attrib.value);1144 -1 }1145 -1 break;1146 -1 case 'menu':1147 -1 if (tagName == 'button') {1148 -1 addResult(attrib.value);1149 -1 }1150 -1 break;1151 -1 case 'list':1152 -1 if (tagName == 'input') {1153 -1 addResult(attrib.value);1154 -1 }1155 -1 break;1156 -1 case 'command':1157 -1 if (tagName == 'menuitem') {1158 -1 addResult(attrib.value);1159 -1 }1160 -1 break;1161 -1 case 'headers':1162 -1 if (tagName == 'td' || tagName == 'tr') {1163 -1 addResult(attrib.value);1164 -1 }1165 -1 break;-1 1282 axs.utils.getRoles = function(element, implicit) { -1 1283 if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit)) -1 1284 return null; -1 1285 var roleValue = element.getAttribute('role'); -1 1286 if (!roleValue && implicit) -1 1287 roleValue = axs.properties.getImplicitRole(element); -1 1288 if (!roleValue) // role='' or implicit role came up empty -1 1289 return null; -1 1290 var roleNames = roleValue.split(' '); -1 1291 var result = { roles: [], valid: false }; -1 1292 for (var i = 0; i < roleNames.length; i++) { -1 1293 var role = roleNames[i]; -1 1294 var ariaRole = axs.constants.ARIA_ROLES[role]; -1 1295 var roleObject = { 'name': role }; -1 1296 if (ariaRole && !ariaRole.abstract) { -1 1297 roleObject.details = ariaRole; -1 1298 if (!result.applied) { -1 1299 result.applied = roleObject; 1166 1300 } -1 1301 roleObject.valid = result.valid = true; -1 1302 } else { -1 1303 roleObject.valid = false; 1167 1304 } -1 1305 result.roles.push(roleObject); 1168 1306 } -1 1307 1169 1308 return result; 1170 1309 }; 1171 1310 1172 1311 /**1173 -1 * Gets elements that refer to this element in an attribute that takes an ID reference list or1174 -1 * single ID reference.1175 -1 * @param {Element} element a potential referent.1176 -1 * @return {Array<Element>} The elements that refer to this element.-1 1312 * @param {!string} propertyName -1 1313 * @param {!string} value -1 1314 * @param {!Element} element -1 1315 * @return {!Object} 1177 1316 */1178 -1 axs.utils.getIdReferrers = function(element) {1179 -1 var result = [];1180 -1 var referrers = axs.utils.getHtmlIdReferrers(element);1181 -1 if (referrers) {1182 -1 result = result.concat(Array.prototype.slice.call(referrers));-1 1317 axs.utils.getAriaPropertyValue = function(propertyName, value, element) { -1 1318 var propertyKey = propertyName.replace(/^aria-/, ''); -1 1319 var property = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 1320 var result = { 'name': propertyName, 'rawValue': value }; -1 1321 if (!property) { -1 1322 result.valid = false; -1 1323 result.reason = '"' + propertyName + '" is not a valid ARIA property'; -1 1324 return result; 1183 1325 }1184 -1 referrers = axs.utils.getAriaIdReferrers(element);1185 -1 if (referrers) {1186 -1 result = result.concat(Array.prototype.slice.call(referrers));-1 1326 -1 1327 var propertyType = property.valueType; -1 1328 if (!propertyType) { -1 1329 result.valid = false; -1 1330 result.reason = '"' + propertyName + '" is not a valid ARIA property'; -1 1331 return result; 1187 1332 }1188 -1 return result;1189 -1 };1190 13331191 -1 /**1192 -1 * Gets elements which this element refers to in the given attribute.1193 -1 * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'.1194 -1 * @param {Element} element The DOM element which has the ARIA attribute.1195 -1 * @return {!Array.<Element>} An array of elements that are referred to by this element.1196 -1 * @example1197 -1 * var owner = document.body.appendChild(document.createElement("div"));1198 -1 * var owned = document.body.appendChild(document.createElement("div"));1199 -1 * owner.setAttribute("aria-owns", "kungfu");1200 -1 * owned.setAttribute("id", "kungfu");1201 -1 * console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned); // This will log 'true'1202 -1 */1203 -1 axs.utils.getIdReferents = function(attributeName, element) {1204 -1 var result = [];1205 -1 var propertyKey = attributeName.replace(/^aria-/, '');1206 -1 var property = axs.constants.ARIA_PROPERTIES[propertyKey];1207 -1 if (!property || !element.hasAttribute(attributeName))-1 1334 switch (propertyType) { -1 1335 case "idref": -1 1336 var isValid = axs.utils.isValidIDRefValue(value, element); -1 1337 result.valid = isValid.valid; -1 1338 result.reason = isValid.reason; -1 1339 result.idref = isValid.idref; -1 1340 // falls through -1 1341 case "idref_list": -1 1342 var idrefValues = value.split(/\s+/); -1 1343 result.valid = true; -1 1344 for (var i = 0; i < idrefValues.length; i++) { -1 1345 var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i], element); -1 1346 if (!refIsValid.valid) -1 1347 result.valid = false; -1 1348 if (result.values) -1 1349 result.values.push(refIsValid); -1 1350 else -1 1351 result.values = [refIsValid]; -1 1352 } 1208 1353 return result;1209 -1 var propertyType = property.valueType;1210 -1 if (propertyType === 'idref_list' || propertyType === 'idref') {1211 -1 var ownerDocument = element.ownerDocument;1212 -1 var ids = element.getAttribute(attributeName);1213 -1 ids = ids.split(/\s+/);1214 -1 for (var i = 0, len = ids.length; i < len; i++) {1215 -1 var next = ownerDocument.getElementById(ids[i]);1216 -1 if (next) {1217 -1 result[result.length] = next;-1 1354 case "integer": -1 1355 var validNumber = axs.utils.isValidNumber(value); -1 1356 if (!validNumber.valid) { -1 1357 result.valid = false; -1 1358 result.reason = validNumber.reason; -1 1359 return result; -1 1360 } -1 1361 if (Math.floor(validNumber.value) !== validNumber.value) { -1 1362 result.valid = false; -1 1363 result.reason = '' + value + ' is not a whole integer'; -1 1364 } else { -1 1365 result.valid = true; -1 1366 result.value = validNumber.value; -1 1367 } -1 1368 return result; -1 1369 case "decimal": -1 1370 case "number": -1 1371 var validNumber = axs.utils.isValidNumber(value); -1 1372 result.valid = validNumber.valid; -1 1373 if (!validNumber.valid) { -1 1374 result.reason = validNumber.reason; -1 1375 return result; -1 1376 } -1 1377 result.value = validNumber.value; -1 1378 return result; -1 1379 case "string": -1 1380 result.valid = true; -1 1381 result.value = value; -1 1382 return result; -1 1383 case "token": -1 1384 var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase()); -1 1385 if (validTokenValue.valid) { -1 1386 result.valid = true; -1 1387 result.value = validTokenValue.value; -1 1388 return result; -1 1389 } else { -1 1390 result.valid = false; -1 1391 result.value = value; -1 1392 result.reason = validTokenValue.reason; -1 1393 return result; -1 1394 } -1 1395 // falls through -1 1396 case "token_list": -1 1397 var tokenValues = value.split(/\s+/); -1 1398 result.valid = true; -1 1399 for (var i = 0; i < tokenValues.length; i++) { -1 1400 var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase()); -1 1401 if (!validTokenValue.valid) { -1 1402 result.valid = false; -1 1403 if (result.reason) { -1 1404 result.reason = [ result.reason ]; -1 1405 result.reason.push(validTokenValue.reason); -1 1406 } else { -1 1407 result.reason = validTokenValue.reason; -1 1408 result.possibleValues = validTokenValue.possibleValues; -1 1409 } 1218 1410 } -1 1411 // TODO (more structured result) -1 1412 if (result.values) -1 1413 result.values.push(validTokenValue.value); -1 1414 else -1 1415 result.values = [validTokenValue.value]; -1 1416 } -1 1417 return result; -1 1418 case "tristate": -1 1419 var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName); -1 1420 if (validTristate.valid) { -1 1421 result.valid = true; -1 1422 result.value = validTristate.value; -1 1423 } else { -1 1424 result.valid = false; -1 1425 result.value = value; -1 1426 result.reason = validTristate.reason; -1 1427 } -1 1428 return result; -1 1429 case "boolean": -1 1430 var validBoolean = axs.utils.isValidBoolean(value); -1 1431 if (validBoolean.valid) { -1 1432 result.valid = true; -1 1433 result.value = validBoolean.value; -1 1434 } else { -1 1435 result.valid = false; -1 1436 result.value = value; -1 1437 result.reason = validBoolean.reason; 1219 1438 } -1 1439 return result; 1220 1440 } -1 1441 result.valid = false; -1 1442 result.reason = 'Not a valid ARIA property'; 1221 1443 return result; 1222 1444 }; 1223 1445 1224 1446 /**1225 -1 * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'.1226 -1 * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list'].1227 -1 * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match.-1 1447 * @param {string} propertyName The name of the property. -1 1448 * @param {string} value The value to check. -1 1449 * @return {!Object} 1228 1450 */1229 -1 axs.utils.getAriaPropertiesByValueType = function(valueTypes) {1230 -1 var result = {};1231 -1 for (var propertyName in axs.constants.ARIA_PROPERTIES) {1232 -1 var property = axs.constants.ARIA_PROPERTIES[propertyName];1233 -1 if (property && valueTypes.indexOf(property.valueType) >= 0) {1234 -1 result[propertyName] = property;1235 -1 }1236 -1 }1237 -1 return result;-1 1451 axs.utils.isValidTokenValue = function(propertyName, value) { -1 1452 var propertyKey = propertyName.replace(/^aria-/, ''); -1 1453 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 1454 var possibleValues = propertyDetails.valuesSet; -1 1455 return axs.utils.isPossibleValue(value, possibleValues, propertyName); 1238 1456 }; 1239 1457 1240 1458 /**1241 -1 * Builds a selector that matches an element with any of these ARIA properties.1242 -1 * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES1243 -1 * @return {!string} The selector.-1 1459 * @param {string} value -1 1460 * @param {Object.<string, boolean>} possibleValues -1 1461 * @param {string} propertyName The name of the property. -1 1462 * @return {!Object} 1244 1463 */1245 -1 axs.utils.getSelectorForAriaProperties = function(ariaProperties) {1246 -1 var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties));1247 -1 var result = propertyNames.map(function(propertyName) {1248 -1 return '[aria-' + propertyName + ']';1249 -1 });1250 -1 result.sort(); // facilitates reading long selectors and unit testing1251 -1 return result.join(',');-1 1464 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) { -1 1465 if (!possibleValues[value]) -1 1466 return { 'valid': false, -1 1467 'value': value, -1 1468 'reason': '"' + value + '" is not a valid value for ' + propertyName, -1 1469 'possibleValues': Object.keys(possibleValues) }; -1 1470 return { 'valid': true, 'value': value }; 1252 1471 }; 1253 1472 1254 1473 /**1255 -1 * Finds descendants of this element which implement the given ARIA role.1256 -1 * Will look for descendants with implicit or explicit role.1257 -1 * @param {Element} element an HTML DOM element.1258 -1 * @param {string} role The role you seek.1259 -1 * @return {!Array.<Element>} An array of matching elements.1260 -1 * @example1261 -1 * var container = document.createElement("div");1262 -1 * var button = document.createElement("button");1263 -1 * var span = document.createElement("span");1264 -1 * span.setAttribute("role", "button");1265 -1 * container.appendChild(button);1266 -1 * container.appendChild(span);1267 -1 * var result = axs.utils.findDescendantsWithRole(container, "button"); // result is an array containing both 'button' and 'span'-1 1474 * @param {string} value -1 1475 * @return {!Object} 1268 1476 */1269 -1 axs.utils.findDescendantsWithRole = function(element, role) {1270 -1 if (!(element && role))1271 -1 return [];1272 -1 var selector = axs.properties.getSelectorForRole(role);1273 -1 if (!selector)1274 -1 return [];1275 -1 var result = element.querySelectorAll(selector);1276 -1 if (result) { // Convert NodeList to Array; methinks 80/20 that's what callers want.1277 -1 result = Array.prototype.map.call(result, function(item) { return item; });1278 -1 } else {1279 -1 return [];-1 1477 axs.utils.isValidBoolean = function(value) { -1 1478 try { -1 1479 var parsedValue = JSON.parse(value); -1 1480 } catch (e) { -1 1481 parsedValue = ''; 1280 1482 }1281 -1 return result;-1 1483 if (typeof(parsedValue) != 'boolean') -1 1484 return { 'valid': false, -1 1485 'value': value, -1 1486 'reason': '"' + value + '" is not a true/false value' }; -1 1487 return { 'valid': true, 'value': parsedValue }; 1282 1488 }; 1283 14891284 -1 },{}],2:[function(require,module,exports){1285 -1 // Copyright 2013 Google Inc.1286 -1 //1287 -1 // Licensed under the Apache License, Version 2.0 (the "License");1288 -1 // you may not use this file except in compliance with the License.1289 -1 // You may obtain a copy of the License at1290 -1 //1291 -1 // http://www.apache.org/licenses/LICENSE-2.01292 -1 //1293 -1 // Unless required by applicable law or agreed to in writing, software1294 -1 // distributed under the License is distributed on an "AS IS" BASIS,1295 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.1296 -1 // See the License for the specific language governing permissions and1297 -1 // limitations under the License.1298 -11299 -1 goog.provide('axs.browserUtils');1300 -11301 1490 /**1302 -1 * Use Webkit matcher when matches() is not supported.1303 -1 * Use Firefox matcher when Webkit is not supported.1304 -1 * Use IE matcher when neither webkit nor Firefox supported.1305 -1 * @param {Element} element1306 -1 * @param {string} selector1307 -1 * @return {boolean} true if the element matches the selector1308 -1 */1309 -1 axs.browserUtils.matchSelector = function(element, selector) {1310 -1 if (element.matches)1311 -1 return element.matches(selector);1312 -1 if (element.webkitMatchesSelector)1313 -1 return element.webkitMatchesSelector(selector);1314 -1 if (element.mozMatchesSelector)1315 -1 return element.mozMatchesSelector(selector);1316 -1 if (element.msMatchesSelector)1317 -1 return element.msMatchesSelector(selector);1318 -1 return false;1319 -1 };1320 -11321 -1 },{}],3:[function(require,module,exports){1322 -1 // Copyright 2015 Google Inc.1323 -1 //1324 -1 // Licensed under the Apache License, Version 2.0 (the "License");1325 -1 // you may not use this file except in compliance with the License.1326 -1 // You may obtain a copy of the License at1327 -1 //1328 -1 // http://www.apache.org/licenses/LICENSE-2.01329 -1 //1330 -1 // Unless required by applicable law or agreed to in writing, software1331 -1 // distributed under the License is distributed on an "AS IS" BASIS,1332 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.1333 -1 // See the License for the specific language governing permissions and1334 -1 // limitations under the License.1335 -11336 -1 goog.provide('axs.color');1337 -1 goog.provide('axs.color.Color');1338 -11339 -1 /**1340 -1 * @constructor1341 -1 * @param {number} red1342 -1 * @param {number} green1343 -1 * @param {number} blue1344 -1 * @param {number} alpha-1 1491 * @param {string} value -1 1492 * @param {!Element} element -1 1493 * @return {!Object} 1345 1494 */1346 -1 axs.color.Color = function(red, green, blue, alpha) {1347 -1 /** @type {number} */1348 -1 this.red = red;1349 -11350 -1 /** @type {number} */1351 -1 this.green = green;1352 -11353 -1 /** @type {number} */1354 -1 this.blue = blue;1355 -11356 -1 /** @type {number} */1357 -1 this.alpha = alpha;-1 1495 axs.utils.isValidIDRefValue = function(value, element) { -1 1496 if (value.length == 0) -1 1497 return { 'valid': true, 'idref': value }; -1 1498 if (!element.ownerDocument.getElementById(value)) -1 1499 return { 'valid': false, -1 1500 'idref': value, -1 1501 'reason': 'No element with ID "' + value + '"' }; -1 1502 return { 'valid': true, 'idref': value }; 1358 1503 }; 1359 1504 1360 1505 /**1361 -1 * @constructor1362 -1 * See https://en.wikipedia.org/wiki/YCbCr for more information.1363 -1 * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr].1364 -1 * All numbers are in the range [0, 1].-1 1506 * Tests if a number is real number for a11y purposes. -1 1507 * Must be a real, numerical, decimal value; heavily inspired by -1 1508 * http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number -1 1509 * @param {string} value -1 1510 * @return {!Object} 1365 1511 */1366 -1 axs.color.YCbCr = function(coords) {1367 -1 /** @type {number} */1368 -1 this.luma = this.z = coords[0];1369 -11370 -1 /** @type {number} */1371 -1 this.Cb = this.x = coords[1];1372 -11373 -1 /** @type {number} */1374 -1 this.Cr = this.y = coords[2];1375 -1 };1376 -11377 -1 axs.color.YCbCr.prototype = {1378 -1 /**1379 -1 * @param {number} scalar1380 -1 * @return {axs.color.YCbCr} This color multiplied by the given scalar1381 -1 */1382 -1 multiply: function(scalar) {1383 -1 var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ];1384 -1 return new axs.color.YCbCr(result);1385 -1 },1386 -11387 -1 /**1388 -1 * @param {axs.color.YCbCr} other1389 -1 * @return {axs.color.YCbCr} This plus other1390 -1 */1391 -1 add: function(other) {1392 -1 var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ];1393 -1 return new axs.color.YCbCr(result);1394 -1 },1395 -11396 -1 /**1397 -1 * @param {axs.color.YCbCr} other1398 -1 * @return {axs.color.YCbCr} This minus other1399 -1 */1400 -1 subtract: function(other) {1401 -1 var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ];1402 -1 return new axs.color.YCbCr(result);-1 1512 axs.utils.isValidNumber = function(value) { -1 1513 var failResult = { -1 1514 'valid': false, -1 1515 'value': value, -1 1516 'reason': '"' + value + '" is not a number' -1 1517 }; -1 1518 if (!value) { -1 1519 return failResult; 1403 1520 }1404 -1-1 1521 if (/^0x/i.test(value)) { -1 1522 failResult.reason = '"' + value + '" is not a decimal number'; // hex is not accepted -1 1523 return failResult; -1 1524 } -1 1525 var parsedValue = value * 1; -1 1526 if (!isFinite(parsedValue)) { -1 1527 return failResult; -1 1528 } -1 1529 return { 'valid': true, 'value': parsedValue }; 1405 1530 }; 1406 15311407 -11408 1532 /**1409 -1 * Calculate the contrast ratio between the two given colors. Returns the ratio1410 -1 * to 1, for example for two two colors with a contrast ratio of 21:1, this1411 -1 * function will return 21.1412 -1 * @param {axs.color.Color} fgColor1413 -1 * @param {axs.color.Color} bgColor1414 -1 * @return {!number}-1 1533 * @param {Element} element -1 1534 * @return {boolean} 1415 1535 */1416 -1 axs.color.calculateContrastRatio = function(fgColor, bgColor) {1417 -1 if (fgColor.alpha < 1)1418 -1 fgColor = axs.color.flattenColors(fgColor, bgColor);-1 1536 axs.utils.isElementImplicitlyFocusable = function(element) { -1 1537 var defaultView = element.ownerDocument.defaultView; 1419 15381420 -1 var fgLuminance = axs.color.calculateLuminance(fgColor);1421 -1 var bgLuminance = axs.color.calculateLuminance(bgColor);1422 -1 var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) /1423 -1 (Math.min(fgLuminance, bgLuminance) + 0.05);1424 -1 return contrastRatio;-1 1539 if (element instanceof defaultView.HTMLAnchorElement || -1 1540 element instanceof defaultView.HTMLAreaElement) -1 1541 return element.hasAttribute('href'); -1 1542 if (element instanceof defaultView.HTMLInputElement || -1 1543 element instanceof defaultView.HTMLSelectElement || -1 1544 element instanceof defaultView.HTMLTextAreaElement || -1 1545 element instanceof defaultView.HTMLButtonElement || -1 1546 element instanceof defaultView.HTMLIFrameElement) -1 1547 return !element.disabled; -1 1548 return false; 1425 1549 }; 1426 1550 1427 1551 /**1428 -1 * Calculate the luminance of the given color using the WCAG algorithm.1429 -1 * @param {axs.color.Color} color1430 -1 * @return {number}-1 1552 * Returns an array containing the values of the given JSON-compatible object. -1 1553 * (Simply ignores any function values.) -1 1554 * @param {Object} obj -1 1555 * @return {Array} 1431 1556 */1432 -1 axs.color.calculateLuminance = function(color) {1433 -1 /* var rSRGB = color.red / 255;1434 -1 var gSRGB = color.green / 255;1435 -1 var bSRGB = color.blue / 255;1436 -11437 -1 var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);1438 -1 var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);1439 -1 var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);1440 -11441 -1 return 0.2126 * r + 0.7152 * g + 0.0722 * b; */1442 -1 var ycc = axs.color.toYCbCr(color);1443 -1 return ycc.luma;-1 1557 axs.utils.values = function(obj) { -1 1558 var values = []; -1 1559 for (var key in obj) { -1 1560 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function') -1 1561 values.push(obj[key]); -1 1562 } -1 1563 return values; 1444 1564 }; 1445 1565 1446 1566 /**1447 -1 * Compute the luminance ratio between two luminance values.1448 -1 * @param {number} luminance11449 -1 * @param {number} luminance2-1 1567 * Returns an object containing the same keys and values as the given -1 1568 * JSON-compatible object. (Simply ignores any function values.) -1 1569 * @param {Object} obj -1 1570 * @return {Object} 1450 1571 */1451 -1 axs.color.luminanceRatio = function(luminance1, luminance2) {1452 -1 return (Math.max(luminance1, luminance2) + 0.05) /1453 -1 (Math.min(luminance1, luminance2) + 0.05);-1 1572 axs.utils.namedValues = function(obj) { -1 1573 var values = {}; -1 1574 for (var key in obj) { -1 1575 if (obj.hasOwnProperty(key) && typeof obj[key] != 'function') -1 1576 values[key] = obj[key]; -1 1577 } -1 1578 return values; 1454 1579 }; 1455 1580 1456 1581 /**1457 -1 * @param {string} colorString The color string from CSS.1458 -1 * @return {?axs.color.Color}1459 -1 */1460 -1 axs.color.parseColor = function(colorString) {1461 -1 if (colorString === "transparent") {1462 -1 return new axs.color.Color(0, 0, 0, 0);1463 -1 }1464 -1 var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/;1465 -1 var match = colorString.match(rgbRegex);-1 1582 * Escapes a given ID to be used in a CSS selector -1 1583 * -1 1584 * @private -1 1585 * @param {!string} id The ID to be escaped -1 1586 * @return {string} The escaped ID -1 1587 */ -1 1588 function escapeId(id) { -1 1589 return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; }); -1 1590 } 1466 15911467 -1 if (match) {1468 -1 var r = parseInt(match[1], 10);1469 -1 var g = parseInt(match[2], 10);1470 -1 var b = parseInt(match[3], 10);1471 -1 var a = 1;1472 -1 return new axs.color.Color(r, g, b, a);1473 -1 }-1 1592 /** Gets a CSS selector text for a DOM object. -1 1593 * @param {Node} obj The DOM object. -1 1594 * @return {string} CSS selector text for the DOM object. -1 1595 */ -1 1596 axs.utils.getQuerySelectorText = function(obj) { -1 1597 if (obj == null || obj.tagName == 'HTML') { -1 1598 return 'html'; -1 1599 } else if (obj.tagName == 'BODY') { -1 1600 return 'body'; -1 1601 } 1474 16021475 -1 var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;1476 -1 match = colorString.match(rgbaRegex);1477 -1 if (match) {1478 -1 var r = parseInt(match[1], 10);1479 -1 var g = parseInt(match[2], 10);1480 -1 var b = parseInt(match[3], 10);1481 -1 var a = parseFloat(match[4]);1482 -1 return new axs.color.Color(r, g, b, a);-1 1603 if (obj.hasAttribute) { -1 1604 if (obj.id) { -1 1605 return '#' + escapeId(obj.id); 1483 1606 } 1484 16071485 -1 return null;1486 -1 };-1 1608 if (obj.className) { -1 1609 var selector = ''; -1 1610 for (var i = 0; i < obj.classList.length; i++) -1 1611 selector += '.' + obj.classList[i]; 1487 16121488 -1 /**1489 -1 * @param {number} value The value of a color channel, 0 <= value <= 0xFF1490 -1 * @return {!string}1491 -1 */1492 -1 axs.color.colorChannelToString = function(value) {1493 -1 value = Math.round(value);1494 -1 if (value <= 0xF)1495 -1 return '0' + value.toString(16);1496 -1 return value.toString(16);1497 -1 };1498 -11499 -1 /**1500 -1 * @param {axs.color.Color} color1501 -1 * @return {!string}1502 -1 */1503 -1 axs.color.colorToString = function(color) {1504 -1 if (color.alpha == 1) {1505 -1 return '#' + axs.color.colorChannelToString(color.red) +1506 -1 axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue);1507 -1 }1508 -1 else1509 -1 return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')';1510 -1 };-1 1613 var total = 0; -1 1614 if (obj.parentNode) { -1 1615 for (i = 0; i < obj.parentNode.children.length; i++) { -1 1616 var similar = obj.parentNode.children[i]; -1 1617 if (axs.browserUtils.matchSelector(similar, selector)) -1 1618 total++; -1 1619 if (similar === obj) -1 1620 break; -1 1621 } -1 1622 } else { -1 1623 total = 1; -1 1624 } 1511 16251512 -1 /**1513 -1 * Compute a desired luminance given a given luminance and a desired contrast ratio.1514 -1 * @param {number} luminance The given luminance.1515 -1 * @param {number} contrast The desired contrast ratio.1516 -1 * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance.1517 -1 * @return {number} The desired luminance.1518 -1 */1519 -1 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) {1520 -1 if (higher) {1521 -1 var newLuminance = (luminance + 0.05) * contrast - 0.05;1522 -1 return newLuminance;1523 -1 } else {1524 -1 var newLuminance = (luminance + 0.05) / contrast - 0.05;1525 -1 return newLuminance;-1 1626 if (total == 1) { -1 1627 return axs.utils.getQuerySelectorText(obj.parentNode) + -1 1628 ' > ' + selector; -1 1629 } 1526 1630 }1527 -1 };1528 -11529 -1 /**1530 -1 * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is1531 -1 * as close as possible to the original color.1532 -1 * @param {axs.color.YCbCr} ycc The original color in YCbCr form.1533 -1 * @param {number} luma The desired luminance1534 -1 * @return {!axs.color.Color} A new color in RGB.1535 -1 */1536 -1 axs.color.translateColor = function(ycc, luma) {1537 -1 var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC;1538 -1 var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE1539 -1 : axs.color.YCC_CUBE_FACES_BLACK;1540 16311541 -1 var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]);1542 -1 var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]);1543 -1 var line = { a: a, b: b };-1 1632 if (obj.parentNode) { -1 1633 var similarTags = obj.parentNode.children; -1 1634 var total = 1; -1 1635 var i = 0; -1 1636 while (similarTags[i] !== obj) { -1 1637 if (similarTags[i].tagName == obj.tagName) { -1 1638 total++; -1 1639 } -1 1640 i++; -1 1641 } 1544 16421545 -1 var intersection = null;1546 -1 for (var i = 0; i < cubeFaces.length; i++) {1547 -1 var cubeFace = cubeFaces[i];1548 -1 intersection = axs.color.findIntersection(line, cubeFace);1549 -1 // If intersection within [0, 1] in Z axis, it is within the cube.1550 -1 if (intersection.z >= 0 && intersection.z <= 1)1551 -1 break;1552 -1 }1553 -1 if (!intersection) {1554 -1 // Should never happen1555 -1 throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + ".";1556 -1 }1557 -1 if (intersection.x != ycc.x || intersection.y != ycc.y) {1558 -1 // Should never happen1559 -1 throw "Intersection has wrong Cb/Cr values.";1560 -1 }-1 1643 var next = ''; -1 1644 if (obj.parentNode.tagName != 'BODY') { -1 1645 next = axs.utils.getQuerySelectorText(obj.parentNode) + -1 1646 ' > '; -1 1647 } 1561 16481562 -1 // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube1563 -1 // and we can immediately return new value.1564 -1 if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) {1565 -1 var translatedColor = [luma, ycc.Cb, ycc.Cr];1566 -1 return axs.color.fromYCbCrArray(translatedColor);-1 1649 if (total == 1) { -1 1650 return next + -1 1651 obj.tagName; -1 1652 } else { -1 1653 return next + -1 1654 obj.tagName + -1 1655 ':nth-of-type(' + total + ')'; -1 1656 } 1567 1657 } 1568 16581569 -1 // Otherwise, translate from intersection towards white/black such that luma is correct.1570 -1 var dLuma = luma - intersection.luma;1571 -1 var scale = dLuma / (endpoint.luma - intersection.luma);1572 -1 var translatedColor = [ luma,1573 -1 intersection.Cb - (intersection.Cb * scale),1574 -1 intersection.Cr - (intersection.Cr * scale) ];-1 1659 } else if (obj.selectorText) { -1 1660 return obj.selectorText; -1 1661 } 1575 16621576 -1 return axs.color.fromYCbCrArray(translatedColor);-1 1663 return ''; 1577 1664 }; 1578 16651579 -1 /** @typedef {{fg: string, bg: string, contrast: string}} */1580 -1 axs.color.SuggestedColors;1581 -11582 1666 /**1583 -1 * @param {axs.color.Color} bgColor1584 -1 * @param {axs.color.Color} fgColor1585 -1 * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio.1586 -1 * @return {Object.<string, axs.color.SuggestedColors>}-1 1667 * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or -1 1668 * single ID reference. -1 1669 * @param {Element} element a potential referent. -1 1670 * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'. -1 1671 * @return {NodeList} The elements that refer to this element or null. 1587 1672 */1588 -1 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) {1589 -1 var colors = {};1590 -1 var bgLuminance = axs.color.calculateLuminance(bgColor);1591 -1 var fgLuminance = axs.color.calculateLuminance(fgColor);1592 -11593 -1 var fgLuminanceIsHigher = fgLuminance > bgLuminance;1594 -1 var fgYCbCr = axs.color.toYCbCr(fgColor);1595 -1 var bgYCbCr = axs.color.toYCbCr(bgColor);1596 -1 for (var desiredLabel in desiredContrastRatios) {1597 -1 var desiredContrast = desiredContrastRatios[desiredLabel];1598 -11599 -1 var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher);1600 -1 if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) {1601 -1 var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance);1602 -1 var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor);1603 -1 var suggestedColors = {};1604 -1 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor));1605 -1 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor));1606 -1 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));1607 -1 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);1608 -1 continue;-1 1673 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) { -1 1674 var propertyToSelector = function(propertyKey) { -1 1675 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 1676 if (propertyDetails) { -1 1677 if (propertyDetails.valueType === ('idref')) { -1 1678 return '[aria-' + propertyKey + '=\'' + id + '\']'; -1 1679 } else if (propertyDetails.valueType === ('idref_list')) { -1 1680 return '[aria-' + propertyKey + '~=\'' + id + '\']'; -1 1681 } 1609 1682 } -1 1683 return ''; -1 1684 }; -1 1685 if (!element) -1 1686 return null; -1 1687 var id = element.id; -1 1688 if (!id) -1 1689 return null; -1 1690 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector 1610 16911611 -1 var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher);1612 -1 if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) {1613 -1 var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance);1614 -1 var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor);1615 -1 var suggestedColors = {};1616 -1 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor));1617 -1 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor));1618 -1 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));1619 -1 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);-1 1692 if (opt_attributeName) { -1 1693 var propertyKey = opt_attributeName.replace(/^aria-/, ''); -1 1694 var referrerQuery = propertyToSelector(propertyKey); -1 1695 if (referrerQuery) { -1 1696 return element.ownerDocument.querySelectorAll(referrerQuery); -1 1697 } -1 1698 } else { -1 1699 var selectors = []; -1 1700 for (var propertyKey in axs.constants.ARIA_PROPERTIES) { -1 1701 var referrerQuery = propertyToSelector(propertyKey); -1 1702 if (referrerQuery) { -1 1703 selectors.push(referrerQuery); -1 1704 } 1620 1705 } -1 1706 return element.ownerDocument.querySelectorAll(selectors.join(',')); 1621 1707 }1622 -1 return colors;-1 1708 return null; 1623 1709 }; 1624 1710 1625 1711 /**1626 -1 * Combine the two given color according to alpha blending.1627 -1 * @param {axs.color.Color} fgColor1628 -1 * @param {axs.color.Color} bgColor1629 -1 * @return {axs.color.Color}-1 1712 * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or -1 1713 * single ID reference. -1 1714 * @param {Element} element a potential referent. -1 1715 * @return {NodeList} The elements that refer to this element. 1630 1716 */1631 -1 axs.color.flattenColors = function(fgColor, bgColor) {1632 -1 var alpha = fgColor.alpha;1633 -1 var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red);1634 -1 var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green);1635 -1 var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue);1636 -1 var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha));1637 -11638 -1 return new axs.color.Color(r, g, b, a);-1 1717 axs.utils.getHtmlIdReferrers = function(element) { -1 1718 if (!element) -1 1719 return null; -1 1720 var id = element.id; -1 1721 if (!id) -1 1722 return null; -1 1723 id = id.replace(/'/g, "\\'"); // make it safe to use in a selector -1 1724 var selectorTemplates = [ -1 1725 '[contextmenu=\'{id}\']', -1 1726 '[itemref~=\'{id}\']', -1 1727 'button[form=\'{id}\']', -1 1728 'button[menu=\'{id}\']', -1 1729 'fieldset[form=\'{id}\']', -1 1730 'input[form=\'{id}\']', -1 1731 'input[list=\'{id}\']', -1 1732 'keygen[form=\'{id}\']', -1 1733 'label[for=\'{id}\']', -1 1734 'label[form=\'{id}\']', -1 1735 'menuitem[command=\'{id}\']', -1 1736 'object[form=\'{id}\']', -1 1737 'output[for~=\'{id}\']', -1 1738 'output[form=\'{id}\']', -1 1739 'select[form=\'{id}\']', -1 1740 'td[headers~=\'{id}\']', -1 1741 'textarea[form=\'{id}\']', -1 1742 'tr[headers~=\'{id}\']']; -1 1743 var selectors = selectorTemplates.map(function(selector) { -1 1744 return selector.replace('\{id\}', id); -1 1745 }); -1 1746 return element.ownerDocument.querySelectorAll(selectors.join(',')); 1639 1747 }; 1640 1748 1641 1749 /**1642 -1 * Multiply the given vector by the given matrix.1643 -1 * @param {Array.<Array.<number>>} matrix A 3x3 matrix1644 -1 * @param {Array.<number>} vector A 3-element vector1645 -1 * @return {Array.<number>} A 3-element vector-1 1750 * Gets a list of all IDs this element references in either ARIA or HTML attributes. -1 1751 * -1 1752 * @param {Element} element The element to check for idref attributes. -1 1753 * @returns {Array.<string>} Any IDs this element references. 1646 1754 */1647 -1 axs.color.multiplyMatrixVector = function(matrix, vector) {1648 -1 var a = matrix[0][0];1649 -1 var b = matrix[0][1];1650 -1 var c = matrix[0][2];1651 -1 var d = matrix[1][0];1652 -1 var e = matrix[1][1];1653 -1 var f = matrix[1][2];1654 -1 var g = matrix[2][0];1655 -1 var h = matrix[2][1];1656 -1 var k = matrix[2][2];1657 -11658 -1 var x = vector[0];1659 -1 var y = vector[1];1660 -1 var z = vector[2];1661 -11662 -1 return [1663 -1 a*x + b*y + c*z,1664 -1 d*x + e*y + f*z,1665 -1 g*x + h*y + k*z1666 -1 ];-1 1755 axs.utils.getReferencedIds = function(element) { -1 1756 var result = []; -1 1757 var addResult = function(ids) { -1 1758 if (ids) { -1 1759 if (ids.indexOf(' ') > 0) { -1 1760 result = result.concat(attrib.value.split(' ')); -1 1761 } else { -1 1762 result.push(ids); -1 1763 } -1 1764 } -1 1765 }; -1 1766 for (var i = 0; i < element.attributes.length; i++) { -1 1767 var tagName = element.tagName.toLowerCase(); -1 1768 var attrib = element.attributes[i]; -1 1769 if (attrib.specified) { -1 1770 var attribName = attrib.name; -1 1771 var ariaAttr = attribName.match(/aria-(.+)/); -1 1772 if (ariaAttr) { -1 1773 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]]; -1 1774 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) { -1 1775 addResult(attrib.value); -1 1776 } -1 1777 continue; -1 1778 } -1 1779 switch (attribName) { -1 1780 case 'contextmenu': -1 1781 case 'itemref': -1 1782 addResult(attrib.value); -1 1783 break; -1 1784 case 'form': -1 1785 if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' || -1 1786 tagName == 'keygen' || tagName == 'label' || tagName == 'object' || -1 1787 tagName == 'output' || tagName == 'select' || tagName == 'textarea') { -1 1788 addResult(attrib.value); -1 1789 } -1 1790 break; -1 1791 case 'for': -1 1792 if (tagName == 'label' || tagName == 'output') { -1 1793 addResult(attrib.value); -1 1794 } -1 1795 break; -1 1796 case 'menu': -1 1797 if (tagName == 'button') { -1 1798 addResult(attrib.value); -1 1799 } -1 1800 break; -1 1801 case 'list': -1 1802 if (tagName == 'input') { -1 1803 addResult(attrib.value); -1 1804 } -1 1805 break; -1 1806 case 'command': -1 1807 if (tagName == 'menuitem') { -1 1808 addResult(attrib.value); -1 1809 } -1 1810 break; -1 1811 case 'headers': -1 1812 if (tagName == 'td' || tagName == 'tr') { -1 1813 addResult(attrib.value); -1 1814 } -1 1815 break; -1 1816 } -1 1817 } -1 1818 } -1 1819 return result; 1667 1820 }; 1668 1821 1669 1822 /**1670 -1 * Convert a given RGB color to YCbCr.1671 -1 * @param {axs.color.Color} color1672 -1 * @return {axs.color.YCbCr}-1 1823 * Gets elements that refer to this element in an attribute that takes an ID reference list or -1 1824 * single ID reference. -1 1825 * @param {Element} element a potential referent. -1 1826 * @return {Array<Element>} The elements that refer to this element. 1673 1827 */1674 -1 axs.color.toYCbCr = function(color) {1675 -1 var rSRGB = color.red / 255;1676 -1 var gSRGB = color.green / 255;1677 -1 var bSRGB = color.blue / 255;1678 -11679 -1 var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);1680 -1 var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);1681 -1 var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);1682 -11683 -1 return new axs.color.YCbCr(axs.color.multiplyMatrixVector(axs.color.YCC_MATRIX, [r, g, b]));-1 1828 axs.utils.getIdReferrers = function(element) { -1 1829 var result = []; -1 1830 var referrers = axs.utils.getHtmlIdReferrers(element); -1 1831 if (referrers) { -1 1832 result = result.concat(Array.prototype.slice.call(referrers)); -1 1833 } -1 1834 referrers = axs.utils.getAriaIdReferrers(element); -1 1835 if (referrers) { -1 1836 result = result.concat(Array.prototype.slice.call(referrers)); -1 1837 } -1 1838 return result; 1684 1839 }; 1685 1840 1686 1841 /**1687 -1 * @param {axs.color.YCbCr} ycc1688 -1 * @return {!axs.color.Color}-1 1842 * Gets elements which this element refers to in the given attribute. -1 1843 * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'. -1 1844 * @param {Element} element The DOM element which has the ARIA attribute. -1 1845 * @return {!Array.<Element>} An array of elements that are referred to by this element. -1 1846 * @example -1 1847 * var owner = document.body.appendChild(document.createElement("div")); -1 1848 * var owned = document.body.appendChild(document.createElement("div")); -1 1849 * owner.setAttribute("aria-owns", "kungfu"); -1 1850 * owned.setAttribute("id", "kungfu"); -1 1851 * console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned); // This will log 'true' 1689 1852 */1690 -1 axs.color.fromYCbCr = function(ycc) {1691 -1 return axs.color.fromYCbCrArray([ycc.luma, ycc.Cb, ycc.Cr]);-1 1853 axs.utils.getIdReferents = function(attributeName, element) { -1 1854 var result = []; -1 1855 var propertyKey = attributeName.replace(/^aria-/, ''); -1 1856 var property = axs.constants.ARIA_PROPERTIES[propertyKey]; -1 1857 if (!property || !element.hasAttribute(attributeName)) -1 1858 return result; -1 1859 var propertyType = property.valueType; -1 1860 if (propertyType === 'idref_list' || propertyType === 'idref') { -1 1861 var ownerDocument = element.ownerDocument; -1 1862 var ids = element.getAttribute(attributeName); -1 1863 ids = ids.split(/\s+/); -1 1864 for (var i = 0, len = ids.length; i < len; i++) { -1 1865 var next = ownerDocument.getElementById(ids[i]); -1 1866 if (next) { -1 1867 result[result.length] = next; -1 1868 } -1 1869 } -1 1870 } -1 1871 return result; 1692 1872 }; 1693 1873 1694 1874 /**1695 -1 * Convert a color from a YCbCr color (as a vector) to an RGB color1696 -1 * @param {Array.<number>} yccArray1697 -1 * @return {!axs.color.Color}-1 1875 * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'. -1 1876 * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list']. -1 1877 * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match. 1698 1878 */1699 -1 axs.color.fromYCbCrArray = function(yccArray) {1700 -1 var rgb = axs.color.multiplyMatrixVector(axs.color.INVERTED_YCC_MATRIX, yccArray);1701 -11702 -1 var r = rgb[0];1703 -1 var g = rgb[1];1704 -1 var b = rgb[2];1705 -1 var rSRGB = r <= 0.00303949 ? (r * 12.92) : (Math.pow(r, (1/2.4)) * 1.055) - 0.055;1706 -1 var gSRGB = g <= 0.00303949 ? (g * 12.92) : (Math.pow(g, (1/2.4)) * 1.055) - 0.055;1707 -1 var bSRGB = b <= 0.00303949 ? (b * 12.92) : (Math.pow(b, (1/2.4)) * 1.055) - 0.055;1708 -11709 -1 var red = Math.min(Math.max(Math.round(rSRGB * 255), 0), 255);1710 -1 var green = Math.min(Math.max(Math.round(gSRGB * 255), 0), 255);1711 -1 var blue = Math.min(Math.max(Math.round(bSRGB * 255), 0), 255);1712 -11713 -1 return new axs.color.Color(red, green, blue, 1);-1 1879 axs.utils.getAriaPropertiesByValueType = function(valueTypes) { -1 1880 var result = {}; -1 1881 for (var propertyName in axs.constants.ARIA_PROPERTIES) { -1 1882 var property = axs.constants.ARIA_PROPERTIES[propertyName]; -1 1883 if (property && valueTypes.indexOf(property.valueType) >= 0) { -1 1884 result[propertyName] = property; -1 1885 } -1 1886 } -1 1887 return result; 1714 1888 }; 1715 1889 1716 1890 /**1717 -1 * Returns an RGB to YCbCr conversion matrix for the given kR, kB constants.1718 -1 * @param {number} kR1719 -1 * @param {number} kB1720 -1 * @return {Array.<Array.<number>>}-1 1891 * Builds a selector that matches an element with any of these ARIA properties. -1 1892 * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES -1 1893 * @return {!string} The selector. 1721 1894 */1722 -1 axs.color.RGBToYCbCrMatrix = function(kR, kB) {1723 -1 return [1724 -1 [1725 -1 kR,1726 -1 (1 - kR - kB),1727 -1 kB1728 -1 ],1729 -1 [1730 -1 -kR/(2 - 2*kB),1731 -1 (kR + kB - 1)/(2 - 2*kB),1732 -1 (1 - kB)/(2 - 2*kB)1733 -1 ],1734 -1 [1735 -1 (1 - kR)/(2 - 2*kR),1736 -1 (kR + kB - 1)/(2 - 2*kR),1737 -1 -kB/(2 - 2*kR)1738 -1 ]1739 -1 ];-1 1895 axs.utils.getSelectorForAriaProperties = function(ariaProperties) { -1 1896 var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties)); -1 1897 var result = propertyNames.map(function(propertyName) { -1 1898 return '[aria-' + propertyName + ']'; -1 1899 }); -1 1900 result.sort(); // facilitates reading long selectors and unit testing -1 1901 return result.join(','); 1740 1902 }; 1741 1903 1742 1904 /**1743 -1 * Return the inverse of the given 3x3 matrix.1744 -1 * @param {Array.<Array.<number>>} matrix1745 -1 * @return Array.<Array.<number>> The inverse of the given matrix.-1 1905 * Finds descendants of this element which implement the given ARIA role. -1 1906 * Will look for descendants with implicit or explicit role. -1 1907 * @param {Element} element an HTML DOM element. -1 1908 * @param {string} role The role you seek. -1 1909 * @return {!Array.<Element>} An array of matching elements. -1 1910 * @example -1 1911 * var container = document.createElement("div"); -1 1912 * var button = document.createElement("button"); -1 1913 * var span = document.createElement("span"); -1 1914 * span.setAttribute("role", "button"); -1 1915 * container.appendChild(button); -1 1916 * container.appendChild(span); -1 1917 * var result = axs.utils.findDescendantsWithRole(container, "button"); // result is an array containing both 'button' and 'span' 1746 1918 */1747 -1 axs.color.invert3x3Matrix = function(matrix) {1748 -1 var a = matrix[0][0];1749 -1 var b = matrix[0][1];1750 -1 var c = matrix[0][2];1751 -1 var d = matrix[1][0];1752 -1 var e = matrix[1][1];1753 -1 var f = matrix[1][2];1754 -1 var g = matrix[2][0];1755 -1 var h = matrix[2][1];1756 -1 var k = matrix[2][2];1757 -11758 -1 var A = (e*k - f*h);1759 -1 var B = (f*g - d*k);1760 -1 var C = (d*h - e*g);1761 -1 var D = (c*h - b*k);1762 -1 var E = (a*k - c*g);1763 -1 var F = (g*b - a*h);1764 -1 var G = (b*f - c*e);1765 -1 var H = (c*d - a*f);1766 -1 var K = (a*e - b*d);1767 -11768 -1 var det = a * (e*k - f*h) - b * (k*d - f*g) + c * (d*h - e*g);1769 -1 var z = 1/det;-1 1919 axs.utils.findDescendantsWithRole = function(element, role) { -1 1920 if (!(element && role)) -1 1921 return []; -1 1922 var selector = axs.properties.getSelectorForRole(role); -1 1923 if (!selector) -1 1924 return []; -1 1925 var result = element.querySelectorAll(selector); -1 1926 if (result) { // Convert NodeList to Array; methinks 80/20 that's what callers want. -1 1927 result = Array.prototype.map.call(result, function(item) { return item; }); -1 1928 } else { -1 1929 return []; -1 1930 } -1 1931 return result; -1 1932 }; 1770 19331771 -1 return axs.color.scalarMultiplyMatrix([1772 -1 [ A, D, G ],1773 -1 [ B, E, H ],1774 -1 [ C, F, K ]1775 -1 ], z);-1 1934 },{}],7:[function(require,module,exports){ -1 1935 // Copyright 2013 Google Inc. -1 1936 // -1 1937 // Licensed under the Apache License, Version 2.0 (the "License"); -1 1938 // you may not use this file except in compliance with the License. -1 1939 // You may obtain a copy of the License at -1 1940 // -1 1941 // http://www.apache.org/licenses/LICENSE-2.0 -1 1942 // -1 1943 // Unless required by applicable law or agreed to in writing, software -1 1944 // distributed under the License is distributed on an "AS IS" BASIS, -1 1945 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 1946 // See the License for the specific language governing permissions and -1 1947 // limitations under the License. -1 1948 -1 1949 goog.provide('axs.browserUtils'); -1 1950 -1 1951 /** -1 1952 * Use Webkit matcher when matches() is not supported. -1 1953 * Use Firefox matcher when Webkit is not supported. -1 1954 * Use IE matcher when neither webkit nor Firefox supported. -1 1955 * @param {Element} element -1 1956 * @param {string} selector -1 1957 * @return {boolean} true if the element matches the selector -1 1958 */ -1 1959 axs.browserUtils.matchSelector = function(element, selector) { -1 1960 if (element.matches) -1 1961 return element.matches(selector); -1 1962 if (element.webkitMatchesSelector) -1 1963 return element.webkitMatchesSelector(selector); -1 1964 if (element.mozMatchesSelector) -1 1965 return element.mozMatchesSelector(selector); -1 1966 if (element.msMatchesSelector) -1 1967 return element.msMatchesSelector(selector); -1 1968 return false; 1776 1969 }; 1777 19701778 -1 /** @typedef {{ a: axs.color.YCbCr, b: axs.color.YCbCr }} */1779 -1 axs.color.Line;-1 1971 },{}],8:[function(require,module,exports){ -1 1972 // Copyright 2015 Google Inc. -1 1973 // -1 1974 // Licensed under the Apache License, Version 2.0 (the "License"); -1 1975 // you may not use this file except in compliance with the License. -1 1976 // You may obtain a copy of the License at -1 1977 // -1 1978 // http://www.apache.org/licenses/LICENSE-2.0 -1 1979 // -1 1980 // Unless required by applicable law or agreed to in writing, software -1 1981 // distributed under the License is distributed on an "AS IS" BASIS, -1 1982 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 1983 // See the License for the specific language governing permissions and -1 1984 // limitations under the License. 1780 19851781 -1 /** @typedef {{ p0: axs.color.YCbCr, p1: axs.color.YCbCr, p2: axs.color.YCbCr }} */1782 -1 axs.color.Plane;-1 1986 goog.provide('axs.color'); -1 1987 goog.provide('axs.color.Color'); 1783 1988 1784 1989 /**1785 -1 * Find the intersection between a line and a plane using1786 -1 * http://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Parametric_form1787 -1 * @param {axs.color.Line} l1788 -1 * @param {axs.color.Plane} p1789 -1 * @return {axs.color.YCbCr}-1 1990 * @constructor -1 1991 * @param {number} red -1 1992 * @param {number} green -1 1993 * @param {number} blue -1 1994 * @param {number} alpha 1790 1995 */1791 -1 axs.color.findIntersection = function(l, p) {1792 -1 var lhs = [ l.a.x - p.p0.x, l.a.y - p.p0.y, l.a.z - p.p0.z ];-1 1996 axs.color.Color = function(red, green, blue, alpha) { -1 1997 /** @type {number} */ -1 1998 this.red = red; 1793 19991794 -1 var matrix = [ [ l.a.x - l.b.x, p.p1.x - p.p0.x, p.p2.x - p.p0.x ],1795 -1 [ l.a.y - l.b.y, p.p1.y - p.p0.y, p.p2.y - p.p0.y ],1796 -1 [ l.a.z - l.b.z, p.p1.z - p.p0.z, p.p2.z - p.p0.z ] ];1797 -1 var invertedMatrix = axs.color.invert3x3Matrix(matrix);-1 2000 /** @type {number} */ -1 2001 this.green = green; 1798 20021799 -1 var tuv = axs.color.multiplyMatrixVector(invertedMatrix, lhs);1800 -1 var t = tuv[0];-1 2003 /** @type {number} */ -1 2004 this.blue = blue; 1801 20051802 -1 var result = l.a.add(l.b.subtract(l.a).multiply(t));1803 -1 return result;-1 2006 /** @type {number} */ -1 2007 this.alpha = alpha; 1804 2008 }; 1805 2009 1806 2010 /**1807 -1 * Multiply a matrix by a scalar.1808 -1 * @param {Array.<Array.<number>>} matrix A 3x3 matrix.1809 -1 * @param {number} scalar1810 -1 * @return {Array.<Array.<number>>}-1 2011 * @constructor -1 2012 * See https://en.wikipedia.org/wiki/YCbCr for more information. -1 2013 * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr]. -1 2014 * All numbers are in the range [0, 1]. 1811 2015 */1812 -1 axs.color.scalarMultiplyMatrix = function(matrix, scalar) {1813 -1 var result = [];-1 2016 axs.color.YCbCr = function(coords) { -1 2017 /** @type {number} */ -1 2018 this.luma = this.z = coords[0]; 1814 20191815 -1 for (var i = 0; i < 3; i++)1816 -1 result[i] = axs.color.scalarMultiplyVector(matrix[i], scalar);-1 2020 /** @type {number} */ -1 2021 this.Cb = this.x = coords[1]; -1 2022 -1 2023 /** @type {number} */ -1 2024 this.Cr = this.y = coords[2]; -1 2025 }; -1 2026 -1 2027 axs.color.YCbCr.prototype = { -1 2028 /** -1 2029 * @param {number} scalar -1 2030 * @return {axs.color.YCbCr} This color multiplied by the given scalar -1 2031 */ -1 2032 multiply: function(scalar) { -1 2033 var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ]; -1 2034 return new axs.color.YCbCr(result); -1 2035 }, -1 2036 -1 2037 /** -1 2038 * @param {axs.color.YCbCr} other -1 2039 * @return {axs.color.YCbCr} This plus other -1 2040 */ -1 2041 add: function(other) { -1 2042 var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ]; -1 2043 return new axs.color.YCbCr(result); -1 2044 }, -1 2045 -1 2046 /** -1 2047 * @param {axs.color.YCbCr} other -1 2048 * @return {axs.color.YCbCr} This minus other -1 2049 */ -1 2050 subtract: function(other) { -1 2051 var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ]; -1 2052 return new axs.color.YCbCr(result); -1 2053 } 1817 20541818 -1 return result;1819 2055 }; 1820 2056 -1 2057 1821 2058 /**1822 -1 * Multiply a vector by a scalar.1823 -1 * @param {Array.<number>} vector1824 -1 * @param {number} scalar1825 -1 * @return {Array.<number>} vector-1 2059 * Calculate the contrast ratio between the two given colors. Returns the ratio -1 2060 * to 1, for example for two two colors with a contrast ratio of 21:1, this -1 2061 * function will return 21. -1 2062 * @param {axs.color.Color} fgColor -1 2063 * @param {axs.color.Color} bgColor -1 2064 * @return {!number} 1826 2065 */1827 -1 axs.color.scalarMultiplyVector = function(vector, scalar) {1828 -1 var result = [];1829 -1 for (var i = 0; i < vector.length; i++)1830 -1 result[i] = vector[i] * scalar;1831 -1 return result;-1 2066 axs.color.calculateContrastRatio = function(fgColor, bgColor) { -1 2067 if (fgColor.alpha < 1) -1 2068 fgColor = axs.color.flattenColors(fgColor, bgColor); -1 2069 -1 2070 var fgLuminance = axs.color.calculateLuminance(fgColor); -1 2071 var bgLuminance = axs.color.calculateLuminance(bgColor); -1 2072 var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) / -1 2073 (Math.min(fgLuminance, bgLuminance) + 0.05); -1 2074 return contrastRatio; 1832 2075 }; 1833 20761834 -1 axs.color.kR = 0.2126;1835 -1 axs.color.kB = 0.0722;1836 -1 axs.color.YCC_MATRIX = axs.color.RGBToYCbCrMatrix(axs.color.kR, axs.color.kB);1837 -1 axs.color.INVERTED_YCC_MATRIX = axs.color.invert3x3Matrix(axs.color.YCC_MATRIX);-1 2077 /** -1 2078 * Calculate the luminance of the given color using the WCAG algorithm. -1 2079 * @param {axs.color.Color} color -1 2080 * @return {number} -1 2081 */ -1 2082 axs.color.calculateLuminance = function(color) { -1 2083 /* var rSRGB = color.red / 255; -1 2084 var gSRGB = color.green / 255; -1 2085 var bSRGB = color.blue / 255; 1838 20861839 -1 axs.color.BLACK = new axs.color.Color(0, 0, 0, 1.0);1840 -1 axs.color.BLACK_YCC = axs.color.toYCbCr(axs.color.BLACK);1841 -1 axs.color.WHITE = new axs.color.Color(255, 255, 255, 1.0);1842 -1 axs.color.WHITE_YCC = axs.color.toYCbCr(axs.color.WHITE);1843 -1 axs.color.RED = new axs.color.Color(255, 0, 0, 1.0);1844 -1 axs.color.RED_YCC = axs.color.toYCbCr(axs.color.RED);1845 -1 axs.color.GREEN = new axs.color.Color(0, 255, 0, 1.0);1846 -1 axs.color.GREEN_YCC = axs.color.toYCbCr(axs.color.GREEN);1847 -1 axs.color.BLUE = new axs.color.Color(0, 0, 255, 1.0);1848 -1 axs.color.BLUE_YCC = axs.color.toYCbCr(axs.color.BLUE);1849 -1 axs.color.CYAN = new axs.color.Color(0, 255, 255, 1.0);1850 -1 axs.color.CYAN_YCC = axs.color.toYCbCr(axs.color.CYAN);1851 -1 axs.color.MAGENTA = new axs.color.Color(255, 0, 255, 1.0);1852 -1 axs.color.MAGENTA_YCC = axs.color.toYCbCr(axs.color.MAGENTA);1853 -1 axs.color.YELLOW = new axs.color.Color(255, 255, 0, 1.0);1854 -1 axs.color.YELLOW_YCC = axs.color.toYCbCr(axs.color.YELLOW);-1 2087 var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4); -1 2088 var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4); -1 2089 var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4); 1855 20901856 -1 axs.color.YCC_CUBE_FACES_BLACK = [ { p0: axs.color.BLACK_YCC, p1: axs.color.RED_YCC, p2: axs.color.GREEN_YCC },1857 -1 { p0: axs.color.BLACK_YCC, p1: axs.color.GREEN_YCC, p2: axs.color.BLUE_YCC },1858 -1 { p0: axs.color.BLACK_YCC, p1: axs.color.BLUE_YCC, p2: axs.color.RED_YCC } ];1859 -1 axs.color.YCC_CUBE_FACES_WHITE = [ { p0: axs.color.WHITE_YCC, p1: axs.color.CYAN_YCC, p2: axs.color.MAGENTA_YCC },1860 -1 { p0: axs.color.WHITE_YCC, p1: axs.color.MAGENTA_YCC, p2: axs.color.YELLOW_YCC },1861 -1 { p0: axs.color.WHITE_YCC, p1: axs.color.YELLOW_YCC, p2: axs.color.CYAN_YCC } ];-1 2091 return 0.2126 * r + 0.7152 * g + 0.0722 * b; */ -1 2092 var ycc = axs.color.toYCbCr(color); -1 2093 return ycc.luma; -1 2094 }; 1862 20951863 -1 },{}],4:[function(require,module,exports){1864 -1 // Copyright 2012 Google Inc.1865 -1 //1866 -1 // Licensed under the Apache License, Version 2.0 (the "License");1867 -1 // you may not use this file except in compliance with the License.1868 -1 // You may obtain a copy of the License at1869 -1 //1870 -1 // http://www.apache.org/licenses/LICENSE-2.01871 -1 //1872 -1 // Unless required by applicable law or agreed to in writing, software1873 -1 // distributed under the License is distributed on an "AS IS" BASIS,1874 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.1875 -1 // See the License for the specific language governing permissions and1876 -1 // limitations under the License.-1 2096 /** -1 2097 * Compute the luminance ratio between two luminance values. -1 2098 * @param {number} luminance1 -1 2099 * @param {number} luminance2 -1 2100 */ -1 2101 axs.color.luminanceRatio = function(luminance1, luminance2) { -1 2102 return (Math.max(luminance1, luminance2) + 0.05) / -1 2103 (Math.min(luminance1, luminance2) + 0.05); -1 2104 }; 1877 21051878 -1 goog.provide('axs.constants');1879 -1 goog.provide('axs.constants.AuditResult');1880 -1 goog.provide('axs.constants.Severity');-1 2106 /** -1 2107 * @param {string} colorString The color string from CSS. -1 2108 * @return {?axs.color.Color} -1 2109 */ -1 2110 axs.color.parseColor = function(colorString) { -1 2111 if (colorString === "transparent") { -1 2112 return new axs.color.Color(0, 0, 0, 0); -1 2113 } -1 2114 var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/; -1 2115 var match = colorString.match(rgbRegex); 1881 21161882 -1 /** @type {Object.<string, Object>} */1883 -1 axs.constants.ARIA_ROLES = {1884 -1 "alert": {1885 -1 "namefrom": [ "author" ],1886 -1 "parent": [ "region" ]1887 -1 },1888 -1 "alertdialog": {1889 -1 "namefrom": [ "author" ],1890 -1 "namerequired": true,1891 -1 "parent": [ "alert", "dialog" ]1892 -1 },1893 -1 "application": {1894 -1 "namefrom": [ "author" ],1895 -1 "namerequired": true,1896 -1 "parent": [ "landmark" ]1897 -1 },1898 -1 "article": {1899 -1 "namefrom": [ "author" ],1900 -1 "parent": [ "document", "region" ]1901 -1 },1902 -1 "banner": {1903 -1 "namefrom": [ "author" ],1904 -1 "parent": [ "landmark" ]1905 -1 },1906 -1 "button": {1907 -1 "childpresentational": true,1908 -1 "namefrom": [ "contents", "author" ],1909 -1 "namerequired": true,1910 -1 "parent": [ "command" ],1911 -1 "properties": [ "aria-expanded", "aria-pressed" ]1912 -1 },1913 -1 "checkbox": {1914 -1 "namefrom": [ "contents", "author" ],1915 -1 "namerequired": true,1916 -1 "parent": [ "input" ],1917 -1 "requiredProperties": [ "aria-checked" ],1918 -1 "properties": [ "aria-checked" ]1919 -1 },1920 -1 "columnheader": {1921 -1 "namefrom": [ "contents", "author" ],1922 -1 "namerequired": true,1923 -1 "parent": [ "gridcell", "sectionhead", "widget" ],1924 -1 "properties": [ "aria-sort" ],1925 -1 "scope": [ "row" ]1926 -1 },1927 -1 "combobox": {1928 -1 "mustcontain": [ "listbox", "textbox" ],1929 -1 "namefrom": [ "author" ],1930 -1 "namerequired": true,1931 -1 "parent": [ "select" ],1932 -1 "requiredProperties": [ "aria-expanded" ],1933 -1 "properties": [ "aria-expanded", "aria-autocomplete", "aria-required" ]1934 -1 },1935 -1 "command": {1936 -1 "abstract": true,1937 -1 "namefrom": [ "author" ],1938 -1 "parent": [ "widget" ]1939 -1 },1940 -1 "complementary": {1941 -1 "namefrom": [ "author" ],1942 -1 "parent": [ "landmark" ]1943 -1 },1944 -1 "composite": {1945 -1 "abstract": true,1946 -1 "childpresentational": false,1947 -1 "namefrom": [ "author" ],1948 -1 "parent": [ "widget" ],1949 -1 "properties": [ "aria-activedescendant" ]1950 -1 },1951 -1 "contentinfo": {1952 -1 "namefrom": [ "author" ],1953 -1 "parent": [ "landmark" ]1954 -1 },1955 -1 "definition": {1956 -1 "namefrom": [ "author" ],1957 -1 "parent": [ "section" ]1958 -1 },1959 -1 "dialog": {1960 -1 "namefrom": [ "author" ],1961 -1 "namerequired": true,1962 -1 "parent": [ "window" ]1963 -1 },1964 -1 "directory": {1965 -1 "namefrom": [ "contents", "author" ],1966 -1 "parent": [ "list" ]1967 -1 },1968 -1 "document": {1969 -1 "namefrom": [ " author" ],1970 -1 "namerequired": true,1971 -1 "parent": [ "structure" ],1972 -1 "properties": [ "aria-expanded" ]1973 -1 },1974 -1 "form": {1975 -1 "namefrom": [ "author" ],1976 -1 "parent": [ "landmark" ]1977 -1 },1978 -1 "grid": {1979 -1 "mustcontain": [ "row", "rowgroup" ],1980 -1 "namefrom": [ "author" ],1981 -1 "namerequired": true,1982 -1 "parent": [ "composite", "region" ],1983 -1 "properties": [ "aria-level", "aria-multiselectable", "aria-readonly" ]1984 -1 },1985 -1 "gridcell": {1986 -1 "namefrom": [ "contents", "author" ],1987 -1 "namerequired": true,1988 -1 "parent": [ "section", "widget" ],1989 -1 "properties": [ "aria-readonly", "aria-required", "aria-selected" ],1990 -1 "scope": [ "row" ]1991 -1 },1992 -1 "group": {1993 -1 "namefrom": [ " author" ],1994 -1 "parent": [ "section" ],1995 -1 "properties": [ "aria-activedescendant" ]1996 -1 },1997 -1 "heading": {1998 -1 "namerequired": true,1999 -1 "parent": [ "sectionhead" ],2000 -1 "properties": [ "aria-level" ]2001 -1 },2002 -1 "img": {2003 -1 "childpresentational": true,2004 -1 "namefrom": [ "author" ],2005 -1 "namerequired": true,2006 -1 "parent": [ "section" ]2007 -1 },2008 -1 "input": {2009 -1 "abstract": true,2010 -1 "namefrom": [ "author" ],2011 -1 "parent": [ "widget" ]2012 -1 },2013 -1 "landmark": {2014 -1 "abstract": true,2015 -1 "namefrom": [ "contents", "author" ],2016 -1 "namerequired": false,2017 -1 "parent": [ "region" ]2018 -1 },2019 -1 "link": {2020 -1 "namefrom": [ "contents", "author" ],2021 -1 "namerequired": true,2022 -1 "parent": [ "command" ],2023 -1 "properties": [ "aria-expanded" ]2024 -1 },2025 -1 "list": {2026 -1 "mustcontain": [ "group", "listitem" ],2027 -1 "namefrom": [ "author" ],2028 -1 "parent": [ "region" ]2029 -1 },2030 -1 "listbox": {2031 -1 "mustcontain": [ "option" ],2032 -1 "namefrom": [ "author" ],2033 -1 "namerequired": true,2034 -1 "parent": [ "list", "select" ],2035 -1 "properties": [ "aria-multiselectable", "aria-required" ]2036 -1 },2037 -1 "listitem": {2038 -1 "namefrom": [ "contents", "author" ],2039 -1 "namerequired": true,2040 -1 "parent": [ "section" ],2041 -1 "properties": [ "aria-level", "aria-posinset", "aria-setsize" ],2042 -1 "scope": [ "list" ]2043 -1 },2044 -1 "log": {2045 -1 "namefrom": [ " author" ],2046 -1 "namerequired": true,2047 -1 "parent": [ "region" ]2048 -1 },2049 -1 "main": {2050 -1 "namefrom": [ "author" ],2051 -1 "parent": [ "landmark" ]2052 -1 },2053 -1 "marquee": {2054 -1 "namerequired": true,2055 -1 "parent": [ "section" ]2056 -1 },2057 -1 "math": {2058 -1 "childpresentational": true,2059 -1 "namefrom": [ "author" ],2060 -1 "parent": [ "section" ]2061 -1 },2062 -1 "menu": {2063 -1 "mustcontain": [2064 -1 "group",2065 -1 "menuitemradio",2066 -1 "menuitem",2067 -1 "menuitemcheckbox"2068 -1 ],2069 -1 "namefrom": [ "author" ],2070 -1 "namerequired": true,2071 -1 "parent": [ "list", "select" ]2072 -1 },2073 -1 "menubar": {2074 -1 "namefrom": [ "author" ],2075 -1 "parent": [ "menu" ]2076 -1 },2077 -1 "menuitem": {2078 -1 "namefrom": [ "contents", "author" ],2079 -1 "namerequired": true,2080 -1 "parent": [ "command" ],2081 -1 "scope": [ "menu", "menubar" ]2082 -1 },2083 -1 "menuitemcheckbox": {2084 -1 "namefrom": [ "contents", "author" ],2085 -1 "namerequired": true,2086 -1 "parent": [ "checkbox", "menuitem" ],2087 -1 "scope": [ "menu", "menubar" ]2088 -1 },2089 -1 "menuitemradio": {2090 -1 "namefrom": [ "contents", "author" ],2091 -1 "namerequired": true,2092 -1 "parent": [ "menuitemcheckbox", "radio" ],2093 -1 "scope": [ "menu", "menubar" ]2094 -1 },2095 -1 "navigation": {2096 -1 "namefrom": [ "author" ],2097 -1 "parent": [ "landmark" ]2098 -1 },2099 -1 "note": {2100 -1 "namefrom": [ "author" ],2101 -1 "parent": [ "section" ]2102 -1 },2103 -1 "option": {2104 -1 "namefrom": [ "contents", "author" ],2105 -1 "namerequired": true,2106 -1 "parent": [ "input" ],2107 -1 "properties": [2108 -1 "aria-checked",2109 -1 "aria-posinset",2110 -1 "aria-selected",2111 -1 "aria-setsize"2112 -1 ]2113 -1 },2114 -1 "presentation": {2115 -1 "parent": [ "structure" ]2116 -1 },2117 -1 "progressbar": {2118 -1 "childpresentational": true,2119 -1 "namefrom": [ "author" ],2120 -1 "namerequired": true,2121 -1 "parent": [ "range" ]2122 -1 },2123 -1 "radio": {2124 -1 "namefrom": [ "contents", "author" ],2125 -1 "namerequired": true,2126 -1 "parent": [ "checkbox", "option" ]2127 -1 },2128 -1 "radiogroup": {2129 -1 "mustcontain": [ "radio" ],2130 -1 "namefrom": [ "author" ],2131 -1 "namerequired": true,2132 -1 "parent": [ "select" ],2133 -1 "properties": [ "aria-required" ]2134 -1 },2135 -1 "range": {2136 -1 "abstract": true,2137 -1 "namefrom": [ "author" ],2138 -1 "parent": [ "widget" ],2139 -1 "properties": [2140 -1 "aria-valuemax",2141 -1 "aria-valuemin",2142 -1 "aria-valuenow",2143 -1 "aria-valuetext"2144 -1 ]2145 -1 },2146 -1 "region": {2147 -1 "namefrom": [ " author" ],2148 -1 "parent": [ "section" ]2149 -1 },2150 -1 "roletype": {2151 -1 "abstract": true,2152 -1 "properties": [2153 -1 "aria-atomic",2154 -1 "aria-busy",2155 -1 "aria-controls",2156 -1 "aria-describedby",2157 -1 "aria-disabled",2158 -1 "aria-dropeffect",2159 -1 "aria-flowto",2160 -1 "aria-grabbed",2161 -1 "aria-haspopup",2162 -1 "aria-hidden",2163 -1 "aria-invalid",2164 -1 "aria-label",2165 -1 "aria-labelledby",2166 -1 "aria-live",2167 -1 "aria-owns",2168 -1 "aria-relevant"2169 -1 ]2170 -1 },2171 -1 "row": {2172 -1 "mustcontain": [ "columnheader", "gridcell", "rowheader" ],2173 -1 "namefrom": [ "contents", "author" ],2174 -1 "parent": [ "group", "widget" ],2175 -1 "properties": [ "aria-level", "aria-selected" ],2176 -1 "scope": [ "grid", "rowgroup", "treegrid" ]2177 -1 },2178 -1 "rowgroup": {2179 -1 "mustcontain": [ "row" ],2180 -1 "namefrom": [ "contents", "author" ],2181 -1 "parent": [ "group" ],2182 -1 "scope": [ "grid" ]2183 -1 },2184 -1 "rowheader": {2185 -1 "namefrom": [ "contents", "author" ],2186 -1 "namerequired": true,2187 -1 "parent": [ "gridcell", "sectionhead", "widget" ],2188 -1 "properties": [ "aria-sort" ],2189 -1 "scope": [ "row" ]2190 -1 },2191 -1 "search": {2192 -1 "namefrom": [ "author" ],2193 -1 "parent": [ "landmark" ]2194 -1 },2195 -1 "section": {2196 -1 "abstract": true,2197 -1 "namefrom": [ "contents", "author" ],2198 -1 "parent": [ "structure" ],2199 -1 "properties": [ "aria-expanded" ]2200 -1 },2201 -1 "sectionhead": {2202 -1 "abstract": true,2203 -1 "namefrom": [ "contents", "author" ],2204 -1 "parent": [ "structure" ],2205 -1 "properties": [ "aria-expanded" ]2206 -1 },2207 -1 "select": {2208 -1 "abstract": true,2209 -1 "namefrom": [ "author" ],2210 -1 "parent": [ "composite", "group", "input" ]2211 -1 },2212 -1 "separator": {2213 -1 "childpresentational": true,2214 -1 "namefrom": [ "author" ],2215 -1 "parent": [ "structure" ],2216 -1 "properties": [ "aria-expanded", "aria-orientation" ]2217 -1 },2218 -1 "scrollbar": {2219 -1 "childpresentational": true,2220 -1 "namefrom": [ "author" ],2221 -1 "namerequired": false,2222 -1 "parent": [ "input", "range" ],2223 -1 "requiredProperties": [2224 -1 "aria-controls",2225 -1 "aria-orientation",2226 -1 "aria-valuemax",2227 -1 "aria-valuemin",2228 -1 "aria-valuenow"2229 -1 ],2230 -1 "properties": [2231 -1 "aria-controls",2232 -1 "aria-orientation",2233 -1 "aria-valuemax",2234 -1 "aria-valuemin",2235 -1 "aria-valuenow"2236 -1 ]2237 -1 },2238 -1 "slider": {2239 -1 "childpresentational": true,2240 -1 "namefrom": [ "author" ],2241 -1 "namerequired": true,2242 -1 "parent": [ "input", "range" ],2243 -1 "requiredProperties": [ "aria-valuemax", "aria-valuemin", "aria-valuenow" ],2244 -1 "properties": [2245 -1 "aria-valuemax",2246 -1 "aria-valuemin",2247 -1 "aria-valuenow",2248 -1 "aria-orientation"2249 -1 ]2250 -1 },2251 -1 "spinbutton": {2252 -1 "namefrom": [ "author" ],2253 -1 "namerequired": true,2254 -1 "parent": [ "input", "range" ],2255 -1 "requiredProperties": [ "aria-valuemax", "aria-valuemin", "aria-valuenow" ],2256 -1 "properties": [2257 -1 "aria-valuemax",2258 -1 "aria-valuemin",2259 -1 "aria-valuenow",2260 -1 "aria-required"2261 -1 ]2262 -1 },2263 -1 "status": {2264 -1 "parent": [ "region" ]2265 -1 },2266 -1 "structure": {2267 -1 "abstract": true,2268 -1 "parent": [ "roletype" ]2269 -1 },2270 -1 "tab": {2271 -1 "namefrom": [ "contents", "author" ],2272 -1 "parent": [ "sectionhead", "widget" ],2273 -1 "properties": [ "aria-selected" ],2274 -1 "scope": [ "tablist" ]2275 -1 },2276 -1 "tablist": {2277 -1 "mustcontain": [ "tab" ],2278 -1 "namefrom": [ "author" ],2279 -1 "parent": [ "composite", "directory" ],2280 -1 "properties": [ "aria-level" ]2281 -1 },2282 -1 "tabpanel": {2283 -1 "namefrom": [ "author" ],2284 -1 "namerequired": true,2285 -1 "parent": [ "region" ]2286 -1 },2287 -1 "textbox": {2288 -1 "namefrom": [ "author" ],2289 -1 "namerequired": true,2290 -1 "parent": [ "input" ],2291 -1 "properties": [2292 -1 "aria-activedescendant",2293 -1 "aria-autocomplete",2294 -1 "aria-multiline",2295 -1 "aria-readonly",2296 -1 "aria-required"2297 -1 ]2298 -1 },2299 -1 "timer": {2300 -1 "namefrom": [ "author" ],2301 -1 "namerequired": true,2302 -1 "parent": [ "status" ]2303 -1 },2304 -1 "toolbar": {2305 -1 "namefrom": [ "author" ],2306 -1 "parent": [ "group" ]2307 -1 },2308 -1 "tooltip": {2309 -1 "namerequired": true,2310 -1 "parent": [ "section" ]2311 -1 },2312 -1 "tree": {2313 -1 "mustcontain": [ "group", "treeitem" ],2314 -1 "namefrom": [ "author" ],2315 -1 "namerequired": true,2316 -1 "parent": [ "select" ],2317 -1 "properties": [ "aria-multiselectable", "aria-required" ]2318 -1 },2319 -1 "treegrid": {2320 -1 "mustcontain": [ "row" ],2321 -1 "namefrom": [ "author" ],2322 -1 "namerequired": true,2323 -1 "parent": [ "grid", "tree" ]2324 -1 },2325 -1 "treeitem": {2326 -1 "namefrom": [ "contents", "author" ],2327 -1 "namerequired": true,2328 -1 "parent": [ "listitem", "option" ],2329 -1 "scope": [ "group", "tree" ]2330 -1 },2331 -1 "widget": {2332 -1 "abstract": true,2333 -1 "parent": [ "roletype" ]2334 -1 },2335 -1 "window": {2336 -1 "abstract": true,2337 -1 "namefrom": [ " author" ],2338 -1 "parent": [ "roletype" ],2339 -1 "properties": [ "aria-expanded" ]2340 -1 }2341 -1 };2342 -12343 -1 axs.constants.WIDGET_ROLES = {};2344 -12345 -1 /**2346 -1 * Squashes the parent hierarchy on to role object.2347 -1 * @param {Object} role2348 -1 * @param {Object} set2349 -1 * @private2350 -1 */2351 -1 axs.constants.addAllParentRolesToSet_ = function(role, set) {2352 -1 if (!role['parent'])2353 -1 return;2354 -1 var parents = role['parent'];2355 -1 for (var j = 0; j < parents.length; j++) {2356 -1 var parentRoleName = parents[j];2357 -1 set[parentRoleName] = true;2358 -1 axs.constants.addAllParentRolesToSet_(2359 -1 axs.constants.ARIA_ROLES[parentRoleName], set);2360 -1 }2361 -1 };2362 -12363 -1 /**2364 -1 * Adds all properties and requiredProperties from parent hierarchy.2365 -1 * @param {Object} role2366 -1 * @param {string} propertiesName2367 -1 * @param {Object} propertiesSet2368 -1 * @private2369 -1 */2370 -1 axs.constants.addAllPropertiesToSet_ = function(role, propertiesName,2371 -1 propertiesSet) {2372 -1 var properties = role[propertiesName];2373 -1 if (properties) {2374 -1 for (var i = 0; i < properties.length; i++)2375 -1 propertiesSet[properties[i]] = true;2376 -1 }2377 -1 if (role['parent']) {2378 -1 var parents = role['parent'];2379 -1 for (var j = 0; j < parents.length; j++) {2380 -1 var parentRoleName = parents[j];2381 -1 axs.constants.addAllPropertiesToSet_(2382 -1 axs.constants.ARIA_ROLES[parentRoleName], propertiesName,2383 -1 propertiesSet);2384 -1 }2385 -1 }2386 -1 };2387 -12388 -1 // TODO make a AriaRole object etc.2389 -1 for (var roleName in axs.constants.ARIA_ROLES) {2390 -1 var role = axs.constants.ARIA_ROLES[roleName];2391 -12392 -1 var propertiesSet = {};2393 -1 axs.constants.addAllPropertiesToSet_(role, 'properties', propertiesSet);2394 -1 role['propertiesSet'] = propertiesSet;2395 -12396 -1 var requiredPropertiesSet = {};2397 -1 axs.constants.addAllPropertiesToSet_(role, 'requiredProperties', requiredPropertiesSet);2398 -1 role['requiredPropertiesSet'] = requiredPropertiesSet;2399 -1 var parentRolesSet = {};2400 -1 axs.constants.addAllParentRolesToSet_(role, parentRolesSet);2401 -1 role['allParentRolesSet'] = parentRolesSet;2402 -1 if ('widget' in parentRolesSet)2403 -1 axs.constants.WIDGET_ROLES[roleName] = role;2404 -1 }2405 -12406 -1 // BEGIN ARIA_PROPERTIES_AUTOGENERATED2407 -1 /** @type {Object.<string, Object>} */2408 -1 axs.constants.ARIA_PROPERTIES = {2409 -1 "activedescendant": {2410 -1 "type": "property",2411 -1 "valueType": "idref"2412 -1 },2413 -1 "atomic": {2414 -1 "defaultValue": "false",2415 -1 "type": "property",2416 -1 "valueType": "boolean"2417 -1 },2418 -1 "autocomplete": {2419 -1 "defaultValue": "none",2420 -1 "type": "property",2421 -1 "valueType": "token",2422 -1 "values": [2423 -1 "inline",2424 -1 "list",2425 -1 "both",2426 -1 "none"2427 -1 ]2428 -1 },2429 -1 "busy": {2430 -1 "defaultValue": "false",2431 -1 "type": "state",2432 -1 "valueType": "boolean"2433 -1 },2434 -1 "checked": {2435 -1 "defaultValue": "undefined",2436 -1 "type": "state",2437 -1 "valueType": "token",2438 -1 "values": [2439 -1 "true",2440 -1 "false",2441 -1 "mixed",2442 -1 "undefined"2443 -1 ]2444 -1 },2445 -1 "controls": {2446 -1 "type": "property",2447 -1 "valueType": "idref_list"2448 -1 },2449 -1 "describedby": {2450 -1 "type": "property",2451 -1 "valueType": "idref_list"2452 -1 },2453 -1 "disabled": {2454 -1 "defaultValue": "false",2455 -1 "type": "state",2456 -1 "valueType": "boolean"2457 -1 },2458 -1 "dropeffect": {2459 -1 "defaultValue": "none",2460 -1 "type": "property",2461 -1 "valueType": "token_list",2462 -1 "values": [2463 -1 "copy",2464 -1 "move",2465 -1 "link",2466 -1 "execute",2467 -1 "popup",2468 -1 "none"2469 -1 ]2470 -1 },2471 -1 "expanded": {2472 -1 "defaultValue": "undefined",2473 -1 "type": "state",2474 -1 "valueType": "token",2475 -1 "values": [2476 -1 "true",2477 -1 "false",2478 -1 "undefined"2479 -1 ]2480 -1 },2481 -1 "flowto": {2482 -1 "type": "property",2483 -1 "valueType": "idref_list"2484 -1 },2485 -1 "grabbed": {2486 -1 "defaultValue": "undefined",2487 -1 "type": "state",2488 -1 "valueType": "token",2489 -1 "values": [2490 -1 "true",2491 -1 "false",2492 -1 "undefined"2493 -1 ]2494 -1 },2495 -1 "haspopup": {2496 -1 "defaultValue": "false",2497 -1 "type": "property",2498 -1 "valueType": "boolean"2499 -1 },2500 -1 "hidden": {2501 -1 "defaultValue": "false",2502 -1 "type": "state",2503 -1 "valueType": "boolean"2504 -1 },2505 -1 "invalid": {2506 -1 "defaultValue": "false",2507 -1 "type": "state",2508 -1 "valueType": "token",2509 -1 "values": [2510 -1 "grammar",2511 -1 "false",2512 -1 "spelling",2513 -1 "true"2514 -1 ]2515 -1 },2516 -1 "label": {2517 -1 "type": "property",2518 -1 "valueType": "string"2519 -1 },2520 -1 "labelledby": {2521 -1 "type": "property",2522 -1 "valueType": "idref_list"2523 -1 },2524 -1 "level": {2525 -1 "type": "property",2526 -1 "valueType": "integer"2527 -1 },2528 -1 "live": {2529 -1 "defaultValue": "off",2530 -1 "type": "property",2531 -1 "valueType": "token",2532 -1 "values": [2533 -1 "off",2534 -1 "polite",2535 -1 "assertive"2536 -1 ]2537 -1 },2538 -1 "multiline": {2539 -1 "defaultValue": "false",2540 -1 "type": "property",2541 -1 "valueType": "boolean"2542 -1 },2543 -1 "multiselectable": {2544 -1 "defaultValue": "false",2545 -1 "type": "property",2546 -1 "valueType": "boolean"2547 -1 },2548 -1 "orientation": {2549 -1 "defaultValue": "vertical",2550 -1 "type": "property",2551 -1 "valueType": "token",2552 -1 "values": [2553 -1 "horizontal",2554 -1 "vertical"2555 -1 ]2556 -1 },2557 -1 "owns": {2558 -1 "type": "property",2559 -1 "valueType": "idref_list"2560 -1 },2561 -1 "posinset": {2562 -1 "type": "property",2563 -1 "valueType": "integer"2564 -1 },2565 -1 "pressed": {2566 -1 "defaultValue": "undefined",2567 -1 "type": "state",2568 -1 "valueType": "token",2569 -1 "values": [2570 -1 "true",2571 -1 "false",2572 -1 "mixed",2573 -1 "undefined"2574 -1 ]2575 -1 },2576 -1 "readonly": {2577 -1 "defaultValue": "false",2578 -1 "type": "property",2579 -1 "valueType": "boolean"2580 -1 },2581 -1 "relevant": {2582 -1 "defaultValue": "additions text",2583 -1 "type": "property",2584 -1 "valueType": "token_list",2585 -1 "values": [2586 -1 "additions",2587 -1 "removals",2588 -1 "text",2589 -1 "all"2590 -1 ]2591 -1 },2592 -1 "required": {2593 -1 "defaultValue": "false",2594 -1 "type": "property",2595 -1 "valueType": "boolean"2596 -1 },2597 -1 "selected": {2598 -1 "defaultValue": "undefined",2599 -1 "type": "state",2600 -1 "valueType": "token",2601 -1 "values": [2602 -1 "true",2603 -1 "false",2604 -1 "undefined"2605 -1 ]2606 -1 },2607 -1 "setsize": {2608 -1 "type": "property",2609 -1 "valueType": "integer"2610 -1 },2611 -1 "sort": {2612 -1 "defaultValue": "none",2613 -1 "type": "property",2614 -1 "valueType": "token",2615 -1 "values": [2616 -1 "ascending",2617 -1 "descending",2618 -1 "none",2619 -1 "other"2620 -1 ]2621 -1 },2622 -1 "valuemax": {2623 -1 "type": "property",2624 -1 "valueType": "decimal"2625 -1 },2626 -1 "valuemin": {2627 -1 "type": "property",2628 -1 "valueType": "decimal"2629 -1 },2630 -1 "valuenow": {2631 -1 "type": "property",2632 -1 "valueType": "decimal"2633 -1 },2634 -1 "valuetext": {2635 -1 "type": "property",2636 -1 "valueType": "string"2637 -1 }2638 -1 };2639 -1 // END ARIA_PROPERTIES_AUTOGENERATED2640 -12641 -1 (function() {2642 -1 // pull values lists into sets2643 -1 for (var propertyName in axs.constants.ARIA_PROPERTIES) {2644 -1 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyName];2645 -1 if (!propertyDetails.values)2646 -1 continue;2647 -1 var valuesSet = {};2648 -1 for (var i = 0; i < propertyDetails.values.length; i++)2649 -1 valuesSet[propertyDetails.values[i]] = true;2650 -1 propertyDetails.valuesSet = valuesSet;2651 -1 }2652 -1 })();2653 -12654 -1 /**2655 -1 * All of the states and properties which apply globally.2656 -1 * @type {Object<!string, !boolean>}2657 -1 */2658 -1 axs.constants.GLOBAL_PROPERTIES = axs.constants.ARIA_ROLES['roletype'].propertiesSet;2659 -12660 -1 /**2661 -1 * A constant indicating no role name.2662 -1 * @type {string}2663 -1 */2664 -1 axs.constants.NO_ROLE_NAME = ' ';2665 -12666 -1 /**2667 -1 * A mapping from ARIA role names to their message ids.2668 -1 * Copied from ChromeVox:2669 -1 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js2670 -1 * @type {Object.<string, string>}2671 -1 */2672 -1 axs.constants.WIDGET_ROLE_TO_NAME = {2673 -1 'alert' : 'aria_role_alert',2674 -1 'alertdialog' : 'aria_role_alertdialog',2675 -1 'button' : 'aria_role_button',2676 -1 'checkbox' : 'aria_role_checkbox',2677 -1 'columnheader' : 'aria_role_columnheader',2678 -1 'combobox' : 'aria_role_combobox',2679 -1 'dialog' : 'aria_role_dialog',2680 -1 'grid' : 'aria_role_grid',2681 -1 'gridcell' : 'aria_role_gridcell',2682 -1 'link' : 'aria_role_link',2683 -1 'listbox' : 'aria_role_listbox',2684 -1 'log' : 'aria_role_log',2685 -1 'marquee' : 'aria_role_marquee',2686 -1 'menu' : 'aria_role_menu',2687 -1 'menubar' : 'aria_role_menubar',2688 -1 'menuitem' : 'aria_role_menuitem',2689 -1 'menuitemcheckbox' : 'aria_role_menuitemcheckbox',2690 -1 'menuitemradio' : 'aria_role_menuitemradio',2691 -1 'option' : axs.constants.NO_ROLE_NAME,2692 -1 'progressbar' : 'aria_role_progressbar',2693 -1 'radio' : 'aria_role_radio',2694 -1 'radiogroup' : 'aria_role_radiogroup',2695 -1 'rowheader' : 'aria_role_rowheader',2696 -1 'scrollbar' : 'aria_role_scrollbar',2697 -1 'slider' : 'aria_role_slider',2698 -1 'spinbutton' : 'aria_role_spinbutton',2699 -1 'status' : 'aria_role_status',2700 -1 'tab' : 'aria_role_tab',2701 -1 'tabpanel' : 'aria_role_tabpanel',2702 -1 'textbox' : 'aria_role_textbox',2703 -1 'timer' : 'aria_role_timer',2704 -1 'toolbar' : 'aria_role_toolbar',2705 -1 'tooltip' : 'aria_role_tooltip',2706 -1 'treeitem' : 'aria_role_treeitem'2707 -1 };2708 -12709 -12710 -1 /**2711 -1 * @type {Object.<string, string>}2712 -1 * Copied from ChromeVox:2713 -1 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js2714 -1 */2715 -1 axs.constants.STRUCTURE_ROLE_TO_NAME = {2716 -1 'article' : 'aria_role_article',2717 -1 'application' : 'aria_role_application',2718 -1 'banner' : 'aria_role_banner',2719 -1 'columnheader' : 'aria_role_columnheader',2720 -1 'complementary' : 'aria_role_complementary',2721 -1 'contentinfo' : 'aria_role_contentinfo',2722 -1 'definition' : 'aria_role_definition',2723 -1 'directory' : 'aria_role_directory',2724 -1 'document' : 'aria_role_document',2725 -1 'form' : 'aria_role_form',2726 -1 'group' : 'aria_role_group',2727 -1 'heading' : 'aria_role_heading',2728 -1 'img' : 'aria_role_img',2729 -1 'list' : 'aria_role_list',2730 -1 'listitem' : 'aria_role_listitem',2731 -1 'main' : 'aria_role_main',2732 -1 'math' : 'aria_role_math',2733 -1 'navigation' : 'aria_role_navigation',2734 -1 'note' : 'aria_role_note',2735 -1 'region' : 'aria_role_region',2736 -1 'rowheader' : 'aria_role_rowheader',2737 -1 'search' : 'aria_role_search',2738 -1 'separator' : 'aria_role_separator'2739 -1 };2740 -12741 -12742 -1 /**2743 -1 * @type {Array.<Object>}2744 -1 * Copied from ChromeVox:2745 -1 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js2746 -1 */2747 -1 axs.constants.ATTRIBUTE_VALUE_TO_STATUS = [2748 -1 { name: 'aria-autocomplete', values:2749 -1 {'inline' : 'aria_autocomplete_inline',2750 -1 'list' : 'aria_autocomplete_list',2751 -1 'both' : 'aria_autocomplete_both'} },2752 -1 { name: 'aria-checked', values:2753 -1 {'true' : 'aria_checked_true',2754 -1 'false' : 'aria_checked_false',2755 -1 'mixed' : 'aria_checked_mixed'} },2756 -1 { name: 'aria-disabled', values:2757 -1 {'true' : 'aria_disabled_true'} },2758 -1 { name: 'aria-expanded', values:2759 -1 {'true' : 'aria_expanded_true',2760 -1 'false' : 'aria_expanded_false'} },2761 -1 { name: 'aria-invalid', values:2762 -1 {'true' : 'aria_invalid_true',2763 -1 'grammar' : 'aria_invalid_grammar',2764 -1 'spelling' : 'aria_invalid_spelling'} },2765 -1 { name: 'aria-multiline', values:2766 -1 {'true' : 'aria_multiline_true'} },2767 -1 { name: 'aria-multiselectable', values:2768 -1 {'true' : 'aria_multiselectable_true'} },2769 -1 { name: 'aria-pressed', values:2770 -1 {'true' : 'aria_pressed_true',2771 -1 'false' : 'aria_pressed_false',2772 -1 'mixed' : 'aria_pressed_mixed'} },2773 -1 { name: 'aria-readonly', values:2774 -1 {'true' : 'aria_readonly_true'} },2775 -1 { name: 'aria-required', values:2776 -1 {'true' : 'aria_required_true'} },2777 -1 { name: 'aria-selected', values:2778 -1 {'true' : 'aria_selected_true',2779 -1 'false' : 'aria_selected_false'} }2780 -1 ];2781 -12782 -1 /**2783 -1 * Copied from ChromeVox:2784 -1 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js2785 -1 * @type {Object}2786 -1 */2787 -1 axs.constants.INPUT_TYPE_TO_INFORMATION_TABLE_MSG = {2788 -1 'button' : 'input_type_button',2789 -1 'checkbox' : 'input_type_checkbox',2790 -1 'color' : 'input_type_color',2791 -1 'datetime' : 'input_type_datetime',2792 -1 'datetime-local' : 'input_type_datetime_local',2793 -1 'date' : 'input_type_date',2794 -1 'email' : 'input_type_email',2795 -1 'file' : 'input_type_file',2796 -1 'image' : 'input_type_image',2797 -1 'month' : 'input_type_month',2798 -1 'number' : 'input_type_number',2799 -1 'password' : 'input_type_password',2800 -1 'radio' : 'input_type_radio',2801 -1 'range' : 'input_type_range',2802 -1 'reset' : 'input_type_reset',2803 -1 'search' : 'input_type_search',2804 -1 'submit' : 'input_type_submit',2805 -1 'tel' : 'input_type_tel',2806 -1 'text' : 'input_type_text',2807 -1 'url' : 'input_type_url',2808 -1 'week' : 'input_type_week'2809 -1 };2810 -12811 -12812 -1 /**2813 -1 * Copied from ChromeVox:2814 -1 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js2815 -1 * @type {Object}2816 -1 */2817 -1 axs.constants.TAG_TO_INFORMATION_TABLE_VERBOSE_MSG = {2818 -1 'A' : 'tag_link',2819 -1 'BUTTON' : 'tag_button',2820 -1 'H1' : 'tag_h1',2821 -1 'H2' : 'tag_h2',2822 -1 'H3' : 'tag_h3',2823 -1 'H4' : 'tag_h4',2824 -1 'H5' : 'tag_h5',2825 -1 'H6' : 'tag_h6',2826 -1 'LI' : 'tag_li',2827 -1 'OL' : 'tag_ol',2828 -1 'SELECT' : 'tag_select',2829 -1 'TEXTAREA' : 'tag_textarea',2830 -1 'UL' : 'tag_ul',2831 -1 'SECTION' : 'tag_section',2832 -1 'NAV' : 'tag_nav',2833 -1 'ARTICLE' : 'tag_article',2834 -1 'ASIDE' : 'tag_aside',2835 -1 'HGROUP' : 'tag_hgroup',2836 -1 'HEADER' : 'tag_header',2837 -1 'FOOTER' : 'tag_footer',2838 -1 'TIME' : 'tag_time',2839 -1 'MARK' : 'tag_mark'2840 -1 };2841 -12842 -1 /**2843 -1 * Copied from ChromeVox:2844 -1 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js2845 -1 * @type {Object}2846 -1 */2847 -1 axs.constants.TAG_TO_INFORMATION_TABLE_BRIEF_MSG = {2848 -1 'BUTTON' : 'tag_button',2849 -1 'SELECT' : 'tag_select',2850 -1 'TEXTAREA' : 'tag_textarea'2851 -1 };2852 -12853 -1 axs.constants.MIXED_VALUES = {2854 -1 "true": true,2855 -1 "false": true,2856 -1 "mixed": true2857 -1 };2858 -12859 -1 /** @enum {string} */2860 -1 axs.constants.Severity = {2861 -1 INFO: 'Info',2862 -1 WARNING: 'Warning',2863 -1 SEVERE: 'Severe'2864 -1 };2865 -12866 -1 /** @enum {string} */2867 -1 axs.constants.AuditResult = {2868 -1 PASS: 'PASS',2869 -1 FAIL: 'FAIL',2870 -1 NA: 'NA'2871 -1 };2872 -12873 -1 /** @enum {boolean} */2874 -1 axs.constants.InlineElements = {2875 -1 // fontstyle2876 -1 'TT': true,2877 -1 'I': true,2878 -1 'B': true,2879 -1 'BIG': true,2880 -1 'SMALL': true,2881 -12882 -1 // phrase2883 -1 'EM': true,2884 -1 'STRONG': true,2885 -1 'DFN': true,2886 -1 'CODE': true,2887 -1 'SAMP': true,2888 -1 'KBD': true,2889 -1 'VAR': true,2890 -1 'CITE': true,2891 -1 'ABBR': true,2892 -1 'ACRONYM': true,2893 -12894 -1 // special2895 -1 'A': true,2896 -1 'IMG': true,2897 -1 'OBJECT': true,2898 -1 'BR': true,2899 -1 'SCRIPT': true,2900 -1 'MAP': true,2901 -1 'Q': true,2902 -1 'SUB': true,2903 -1 'SUP': true,2904 -1 'SPAN': true,2905 -1 'BDO': true,2906 -12907 -1 // formctrl2908 -1 'INPUT': true,2909 -1 'SELECT': true,2910 -1 'TEXTAREA': true,2911 -1 'LABEL': true,2912 -1 'BUTTON': true2913 -1 };2914 -12915 -1 /** @enum {boolean} */2916 -1 axs.constants.NATIVELY_DISABLEABLE = {2917 -1 // W3C and WHATWG https://html.spec.whatwg.org/#enabling-and-disabling-form-controls:-the-disabled-attribute2918 -1 'BUTTON': true,2919 -1 'INPUT': true,2920 -1 'SELECT': true,2921 -1 'TEXTAREA': true,2922 -1 'FIELDSET': true,2923 -12924 -1 // W3C http://www.w3.org/TR/html5/disabled-elements.html#disabled-elements2925 -1 'OPTGROUP': true,2926 -1 'OPTION': true2927 -1 };2928 -12929 -1 /**2930 -1 * Maps ARIA attributes to their exactly equivalent HTML attributes.2931 -1 * @type {Object.<string, string>}2932 -1 */2933 -1 axs.constants.ARIA_TO_HTML_ATTRIBUTE = {2934 -1 'aria-checked' : 'checked',2935 -1 'aria-disabled' : 'disabled',2936 -1 'aria-hidden' : 'hidden',2937 -1 'aria-expanded' : 'open',2938 -1 'aria-valuemax' : 'max',2939 -1 'aria-valuemin' : 'min',2940 -1 'aria-readonly' : 'readonly',2941 -1 'aria-required' : 'required',2942 -1 'aria-selected' : 'selected',2943 -1 'aria-valuenow' : 'value'2944 -1 };2945 -12946 -1 /**2947 -1 * Holds information about implicit ARIA semantics for a given HTML element type.2948 -1 * This object has the following properties:2949 -1 * <ul>2950 -1 * <li>`role` will contain the implicit role if it exists, otherwise empty string.</li>2951 -1 * <li>`allowed` contains the roles that can reasonably be applied to this element.2952 -1 * Note: A tag that can take any role is signified by a '*' wildcard in the array. It is not2953 -1 * an error if the array contains other roles but currently this has no meaning. In future it may2954 -1 * be used to indicate recommended roles.2955 -1 * </li>2956 -1 * <li>`selector` is present if this is a 'subclass' of the base HTML element, i.e. its semantics are2957 -1 * modified by context or attributes. It can be used with the selectors API to find and/or match2958 -1 * elements.2959 -1 * </li>2960 -1 * <li>`reserved` will be true if this is a semantically strong element that you may not modify with any2961 -1 * ARIA attributes, including role or global attributes.2962 -1 * </li>2963 -1 * </ul>2964 -1 *2965 -1 * @typedef {{ role: string,2966 -1 * allowed: Array.<string>,2967 -1 * selector: string,2968 -1 * reserved: boolean }}2969 -1 */2970 -1 axs.constants.HtmlInfo;2971 -1 /**2972 -1 * A lookup table which maps uppercase tagName to information about implicit ARIA semantics.2973 -1 * This table is based on the document: http://w3c.github.io/aria-in-html/2974 -1 * It is not complete and never can be. Complex scenarios require specific handling not provided here.2975 -1 * Any element not listed here:2976 -1 * - has no implicit role2977 -1 * - can take any role2978 -1 * e.g. em,strong,small,s,cite,q,dfn,abbr,time,code,var,samp,kbd,sub and sup,i,b,u,mark ,ruby,rt,rp,bdi,bdo,br,wbr2979 -1 *2980 -1 * Where there is any ambiguity this table will endeavor to provide for the most broad case (to avoid2981 -1 * false failures in conformance checking).2982 -1 *2983 -1 * For example 'table' can take any role however in practice it should only be given the role 'grid' when2984 -1 * being used as a data grid or 'presentation' when used for layout. This lookup ignores these nuances and2985 -1 * allows all roles.2986 -1 *2987 -1 * @type {Object.<string, Array.<axs.constants.HtmlInfo>>}2988 -1 */2989 -1 axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO = {2990 -1 'A': [{2991 -1 role: 'link',2992 -1 allowed: [2993 -1 'button',2994 -1 'checkbox',2995 -1 'menuitem',2996 -1 'menuitemcheckbox',2997 -1 'menuitemradio',2998 -1 'tab',2999 -1 'treeitem'],3000 -1 selector: 'a[href]'3001 -1 }],3002 -1 'ADDRESS': [{3003 -1 role: '',3004 -1 allowed: [3005 -1 'contentinfo',3006 -1 'presentation']3007 -1 }],3008 -1 'AREA': [{3009 -1 role: 'link',3010 -1 selector: 'area[href]'3011 -1 }],3012 -1 'ARTICLE': [{3013 -1 role: 'article',3014 -1 allowed: [3015 -1 'presentation',3016 -1 'article',3017 -1 'document',3018 -1 'application',3019 -1 'main']3020 -1 }],3021 -1 'ASIDE': [{3022 -1 role: 'complementary',3023 -1 allowed: [3024 -1 'note',3025 -1 'complementary',3026 -1 'search',3027 -1 'presentation']3028 -1 }],3029 -1 'AUDIO': [{3030 -1 role: '',3031 -1 allowed: ['application', 'presentation']3032 -1 }],3033 -1 'BASE': [{3034 -1 role: '',3035 -1 reserved: true3036 -1 }],3037 -1 'BODY': [{3038 -1 role: 'document',3039 -1 allowed: ['presentation']3040 -1 }],3041 -1 'BUTTON': [{3042 -1 role: 'button',3043 -1 allowed: [3044 -1 'link',3045 -1 'menuitem',3046 -1 'menuitemcheckbox',3047 -1 'menuitemradio',3048 -1 'radio'],3049 -1 selector: 'button:not([aria-pressed]):not([type="menu"])'3050 -1 }, {3051 -1 role: 'button',3052 -1 allowed: ['button'],3053 -1 selector: 'button[aria-pressed]'3054 -1 }, {3055 -1 role: 'button',3056 -1 attributes: {3057 -1 'aria-haspopup': true3058 -1 },3059 -1 allowed: [3060 -1 'link',3061 -1 'menuitem',3062 -1 'menuitemcheckbox',3063 -1 'menuitemradio',3064 -1 'radio'],3065 -1 selector: 'button[type="menu"]'3066 -1 }],3067 -1 'CAPTION': [{3068 -1 role: '',3069 -1 allowed: ['presentation']3070 -1 }],3071 -1 'COL': [{3072 -1 role: '',3073 -1 reserved: true3074 -1 }],3075 -1 'COLGROUP': [{3076 -1 role: '',3077 -1 reserved: true3078 -1 }],3079 -1 'DATALIST': [{3080 -1 role: 'listbox',3081 -1 attributes: {3082 -1 'aria-multiselectable': false3083 -1 },3084 -1 allowed: ['presentation']3085 -1 }],3086 -1 'DEL': [{3087 -1 role: '',3088 -1 allowed: ['*']3089 -1 }],3090 -1 'DD': [{3091 -1 role: '',3092 -1 allowed: ['presentation']3093 -1 }],3094 -1 'DT': [{3095 -1 role: '',3096 -1 allowed: ['presentation']3097 -1 }],3098 -1 'DETAILS': [{3099 -1 role: 'group',3100 -1 allowed: [3101 -1 'group',3102 -1 'presentation']3103 -1 }],3104 -1 'DIALOG': [{ // updated 'allowed' from: http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-dialog-element3105 -1 role: 'dialog',3106 -1 allowed: ['dialog', 'alert', 'alertdialog', 'application', 'log', 'marquee', 'status'],3107 -1 selector: 'dialog[open]'3108 -1 }, {3109 -1 role: 'dialog',3110 -1 attributes: {3111 -1 'aria-hidden': true3112 -1 },3113 -1 allowed: ['dialog', 'alert', 'alertdialog', 'application', 'log', 'marquee', 'status'],3114 -1 selector: 'dialog:not([open])'3115 -1 }],3116 -1 'DIV': [{3117 -1 role: '',3118 -1 allowed: ['*']3119 -1 }],3120 -1 'DL': [{3121 -1 role: 'list',3122 -1 allowed: ['presentation']3123 -1 }],3124 -1 'EMBED': [{3125 -1 role: '',3126 -1 allowed: [3127 -1 'application',3128 -1 'document',3129 -1 'img',3130 -1 'presentation']3131 -1 }],3132 -1 'FIGURE': [{3133 -1 role: '',3134 -1 allowed: ['*']3135 -1 }],3136 -1 'FOOTER': [{3137 -1 role: '',3138 -1 allowed: ['contentinfo', 'presentation']3139 -1 }],3140 -1 'FORM': [{3141 -1 role: 'form',3142 -1 allowed: ['presentation']3143 -1 }],3144 -1 'P': [{3145 -1 role: '',3146 -1 allowed: ['*']3147 -1 }],3148 -1 'PRE': [{3149 -1 role: '',3150 -1 allowed: ['*']3151 -1 }],3152 -1 'BLOCKQUOTE': [{3153 -1 role: '',3154 -1 allowed: ['*']3155 -1 }],3156 -1 H1: [{3157 -1 role: 'heading'3158 -1 }],3159 -1 H2: [{3160 -1 role: 'heading'3161 -1 }],3162 -1 H3: [{3163 -1 role: 'heading'3164 -1 }],3165 -1 H4: [{3166 -1 role: 'heading'3167 -1 }],3168 -1 H5: [{3169 -1 role: 'heading'3170 -1 }],3171 -1 H6: [{3172 -1 role: 'heading'3173 -1 }],3174 -1 'HEAD': [{3175 -1 role: '',3176 -1 reserved: true3177 -1 }],3178 -1 'HEADER': [{3179 -1 role: '',3180 -1 allowed: [3181 -1 'banner',3182 -1 'presentation']3183 -1 }],3184 -1 'HR': [{3185 -1 role: 'separator',3186 -1 allowed: ['presentation']3187 -1 }],3188 -1 'HTML': [{3189 -1 role: '',3190 -1 reserved: true3191 -1 }],3192 -1 'IFRAME': [{3193 -1 role: '',3194 -1 allowed: [3195 -1 'application',3196 -1 'document',3197 -1 'img',3198 -1 'presentation'],3199 -1 selector: 'iframe:not([seamless])'3200 -1 }, {3201 -1 role: '',3202 -1 allowed: [3203 -1 'application',3204 -1 'document',3205 -1 'img',3206 -1 'presentation',3207 -1 'group'],3208 -1 selector: 'iframe[seamless]'3209 -1 }],3210 -1 'IMG': [{3211 -1 role: 'presentation',3212 -1 reserved: true,3213 -1 selector: 'img[alt=""]'3214 -1 }, {3215 -1 role: 'img',3216 -1 allowed: ['*'],3217 -1 selector: 'img[alt]:not([alt=""])'3218 -1 }],3219 -1 'INPUT': [{3220 -1 role: 'button',3221 -1 allowed: [3222 -1 'link',3223 -1 'menuitem',3224 -1 'menuitemcheckbox',3225 -1 'menuitemradio',3226 -1 'radio'],3227 -1 selector: 'input[type="button"]:not([aria-pressed])'3228 -1 }, {3229 -1 role: 'button',3230 -1 allowed: ['button'],3231 -1 selector: 'input[type="button"][aria-pressed]'3232 -1 }, {3233 -1 role: 'checkbox',3234 -1 allowed: ['checkbox'],3235 -1 selector: 'input[type="checkbox"]'3236 -1 }, {3237 -1 role: '',3238 -1 selector: 'input[type="color"]'3239 -1 }, {3240 -1 role: '',3241 -1 selector: 'input[type="date"]'3242 -1 }, {3243 -1 role: '',3244 -1 selector: 'input[type="datetime"]'3245 -1 }, {3246 -1 role: 'textbox',3247 -1 selector: 'input[type="email"]:not([list])'3248 -1 }, {3249 -1 role: '',3250 -1 selector: 'input[type="file"]'3251 -1 }, {3252 -1 role: '',3253 -1 reserved: true,3254 -1 selector: 'input[type="hidden"]'3255 -1 }, {3256 -1 role: 'button',3257 -1 allowed: ['button'],3258 -1 selector: 'input[type="image"][aria-pressed]'3259 -1 }, {3260 -1 role: 'button',3261 -1 allowed: [3262 -1 'link',3263 -1 'menuitem',3264 -1 'menuitemcheckbox',3265 -1 'menuitemradio',3266 -1 'radio'],3267 -1 selector: 'input[type="image"]:not([aria-pressed])'3268 -1 }, {3269 -1 role: '',3270 -1 selector: 'input[type="month"]'3271 -1 }, {3272 -1 role: '',3273 -1 selector: 'input[type="number"]'3274 -1 }, {3275 -1 role: 'textbox',3276 -1 selector: 'input[type="password"]'3277 -1 }, {3278 -1 role: 'radio',3279 -1 allowed: ['menuitemradio'],3280 -1 selector: 'input[type="radio"]'3281 -1 }, {3282 -1 role: 'slider',3283 -1 selector: 'input[type="range"]'3284 -1 }, {3285 -1 role: 'button',3286 -1 selector: 'input[type="reset"]'3287 -1 }, {3288 -1 role: 'combobox', // aria-owns is set to the same value as the list attribute3289 -1 selector: 'input[type="search"][list]'3290 -1 }, {3291 -1 role: 'textbox',3292 -1 selector: 'input[type="search"]:not([list])'3293 -1 }, {3294 -1 role: 'button',3295 -1 selector: 'input[type="submit"]'3296 -1 }, {3297 -1 role: 'combobox', // aria-owns is set to the same value as the list attribute3298 -1 selector: 'input[type="tel"][list]'3299 -1 }, {3300 -1 role: 'textbox',3301 -1 selector: 'input[type="tel"]:not([list])'3302 -1 }, {3303 -1 role: 'combobox', // aria-owns is set to the same value as the list attribute3304 -1 selector: 'input[type="text"][list]'3305 -1 }, {3306 -1 role: 'textbox',3307 -1 selector: 'input[type="text"]:not([list])'3308 -1 }, {3309 -1 role: 'textbox',3310 -1 selector: 'input:not([type])'3311 -1 }, {3312 -1 role: '',3313 -1 selector: 'input[type="time"]'3314 -1 }, {3315 -1 role: 'combobox', // aria-owns is set to the same value as the list attribute3316 -1 selector: 'input[type="url"][list]'3317 -1 }, {3318 -1 role: 'textbox',3319 -1 selector: 'input[type="url"]:not([list])'3320 -1 }, {3321 -1 role: '',3322 -1 selector: 'input[type="week"]'3323 -1 }],3324 -1 'INS': [{3325 -1 role: '',3326 -1 allowed: ['*']3327 -1 }],3328 -1 'KEYGEN': [{3329 -1 role: ''3330 -1 }],3331 -1 'LABEL': [{3332 -1 role: '',3333 -1 allowed: ['presentation']3334 -1 }],3335 -1 'LI': [{3336 -1 role: 'listitem',3337 -1 allowed: [3338 -1 'menuitem',3339 -1 'menuitemcheckbox',3340 -1 'menuitemradio',3341 -1 'option',3342 -1 'tab',3343 -1 'treeitem',3344 -1 'presentation'],3345 -1 selector: 'ol:not([role="presentation"])>li, ul:not([role="presentation"])>li'3346 -1 }, {3347 -1 role: 'listitem',3348 -1 allowed: [3349 -1 'listitem',3350 -1 'menuitem',3351 -1 'menuitemcheckbox',3352 -1 'menuitemradio',3353 -1 'option',3354 -1 'tab',3355 -1 'treeitem',3356 -1 'presentation'],3357 -1 selector: 'ol[role="presentation"]>li, ul[role="presentation"]>li'3358 -1 }],3359 -1 'LINK': [{3360 -1 role: 'link',3361 -1 reserved: true,3362 -1 selector: 'link[href]'3363 -1 }],3364 -1 'MAIN': [{3365 -1 role: '',3366 -1 allowed: [3367 -1 'main',3368 -1 'presentation']3369 -1 }],3370 -1 'MAP': [{3371 -1 role: '',3372 -1 reserved: true3373 -1 }],3374 -1 'MATH': [{3375 -1 role: '',3376 -1 allowed: ['presentation']3377 -1 }],3378 -1 'MENU': [{3379 -1 role: 'toolbar',3380 -1 selector: 'menu[type="toolbar"]'3381 -1 }],3382 -1 'MENUITEM': [{3383 -1 role: 'menuitem',3384 -1 selector: 'menuitem[type="command"]'3385 -1 }, {3386 -1 role: 'menuitemcheckbox',3387 -1 selector: 'menuitem[type="checkbox"]'3388 -1 }, {3389 -1 role: 'menuitemradio',3390 -1 selector: 'menuitem[type="radio"]'3391 -1 }],3392 -1 'META': [{3393 -1 role: '',3394 -1 reserved: true3395 -1 }],3396 -1 'METER': [{3397 -1 role: 'progressbar',3398 -1 allowed: ['presentation']3399 -1 }],3400 -1 'NAV': [{3401 -1 role: 'navigation',3402 -1 allowed: ['navigation', 'presentation']3403 -1 }],3404 -1 'NOSCRIPT': [{3405 -1 role: '',3406 -1 reserved: true3407 -1 }],3408 -1 'OBJECT': [{3409 -1 role: '',3410 -1 allowed: ['application', 'document', 'img', 'presentation']3411 -1 }],3412 -1 'OL': [{3413 -1 role: 'list',3414 -1 allowed: ['directory', 'group', 'listbox', 'menu', 'menubar', 'tablist', 'toolbar', 'tree', 'presentation']3415 -1 }],3416 -1 'OPTGROUP': [{3417 -1 role: '',3418 -1 allowed: ['presentation']3419 -1 }],3420 -1 'OPTION': [{3421 -1 role: 'option'3422 -1 }],3423 -1 'OUTPUT': [{3424 -1 role: 'status',3425 -1 allowed: ['*']3426 -1 }],3427 -1 'PARAM': [{3428 -1 role: '',3429 -1 reserved: true3430 -1 }],3431 -1 'PICTURE': [{3432 -1 role: '',3433 -1 reserved: true3434 -1 }],3435 -1 'PROGRESS': [{3436 -1 role: 'progressbar',3437 -1 allowed: ['presentation']3438 -1 }],3439 -1 'SCRIPT': [{3440 -1 role: '',3441 -1 reserved: true3442 -1 }],3443 -1 'SECTION': [{3444 -1 role: 'region',3445 -1 allowed: [3446 -1 'alert',3447 -1 'alertdialog',3448 -1 'application',3449 -1 'contentinfo',3450 -1 'dialog',3451 -1 'document',3452 -1 'log',3453 -1 'marquee',3454 -1 'search',3455 -1 'status',3456 -1 'presentation']3457 -1 }],3458 -1 'SELECT': [{3459 -1 role: 'listbox'3460 -1 }],3461 -1 'SOURCE': [{3462 -1 role: '',3463 -1 reserved: true3464 -1 }],3465 -1 'SPAN': [{3466 -1 role: '',3467 -1 allowed: ['*']3468 -1 }],3469 -1 'STYLE': [{3470 -1 role: '',3471 -1 reserved: true3472 -1 }],3473 -1 'SVG': [{3474 -1 role: '',3475 -1 allowed: [3476 -1 'application',3477 -1 'document',3478 -1 'img',3479 -1 'presentation']3480 -1 }],3481 -1 'SUMMARY': [{3482 -1 role: '',3483 -1 allowed: ['presentation']3484 -1 }],3485 -1 'TABLE': [{3486 -1 role: '',3487 -1 allowed: ['*']3488 -1 }],3489 -1 'TEMPLATE': [{3490 -1 role: '',3491 -1 reserved: true3492 -1 }],3493 -1 'TEXTAREA': [{3494 -1 role: 'textbox'3495 -1 }],3496 -1 'TBODY': [{3497 -1 role: 'rowgroup',3498 -1 allowed: ['*']3499 -1 }],3500 -1 'THEAD': [{3501 -1 role: 'rowgroup',3502 -1 allowed: ['*']3503 -1 }],3504 -1 'TFOOT': [{3505 -1 role: 'rowgroup',3506 -1 allowed: ['*']3507 -1 }],3508 -1 'TITLE': [{3509 -1 role: '',3510 -1 reserved: true3511 -1 }],3512 -1 'TD': [{3513 -1 role: '',3514 -1 allowed: ['*']3515 -1 }],3516 -1 'TH': [{3517 -1 role: '',3518 -1 allowed: ['*']3519 -1 }],3520 -1 'TR': [{3521 -1 role: '',3522 -1 allowed: ['*']3523 -1 }],3524 -1 'TRACK': [{3525 -1 role: '',3526 -1 reserved: true3527 -1 }],3528 -1 'UL': [{3529 -1 role: 'list',3530 -1 allowed: [3531 -1 'directory',3532 -1 'group',3533 -1 'listbox',3534 -1 'menu',3535 -1 'menubar',3536 -1 'tablist',3537 -1 'toolbar',3538 -1 'tree',3539 -1 'presentation']3540 -1 }],3541 -1 'VIDEO': [{3542 -1 role: '',3543 -1 allowed: ['application', 'presentation']3544 -1 }]-1 2117 if (match) { -1 2118 var r = parseInt(match[1], 10); -1 2119 var g = parseInt(match[2], 10); -1 2120 var b = parseInt(match[3], 10); -1 2121 var a = 1; -1 2122 return new axs.color.Color(r, g, b, a); -1 2123 } -1 2124 -1 2125 var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/; -1 2126 match = colorString.match(rgbaRegex); -1 2127 if (match) { -1 2128 var r = parseInt(match[1], 10); -1 2129 var g = parseInt(match[2], 10); -1 2130 var b = parseInt(match[3], 10); -1 2131 var a = parseFloat(match[4]); -1 2132 return new axs.color.Color(r, g, b, a); -1 2133 } -1 2134 -1 2135 return null; 3545 2136 }; 3546 21373547 -1 },{}],5:[function(require,module,exports){3548 -1 // Copyright 2015 Google Inc.3549 -1 //3550 -1 // Licensed under the Apache License, Version 2.0 (the "License");3551 -1 // you may not use this file except in compliance with the License.3552 -1 // You may obtain a copy of the License at3553 -1 //3554 -1 // http://www.apache.org/licenses/LICENSE-2.03555 -1 //3556 -1 // Unless required by applicable law or agreed to in writing, software3557 -1 // distributed under the License is distributed on an "AS IS" BASIS,3558 -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.3559 -1 // See the License for the specific language governing permissions and3560 -1 // limitations under the License.-1 2138 /** -1 2139 * @param {number} value The value of a color channel, 0 <= value <= 0xFF -1 2140 * @return {!string} -1 2141 */ -1 2142 axs.color.colorChannelToString = function(value) { -1 2143 value = Math.round(value); -1 2144 if (value <= 0xF) -1 2145 return '0' + value.toString(16); -1 2146 return value.toString(16); -1 2147 }; 3561 21483562 -1 goog.provide('axs.dom');-1 2149 /** -1 2150 * @param {axs.color.Color} color -1 2151 * @return {!string} -1 2152 */ -1 2153 axs.color.colorToString = function(color) { -1 2154 if (color.alpha == 1) { -1 2155 return '#' + axs.color.colorChannelToString(color.red) + -1 2156 axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue); -1 2157 } -1 2158 else -1 2159 return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')'; -1 2160 }; -1 2161 -1 2162 /** -1 2163 * Compute a desired luminance given a given luminance and a desired contrast ratio. -1 2164 * @param {number} luminance The given luminance. -1 2165 * @param {number} contrast The desired contrast ratio. -1 2166 * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance. -1 2167 * @return {number} The desired luminance. -1 2168 */ -1 2169 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) { -1 2170 if (higher) { -1 2171 var newLuminance = (luminance + 0.05) * contrast - 0.05; -1 2172 return newLuminance; -1 2173 } else { -1 2174 var newLuminance = (luminance + 0.05) / contrast - 0.05; -1 2175 return newLuminance; -1 2176 } -1 2177 }; -1 2178 -1 2179 /** -1 2180 * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is -1 2181 * as close as possible to the original color. -1 2182 * @param {axs.color.YCbCr} ycc The original color in YCbCr form. -1 2183 * @param {number} luma The desired luminance -1 2184 * @return {!axs.color.Color} A new color in RGB. -1 2185 */ -1 2186 axs.color.translateColor = function(ycc, luma) { -1 2187 var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC; -1 2188 var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE -1 2189 : axs.color.YCC_CUBE_FACES_BLACK; -1 2190 -1 2191 var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]); -1 2192 var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]); -1 2193 var line = { a: a, b: b }; -1 2194 -1 2195 var intersection = null; -1 2196 for (var i = 0; i < cubeFaces.length; i++) { -1 2197 var cubeFace = cubeFaces[i]; -1 2198 intersection = axs.color.findIntersection(line, cubeFace); -1 2199 // If intersection within [0, 1] in Z axis, it is within the cube. -1 2200 if (intersection.z >= 0 && intersection.z <= 1) -1 2201 break; -1 2202 } -1 2203 if (!intersection) { -1 2204 // Should never happen -1 2205 throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + "."; -1 2206 } -1 2207 if (intersection.x != ycc.x || intersection.y != ycc.y) { -1 2208 // Should never happen -1 2209 throw "Intersection has wrong Cb/Cr values."; -1 2210 } -1 2211 -1 2212 // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube -1 2213 // and we can immediately return new value. -1 2214 if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) { -1 2215 var translatedColor = [luma, ycc.Cb, ycc.Cr]; -1 2216 return axs.color.fromYCbCrArray(translatedColor); -1 2217 } -1 2218 -1 2219 // Otherwise, translate from intersection towards white/black such that luma is correct. -1 2220 var dLuma = luma - intersection.luma; -1 2221 var scale = dLuma / (endpoint.luma - intersection.luma); -1 2222 var translatedColor = [ luma, -1 2223 intersection.Cb - (intersection.Cb * scale), -1 2224 intersection.Cr - (intersection.Cr * scale) ]; -1 2225 -1 2226 return axs.color.fromYCbCrArray(translatedColor); -1 2227 }; -1 2228 -1 2229 /** @typedef {{fg: string, bg: string, contrast: string}} */ -1 2230 axs.color.SuggestedColors; -1 2231 -1 2232 /** -1 2233 * @param {axs.color.Color} bgColor -1 2234 * @param {axs.color.Color} fgColor -1 2235 * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio. -1 2236 * @return {Object.<string, axs.color.SuggestedColors>} -1 2237 */ -1 2238 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) { -1 2239 var colors = {}; -1 2240 var bgLuminance = axs.color.calculateLuminance(bgColor); -1 2241 var fgLuminance = axs.color.calculateLuminance(fgColor); -1 2242 -1 2243 var fgLuminanceIsHigher = fgLuminance > bgLuminance; -1 2244 var fgYCbCr = axs.color.toYCbCr(fgColor); -1 2245 var bgYCbCr = axs.color.toYCbCr(bgColor); -1 2246 for (var desiredLabel in desiredContrastRatios) { -1 2247 var desiredContrast = desiredContrastRatios[desiredLabel]; -1 2248 -1 2249 var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher); -1 2250 if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) { -1 2251 var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance); -1 2252 var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor); -1 2253 var suggestedColors = {}; -1 2254 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor)); -1 2255 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor)); -1 2256 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2)); -1 2257 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors); -1 2258 continue; -1 2259 } -1 2260 -1 2261 var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher); -1 2262 if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) { -1 2263 var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance); -1 2264 var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor); -1 2265 var suggestedColors = {}; -1 2266 suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor)); -1 2267 suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor)); -1 2268 suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2)); -1 2269 colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors); -1 2270 } -1 2271 } -1 2272 return colors; -1 2273 }; -1 2274 -1 2275 /** -1 2276 * Combine the two given color according to alpha blending. -1 2277 * @param {axs.color.Color} fgColor -1 2278 * @param {axs.color.Color} bgColor -1 2279 * @return {axs.color.Color} -1 2280 */ -1 2281 axs.color.flattenColors = function(fgColor, bgColor) { -1 2282 var alpha = fgColor.alpha; -1 2283 var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red); -1 2284 var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green); -1 2285 var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue); -1 2286 var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha)); -1 2287 -1 2288 return new axs.color.Color(r, g, b, a); -1 2289 }; -1 2290 -1 2291 /** -1 2292 * Multiply the given vector by the given matrix. -1 2293 * @param {Array.<Array.<number>>} matrix A 3x3 matrix -1 2294 * @param {Array.<number>} vector A 3-element vector -1 2295 * @return {Array.<number>} A 3-element vector -1 2296 */ -1 2297 axs.color.multiplyMatrixVector = function(matrix, vector) { -1 2298 var a = matrix[0][0]; -1 2299 var b = matrix[0][1]; -1 2300 var c = matrix[0][2]; -1 2301 var d = matrix[1][0]; -1 2302 var e = matrix[1][1]; -1 2303 var f = matrix[1][2]; -1 2304 var g = matrix[2][0]; -1 2305 var h = matrix[2][1]; -1 2306 var k = matrix[2][2]; -1 2307 -1 2308 var x = vector[0]; -1 2309 var y = vector[1]; -1 2310 var z = vector[2]; -1 2311 -1 2312 return [ -1 2313 a*x + b*y + c*z, -1 2314 d*x + e*y + f*z, -1 2315 g*x + h*y + k*z -1 2316 ]; -1 2317 }; -1 2318 -1 2319 /** -1 2320 * Convert a given RGB color to YCbCr. -1 2321 * @param {axs.color.Color} color -1 2322 * @return {axs.color.YCbCr} -1 2323 */ -1 2324 axs.color.toYCbCr = function(color) { -1 2325 var rSRGB = color.red / 255; -1 2326 var gSRGB = color.green / 255; -1 2327 var bSRGB = color.blue / 255; -1 2328 -1 2329 var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4); -1 2330 var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4); -1 2331 var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4); -1 2332 -1 2333 return new axs.color.YCbCr(axs.color.multiplyMatrixVector(axs.color.YCC_MATRIX, [r, g, b])); -1 2334 }; -1 2335 -1 2336 /** -1 2337 * @param {axs.color.YCbCr} ycc -1 2338 * @return {!axs.color.Color} -1 2339 */ -1 2340 axs.color.fromYCbCr = function(ycc) { -1 2341 return axs.color.fromYCbCrArray([ycc.luma, ycc.Cb, ycc.Cr]); -1 2342 }; 3563 2343 3564 2344 /**3565 -1 * Returns the nearest ancestor which is an Element.3566 -1 * @param {Node} node3567 -1 * @return {?Element}-1 2345 * Convert a color from a YCbCr color (as a vector) to an RGB color -1 2346 * @param {Array.<number>} yccArray -1 2347 * @return {!axs.color.Color} 3568 2348 */3569 -1 axs.dom.parentElement = function(node) {3570 -1 if (!node)3571 -1 return null;-1 2349 axs.color.fromYCbCrArray = function(yccArray) { -1 2350 var rgb = axs.color.multiplyMatrixVector(axs.color.INVERTED_YCC_MATRIX, yccArray); 3572 23513573 -1 var parentNode = axs.dom.composedParentNode(node);3574 -1 if (!parentNode)3575 -1 return null;-1 2352 var r = rgb[0]; -1 2353 var g = rgb[1]; -1 2354 var b = rgb[2]; -1 2355 var rSRGB = r <= 0.00303949 ? (r * 12.92) : (Math.pow(r, (1/2.4)) * 1.055) - 0.055; -1 2356 var gSRGB = g <= 0.00303949 ? (g * 12.92) : (Math.pow(g, (1/2.4)) * 1.055) - 0.055; -1 2357 var bSRGB = b <= 0.00303949 ? (b * 12.92) : (Math.pow(b, (1/2.4)) * 1.055) - 0.055; 3576 23583577 -1 switch (parentNode.nodeType) {3578 -1 case Node.ELEMENT_NODE:3579 -1 return /** @type {Element} */ (parentNode);3580 -1 default:3581 -1 return axs.dom.parentElement(parentNode);3582 -1 }-1 2359 var red = Math.min(Math.max(Math.round(rSRGB * 255), 0), 255); -1 2360 var green = Math.min(Math.max(Math.round(gSRGB * 255), 0), 255); -1 2361 var blue = Math.min(Math.max(Math.round(bSRGB * 255), 0), 255); -1 2362 -1 2363 return new axs.color.Color(red, green, blue, 1); 3583 2364 }; 3584 2365 3585 2366 /**3586 -1 * Returns the shadow host of a document fragment if it is a Shadow DOM fragment3587 -1 * otherwise returns `null`.3588 -1 * @param {DocumentFragment} fragment3589 -1 * @return {?Element}-1 2367 * Returns an RGB to YCbCr conversion matrix for the given kR, kB constants. -1 2368 * @param {number} kR -1 2369 * @param {number} kB -1 2370 * @return {Array.<Array.<number>>} 3590 2371 */3591 -1 axs.dom.shadowHost = function(fragment) {3592 -1 // If host exists, this is a Shadow DOM fragment.3593 -1 if ('host' in fragment)3594 -1 return fragment.host;3595 -1 else3596 -1 return null;-1 2372 axs.color.RGBToYCbCrMatrix = function(kR, kB) { -1 2373 return [ -1 2374 [ -1 2375 kR, -1 2376 (1 - kR - kB), -1 2377 kB -1 2378 ], -1 2379 [ -1 2380 -kR/(2 - 2*kB), -1 2381 (kR + kB - 1)/(2 - 2*kB), -1 2382 (1 - kB)/(2 - 2*kB) -1 2383 ], -1 2384 [ -1 2385 (1 - kR)/(2 - 2*kR), -1 2386 (kR + kB - 1)/(2 - 2*kR), -1 2387 -kB/(2 - 2*kR) -1 2388 ] -1 2389 ]; 3597 2390 }; 3598 2391 3599 2392 /**3600 -1 * Returns the given Node's parent in the composed tree.3601 -1 * @param {Node} node3602 -1 * @return {?Node}-1 2393 * Return the inverse of the given 3x3 matrix. -1 2394 * @param {Array.<Array.<number>>} matrix -1 2395 * @return Array.<Array.<number>> The inverse of the given matrix. 3603 2396 */3604 -1 axs.dom.composedParentNode = function(node) {3605 -1 if (!node)3606 -1 return null;3607 -1 if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE)3608 -1 return axs.dom.shadowHost(/** @type {DocumentFragment} */ (node));3609 -13610 -1 var parentNode = node.parentNode;3611 -1 if (!parentNode)3612 -1 return null;-1 2397 axs.color.invert3x3Matrix = function(matrix) { -1 2398 var a = matrix[0][0]; -1 2399 var b = matrix[0][1]; -1 2400 var c = matrix[0][2]; -1 2401 var d = matrix[1][0]; -1 2402 var e = matrix[1][1]; -1 2403 var f = matrix[1][2]; -1 2404 var g = matrix[2][0]; -1 2405 var h = matrix[2][1]; -1 2406 var k = matrix[2][2]; 3613 24073614 -1 if (parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE)3615 -1 return axs.dom.shadowHost(/** @type {DocumentFragment} */ (parentNode));-1 2408 var A = (e*k - f*h); -1 2409 var B = (f*g - d*k); -1 2410 var C = (d*h - e*g); -1 2411 var D = (c*h - b*k); -1 2412 var E = (a*k - c*g); -1 2413 var F = (g*b - a*h); -1 2414 var G = (b*f - c*e); -1 2415 var H = (c*d - a*f); -1 2416 var K = (a*e - b*d); 3616 24173617 -1 if (!parentNode.shadowRoot)3618 -1 return parentNode;-1 2418 var det = a * (e*k - f*h) - b * (k*d - f*g) + c * (d*h - e*g); -1 2419 var z = 1/det; 3619 24203620 -1 // Shadow DOM v13621 -1 if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE) {3622 -1 var assignedSlot = node.assignedSlot;3623 -1 if (HTMLSlotElement && assignedSlot instanceof HTMLSlotElement)3624 -1 return axs.dom.composedParentNode(assignedSlot);3625 -1 }-1 2421 return axs.color.scalarMultiplyMatrix([ -1 2422 [ A, D, G ], -1 2423 [ B, E, H ], -1 2424 [ C, F, K ] -1 2425 ], z); -1 2426 }; 3626 24273627 -1 // Shadow DOM v03628 -1 if (typeof node.getDestinationInsertionPoints === 'function') {3629 -1 var insertionPoints = node.getDestinationInsertionPoints();3630 -1 if (insertionPoints.length > 0)3631 -1 return axs.dom.composedParentNode(insertionPoints[insertionPoints.length - 1]);3632 -1 }-1 2428 /** @typedef {{ a: axs.color.YCbCr, b: axs.color.YCbCr }} */ -1 2429 axs.color.Line; 3633 24303634 -1 return null;3635 -1 };-1 2431 /** @typedef {{ p0: axs.color.YCbCr, p1: axs.color.YCbCr, p2: axs.color.YCbCr }} */ -1 2432 axs.color.Plane; 3636 2433 3637 2434 /**3638 -1 * Return the corresponding element for the given node.3639 -1 * @param {Node} node3640 -1 * @return {Element}3641 -1 * @suppress {checkTypes}-1 2435 * Find the intersection between a line and a plane using -1 2436 * http://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Parametric_form -1 2437 * @param {axs.color.Line} l -1 2438 * @param {axs.color.Plane} p -1 2439 * @return {axs.color.YCbCr} 3642 2440 */3643 -1 axs.dom.asElement = function(node) {3644 -1 /** @type {Element} */ var element;3645 -1 switch (node.nodeType) {3646 -1 case Node.COMMENT_NODE:3647 -1 return null; // Skip comments3648 -1 case Node.ELEMENT_NODE:3649 -1 element = /** (@type {Element}) */ node;3650 -1 if (element.localName == 'script' ||3651 -1 element.localName == 'template')3652 -1 return null; // Skip script-supporting elements3653 -1 return element;3654 -1 case Node.DOCUMENT_FRAGMENT_NODE:3655 -1 return node.host;3656 -1 case Node.TEXT_NODE:3657 -1 return axs.dom.parentElement(node);3658 -1 default:3659 -1 console.warn('Unhandled node type: ', node.nodeType);3660 -1 }3661 -1 return null;-1 2441 axs.color.findIntersection = function(l, p) { -1 2442 var lhs = [ l.a.x - p.p0.x, l.a.y - p.p0.y, l.a.z - p.p0.z ]; -1 2443 -1 2444 var matrix = [ [ l.a.x - l.b.x, p.p1.x - p.p0.x, p.p2.x - p.p0.x ], -1 2445 [ l.a.y - l.b.y, p.p1.y - p.p0.y, p.p2.y - p.p0.y ], -1 2446 [ l.a.z - l.b.z, p.p1.z - p.p0.z, p.p2.z - p.p0.z ] ]; -1 2447 var invertedMatrix = axs.color.invert3x3Matrix(matrix); -1 2448 -1 2449 var tuv = axs.color.multiplyMatrixVector(invertedMatrix, lhs); -1 2450 var t = tuv[0]; -1 2451 -1 2452 var result = l.a.add(l.b.subtract(l.a).multiply(t)); -1 2453 return result; 3662 2454 }; 3663 2455 3664 2456 /**3665 -1 * Recursively walk the composed tree from |node|, aborting if |end| is encountered.3666 -1 * @param {Node} node3667 -1 * @param {?Node} end3668 -1 * @param {{preorder: (function (Node, Object):boolean|undefined),3669 -1 * postorder: (function (Node, Object)|undefined)}} callbacks3670 -1 * Callbacks to be called for each element traversed, excluding3671 -1 * |end|. Possible callbacks are |preorder|, called before descending into3672 -1 * child nodes, and |postorder| called after all child nodes have been3673 -1 * traversed. If |preorder| returns false, its child nodes will not be3674 -1 * traversed.3675 -1 * @param {Object} parentFlags3676 -1 * @param {ShadowRoot=} opt_shadowRoot The nearest ShadowRoot ancestor, if any.3677 -1 * @return {boolean} Whether |end| was found, if provided.-1 2457 * Multiply a matrix by a scalar. -1 2458 * @param {Array.<Array.<number>>} matrix A 3x3 matrix. -1 2459 * @param {number} scalar -1 2460 * @return {Array.<Array.<number>>} 3678 2461 */3679 -1 axs.dom.composedTreeSearch = function(node, end, callbacks, parentFlags, opt_shadowRoot) {3680 -1 if (node === end)3681 -1 return true;3682 -13683 -1 if (node.nodeType == Node.ELEMENT_NODE)3684 -1 var element = /** @type {Element} */ (node);3685 -13686 -1 var found = false;3687 -1 var flags = Object.create(parentFlags);-1 2462 axs.color.scalarMultiplyMatrix = function(matrix, scalar) { -1 2463 var result = []; 3688 24643689 -1 // Descend into node:3690 -1 // If it has a ShadowRoot, ignore all child elements - these will be picked3691 -1 // up by the <content> or <shadow> elements. Descend straight into the3692 -1 // ShadowRoot.3693 -1 if (element) {3694 -1 var localName = element.localName;3695 -1 if (flags.collectIdRefs) {3696 -1 flags.idrefs = axs.utils.getReferencedIds(element);3697 -1 }3698 -1 if (!flags.disabled || (localName === 'legend') && axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type')) {3699 -1 flags.disabled = axs.utils.isElementDisabled(element, true);3700 -1 }3701 -1 if (!flags.hidden) {3702 -1 flags.hidden = axs.utils.isElementHidden(element);3703 -1 }3704 -1 if (callbacks.preorder) {3705 -1 if (!callbacks.preorder(element, flags))3706 -1 return found;3707 -1 }3708 -1 // NOTE: grunt qunit DOES NOT support Shadow DOM, so if changing this3709 -1 // code, be sure to run the tests in the browser before committing.3710 -1 var shadowRoot = element.shadowRoot || element.webkitShadowRoot;3711 -1 if (shadowRoot) {3712 -1 flags.level++;3713 -1 found = axs.dom.composedTreeSearch(shadowRoot,3714 -1 end,3715 -1 callbacks,3716 -1 flags,3717 -1 shadowRoot);3718 -1 if (element && callbacks.postorder && !found)3719 -1 callbacks.postorder(element, flags);3720 -1 return found;3721 -1 }-1 2465 for (var i = 0; i < 3; i++) -1 2466 result[i] = axs.color.scalarMultiplyVector(matrix[i], scalar); 3722 24673723 -1 // If it is a <content> element, descend into distributed elements - these3724 -1 // are elements from outside the shadow root which are rendered inside the3725 -1 // shadow DOM.3726 -1 if (localName == 'content' && typeof element.getDistributedNodes === 'function') {3727 -1 var content = /** @type {HTMLContentElement} */ (element);3728 -1 var distributedNodes = content.getDistributedNodes();3729 -1 for (var i = 0; i < distributedNodes.length && !found; i++) {3730 -1 found = axs.dom.composedTreeSearch(distributedNodes[i],3731 -1 end,3732 -1 callbacks,3733 -1 flags,3734 -1 opt_shadowRoot);3735 -1 }3736 -1 if (callbacks.postorder && !found)3737 -1 callbacks.postorder.call(null, element, flags);3738 -1 return found;3739 -1 }3740 -1 }-1 2468 return result; -1 2469 }; 3741 2470 -1 2471 /** -1 2472 * Multiply a vector by a scalar. -1 2473 * @param {Array.<number>} vector -1 2474 * @param {number} scalar -1 2475 * @return {Array.<number>} vector -1 2476 */ -1 2477 axs.color.scalarMultiplyVector = function(vector, scalar) { -1 2478 var result = []; -1 2479 for (var i = 0; i < vector.length; i++) -1 2480 result[i] = vector[i] * scalar; -1 2481 return result; -1 2482 }; 3742 2483 -1 2484 axs.color.kR = 0.2126; -1 2485 axs.color.kB = 0.0722; -1 2486 axs.color.YCC_MATRIX = axs.color.RGBToYCbCrMatrix(axs.color.kR, axs.color.kB); -1 2487 axs.color.INVERTED_YCC_MATRIX = axs.color.invert3x3Matrix(axs.color.YCC_MATRIX); 3743 24883744 -1 // If it is neither the parent of a ShadowRoot, a <content> element, nor3745 -1 // a <shadow> element recurse normally.3746 -1 var child = node.firstChild;3747 -1 while (child != null && !found) {3748 -1 found = axs.dom.composedTreeSearch(child,3749 -1 end,3750 -1 callbacks,3751 -1 flags,3752 -1 opt_shadowRoot);3753 -1 child = child.nextSibling;3754 -1 }-1 2489 axs.color.BLACK = new axs.color.Color(0, 0, 0, 1.0); -1 2490 axs.color.BLACK_YCC = axs.color.toYCbCr(axs.color.BLACK); -1 2491 axs.color.WHITE = new axs.color.Color(255, 255, 255, 1.0); -1 2492 axs.color.WHITE_YCC = axs.color.toYCbCr(axs.color.WHITE); -1 2493 axs.color.RED = new axs.color.Color(255, 0, 0, 1.0); -1 2494 axs.color.RED_YCC = axs.color.toYCbCr(axs.color.RED); -1 2495 axs.color.GREEN = new axs.color.Color(0, 255, 0, 1.0); -1 2496 axs.color.GREEN_YCC = axs.color.toYCbCr(axs.color.GREEN); -1 2497 axs.color.BLUE = new axs.color.Color(0, 0, 255, 1.0); -1 2498 axs.color.BLUE_YCC = axs.color.toYCbCr(axs.color.BLUE); -1 2499 axs.color.CYAN = new axs.color.Color(0, 255, 255, 1.0); -1 2500 axs.color.CYAN_YCC = axs.color.toYCbCr(axs.color.CYAN); -1 2501 axs.color.MAGENTA = new axs.color.Color(255, 0, 255, 1.0); -1 2502 axs.color.MAGENTA_YCC = axs.color.toYCbCr(axs.color.MAGENTA); -1 2503 axs.color.YELLOW = new axs.color.Color(255, 255, 0, 1.0); -1 2504 axs.color.YELLOW_YCC = axs.color.toYCbCr(axs.color.YELLOW); 3755 25053756 -1 if (element && callbacks.postorder && !found)3757 -1 callbacks.postorder.call(null, element, flags);3758 -1 return found;3759 -1 };-1 2506 axs.color.YCC_CUBE_FACES_BLACK = [ { p0: axs.color.BLACK_YCC, p1: axs.color.RED_YCC, p2: axs.color.GREEN_YCC }, -1 2507 { p0: axs.color.BLACK_YCC, p1: axs.color.GREEN_YCC, p2: axs.color.BLUE_YCC }, -1 2508 { p0: axs.color.BLACK_YCC, p1: axs.color.BLUE_YCC, p2: axs.color.RED_YCC } ]; -1 2509 axs.color.YCC_CUBE_FACES_WHITE = [ { p0: axs.color.WHITE_YCC, p1: axs.color.CYAN_YCC, p2: axs.color.MAGENTA_YCC }, -1 2510 { p0: axs.color.WHITE_YCC, p1: axs.color.MAGENTA_YCC, p2: axs.color.YELLOW_YCC }, -1 2511 { p0: axs.color.WHITE_YCC, p1: axs.color.YELLOW_YCC, p2: axs.color.CYAN_YCC } ]; 3760 25123761 -1 },{}],6:[function(require,module,exports){-1 2513 },{}],9:[function(require,module,exports){ 3762 2514 // Copyright 2012 Google Inc. 3763 2515 // 3764 2516 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -3773,1560 +2525,2817 @@ axs.dom.composedTreeSearch = function(node, end, callbacks, parentFlags, opt_sha 3773 2525 // See the License for the specific language governing permissions and 3774 2526 // limitations under the License. 3775 25273776 -1 goog.require('axs.browserUtils');3777 -1 goog.require('axs.color');3778 -1 goog.require('axs.dom');3779 -1 goog.require('axs.utils');3780 -13781 -1 goog.provide('axs.properties');3782 -13783 -1 /**3784 -1 * @const3785 -1 * @type {string}3786 -1 */3787 -1 axs.properties.TEXT_CONTENT_XPATH = './/text()[normalize-space(.)!=""]/parent::*[name()!="script"]';3788 -13789 -1 /**3790 -1 * @param {Element} element3791 -1 * @return {Object.<string, Object>}3792 -1 */3793 -1 axs.properties.getFocusProperties = function(element) {3794 -1 var focusProperties = {};3795 -1 var tabindex = element.getAttribute('tabindex');3796 -1 if (tabindex != undefined) {3797 -1 focusProperties['tabindex'] = { value: tabindex, valid: true };3798 -1 } else {3799 -1 if (axs.utils.isElementImplicitlyFocusable(element))3800 -1 focusProperties['implicitlyFocusable'] = { value: true, valid: true };3801 -1 }3802 -1 if (Object.keys(focusProperties).length == 0)3803 -1 return null;3804 -1 var transparent = axs.utils.elementIsTransparent(element);3805 -1 var zeroArea = axs.utils.elementHasZeroArea(element);3806 -1 var outsideScrollArea = axs.utils.elementIsOutsideScrollArea(element);3807 -1 var overlappingElements = axs.utils.overlappingElements(element);3808 -1 if (transparent || zeroArea || outsideScrollArea || overlappingElements.length > 0) {3809 -1 var hidden = axs.utils.isElementOrAncestorHidden(element);3810 -1 var visibleProperties = { value: false,3811 -1 valid: hidden };3812 -1 if (transparent)3813 -1 visibleProperties['transparent'] = true;3814 -1 if (zeroArea)3815 -1 visibleProperties['zeroArea'] = true;3816 -1 if (outsideScrollArea)3817 -1 visibleProperties['outsideScrollArea'] = true;3818 -1 if (overlappingElements && overlappingElements.length > 0)3819 -1 visibleProperties['overlappingElements'] = overlappingElements;3820 -1 var hiddenProperties = { value: hidden, valid: hidden };3821 -1 if (hidden)3822 -1 hiddenProperties['reason'] = axs.properties.getHiddenReason(element);3823 -1 visibleProperties['hidden'] = hiddenProperties;3824 -1 focusProperties['visible'] = visibleProperties;3825 -1 } else {3826 -1 focusProperties['visible'] = { value: true, valid: true };3827 -1 }3828 -13829 -1 return focusProperties;3830 -1 };3831 -13832 -1 /**3833 -1 * @typedef {{ property: string,3834 -1 * on: Element }}3835 -1 *3836 -1 * property examples: 'display: none', 'visibility: hidden', 'aria-hidden'3837 -1 */3838 -1 axs.properties.hiddenReason;3839 -13840 -1 /**3841 -1 * Determine the reason an element is not visible.3842 -1 * Will give the CSS rule or attribute and the element/ancestor it is set on.3843 -1 * @param {Element} element3844 -1 * @return {?axs.properties.hiddenReason}3845 -1 */3846 -1 axs.properties.getHiddenReason = function(element) {3847 -1 if (!element || !(element instanceof element.ownerDocument.defaultView.HTMLElement))3848 -1 return null;3849 -13850 -1 if (element.hasAttribute('chromevoxignoreariahidden'))3851 -1 var chromevoxignoreariahidden = true;3852 -13853 -1 var style = window.getComputedStyle(element, null);3854 -1 if (style.display == 'none')3855 -1 return { 'property': 'display: none',3856 -1 'on': element };3857 -13858 -1 if (style.visibility == 'hidden')3859 -1 return { 'property': 'visibility: hidden',3860 -1 'on': element };-1 2528 goog.provide('axs.constants'); -1 2529 goog.provide('axs.constants.AuditResult'); -1 2530 goog.provide('axs.constants.Severity'); 3861 25313862 -1 if (element.hasAttribute('aria-hidden') &&3863 -1 element.getAttribute('aria-hidden').toLowerCase() == 'true') {3864 -1 if (!chromevoxignoreariahidden)3865 -1 return { 'property': 'aria-hidden',3866 -1 'on': element };-1 2532 /** @type {Object.<string, Object>} */ -1 2533 axs.constants.ARIA_ROLES = { -1 2534 "alert": { -1 2535 "namefrom": [ "author" ], -1 2536 "parent": [ "region" ] -1 2537 }, -1 2538 "alertdialog": { -1 2539 "namefrom": [ "author" ], -1 2540 "namerequired": true, -1 2541 "parent": [ "alert", "dialog" ] -1 2542 }, -1 2543 "application": { -1 2544 "namefrom": [ "author" ], -1 2545 "namerequired": true, -1 2546 "parent": [ "landmark" ] -1 2547 }, -1 2548 "article": { -1 2549 "namefrom": [ "author" ], -1 2550 "parent": [ "document", "region" ] -1 2551 }, -1 2552 "banner": { -1 2553 "namefrom": [ "author" ], -1 2554 "parent": [ "landmark" ] -1 2555 }, -1 2556 "button": { -1 2557 "childpresentational": true, -1 2558 "namefrom": [ "contents", "author" ], -1 2559 "namerequired": true, -1 2560 "parent": [ "command" ], -1 2561 "properties": [ "aria-expanded", "aria-pressed" ] -1 2562 }, -1 2563 "checkbox": { -1 2564 "namefrom": [ "contents", "author" ], -1 2565 "namerequired": true, -1 2566 "parent": [ "input" ], -1 2567 "requiredProperties": [ "aria-checked" ], -1 2568 "properties": [ "aria-checked" ] -1 2569 }, -1 2570 "columnheader": { -1 2571 "namefrom": [ "contents", "author" ], -1 2572 "namerequired": true, -1 2573 "parent": [ "gridcell", "sectionhead", "widget" ], -1 2574 "properties": [ "aria-sort" ], -1 2575 "scope": [ "row" ] -1 2576 }, -1 2577 "combobox": { -1 2578 "mustcontain": [ "listbox", "textbox" ], -1 2579 "namefrom": [ "author" ], -1 2580 "namerequired": true, -1 2581 "parent": [ "select" ], -1 2582 "requiredProperties": [ "aria-expanded" ], -1 2583 "properties": [ "aria-expanded", "aria-autocomplete", "aria-required" ] -1 2584 }, -1 2585 "command": { -1 2586 "abstract": true, -1 2587 "namefrom": [ "author" ], -1 2588 "parent": [ "widget" ] -1 2589 }, -1 2590 "complementary": { -1 2591 "namefrom": [ "author" ], -1 2592 "parent": [ "landmark" ] -1 2593 }, -1 2594 "composite": { -1 2595 "abstract": true, -1 2596 "childpresentational": false, -1 2597 "namefrom": [ "author" ], -1 2598 "parent": [ "widget" ], -1 2599 "properties": [ "aria-activedescendant" ] -1 2600 }, -1 2601 "contentinfo": { -1 2602 "namefrom": [ "author" ], -1 2603 "parent": [ "landmark" ] -1 2604 }, -1 2605 "definition": { -1 2606 "namefrom": [ "author" ], -1 2607 "parent": [ "section" ] -1 2608 }, -1 2609 "dialog": { -1 2610 "namefrom": [ "author" ], -1 2611 "namerequired": true, -1 2612 "parent": [ "window" ] -1 2613 }, -1 2614 "directory": { -1 2615 "namefrom": [ "contents", "author" ], -1 2616 "parent": [ "list" ] -1 2617 }, -1 2618 "document": { -1 2619 "namefrom": [ " author" ], -1 2620 "namerequired": true, -1 2621 "parent": [ "structure" ], -1 2622 "properties": [ "aria-expanded" ] -1 2623 }, -1 2624 "form": { -1 2625 "namefrom": [ "author" ], -1 2626 "parent": [ "landmark" ] -1 2627 }, -1 2628 "grid": { -1 2629 "mustcontain": [ "row", "rowgroup" ], -1 2630 "namefrom": [ "author" ], -1 2631 "namerequired": true, -1 2632 "parent": [ "composite", "region" ], -1 2633 "properties": [ "aria-level", "aria-multiselectable", "aria-readonly" ] -1 2634 }, -1 2635 "gridcell": { -1 2636 "namefrom": [ "contents", "author" ], -1 2637 "namerequired": true, -1 2638 "parent": [ "section", "widget" ], -1 2639 "properties": [ "aria-readonly", "aria-required", "aria-selected" ], -1 2640 "scope": [ "row" ] -1 2641 }, -1 2642 "group": { -1 2643 "namefrom": [ " author" ], -1 2644 "parent": [ "section" ], -1 2645 "properties": [ "aria-activedescendant" ] -1 2646 }, -1 2647 "heading": { -1 2648 "namerequired": true, -1 2649 "parent": [ "sectionhead" ], -1 2650 "properties": [ "aria-level" ] -1 2651 }, -1 2652 "img": { -1 2653 "childpresentational": true, -1 2654 "namefrom": [ "author" ], -1 2655 "namerequired": true, -1 2656 "parent": [ "section" ] -1 2657 }, -1 2658 "input": { -1 2659 "abstract": true, -1 2660 "namefrom": [ "author" ], -1 2661 "parent": [ "widget" ] -1 2662 }, -1 2663 "landmark": { -1 2664 "abstract": true, -1 2665 "namefrom": [ "contents", "author" ], -1 2666 "namerequired": false, -1 2667 "parent": [ "region" ] -1 2668 }, -1 2669 "link": { -1 2670 "namefrom": [ "contents", "author" ], -1 2671 "namerequired": true, -1 2672 "parent": [ "command" ], -1 2673 "properties": [ "aria-expanded" ] -1 2674 }, -1 2675 "list": { -1 2676 "mustcontain": [ "group", "listitem" ], -1 2677 "namefrom": [ "author" ], -1 2678 "parent": [ "region" ] -1 2679 }, -1 2680 "listbox": { -1 2681 "mustcontain": [ "option" ], -1 2682 "namefrom": [ "author" ], -1 2683 "namerequired": true, -1 2684 "parent": [ "list", "select" ], -1 2685 "properties": [ "aria-multiselectable", "aria-required" ] -1 2686 }, -1 2687 "listitem": { -1 2688 "namefrom": [ "contents", "author" ], -1 2689 "namerequired": true, -1 2690 "parent": [ "section" ], -1 2691 "properties": [ "aria-level", "aria-posinset", "aria-setsize" ], -1 2692 "scope": [ "list" ] -1 2693 }, -1 2694 "log": { -1 2695 "namefrom": [ " author" ], -1 2696 "namerequired": true, -1 2697 "parent": [ "region" ] -1 2698 }, -1 2699 "main": { -1 2700 "namefrom": [ "author" ], -1 2701 "parent": [ "landmark" ] -1 2702 }, -1 2703 "marquee": { -1 2704 "namerequired": true, -1 2705 "parent": [ "section" ] -1 2706 }, -1 2707 "math": { -1 2708 "childpresentational": true, -1 2709 "namefrom": [ "author" ], -1 2710 "parent": [ "section" ] -1 2711 }, -1 2712 "menu": { -1 2713 "mustcontain": [ -1 2714 "group", -1 2715 "menuitemradio", -1 2716 "menuitem", -1 2717 "menuitemcheckbox" -1 2718 ], -1 2719 "namefrom": [ "author" ], -1 2720 "namerequired": true, -1 2721 "parent": [ "list", "select" ] -1 2722 }, -1 2723 "menubar": { -1 2724 "namefrom": [ "author" ], -1 2725 "parent": [ "menu" ] -1 2726 }, -1 2727 "menuitem": { -1 2728 "namefrom": [ "contents", "author" ], -1 2729 "namerequired": true, -1 2730 "parent": [ "command" ], -1 2731 "scope": [ "menu", "menubar" ] -1 2732 }, -1 2733 "menuitemcheckbox": { -1 2734 "namefrom": [ "contents", "author" ], -1 2735 "namerequired": true, -1 2736 "parent": [ "checkbox", "menuitem" ], -1 2737 "scope": [ "menu", "menubar" ] -1 2738 }, -1 2739 "menuitemradio": { -1 2740 "namefrom": [ "contents", "author" ], -1 2741 "namerequired": true, -1 2742 "parent": [ "menuitemcheckbox", "radio" ], -1 2743 "scope": [ "menu", "menubar" ] -1 2744 }, -1 2745 "navigation": { -1 2746 "namefrom": [ "author" ], -1 2747 "parent": [ "landmark" ] -1 2748 }, -1 2749 "note": { -1 2750 "namefrom": [ "author" ], -1 2751 "parent": [ "section" ] -1 2752 }, -1 2753 "option": { -1 2754 "namefrom": [ "contents", "author" ], -1 2755 "namerequired": true, -1 2756 "parent": [ "input" ], -1 2757 "properties": [ -1 2758 "aria-checked", -1 2759 "aria-posinset", -1 2760 "aria-selected", -1 2761 "aria-setsize" -1 2762 ] -1 2763 }, -1 2764 "presentation": { -1 2765 "parent": [ "structure" ] -1 2766 }, -1 2767 "progressbar": { -1 2768 "childpresentational": true, -1 2769 "namefrom": [ "author" ], -1 2770 "namerequired": true, -1 2771 "parent": [ "range" ] -1 2772 }, -1 2773 "radio": { -1 2774 "namefrom": [ "contents", "author" ], -1 2775 "namerequired": true, -1 2776 "parent": [ "checkbox", "option" ] -1 2777 }, -1 2778 "radiogroup": { -1 2779 "mustcontain": [ "radio" ], -1 2780 "namefrom": [ "author" ], -1 2781 "namerequired": true, -1 2782 "parent": [ "select" ], -1 2783 "properties": [ "aria-required" ] -1 2784 }, -1 2785 "range": { -1 2786 "abstract": true, -1 2787 "namefrom": [ "author" ], -1 2788 "parent": [ "widget" ], -1 2789 "properties": [ -1 2790 "aria-valuemax", -1 2791 "aria-valuemin", -1 2792 "aria-valuenow", -1 2793 "aria-valuetext" -1 2794 ] -1 2795 }, -1 2796 "region": { -1 2797 "namefrom": [ " author" ], -1 2798 "parent": [ "section" ] -1 2799 }, -1 2800 "roletype": { -1 2801 "abstract": true, -1 2802 "properties": [ -1 2803 "aria-atomic", -1 2804 "aria-busy", -1 2805 "aria-controls", -1 2806 "aria-describedby", -1 2807 "aria-disabled", -1 2808 "aria-dropeffect", -1 2809 "aria-flowto", -1 2810 "aria-grabbed", -1 2811 "aria-haspopup", -1 2812 "aria-hidden", -1 2813 "aria-invalid", -1 2814 "aria-label", -1 2815 "aria-labelledby", -1 2816 "aria-live", -1 2817 "aria-owns", -1 2818 "aria-relevant" -1 2819 ] -1 2820 }, -1 2821 "row": { -1 2822 "mustcontain": [ "columnheader", "gridcell", "rowheader" ], -1 2823 "namefrom": [ "contents", "author" ], -1 2824 "parent": [ "group", "widget" ], -1 2825 "properties": [ "aria-level", "aria-selected" ], -1 2826 "scope": [ "grid", "rowgroup", "treegrid" ] -1 2827 }, -1 2828 "rowgroup": { -1 2829 "mustcontain": [ "row" ], -1 2830 "namefrom": [ "contents", "author" ], -1 2831 "parent": [ "group" ], -1 2832 "scope": [ "grid" ] -1 2833 }, -1 2834 "rowheader": { -1 2835 "namefrom": [ "contents", "author" ], -1 2836 "namerequired": true, -1 2837 "parent": [ "gridcell", "sectionhead", "widget" ], -1 2838 "properties": [ "aria-sort" ], -1 2839 "scope": [ "row" ] -1 2840 }, -1 2841 "search": { -1 2842 "namefrom": [ "author" ], -1 2843 "parent": [ "landmark" ] -1 2844 }, -1 2845 "section": { -1 2846 "abstract": true, -1 2847 "namefrom": [ "contents", "author" ], -1 2848 "parent": [ "structure" ], -1 2849 "properties": [ "aria-expanded" ] -1 2850 }, -1 2851 "sectionhead": { -1 2852 "abstract": true, -1 2853 "namefrom": [ "contents", "author" ], -1 2854 "parent": [ "structure" ], -1 2855 "properties": [ "aria-expanded" ] -1 2856 }, -1 2857 "select": { -1 2858 "abstract": true, -1 2859 "namefrom": [ "author" ], -1 2860 "parent": [ "composite", "group", "input" ] -1 2861 }, -1 2862 "separator": { -1 2863 "childpresentational": true, -1 2864 "namefrom": [ "author" ], -1 2865 "parent": [ "structure" ], -1 2866 "properties": [ "aria-expanded", "aria-orientation" ] -1 2867 }, -1 2868 "scrollbar": { -1 2869 "childpresentational": true, -1 2870 "namefrom": [ "author" ], -1 2871 "namerequired": false, -1 2872 "parent": [ "input", "range" ], -1 2873 "requiredProperties": [ -1 2874 "aria-controls", -1 2875 "aria-orientation", -1 2876 "aria-valuemax", -1 2877 "aria-valuemin", -1 2878 "aria-valuenow" -1 2879 ], -1 2880 "properties": [ -1 2881 "aria-controls", -1 2882 "aria-orientation", -1 2883 "aria-valuemax", -1 2884 "aria-valuemin", -1 2885 "aria-valuenow" -1 2886 ] -1 2887 }, -1 2888 "slider": { -1 2889 "childpresentational": true, -1 2890 "namefrom": [ "author" ], -1 2891 "namerequired": true, -1 2892 "parent": [ "input", "range" ], -1 2893 "requiredProperties": [ "aria-valuemax", "aria-valuemin", "aria-valuenow" ], -1 2894 "properties": [ -1 2895 "aria-valuemax", -1 2896 "aria-valuemin", -1 2897 "aria-valuenow", -1 2898 "aria-orientation" -1 2899 ] -1 2900 }, -1 2901 "spinbutton": { -1 2902 "namefrom": [ "author" ], -1 2903 "namerequired": true, -1 2904 "parent": [ "input", "range" ], -1 2905 "requiredProperties": [ "aria-valuemax", "aria-valuemin", "aria-valuenow" ], -1 2906 "properties": [ -1 2907 "aria-valuemax", -1 2908 "aria-valuemin", -1 2909 "aria-valuenow", -1 2910 "aria-required" -1 2911 ] -1 2912 }, -1 2913 "status": { -1 2914 "parent": [ "region" ] -1 2915 }, -1 2916 "structure": { -1 2917 "abstract": true, -1 2918 "parent": [ "roletype" ] -1 2919 }, -1 2920 "tab": { -1 2921 "namefrom": [ "contents", "author" ], -1 2922 "parent": [ "sectionhead", "widget" ], -1 2923 "properties": [ "aria-selected" ], -1 2924 "scope": [ "tablist" ] -1 2925 }, -1 2926 "tablist": { -1 2927 "mustcontain": [ "tab" ], -1 2928 "namefrom": [ "author" ], -1 2929 "parent": [ "composite", "directory" ], -1 2930 "properties": [ "aria-level" ] -1 2931 }, -1 2932 "tabpanel": { -1 2933 "namefrom": [ "author" ], -1 2934 "namerequired": true, -1 2935 "parent": [ "region" ] -1 2936 }, -1 2937 "textbox": { -1 2938 "namefrom": [ "author" ], -1 2939 "namerequired": true, -1 2940 "parent": [ "input" ], -1 2941 "properties": [ -1 2942 "aria-activedescendant", -1 2943 "aria-autocomplete", -1 2944 "aria-multiline", -1 2945 "aria-readonly", -1 2946 "aria-required" -1 2947 ] -1 2948 }, -1 2949 "timer": { -1 2950 "namefrom": [ "author" ], -1 2951 "namerequired": true, -1 2952 "parent": [ "status" ] -1 2953 }, -1 2954 "toolbar": { -1 2955 "namefrom": [ "author" ], -1 2956 "parent": [ "group" ] -1 2957 }, -1 2958 "tooltip": { -1 2959 "namerequired": true, -1 2960 "parent": [ "section" ] -1 2961 }, -1 2962 "tree": { -1 2963 "mustcontain": [ "group", "treeitem" ], -1 2964 "namefrom": [ "author" ], -1 2965 "namerequired": true, -1 2966 "parent": [ "select" ], -1 2967 "properties": [ "aria-multiselectable", "aria-required" ] -1 2968 }, -1 2969 "treegrid": { -1 2970 "mustcontain": [ "row" ], -1 2971 "namefrom": [ "author" ], -1 2972 "namerequired": true, -1 2973 "parent": [ "grid", "tree" ] -1 2974 }, -1 2975 "treeitem": { -1 2976 "namefrom": [ "contents", "author" ], -1 2977 "namerequired": true, -1 2978 "parent": [ "listitem", "option" ], -1 2979 "scope": [ "group", "tree" ] -1 2980 }, -1 2981 "widget": { -1 2982 "abstract": true, -1 2983 "parent": [ "roletype" ] -1 2984 }, -1 2985 "window": { -1 2986 "abstract": true, -1 2987 "namefrom": [ " author" ], -1 2988 "parent": [ "roletype" ], -1 2989 "properties": [ "aria-expanded" ] 3867 2990 }3868 -13869 -1 return axs.properties.getHiddenReason(axs.dom.parentElement(element));3870 -1 };3871 -13872 -13873 -1 /**3874 -1 * @param {Element} element3875 -1 * @return {Object.<string, Object>}3876 -1 */3877 -1 axs.properties.getColorProperties = function(element) {3878 -1 var colorProperties = {};3879 -1 var contrastRatioProperties =3880 -1 axs.properties.getContrastRatioProperties(element);3881 -1 if (contrastRatioProperties)3882 -1 colorProperties['contrastRatio'] = contrastRatioProperties;3883 -1 if (Object.keys(colorProperties).length == 0)3884 -1 return null;3885 -1 return colorProperties;3886 2991 }; 3887 29923888 -1 /**3889 -1 * Determines whether the given element has a text node as a direct descendant.3890 -1 * @param {Element} element3891 -1 * @return {boolean}3892 -1 */3893 -1 axs.properties.hasDirectTextDescendant = function(element) {3894 -1 var ownerDocument;3895 -1 if (element.nodeType == Node.DOCUMENT_NODE)3896 -1 ownerDocument = element;3897 -1 else3898 -1 ownerDocument = element.ownerDocument;3899 -1 if (ownerDocument.evaluate) {3900 -1 return hasDirectTextDescendantXpath();3901 -1 }3902 -1 return hasDirectTextDescendantTreeWalker();3903 -13904 -1 /**3905 -1 * Determines whether element has a text node as a direct descendant.3906 -1 * This method uses XPath on HTML DOM which is not universally supported.3907 -1 * @return {boolean}3908 -1 */3909 -1 function hasDirectTextDescendantXpath() {3910 -1 var selectorResults = ownerDocument.evaluate(axs.properties.TEXT_CONTENT_XPATH,3911 -1 element,3912 -1 null,3913 -1 XPathResult.ANY_TYPE,3914 -1 null);3915 -1 for (var resultElement = selectorResults.iterateNext();3916 -1 resultElement != null;3917 -1 resultElement = selectorResults.iterateNext()) {3918 -1 if (resultElement !== element)3919 -1 continue;3920 -1 return true;3921 -1 }3922 -1 return false;3923 -1 }3924 -13925 -1 /**3926 -1 * Determines whether element has a text node as a direct descendant.3927 -1 * This method uses TreeWalker as a fallback (at time of writing no version3928 -1 * of IE (including IE11) supports XPath in the HTML DOM).3929 -1 * @return {boolean}3930 -1 */3931 -1 function hasDirectTextDescendantTreeWalker() {3932 -1 var treeWalker = ownerDocument.createTreeWalker(element,3933 -1 NodeFilter.SHOW_TEXT,3934 -1 null,3935 -1 false);3936 -1 while (treeWalker.nextNode()) {3937 -1 var resultElement = treeWalker.currentNode;3938 -1 var parent = resultElement.parentNode;3939 -1 // Handle elements hosted in <template>.content.3940 -1 parent = parent.host || parent;3941 -1 var tagName = parent.tagName.toLowerCase();3942 -1 var value = resultElement.nodeValue.trim();3943 -1 if (value && tagName !== 'script' && element !== resultElement)3944 -1 return true;3945 -1 }3946 -1 return false;3947 -1 }3948 -1 };-1 2993 axs.constants.WIDGET_ROLES = {}; 3949 2994 3950 2995 /**3951 -1 * @param {Element} element3952 -1 * @return {Object.<string, Object>}-1 2996 * Squashes the parent hierarchy on to role object. -1 2997 * @param {Object} role -1 2998 * @param {Object} set -1 2999 * @private 3953 3000 */3954 -1 axs.properties.getContrastRatioProperties = function(element) {3955 -1 if (!axs.properties.hasDirectTextDescendant(element))3956 -1 return null;3957 -13958 -1 var contrastRatioProperties = {};3959 -1 var style = window.getComputedStyle(element, null);3960 -1 var bgColor = axs.utils.getBgColor(style, element);3961 -1 if (!bgColor)3962 -1 return null;3963 -13964 -1 contrastRatioProperties['backgroundColor'] = axs.color.colorToString(bgColor);3965 -1 var fgColor = axs.utils.getFgColor(style, element, bgColor);3966 -1 contrastRatioProperties['foregroundColor'] = axs.color.colorToString(fgColor);3967 -1 var contrast = axs.utils.getContrastRatioForElementWithComputedStyle(style, element);3968 -1 if (!contrast)3969 -1 return null;3970 -1 contrastRatioProperties['value'] = contrast.toFixed(2);3971 -1 if (axs.utils.isLowContrast(contrast, style))3972 -1 contrastRatioProperties['alert'] = true;3973 -13974 -1 var levelAAContrast = axs.utils.isLargeFont(style) ? 3.0 : 4.5;3975 -1 var levelAAAContrast = axs.utils.isLargeFont(style) ? 4.5 : 7.0;3976 -1 var desiredContrastRatios = {};3977 -1 if (levelAAContrast > contrast)3978 -1 desiredContrastRatios['AA'] = levelAAContrast;3979 -1 if (levelAAAContrast > contrast)3980 -1 desiredContrastRatios['AAA'] = levelAAAContrast;3981 -13982 -1 if (!Object.keys(desiredContrastRatios).length)3983 -1 return contrastRatioProperties;3984 -13985 -1 var suggestedColors = axs.color.suggestColors(bgColor, fgColor, desiredContrastRatios);3986 -1 if (suggestedColors && Object.keys(suggestedColors).length)3987 -1 contrastRatioProperties['suggestedColors'] = suggestedColors;3988 -1 return contrastRatioProperties;-1 3001 axs.constants.addAllParentRolesToSet_ = function(role, set) { -1 3002 if (!role['parent']) -1 3003 return; -1 3004 var parents = role['parent']; -1 3005 for (var j = 0; j < parents.length; j++) { -1 3006 var parentRoleName = parents[j]; -1 3007 set[parentRoleName] = true; -1 3008 axs.constants.addAllParentRolesToSet_( -1 3009 axs.constants.ARIA_ROLES[parentRoleName], set); -1 3010 } 3989 3011 }; 3990 3012 3991 3013 /**3992 -1 * @param {Node} node3993 -1 * @param {!Object} textAlternatives The properties object to fill in3994 -1 * @param {boolean=} opt_recursive Whether this is a recursive call or not3995 -1 * @param {boolean=} opt_force Whether to return text alternatives for this3996 -1 * element regardless of its hidden state.3997 -1 * @return {?string} The calculated text alternative for the given element-1 3014 * Adds all properties and requiredProperties from parent hierarchy. -1 3015 * @param {Object} role -1 3016 * @param {string} propertiesName -1 3017 * @param {Object} propertiesSet -1 3018 * @private 3998 3019 */3999 -1 axs.properties.findTextAlternatives = function(node, textAlternatives, opt_recursive, opt_force) {4000 -1 var recursive = opt_recursive || false;4001 -14002 -1 /** @type {Element} */ var element = axs.dom.asElement(node);4003 -1 if (!element)4004 -1 return null;4005 -14006 -1 // 1. Skip hidden elements unless the author specifies to use them via an aria-labelledby or4007 -1 // aria-describedby being used in the current computation.4008 -1 if (!opt_force && axs.utils.isElementOrAncestorHidden(element))4009 -1 return null;4010 -14011 -1 // if this is a text node, just return text content.4012 -1 if (node.nodeType == Node.TEXT_NODE) {4013 -1 var textContentValue = {};4014 -1 textContentValue.type = 'text';4015 -1 textContentValue.text = node.textContent;4016 -1 textContentValue.lastWord = axs.properties.getLastWord(textContentValue.text);4017 -1 textAlternatives['content'] = textContentValue;4018 -14019 -1 return node.textContent;-1 3020 axs.constants.addAllPropertiesToSet_ = function(role, propertiesName, -1 3021 propertiesSet) { -1 3022 var properties = role[propertiesName]; -1 3023 if (properties) { -1 3024 for (var i = 0; i < properties.length; i++) -1 3025 propertiesSet[properties[i]] = true; -1 3026 } -1 3027 if (role['parent']) { -1 3028 var parents = role['parent']; -1 3029 for (var j = 0; j < parents.length; j++) { -1 3030 var parentRoleName = parents[j]; -1 3031 axs.constants.addAllPropertiesToSet_( -1 3032 axs.constants.ARIA_ROLES[parentRoleName], propertiesName, -1 3033 propertiesSet); 4020 3034 } -1 3035 } -1 3036 }; 4021 30374022 -1 var computedName = null;-1 3038 // TODO make a AriaRole object etc. -1 3039 for (var roleName in axs.constants.ARIA_ROLES) { -1 3040 var role = axs.constants.ARIA_ROLES[roleName]; 4023 30414024 -1 if (!recursive) {4025 -1 // 2A. The aria-labelledby attribute takes precedence as the element's text alternative4026 -1 // unless this computation is already occurring as the result of a recursive aria-labelledby4027 -1 // declaration.4028 -1 computedName = axs.properties.getTextFromAriaLabelledby(element, textAlternatives);4029 -1 }-1 3042 var propertiesSet = {}; -1 3043 axs.constants.addAllPropertiesToSet_(role, 'properties', propertiesSet); -1 3044 role['propertiesSet'] = propertiesSet; 4030 30454031 -1 // 2A. If aria-labelledby is empty or undefined, the aria-label attribute, which defines an4032 -1 // explicit text string, is used.4033 -1 if (element.hasAttribute('aria-label')) {4034 -1 var ariaLabelValue = {};4035 -1 ariaLabelValue.type = 'text';4036 -1 ariaLabelValue.text = element.getAttribute('aria-label');4037 -1 ariaLabelValue.lastWord = axs.properties.getLastWord(ariaLabelValue.text);4038 -1 if (computedName)4039 -1 ariaLabelValue.unused = true;4040 -1 else if (!(recursive && axs.utils.elementIsHtmlControl(element)))4041 -1 computedName = ariaLabelValue.text;4042 -1 textAlternatives['ariaLabel'] = ariaLabelValue;4043 -1 }-1 3046 var requiredPropertiesSet = {}; -1 3047 axs.constants.addAllPropertiesToSet_(role, 'requiredProperties', requiredPropertiesSet); -1 3048 role['requiredPropertiesSet'] = requiredPropertiesSet; -1 3049 var parentRolesSet = {}; -1 3050 axs.constants.addAllParentRolesToSet_(role, parentRolesSet); -1 3051 role['allParentRolesSet'] = parentRolesSet; -1 3052 if ('widget' in parentRolesSet) -1 3053 axs.constants.WIDGET_ROLES[roleName] = role; -1 3054 } 4044 30554045 -1 // 2A. If aria-labelledby and aria-label are both empty or undefined, and if the element is not4046 -1 // marked as presentational (role="presentation", check for the presence of an equivalent host4047 -1 // language attribute or element for associating a label, and use those mechanisms to determine4048 -1 // a text alternative.4049 -1 if (!element.hasAttribute('role') || element.getAttribute('role') != 'presentation') {4050 -1 computedName = axs.properties.getTextFromHostLanguageAttributes(element,4051 -1 textAlternatives,4052 -1 computedName,4053 -1 recursive);-1 3056 // BEGIN ARIA_PROPERTIES_AUTOGENERATED -1 3057 /** @type {Object.<string, Object>} */ -1 3058 axs.constants.ARIA_PROPERTIES = { -1 3059 "activedescendant": { -1 3060 "type": "property", -1 3061 "valueType": "idref" -1 3062 }, -1 3063 "atomic": { -1 3064 "defaultValue": "false", -1 3065 "type": "property", -1 3066 "valueType": "boolean" -1 3067 }, -1 3068 "autocomplete": { -1 3069 "defaultValue": "none", -1 3070 "type": "property", -1 3071 "valueType": "token", -1 3072 "values": [ -1 3073 "inline", -1 3074 "list", -1 3075 "both", -1 3076 "none" -1 3077 ] -1 3078 }, -1 3079 "busy": { -1 3080 "defaultValue": "false", -1 3081 "type": "state", -1 3082 "valueType": "boolean" -1 3083 }, -1 3084 "checked": { -1 3085 "defaultValue": "undefined", -1 3086 "type": "state", -1 3087 "valueType": "token", -1 3088 "values": [ -1 3089 "true", -1 3090 "false", -1 3091 "mixed", -1 3092 "undefined" -1 3093 ] -1 3094 }, -1 3095 "controls": { -1 3096 "type": "property", -1 3097 "valueType": "idref_list" -1 3098 }, -1 3099 "describedby": { -1 3100 "type": "property", -1 3101 "valueType": "idref_list" -1 3102 }, -1 3103 "disabled": { -1 3104 "defaultValue": "false", -1 3105 "type": "state", -1 3106 "valueType": "boolean" -1 3107 }, -1 3108 "dropeffect": { -1 3109 "defaultValue": "none", -1 3110 "type": "property", -1 3111 "valueType": "token_list", -1 3112 "values": [ -1 3113 "copy", -1 3114 "move", -1 3115 "link", -1 3116 "execute", -1 3117 "popup", -1 3118 "none" -1 3119 ] -1 3120 }, -1 3121 "expanded": { -1 3122 "defaultValue": "undefined", -1 3123 "type": "state", -1 3124 "valueType": "token", -1 3125 "values": [ -1 3126 "true", -1 3127 "false", -1 3128 "undefined" -1 3129 ] -1 3130 }, -1 3131 "flowto": { -1 3132 "type": "property", -1 3133 "valueType": "idref_list" -1 3134 }, -1 3135 "grabbed": { -1 3136 "defaultValue": "undefined", -1 3137 "type": "state", -1 3138 "valueType": "token", -1 3139 "values": [ -1 3140 "true", -1 3141 "false", -1 3142 "undefined" -1 3143 ] -1 3144 }, -1 3145 "haspopup": { -1 3146 "defaultValue": "false", -1 3147 "type": "property", -1 3148 "valueType": "boolean" -1 3149 }, -1 3150 "hidden": { -1 3151 "defaultValue": "false", -1 3152 "type": "state", -1 3153 "valueType": "boolean" -1 3154 }, -1 3155 "invalid": { -1 3156 "defaultValue": "false", -1 3157 "type": "state", -1 3158 "valueType": "token", -1 3159 "values": [ -1 3160 "grammar", -1 3161 "false", -1 3162 "spelling", -1 3163 "true" -1 3164 ] -1 3165 }, -1 3166 "label": { -1 3167 "type": "property", -1 3168 "valueType": "string" -1 3169 }, -1 3170 "labelledby": { -1 3171 "type": "property", -1 3172 "valueType": "idref_list" -1 3173 }, -1 3174 "level": { -1 3175 "type": "property", -1 3176 "valueType": "integer" -1 3177 }, -1 3178 "live": { -1 3179 "defaultValue": "off", -1 3180 "type": "property", -1 3181 "valueType": "token", -1 3182 "values": [ -1 3183 "off", -1 3184 "polite", -1 3185 "assertive" -1 3186 ] -1 3187 }, -1 3188 "multiline": { -1 3189 "defaultValue": "false", -1 3190 "type": "property", -1 3191 "valueType": "boolean" -1 3192 }, -1 3193 "multiselectable": { -1 3194 "defaultValue": "false", -1 3195 "type": "property", -1 3196 "valueType": "boolean" -1 3197 }, -1 3198 "orientation": { -1 3199 "defaultValue": "vertical", -1 3200 "type": "property", -1 3201 "valueType": "token", -1 3202 "values": [ -1 3203 "horizontal", -1 3204 "vertical" -1 3205 ] -1 3206 }, -1 3207 "owns": { -1 3208 "type": "property", -1 3209 "valueType": "idref_list" -1 3210 }, -1 3211 "posinset": { -1 3212 "type": "property", -1 3213 "valueType": "integer" -1 3214 }, -1 3215 "pressed": { -1 3216 "defaultValue": "undefined", -1 3217 "type": "state", -1 3218 "valueType": "token", -1 3219 "values": [ -1 3220 "true", -1 3221 "false", -1 3222 "mixed", -1 3223 "undefined" -1 3224 ] -1 3225 }, -1 3226 "readonly": { -1 3227 "defaultValue": "false", -1 3228 "type": "property", -1 3229 "valueType": "boolean" -1 3230 }, -1 3231 "relevant": { -1 3232 "defaultValue": "additions text", -1 3233 "type": "property", -1 3234 "valueType": "token_list", -1 3235 "values": [ -1 3236 "additions", -1 3237 "removals", -1 3238 "text", -1 3239 "all" -1 3240 ] -1 3241 }, -1 3242 "required": { -1 3243 "defaultValue": "false", -1 3244 "type": "property", -1 3245 "valueType": "boolean" -1 3246 }, -1 3247 "selected": { -1 3248 "defaultValue": "undefined", -1 3249 "type": "state", -1 3250 "valueType": "token", -1 3251 "values": [ -1 3252 "true", -1 3253 "false", -1 3254 "undefined" -1 3255 ] -1 3256 }, -1 3257 "setsize": { -1 3258 "type": "property", -1 3259 "valueType": "integer" -1 3260 }, -1 3261 "sort": { -1 3262 "defaultValue": "none", -1 3263 "type": "property", -1 3264 "valueType": "token", -1 3265 "values": [ -1 3266 "ascending", -1 3267 "descending", -1 3268 "none", -1 3269 "other" -1 3270 ] -1 3271 }, -1 3272 "valuemax": { -1 3273 "type": "property", -1 3274 "valueType": "decimal" -1 3275 }, -1 3276 "valuemin": { -1 3277 "type": "property", -1 3278 "valueType": "decimal" -1 3279 }, -1 3280 "valuenow": { -1 3281 "type": "property", -1 3282 "valueType": "decimal" -1 3283 }, -1 3284 "valuetext": { -1 3285 "type": "property", -1 3286 "valueType": "string" 4054 3287 } -1 3288 }; -1 3289 // END ARIA_PROPERTIES_AUTOGENERATED 4055 32904056 -1 // 2B (HTML version).4057 -1 if (recursive && axs.utils.elementIsHtmlControl(element)) {4058 -1 var defaultView = element.ownerDocument.defaultView;-1 3291 (function() { -1 3292 // pull values lists into sets -1 3293 for (var propertyName in axs.constants.ARIA_PROPERTIES) { -1 3294 var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyName]; -1 3295 if (!propertyDetails.values) -1 3296 continue; -1 3297 var valuesSet = {}; -1 3298 for (var i = 0; i < propertyDetails.values.length; i++) -1 3299 valuesSet[propertyDetails.values[i]] = true; -1 3300 propertyDetails.valuesSet = valuesSet; -1 3301 } -1 3302 })(); 4059 33034060 -1 // include the value of the embedded control as part of the text alternative in the4061 -1 // following manner:4062 -1 if (element instanceof defaultView.HTMLInputElement) {4063 -1 // If the embedded control is a text field, use its value.4064 -1 var inputElement = /** @type {HTMLInputElement} */ (element);4065 -1 if (inputElement.type == 'text') {4066 -1 if (inputElement.value && inputElement.value.length > 0)4067 -1 textAlternatives['controlValue'] = { 'text': inputElement.value };4068 -1 }4069 -1 // If the embedded control is a range (e.g. a spinbutton or slider), use the value of the4070 -1 // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow4071 -1 // attribute.4072 -1 if (inputElement.type == 'range')4073 -1 textAlternatives['controlValue'] = { 'text': inputElement.value };4074 -1 }4075 -1 // If the embedded control is a menu, use the text alternative of the chosen menu item.4076 -1 // If the embedded control is a select or combobox, use the chosen option.4077 -1 if (element instanceof defaultView.HTMLSelectElement) {4078 -1 var inputElement = /** @type {HTMLSelectElement} */ (element);4079 -1 textAlternatives['controlValue'] = { 'text': inputElement.value };4080 -1 }-1 3304 /** -1 3305 * All of the states and properties which apply globally. -1 3306 * @type {Object<!string, !boolean>} -1 3307 */ -1 3308 axs.constants.GLOBAL_PROPERTIES = axs.constants.ARIA_ROLES['roletype'].propertiesSet; 4081 33094082 -1 if (textAlternatives['controlValue']) {4083 -1 var controlValue = textAlternatives['controlValue'];4084 -1 if (computedName)4085 -1 controlValue.unused = true;4086 -1 else4087 -1 computedName = controlValue.text;4088 -1 }4089 -1 }-1 3310 /** -1 3311 * A constant indicating no role name. -1 3312 * @type {string} -1 3313 */ -1 3314 axs.constants.NO_ROLE_NAME = ' '; 4090 33154091 -1 // 2B (ARIA version).4092 -1 if (recursive && axs.utils.elementIsAriaWidget(element)) {4093 -1 var role = element.getAttribute('role');4094 -1 // If the embedded control is a text field, use its value.4095 -1 if (role == 'textbox') {4096 -1 if (element.textContent && element.textContent.length > 0)4097 -1 textAlternatives['controlValue'] = { 'text': element.textContent };4098 -1 }4099 -1 // If the embedded control is a range (e.g. a spinbutton or slider), use the value of the4100 -1 // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow4101 -1 // attribute.4102 -1 if (role == 'slider' || role == 'spinbutton') {4103 -1 if (element.hasAttribute('aria-valuetext'))4104 -1 textAlternatives['controlValue'] = { 'text': element.getAttribute('aria-valuetext') };4105 -1 else if (element.hasAttribute('aria-valuenow'))4106 -1 textAlternatives['controlValue'] = { 'value': element.getAttribute('aria-valuenow'),4107 -1 'text': '' + element.getAttribute('aria-valuenow') };4108 -1 }4109 -1 // If the embedded control is a menu, use the text alternative of the chosen menu item.4110 -1 if (role == 'menu') {4111 -1 var menuitems = element.querySelectorAll('[role=menuitemcheckbox], [role=menuitemradio]');4112 -1 var selectedMenuitems = [];4113 -1 for (var i = 0; i < menuitems.length; i++) {4114 -1 if (menuitems[i].getAttribute('aria-checked') == 'true')4115 -1 selectedMenuitems.push(menuitems[i]);4116 -1 }4117 -1 if (selectedMenuitems.length > 0) {4118 -1 var selectedMenuText = '';4119 -1 for (var i = 0; i < selectedMenuitems.length; i++) {4120 -1 selectedMenuText += axs.properties.findTextAlternatives(selectedMenuitems[i], {}, true);4121 -1 if (i < selectedMenuitems.length - 1)4122 -1 selectedMenuText += ', ';4123 -1 }4124 -1 textAlternatives['controlValue'] = { 'text': selectedMenuText };4125 -1 }4126 -1 }4127 -1 // If the embedded control is a select or combobox, use the chosen option.4128 -1 if (role == 'combobox' || role == 'select') {4129 -1 // TODO4130 -1 textAlternatives['controlValue'] = { 'text': 'TODO' };4131 -1 }-1 3316 /** -1 3317 * A mapping from ARIA role names to their message ids. -1 3318 * Copied from ChromeVox: -1 3319 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js -1 3320 * @type {Object.<string, string>} -1 3321 */ -1 3322 axs.constants.WIDGET_ROLE_TO_NAME = { -1 3323 'alert' : 'aria_role_alert', -1 3324 'alertdialog' : 'aria_role_alertdialog', -1 3325 'button' : 'aria_role_button', -1 3326 'checkbox' : 'aria_role_checkbox', -1 3327 'columnheader' : 'aria_role_columnheader', -1 3328 'combobox' : 'aria_role_combobox', -1 3329 'dialog' : 'aria_role_dialog', -1 3330 'grid' : 'aria_role_grid', -1 3331 'gridcell' : 'aria_role_gridcell', -1 3332 'link' : 'aria_role_link', -1 3333 'listbox' : 'aria_role_listbox', -1 3334 'log' : 'aria_role_log', -1 3335 'marquee' : 'aria_role_marquee', -1 3336 'menu' : 'aria_role_menu', -1 3337 'menubar' : 'aria_role_menubar', -1 3338 'menuitem' : 'aria_role_menuitem', -1 3339 'menuitemcheckbox' : 'aria_role_menuitemcheckbox', -1 3340 'menuitemradio' : 'aria_role_menuitemradio', -1 3341 'option' : axs.constants.NO_ROLE_NAME, -1 3342 'progressbar' : 'aria_role_progressbar', -1 3343 'radio' : 'aria_role_radio', -1 3344 'radiogroup' : 'aria_role_radiogroup', -1 3345 'rowheader' : 'aria_role_rowheader', -1 3346 'scrollbar' : 'aria_role_scrollbar', -1 3347 'slider' : 'aria_role_slider', -1 3348 'spinbutton' : 'aria_role_spinbutton', -1 3349 'status' : 'aria_role_status', -1 3350 'tab' : 'aria_role_tab', -1 3351 'tabpanel' : 'aria_role_tabpanel', -1 3352 'textbox' : 'aria_role_textbox', -1 3353 'timer' : 'aria_role_timer', -1 3354 'toolbar' : 'aria_role_toolbar', -1 3355 'tooltip' : 'aria_role_tooltip', -1 3356 'treeitem' : 'aria_role_treeitem' -1 3357 }; 4132 33584133 -1 if (textAlternatives['controlValue']) {4134 -1 var controlValue = textAlternatives['controlValue'];4135 -1 if (computedName)4136 -1 controlValue.unused = true;4137 -1 else4138 -1 computedName = controlValue.text;4139 -1 }4140 -1 }4141 33594142 -1 // 2C. Otherwise, if the attributes checked in rules A and B didn't provide results, text is4143 -1 // collected from descendant content if the current element's role allows "Name From: contents."4144 -1 var hasRole = element.hasAttribute('role');4145 -1 var canGetNameFromContents = true;4146 -1 if (hasRole) {4147 -1 var roleName = element.getAttribute('role');4148 -1 // if element has a role, check that it allows "Name From: contents"4149 -1 var role = axs.constants.ARIA_ROLES[roleName];4150 -1 if (role && (!role.namefrom || role.namefrom.indexOf('contents') < 0))4151 -1 canGetNameFromContents = false;4152 -1 }4153 -1 var textFromContent = axs.properties.getTextFromDescendantContent(element, opt_force);4154 -1 if (textFromContent && canGetNameFromContents) {4155 -1 var textFromContentValue = {};4156 -1 textFromContentValue.type = 'text';4157 -1 textFromContentValue.text = textFromContent;4158 -1 textFromContentValue.lastWord = axs.properties.getLastWord(textFromContentValue.text);4159 -1 if (computedName)4160 -1 textFromContentValue.unused = true;4161 -1 else4162 -1 computedName = textFromContent;4163 -1 textAlternatives['content'] = textFromContentValue;4164 -1 }-1 3360 /** -1 3361 * @type {Object.<string, string>} -1 3362 * Copied from ChromeVox: -1 3363 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js -1 3364 */ -1 3365 axs.constants.STRUCTURE_ROLE_TO_NAME = { -1 3366 'article' : 'aria_role_article', -1 3367 'application' : 'aria_role_application', -1 3368 'banner' : 'aria_role_banner', -1 3369 'columnheader' : 'aria_role_columnheader', -1 3370 'complementary' : 'aria_role_complementary', -1 3371 'contentinfo' : 'aria_role_contentinfo', -1 3372 'definition' : 'aria_role_definition', -1 3373 'directory' : 'aria_role_directory', -1 3374 'document' : 'aria_role_document', -1 3375 'form' : 'aria_role_form', -1 3376 'group' : 'aria_role_group', -1 3377 'heading' : 'aria_role_heading', -1 3378 'img' : 'aria_role_img', -1 3379 'list' : 'aria_role_list', -1 3380 'listitem' : 'aria_role_listitem', -1 3381 'main' : 'aria_role_main', -1 3382 'math' : 'aria_role_math', -1 3383 'navigation' : 'aria_role_navigation', -1 3384 'note' : 'aria_role_note', -1 3385 'region' : 'aria_role_region', -1 3386 'rowheader' : 'aria_role_rowheader', -1 3387 'search' : 'aria_role_search', -1 3388 'separator' : 'aria_role_separator' -1 3389 }; 4165 33904166 -1 // 2D. The last resort is to use text from a tooltip attribute (such as the title attribute in4167 -1 // HTML). This is used only if nothing else, including subtree content, has provided results.4168 -1 if (element.hasAttribute('title')) {4169 -1 var titleValue = {};4170 -1 titleValue.type = 'string';4171 -1 titleValue.valid = true;4172 -1 titleValue.text = element.getAttribute('title');4173 -1 titleValue.lastWord = axs.properties.getLastWord(titleValue.lastWord);4174 -1 if (computedName)4175 -1 titleValue.unused = true;4176 -1 else4177 -1 computedName = titleValue.text;4178 -1 textAlternatives['title'] = titleValue;4179 -1 }4180 33914181 -1 if (Object.keys(textAlternatives).length == 0 && computedName == null)4182 -1 return null;-1 3392 /** -1 3393 * @type {Array.<Object>} -1 3394 * Copied from ChromeVox: -1 3395 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js -1 3396 */ -1 3397 axs.constants.ATTRIBUTE_VALUE_TO_STATUS = [ -1 3398 { name: 'aria-autocomplete', values: -1 3399 {'inline' : 'aria_autocomplete_inline', -1 3400 'list' : 'aria_autocomplete_list', -1 3401 'both' : 'aria_autocomplete_both'} }, -1 3402 { name: 'aria-checked', values: -1 3403 {'true' : 'aria_checked_true', -1 3404 'false' : 'aria_checked_false', -1 3405 'mixed' : 'aria_checked_mixed'} }, -1 3406 { name: 'aria-disabled', values: -1 3407 {'true' : 'aria_disabled_true'} }, -1 3408 { name: 'aria-expanded', values: -1 3409 {'true' : 'aria_expanded_true', -1 3410 'false' : 'aria_expanded_false'} }, -1 3411 { name: 'aria-invalid', values: -1 3412 {'true' : 'aria_invalid_true', -1 3413 'grammar' : 'aria_invalid_grammar', -1 3414 'spelling' : 'aria_invalid_spelling'} }, -1 3415 { name: 'aria-multiline', values: -1 3416 {'true' : 'aria_multiline_true'} }, -1 3417 { name: 'aria-multiselectable', values: -1 3418 {'true' : 'aria_multiselectable_true'} }, -1 3419 { name: 'aria-pressed', values: -1 3420 {'true' : 'aria_pressed_true', -1 3421 'false' : 'aria_pressed_false', -1 3422 'mixed' : 'aria_pressed_mixed'} }, -1 3423 { name: 'aria-readonly', values: -1 3424 {'true' : 'aria_readonly_true'} }, -1 3425 { name: 'aria-required', values: -1 3426 {'true' : 'aria_required_true'} }, -1 3427 { name: 'aria-selected', values: -1 3428 {'true' : 'aria_selected_true', -1 3429 'false' : 'aria_selected_false'} } -1 3430 ]; 4183 34314184 -1 return computedName;-1 3432 /** -1 3433 * Copied from ChromeVox: -1 3434 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js -1 3435 * @type {Object} -1 3436 */ -1 3437 axs.constants.INPUT_TYPE_TO_INFORMATION_TABLE_MSG = { -1 3438 'button' : 'input_type_button', -1 3439 'checkbox' : 'input_type_checkbox', -1 3440 'color' : 'input_type_color', -1 3441 'datetime' : 'input_type_datetime', -1 3442 'datetime-local' : 'input_type_datetime_local', -1 3443 'date' : 'input_type_date', -1 3444 'email' : 'input_type_email', -1 3445 'file' : 'input_type_file', -1 3446 'image' : 'input_type_image', -1 3447 'month' : 'input_type_month', -1 3448 'number' : 'input_type_number', -1 3449 'password' : 'input_type_password', -1 3450 'radio' : 'input_type_radio', -1 3451 'range' : 'input_type_range', -1 3452 'reset' : 'input_type_reset', -1 3453 'search' : 'input_type_search', -1 3454 'submit' : 'input_type_submit', -1 3455 'tel' : 'input_type_tel', -1 3456 'text' : 'input_type_text', -1 3457 'url' : 'input_type_url', -1 3458 'week' : 'input_type_week' 4185 3459 }; 4186 3460 -1 3461 4187 3462 /**4188 -1 * @param {Element} element4189 -1 * @param {boolean=} opt_force Whether to return text alternatives for this4190 -1 * element regardless of its hidden state.4191 -1 * @return {?string}-1 3463 * Copied from ChromeVox: -1 3464 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js -1 3465 * @type {Object} 4192 3466 */4193 -1 axs.properties.getTextFromDescendantContent = function(element, opt_force) {4194 -1 var children = element.childNodes;4195 -1 var childrenTextContent = [];4196 -1 for (var i = 0; i < children.length; i++) {4197 -1 var childTextContent = axs.properties.findTextAlternatives(children[i], {}, true, opt_force);4198 -1 if (childTextContent)4199 -1 childrenTextContent.push(childTextContent.trim());4200 -1 }4201 -1 if (childrenTextContent.length) {4202 -1 var result = '';4203 -1 // Empty children are allowed, but collapse all of them4204 -1 for (var i = 0; i < childrenTextContent.length; i++)4205 -1 result = [result, childrenTextContent[i]].join(' ').trim();4206 -1 return result;4207 -1 }4208 -1 return null;-1 3467 axs.constants.TAG_TO_INFORMATION_TABLE_VERBOSE_MSG = { -1 3468 'A' : 'tag_link', -1 3469 'BUTTON' : 'tag_button', -1 3470 'H1' : 'tag_h1', -1 3471 'H2' : 'tag_h2', -1 3472 'H3' : 'tag_h3', -1 3473 'H4' : 'tag_h4', -1 3474 'H5' : 'tag_h5', -1 3475 'H6' : 'tag_h6', -1 3476 'LI' : 'tag_li', -1 3477 'OL' : 'tag_ol', -1 3478 'SELECT' : 'tag_select', -1 3479 'TEXTAREA' : 'tag_textarea', -1 3480 'UL' : 'tag_ul', -1 3481 'SECTION' : 'tag_section', -1 3482 'NAV' : 'tag_nav', -1 3483 'ARTICLE' : 'tag_article', -1 3484 'ASIDE' : 'tag_aside', -1 3485 'HGROUP' : 'tag_hgroup', -1 3486 'HEADER' : 'tag_header', -1 3487 'FOOTER' : 'tag_footer', -1 3488 'TIME' : 'tag_time', -1 3489 'MARK' : 'tag_mark' 4209 3490 }; 4210 3491 4211 3492 /**4212 -1 * @param {Element} element4213 -1 * @param {Object} textAlternatives4214 -1 * @return {?string}-1 3493 * Copied from ChromeVox: -1 3494 * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js -1 3495 * @type {Object} 4215 3496 */4216 -1 axs.properties.getTextFromAriaLabelledby = function(element, textAlternatives) {4217 -1 var computedName = null;4218 -1 if (!element.hasAttribute('aria-labelledby'))4219 -1 return computedName;-1 3497 axs.constants.TAG_TO_INFORMATION_TABLE_BRIEF_MSG = { -1 3498 'BUTTON' : 'tag_button', -1 3499 'SELECT' : 'tag_select', -1 3500 'TEXTAREA' : 'tag_textarea' -1 3501 }; 4220 35024221 -1 var labelledbyAttr = element.getAttribute('aria-labelledby');4222 -1 var labelledbyIds = labelledbyAttr.split(/\s+/);4223 -1 var labelledbyValue = {};4224 -1 labelledbyValue.valid = true;4225 -1 var labelledbyText = [];4226 -1 var labelledbyValues = [];4227 -1 for (var i = 0; i < labelledbyIds.length; i++) {4228 -1 var labelledby = {};4229 -1 labelledby.type = 'element';4230 -1 var labelledbyId = labelledbyIds[i];4231 -1 labelledby.value = labelledbyId;4232 -1 var labelledbyElement = document.getElementById(labelledbyId);4233 -1 if (!labelledbyElement) {4234 -1 labelledby.valid = false;4235 -1 labelledbyValue.valid = false;4236 -1 labelledby.errorMessage = { 'messageKey': 'noElementWithId', 'args': [labelledbyId] };4237 -1 } else {4238 -1 labelledby.valid = true;4239 -1 labelledby.text = axs.properties.findTextAlternatives(labelledbyElement, {}, true, true);4240 -1 labelledby.lastWord = axs.properties.getLastWord(labelledby.text);4241 -1 labelledbyText.push(labelledby.text);4242 -1 labelledby.element = labelledbyElement;4243 -1 }4244 -1 labelledbyValues.push(labelledby);4245 -1 }4246 -1 if (labelledbyValues.length > 0) {4247 -1 labelledbyValues[labelledbyValues.length - 1].last = true;4248 -1 labelledbyValue.values = labelledbyValues;4249 -1 labelledbyValue.text = labelledbyText.join(' ');4250 -1 labelledbyValue.lastWord = axs.properties.getLastWord(labelledbyValue.text);4251 -1 computedName = labelledbyValue.text;4252 -1 textAlternatives['ariaLabelledby'] = labelledbyValue;4253 -1 }-1 3503 axs.constants.MIXED_VALUES = { -1 3504 "true": true, -1 3505 "false": true, -1 3506 "mixed": true -1 3507 }; 4254 35084255 -1 return computedName;-1 3509 /** @enum {string} */ -1 3510 axs.constants.Severity = { -1 3511 INFO: 'Info', -1 3512 WARNING: 'Warning', -1 3513 SEVERE: 'Severe' -1 3514 }; -1 3515 -1 3516 /** @enum {string} */ -1 3517 axs.constants.AuditResult = { -1 3518 PASS: 'PASS', -1 3519 FAIL: 'FAIL', -1 3520 NA: 'NA' -1 3521 }; -1 3522 -1 3523 /** @enum {boolean} */ -1 3524 axs.constants.InlineElements = { -1 3525 // fontstyle -1 3526 'TT': true, -1 3527 'I': true, -1 3528 'B': true, -1 3529 'BIG': true, -1 3530 'SMALL': true, -1 3531 -1 3532 // phrase -1 3533 'EM': true, -1 3534 'STRONG': true, -1 3535 'DFN': true, -1 3536 'CODE': true, -1 3537 'SAMP': true, -1 3538 'KBD': true, -1 3539 'VAR': true, -1 3540 'CITE': true, -1 3541 'ABBR': true, -1 3542 'ACRONYM': true, -1 3543 -1 3544 // special -1 3545 'A': true, -1 3546 'IMG': true, -1 3547 'OBJECT': true, -1 3548 'BR': true, -1 3549 'SCRIPT': true, -1 3550 'MAP': true, -1 3551 'Q': true, -1 3552 'SUB': true, -1 3553 'SUP': true, -1 3554 'SPAN': true, -1 3555 'BDO': true, -1 3556 -1 3557 // formctrl -1 3558 'INPUT': true, -1 3559 'SELECT': true, -1 3560 'TEXTAREA': true, -1 3561 'LABEL': true, -1 3562 'BUTTON': true -1 3563 }; -1 3564 -1 3565 /** @enum {boolean} */ -1 3566 axs.constants.NATIVELY_DISABLEABLE = { -1 3567 // W3C and WHATWG https://html.spec.whatwg.org/#enabling-and-disabling-form-controls:-the-disabled-attribute -1 3568 'BUTTON': true, -1 3569 'INPUT': true, -1 3570 'SELECT': true, -1 3571 'TEXTAREA': true, -1 3572 'FIELDSET': true, -1 3573 -1 3574 // W3C http://www.w3.org/TR/html5/disabled-elements.html#disabled-elements -1 3575 'OPTGROUP': true, -1 3576 'OPTION': true 4256 3577 }; 4257 3578 -1 3579 /** -1 3580 * Maps ARIA attributes to their exactly equivalent HTML attributes. -1 3581 * @type {Object.<string, string>} -1 3582 */ -1 3583 axs.constants.ARIA_TO_HTML_ATTRIBUTE = { -1 3584 'aria-checked' : 'checked', -1 3585 'aria-disabled' : 'disabled', -1 3586 'aria-hidden' : 'hidden', -1 3587 'aria-expanded' : 'open', -1 3588 'aria-valuemax' : 'max', -1 3589 'aria-valuemin' : 'min', -1 3590 'aria-readonly' : 'readonly', -1 3591 'aria-required' : 'required', -1 3592 'aria-selected' : 'selected', -1 3593 'aria-valuenow' : 'value' -1 3594 }; 4258 3595 4259 3596 /**4260 -1 * Determine the text description/label for an element.4261 -1 * For example will attempt to find the alt text for an image or label text for a form control.4262 -1 * @param {!Element} element4263 -1 * @param {!Object} textAlternatives An object that will be updated with information.4264 -1 * @param {?string} existingComputedname4265 -1 * @param {boolean} recursive Whether this method is being called recursively as described in4266 -1 * http://www.w3.org/TR/wai-aria/roles#textalternativecomputation section 2A.4267 -1 * @return {Object}-1 3597 * Holds information about implicit ARIA semantics for a given HTML element type. -1 3598 * This object has the following properties: -1 3599 * <ul> -1 3600 * <li>`role` will contain the implicit role if it exists, otherwise empty string.</li> -1 3601 * <li>`allowed` contains the roles that can reasonably be applied to this element. -1 3602 * Note: A tag that can take any role is signified by a '*' wildcard in the array. It is not -1 3603 * an error if the array contains other roles but currently this has no meaning. In future it may -1 3604 * be used to indicate recommended roles. -1 3605 * </li> -1 3606 * <li>`selector` is present if this is a 'subclass' of the base HTML element, i.e. its semantics are -1 3607 * modified by context or attributes. It can be used with the selectors API to find and/or match -1 3608 * elements. -1 3609 * </li> -1 3610 * <li>`reserved` will be true if this is a semantically strong element that you may not modify with any -1 3611 * ARIA attributes, including role or global attributes. -1 3612 * </li> -1 3613 * </ul> -1 3614 * -1 3615 * @typedef {{ role: string, -1 3616 * allowed: Array.<string>, -1 3617 * selector: string, -1 3618 * reserved: boolean }} -1 3619 */ -1 3620 axs.constants.HtmlInfo; -1 3621 /** -1 3622 * A lookup table which maps uppercase tagName to information about implicit ARIA semantics. -1 3623 * This table is based on the document: http://w3c.github.io/aria-in-html/ -1 3624 * It is not complete and never can be. Complex scenarios require specific handling not provided here. -1 3625 * Any element not listed here: -1 3626 * - has no implicit role -1 3627 * - can take any role -1 3628 * e.g. em,strong,small,s,cite,q,dfn,abbr,time,code,var,samp,kbd,sub and sup,i,b,u,mark ,ruby,rt,rp,bdi,bdo,br,wbr -1 3629 * -1 3630 * Where there is any ambiguity this table will endeavor to provide for the most broad case (to avoid -1 3631 * false failures in conformance checking). -1 3632 * -1 3633 * For example 'table' can take any role however in practice it should only be given the role 'grid' when -1 3634 * being used as a data grid or 'presentation' when used for layout. This lookup ignores these nuances and -1 3635 * allows all roles. -1 3636 * -1 3637 * @type {Object.<string, Array.<axs.constants.HtmlInfo>>} 4268 3638 */4269 -1 axs.properties.getTextFromHostLanguageAttributes = function(element,4270 -1 textAlternatives,4271 -1 existingComputedname,4272 -1 recursive) {4273 -1 var computedName = existingComputedname;4274 -1 if (axs.browserUtils.matchSelector(element, 'img') && element.hasAttribute('alt')) {4275 -1 var altValue = {};4276 -1 altValue.type = 'string';4277 -1 altValue.valid = true;4278 -1 altValue.text = element.getAttribute('alt');4279 -1 if (computedName)4280 -1 altValue.unused = true;4281 -1 else4282 -1 computedName = altValue.text;4283 -1 textAlternatives['alt'] = altValue;4284 -1 }4285 -14286 -1 var controlsSelector = ['input:not([type="hidden"]):not([disabled])',4287 -1 'select:not([disabled])',4288 -1 'textarea:not([disabled])',4289 -1 'button:not([disabled])',4290 -1 'video:not([disabled])'].join(', ');4291 -1 if (axs.browserUtils.matchSelector(element, controlsSelector) && !recursive) {4292 -1 if (element.hasAttribute('id')) {4293 -1 var labelForQuerySelector = 'label[for="' + element.id + '"]';4294 -1 var labelsFor = document.querySelectorAll(labelForQuerySelector);4295 -1 var labelForValue = {};4296 -1 var labelForValues = [];4297 -1 var labelForText = [];4298 -1 for (var i = 0; i < labelsFor.length; i++) {4299 -1 var labelFor = {};4300 -1 labelFor.type = 'element';4301 -1 var label = labelsFor[i];4302 -1 var labelText = axs.properties.findTextAlternatives(label, {}, true);4303 -1 if (labelText && labelText.trim().length > 0) {4304 -1 labelFor.text = labelText.trim();4305 -1 labelForText.push(labelText.trim());4306 -1 }4307 -1 labelFor.element = label;4308 -1 labelForValues.push(labelFor);4309 -1 }4310 -1 if (labelForValues.length > 0) {4311 -1 labelForValues[labelForValues.length - 1].last = true;4312 -1 labelForValue.values = labelForValues;4313 -1 labelForValue.text = labelForText.join(' ');4314 -1 labelForValue.lastWord = axs.properties.getLastWord(labelForValue.text);4315 -1 if (computedName)4316 -1 labelForValue.unused = true;4317 -1 else4318 -1 computedName = labelForValue.text;4319 -1 textAlternatives['labelFor'] = labelForValue;4320 -1 }4321 -1 }4322 -14323 -1 var parent = axs.dom.parentElement(element);4324 -1 var labelWrappedValue = {};4325 -1 while (parent) {4326 -1 if (parent.tagName.toLowerCase() == 'label') {4327 -1 var parentLabel = /** @type {HTMLLabelElement} */ (parent);4328 -1 if (parentLabel.control == element) {4329 -1 labelWrappedValue.type = 'element';4330 -1 labelWrappedValue.text = axs.properties.findTextAlternatives(parentLabel, {}, true);4331 -1 labelWrappedValue.lastWord = axs.properties.getLastWord(labelWrappedValue.text);4332 -1 labelWrappedValue.element = parentLabel;4333 -1 break;4334 -1 }4335 -1 }4336 -1 parent = axs.dom.parentElement(parent);4337 -1 }4338 -1 if (labelWrappedValue.text) {4339 -1 if (computedName)4340 -1 labelWrappedValue.unused = true;4341 -1 else4342 -1 computedName = labelWrappedValue.text;4343 -1 textAlternatives['labelWrapped'] = labelWrappedValue;4344 -1 }4345 -1 // If all else fails input of type image can fall back to its alt text4346 -1 if (axs.browserUtils.matchSelector(element, 'input[type="image"]') && element.hasAttribute('alt')) {4347 -1 var altValue = {};4348 -1 altValue.type = 'string';4349 -1 altValue.valid = true;4350 -1 altValue.text = element.getAttribute('alt');4351 -1 if (computedName)4352 -1 altValue.unused = true;4353 -1 else4354 -1 computedName = altValue.text;4355 -1 textAlternatives['alt'] = altValue;4356 -1 }4357 -1 if (!Object.keys(textAlternatives).length)4358 -1 textAlternatives['noLabel'] = true;4359 -1 }4360 -1 return computedName;-1 3639 axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO = { -1 3640 'A': [{ -1 3641 role: 'link', -1 3642 allowed: [ -1 3643 'button', -1 3644 'checkbox', -1 3645 'menuitem', -1 3646 'menuitemcheckbox', -1 3647 'menuitemradio', -1 3648 'tab', -1 3649 'treeitem'], -1 3650 selector: 'a[href]' -1 3651 }], -1 3652 'ADDRESS': [{ -1 3653 role: '', -1 3654 allowed: [ -1 3655 'contentinfo', -1 3656 'presentation'] -1 3657 }], -1 3658 'AREA': [{ -1 3659 role: 'link', -1 3660 selector: 'area[href]' -1 3661 }], -1 3662 'ARTICLE': [{ -1 3663 role: 'article', -1 3664 allowed: [ -1 3665 'presentation', -1 3666 'article', -1 3667 'document', -1 3668 'application', -1 3669 'main'] -1 3670 }], -1 3671 'ASIDE': [{ -1 3672 role: 'complementary', -1 3673 allowed: [ -1 3674 'note', -1 3675 'complementary', -1 3676 'search', -1 3677 'presentation'] -1 3678 }], -1 3679 'AUDIO': [{ -1 3680 role: '', -1 3681 allowed: ['application', 'presentation'] -1 3682 }], -1 3683 'BASE': [{ -1 3684 role: '', -1 3685 reserved: true -1 3686 }], -1 3687 'BODY': [{ -1 3688 role: 'document', -1 3689 allowed: ['presentation'] -1 3690 }], -1 3691 'BUTTON': [{ -1 3692 role: 'button', -1 3693 allowed: [ -1 3694 'link', -1 3695 'menuitem', -1 3696 'menuitemcheckbox', -1 3697 'menuitemradio', -1 3698 'radio'], -1 3699 selector: 'button:not([aria-pressed]):not([type="menu"])' -1 3700 }, { -1 3701 role: 'button', -1 3702 allowed: ['button'], -1 3703 selector: 'button[aria-pressed]' -1 3704 }, { -1 3705 role: 'button', -1 3706 attributes: { -1 3707 'aria-haspopup': true -1 3708 }, -1 3709 allowed: [ -1 3710 'link', -1 3711 'menuitem', -1 3712 'menuitemcheckbox', -1 3713 'menuitemradio', -1 3714 'radio'], -1 3715 selector: 'button[type="menu"]' -1 3716 }], -1 3717 'CAPTION': [{ -1 3718 role: '', -1 3719 allowed: ['presentation'] -1 3720 }], -1 3721 'COL': [{ -1 3722 role: '', -1 3723 reserved: true -1 3724 }], -1 3725 'COLGROUP': [{ -1 3726 role: '', -1 3727 reserved: true -1 3728 }], -1 3729 'DATALIST': [{ -1 3730 role: 'listbox', -1 3731 attributes: { -1 3732 'aria-multiselectable': false -1 3733 }, -1 3734 allowed: ['presentation'] -1 3735 }], -1 3736 'DEL': [{ -1 3737 role: '', -1 3738 allowed: ['*'] -1 3739 }], -1 3740 'DD': [{ -1 3741 role: '', -1 3742 allowed: ['presentation'] -1 3743 }], -1 3744 'DT': [{ -1 3745 role: '', -1 3746 allowed: ['presentation'] -1 3747 }], -1 3748 'DETAILS': [{ -1 3749 role: 'group', -1 3750 allowed: [ -1 3751 'group', -1 3752 'presentation'] -1 3753 }], -1 3754 'DIALOG': [{ // updated 'allowed' from: http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-dialog-element -1 3755 role: 'dialog', -1 3756 allowed: ['dialog', 'alert', 'alertdialog', 'application', 'log', 'marquee', 'status'], -1 3757 selector: 'dialog[open]' -1 3758 }, { -1 3759 role: 'dialog', -1 3760 attributes: { -1 3761 'aria-hidden': true -1 3762 }, -1 3763 allowed: ['dialog', 'alert', 'alertdialog', 'application', 'log', 'marquee', 'status'], -1 3764 selector: 'dialog:not([open])' -1 3765 }], -1 3766 'DIV': [{ -1 3767 role: '', -1 3768 allowed: ['*'] -1 3769 }], -1 3770 'DL': [{ -1 3771 role: 'list', -1 3772 allowed: ['presentation'] -1 3773 }], -1 3774 'EMBED': [{ -1 3775 role: '', -1 3776 allowed: [ -1 3777 'application', -1 3778 'document', -1 3779 'img', -1 3780 'presentation'] -1 3781 }], -1 3782 'FIGURE': [{ -1 3783 role: '', -1 3784 allowed: ['*'] -1 3785 }], -1 3786 'FOOTER': [{ -1 3787 role: '', -1 3788 allowed: ['contentinfo', 'presentation'] -1 3789 }], -1 3790 'FORM': [{ -1 3791 role: 'form', -1 3792 allowed: ['presentation'] -1 3793 }], -1 3794 'P': [{ -1 3795 role: '', -1 3796 allowed: ['*'] -1 3797 }], -1 3798 'PRE': [{ -1 3799 role: '', -1 3800 allowed: ['*'] -1 3801 }], -1 3802 'BLOCKQUOTE': [{ -1 3803 role: '', -1 3804 allowed: ['*'] -1 3805 }], -1 3806 H1: [{ -1 3807 role: 'heading' -1 3808 }], -1 3809 H2: [{ -1 3810 role: 'heading' -1 3811 }], -1 3812 H3: [{ -1 3813 role: 'heading' -1 3814 }], -1 3815 H4: [{ -1 3816 role: 'heading' -1 3817 }], -1 3818 H5: [{ -1 3819 role: 'heading' -1 3820 }], -1 3821 H6: [{ -1 3822 role: 'heading' -1 3823 }], -1 3824 'HEAD': [{ -1 3825 role: '', -1 3826 reserved: true -1 3827 }], -1 3828 'HEADER': [{ -1 3829 role: '', -1 3830 allowed: [ -1 3831 'banner', -1 3832 'presentation'] -1 3833 }], -1 3834 'HR': [{ -1 3835 role: 'separator', -1 3836 allowed: ['presentation'] -1 3837 }], -1 3838 'HTML': [{ -1 3839 role: '', -1 3840 reserved: true -1 3841 }], -1 3842 'IFRAME': [{ -1 3843 role: '', -1 3844 allowed: [ -1 3845 'application', -1 3846 'document', -1 3847 'img', -1 3848 'presentation'], -1 3849 selector: 'iframe:not([seamless])' -1 3850 }, { -1 3851 role: '', -1 3852 allowed: [ -1 3853 'application', -1 3854 'document', -1 3855 'img', -1 3856 'presentation', -1 3857 'group'], -1 3858 selector: 'iframe[seamless]' -1 3859 }], -1 3860 'IMG': [{ -1 3861 role: 'presentation', -1 3862 reserved: true, -1 3863 selector: 'img[alt=""]' -1 3864 }, { -1 3865 role: 'img', -1 3866 allowed: ['*'], -1 3867 selector: 'img[alt]:not([alt=""])' -1 3868 }], -1 3869 'INPUT': [{ -1 3870 role: 'button', -1 3871 allowed: [ -1 3872 'link', -1 3873 'menuitem', -1 3874 'menuitemcheckbox', -1 3875 'menuitemradio', -1 3876 'radio'], -1 3877 selector: 'input[type="button"]:not([aria-pressed])' -1 3878 }, { -1 3879 role: 'button', -1 3880 allowed: ['button'], -1 3881 selector: 'input[type="button"][aria-pressed]' -1 3882 }, { -1 3883 role: 'checkbox', -1 3884 allowed: ['checkbox'], -1 3885 selector: 'input[type="checkbox"]' -1 3886 }, { -1 3887 role: '', -1 3888 selector: 'input[type="color"]' -1 3889 }, { -1 3890 role: '', -1 3891 selector: 'input[type="date"]' -1 3892 }, { -1 3893 role: '', -1 3894 selector: 'input[type="datetime"]' -1 3895 }, { -1 3896 role: 'textbox', -1 3897 selector: 'input[type="email"]:not([list])' -1 3898 }, { -1 3899 role: '', -1 3900 selector: 'input[type="file"]' -1 3901 }, { -1 3902 role: '', -1 3903 reserved: true, -1 3904 selector: 'input[type="hidden"]' -1 3905 }, { -1 3906 role: 'button', -1 3907 allowed: ['button'], -1 3908 selector: 'input[type="image"][aria-pressed]' -1 3909 }, { -1 3910 role: 'button', -1 3911 allowed: [ -1 3912 'link', -1 3913 'menuitem', -1 3914 'menuitemcheckbox', -1 3915 'menuitemradio', -1 3916 'radio'], -1 3917 selector: 'input[type="image"]:not([aria-pressed])' -1 3918 }, { -1 3919 role: '', -1 3920 selector: 'input[type="month"]' -1 3921 }, { -1 3922 role: '', -1 3923 selector: 'input[type="number"]' -1 3924 }, { -1 3925 role: 'textbox', -1 3926 selector: 'input[type="password"]' -1 3927 }, { -1 3928 role: 'radio', -1 3929 allowed: ['menuitemradio'], -1 3930 selector: 'input[type="radio"]' -1 3931 }, { -1 3932 role: 'slider', -1 3933 selector: 'input[type="range"]' -1 3934 }, { -1 3935 role: 'button', -1 3936 selector: 'input[type="reset"]' -1 3937 }, { -1 3938 role: 'combobox', // aria-owns is set to the same value as the list attribute -1 3939 selector: 'input[type="search"][list]' -1 3940 }, { -1 3941 role: 'textbox', -1 3942 selector: 'input[type="search"]:not([list])' -1 3943 }, { -1 3944 role: 'button', -1 3945 selector: 'input[type="submit"]' -1 3946 }, { -1 3947 role: 'combobox', // aria-owns is set to the same value as the list attribute -1 3948 selector: 'input[type="tel"][list]' -1 3949 }, { -1 3950 role: 'textbox', -1 3951 selector: 'input[type="tel"]:not([list])' -1 3952 }, { -1 3953 role: 'combobox', // aria-owns is set to the same value as the list attribute -1 3954 selector: 'input[type="text"][list]' -1 3955 }, { -1 3956 role: 'textbox', -1 3957 selector: 'input[type="text"]:not([list])' -1 3958 }, { -1 3959 role: 'textbox', -1 3960 selector: 'input:not([type])' -1 3961 }, { -1 3962 role: '', -1 3963 selector: 'input[type="time"]' -1 3964 }, { -1 3965 role: 'combobox', // aria-owns is set to the same value as the list attribute -1 3966 selector: 'input[type="url"][list]' -1 3967 }, { -1 3968 role: 'textbox', -1 3969 selector: 'input[type="url"]:not([list])' -1 3970 }, { -1 3971 role: '', -1 3972 selector: 'input[type="week"]' -1 3973 }], -1 3974 'INS': [{ -1 3975 role: '', -1 3976 allowed: ['*'] -1 3977 }], -1 3978 'KEYGEN': [{ -1 3979 role: '' -1 3980 }], -1 3981 'LABEL': [{ -1 3982 role: '', -1 3983 allowed: ['presentation'] -1 3984 }], -1 3985 'LI': [{ -1 3986 role: 'listitem', -1 3987 allowed: [ -1 3988 'menuitem', -1 3989 'menuitemcheckbox', -1 3990 'menuitemradio', -1 3991 'option', -1 3992 'tab', -1 3993 'treeitem', -1 3994 'presentation'], -1 3995 selector: 'ol:not([role="presentation"])>li, ul:not([role="presentation"])>li' -1 3996 }, { -1 3997 role: 'listitem', -1 3998 allowed: [ -1 3999 'listitem', -1 4000 'menuitem', -1 4001 'menuitemcheckbox', -1 4002 'menuitemradio', -1 4003 'option', -1 4004 'tab', -1 4005 'treeitem', -1 4006 'presentation'], -1 4007 selector: 'ol[role="presentation"]>li, ul[role="presentation"]>li' -1 4008 }], -1 4009 'LINK': [{ -1 4010 role: 'link', -1 4011 reserved: true, -1 4012 selector: 'link[href]' -1 4013 }], -1 4014 'MAIN': [{ -1 4015 role: '', -1 4016 allowed: [ -1 4017 'main', -1 4018 'presentation'] -1 4019 }], -1 4020 'MAP': [{ -1 4021 role: '', -1 4022 reserved: true -1 4023 }], -1 4024 'MATH': [{ -1 4025 role: '', -1 4026 allowed: ['presentation'] -1 4027 }], -1 4028 'MENU': [{ -1 4029 role: 'toolbar', -1 4030 selector: 'menu[type="toolbar"]' -1 4031 }], -1 4032 'MENUITEM': [{ -1 4033 role: 'menuitem', -1 4034 selector: 'menuitem[type="command"]' -1 4035 }, { -1 4036 role: 'menuitemcheckbox', -1 4037 selector: 'menuitem[type="checkbox"]' -1 4038 }, { -1 4039 role: 'menuitemradio', -1 4040 selector: 'menuitem[type="radio"]' -1 4041 }], -1 4042 'META': [{ -1 4043 role: '', -1 4044 reserved: true -1 4045 }], -1 4046 'METER': [{ -1 4047 role: 'progressbar', -1 4048 allowed: ['presentation'] -1 4049 }], -1 4050 'NAV': [{ -1 4051 role: 'navigation', -1 4052 allowed: ['navigation', 'presentation'] -1 4053 }], -1 4054 'NOSCRIPT': [{ -1 4055 role: '', -1 4056 reserved: true -1 4057 }], -1 4058 'OBJECT': [{ -1 4059 role: '', -1 4060 allowed: ['application', 'document', 'img', 'presentation'] -1 4061 }], -1 4062 'OL': [{ -1 4063 role: 'list', -1 4064 allowed: ['directory', 'group', 'listbox', 'menu', 'menubar', 'tablist', 'toolbar', 'tree', 'presentation'] -1 4065 }], -1 4066 'OPTGROUP': [{ -1 4067 role: '', -1 4068 allowed: ['presentation'] -1 4069 }], -1 4070 'OPTION': [{ -1 4071 role: 'option' -1 4072 }], -1 4073 'OUTPUT': [{ -1 4074 role: 'status', -1 4075 allowed: ['*'] -1 4076 }], -1 4077 'PARAM': [{ -1 4078 role: '', -1 4079 reserved: true -1 4080 }], -1 4081 'PICTURE': [{ -1 4082 role: '', -1 4083 reserved: true -1 4084 }], -1 4085 'PROGRESS': [{ -1 4086 role: 'progressbar', -1 4087 allowed: ['presentation'] -1 4088 }], -1 4089 'SCRIPT': [{ -1 4090 role: '', -1 4091 reserved: true -1 4092 }], -1 4093 'SECTION': [{ -1 4094 role: 'region', -1 4095 allowed: [ -1 4096 'alert', -1 4097 'alertdialog', -1 4098 'application', -1 4099 'contentinfo', -1 4100 'dialog', -1 4101 'document', -1 4102 'log', -1 4103 'marquee', -1 4104 'search', -1 4105 'status', -1 4106 'presentation'] -1 4107 }], -1 4108 'SELECT': [{ -1 4109 role: 'listbox' -1 4110 }], -1 4111 'SOURCE': [{ -1 4112 role: '', -1 4113 reserved: true -1 4114 }], -1 4115 'SPAN': [{ -1 4116 role: '', -1 4117 allowed: ['*'] -1 4118 }], -1 4119 'STYLE': [{ -1 4120 role: '', -1 4121 reserved: true -1 4122 }], -1 4123 'SVG': [{ -1 4124 role: '', -1 4125 allowed: [ -1 4126 'application', -1 4127 'document', -1 4128 'img', -1 4129 'presentation'] -1 4130 }], -1 4131 'SUMMARY': [{ -1 4132 role: '', -1 4133 allowed: ['presentation'] -1 4134 }], -1 4135 'TABLE': [{ -1 4136 role: '', -1 4137 allowed: ['*'] -1 4138 }], -1 4139 'TEMPLATE': [{ -1 4140 role: '', -1 4141 reserved: true -1 4142 }], -1 4143 'TEXTAREA': [{ -1 4144 role: 'textbox' -1 4145 }], -1 4146 'TBODY': [{ -1 4147 role: 'rowgroup', -1 4148 allowed: ['*'] -1 4149 }], -1 4150 'THEAD': [{ -1 4151 role: 'rowgroup', -1 4152 allowed: ['*'] -1 4153 }], -1 4154 'TFOOT': [{ -1 4155 role: 'rowgroup', -1 4156 allowed: ['*'] -1 4157 }], -1 4158 'TITLE': [{ -1 4159 role: '', -1 4160 reserved: true -1 4161 }], -1 4162 'TD': [{ -1 4163 role: '', -1 4164 allowed: ['*'] -1 4165 }], -1 4166 'TH': [{ -1 4167 role: '', -1 4168 allowed: ['*'] -1 4169 }], -1 4170 'TR': [{ -1 4171 role: '', -1 4172 allowed: ['*'] -1 4173 }], -1 4174 'TRACK': [{ -1 4175 role: '', -1 4176 reserved: true -1 4177 }], -1 4178 'UL': [{ -1 4179 role: 'list', -1 4180 allowed: [ -1 4181 'directory', -1 4182 'group', -1 4183 'listbox', -1 4184 'menu', -1 4185 'menubar', -1 4186 'tablist', -1 4187 'toolbar', -1 4188 'tree', -1 4189 'presentation'] -1 4190 }], -1 4191 'VIDEO': [{ -1 4192 role: '', -1 4193 allowed: ['application', 'presentation'] -1 4194 }] 4361 4195 }; 4362 41964363 -1 /**4364 -1 * @param {?string} text4365 -1 * @return {?string}4366 -1 */4367 -1 axs.properties.getLastWord = function(text) {4368 -1 if (!text)4369 -1 return null;-1 4197 },{}],10:[function(require,module,exports){ -1 4198 // Copyright 2015 Google Inc. -1 4199 // -1 4200 // Licensed under the Apache License, Version 2.0 (the "License"); -1 4201 // you may not use this file except in compliance with the License. -1 4202 // You may obtain a copy of the License at -1 4203 // -1 4204 // http://www.apache.org/licenses/LICENSE-2.0 -1 4205 // -1 4206 // Unless required by applicable law or agreed to in writing, software -1 4207 // distributed under the License is distributed on an "AS IS" BASIS, -1 4208 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 4209 // See the License for the specific language governing permissions and -1 4210 // limitations under the License. 4370 42114371 -1 // TODO: this makes a lot of assumptions.4372 -1 var lastSpace = text.lastIndexOf(' ') + 1;4373 -1 var MAXLENGTH = 10;4374 -1 var cutoff = text.length - MAXLENGTH;4375 -1 var wordStart = lastSpace > cutoff ? lastSpace : cutoff;4376 -1 return text.substring(wordStart);4377 -1 };-1 4212 goog.provide('axs.dom'); 4378 4213 4379 4214 /** -1 4215 * Returns the nearest ancestor which is an Element. 4380 4216 * @param {Node} node4381 -1 * @return {Object}4382 -1 */4383 -1 axs.properties.getTextProperties = function(node) {4384 -1 var textProperties = {};4385 -1 var computedName = axs.properties.findTextAlternatives(node, textProperties, false, true);4386 -14387 -1 if (Object.keys(textProperties).length == 0) {4388 -1 /** @type {Element} */ var element = axs.dom.asElement(node);4389 -1 if (element && axs.browserUtils.matchSelector(element, 'img')) {4390 -1 var altValue = {};4391 -1 altValue.valid = false;4392 -1 altValue.errorMessage = 'No alt value provided';4393 -1 textProperties['alt'] = altValue;4394 -14395 -1 var src = element.src;4396 -1 if (typeof src == 'string') {4397 -1 var parts = src.split('/');4398 -1 var filename = parts.pop();4399 -1 var filenameValue = { text: filename };4400 -1 textProperties['filename'] = filenameValue;4401 -1 computedName = filename;4402 -1 }4403 -1 }4404 -14405 -1 if (!computedName)4406 -1 return null;4407 -1 }4408 -14409 -1 textProperties.hasProperties = Boolean(Object.keys(textProperties).length);4410 -1 textProperties.computedText = computedName;4411 -1 textProperties.lastWord = axs.properties.getLastWord(computedName);4412 -1 return textProperties;4413 -1 };4414 -14415 -1 /**4416 -1 * Finds any ARIA attributes (roles, states and properties) explicitly set on this element.4417 -1 * @param {Element} element4418 -1 * @return {Object}-1 4217 * @return {?Element} 4419 4218 */4420 -1 axs.properties.getAriaProperties = function(element) {4421 -1 var ariaProperties = {};4422 -1 var statesAndProperties = axs.properties.getGlobalAriaProperties(element);4423 -14424 -1 for (var property in axs.constants.ARIA_PROPERTIES) {4425 -1 var attributeName = 'aria-' + property;4426 -1 if (element.hasAttribute(attributeName)) {4427 -1 var propertyValue = element.getAttribute(attributeName);4428 -1 statesAndProperties[attributeName] =4429 -1 axs.utils.getAriaPropertyValue(attributeName, propertyValue, element);4430 -1 }4431 -1 }4432 -1 if (Object.keys(statesAndProperties).length > 0)4433 -1 ariaProperties['properties'] = axs.utils.values(statesAndProperties);-1 4219 axs.dom.parentElement = function(node) { -1 4220 if (!node) -1 4221 return null; 4434 42224435 -1 var roles = axs.utils.getRoles(element);4436 -1 if (!roles) {4437 -1 if (Object.keys(ariaProperties).length)4438 -1 return ariaProperties;-1 4223 var parentNode = axs.dom.composedParentNode(node); -1 4224 if (!parentNode) 4439 4225 return null;4440 -1 }4441 -1 ariaProperties['roles'] = roles;4442 -1 if (!roles.valid || !roles['roles'])4443 -1 return ariaProperties;4444 42264445 -1 var roleDetails = roles['roles'];4446 -1 for (var i = 0; i < roleDetails.length; i++) {4447 -1 var role = roleDetails[i];4448 -1 if (!role.details || !role.details.propertiesSet)4449 -1 continue;4450 -1 for (var property in role.details.propertiesSet) {4451 -1 if (property in statesAndProperties)4452 -1 continue;4453 -1 if (element.hasAttribute(property)) {4454 -1 var propertyValue = element.getAttribute(property);4455 -1 statesAndProperties[property] =4456 -1 axs.utils.getAriaPropertyValue(property, propertyValue, element);4457 -1 if ('values' in statesAndProperties[property]) {4458 -1 var values = statesAndProperties[property].values;4459 -1 values[values.length - 1].isLast = true;4460 -1 }4461 -1 } else if (role.details.requiredPropertiesSet[property]) {4462 -1 statesAndProperties[property] =4463 -1 { 'name': property, 'valid': false, 'reason': 'Required property not set' };4464 -1 }4465 -1 }-1 4227 switch (parentNode.nodeType) { -1 4228 case Node.ELEMENT_NODE: -1 4229 return /** @type {Element} */ (parentNode); -1 4230 default: -1 4231 return axs.dom.parentElement(parentNode); 4466 4232 }4467 -1 if (Object.keys(statesAndProperties).length > 0)4468 -1 ariaProperties['properties'] = axs.utils.values(statesAndProperties);4469 -1 if (Object.keys(ariaProperties).length > 0)4470 -1 return ariaProperties;4471 -1 return null;4472 4233 }; 4473 4234 4474 4235 /**4475 -1 * Gets the ARIA properties found on this element which apply to all elements, not just elements with ARIA roles.4476 -1 * @param {Element} element4477 -1 * @return {!Object}-1 4236 * Returns the shadow host of a document fragment if it is a Shadow DOM fragment -1 4237 * otherwise returns `null`. -1 4238 * @param {DocumentFragment} fragment -1 4239 * @return {?Element} 4478 4240 */4479 -1 axs.properties.getGlobalAriaProperties = function(element) {4480 -1 var globalProperties = {};4481 -1 for (var property in axs.constants.GLOBAL_PROPERTIES) {4482 -1 if (element.hasAttribute(property)) {4483 -1 var propertyValue = element.getAttribute(property);4484 -1 globalProperties[property] =4485 -1 axs.utils.getAriaPropertyValue(property, propertyValue, element);4486 -1 }4487 -1 }4488 -1 return globalProperties;-1 4241 axs.dom.shadowHost = function(fragment) { -1 4242 // If host exists, this is a Shadow DOM fragment. -1 4243 if ('host' in fragment) -1 4244 return fragment.host; -1 4245 else -1 4246 return null; 4489 4247 }; 4490 4248 4491 4249 /**4492 -1 * @param {Element} element4493 -1 * @return {Object.<string, Object>}-1 4250 * Returns the given Node's parent in the composed tree. -1 4251 * @param {Node} node -1 4252 * @return {?Node} 4494 4253 */4495 -1 axs.properties.getVideoProperties = function(element) {4496 -1 var videoSelector = 'video';4497 -1 if (!axs.browserUtils.matchSelector(element, videoSelector))-1 4254 axs.dom.composedParentNode = function(node) { -1 4255 if (!node) 4498 4256 return null;4499 -1 var videoProperties = {};4500 -1 videoProperties['captionTracks'] = axs.properties.getTrackElements(element, 'captions');4501 -1 videoProperties['descriptionTracks'] = axs.properties.getTrackElements(element, 'descriptions');4502 -1 videoProperties['chapterTracks'] = axs.properties.getTrackElements(element, 'chapters');4503 -1 // error if no text alternatives?4504 -1 return videoProperties;-1 4257 if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) -1 4258 return axs.dom.shadowHost(/** @type {DocumentFragment} */ (node)); -1 4259 -1 4260 var parentNode = node.parentNode; -1 4261 if (!parentNode) -1 4262 return null; -1 4263 -1 4264 if (parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) -1 4265 return axs.dom.shadowHost(/** @type {DocumentFragment} */ (parentNode)); -1 4266 -1 4267 if (!parentNode.shadowRoot) -1 4268 return parentNode; -1 4269 -1 4270 // Shadow DOM v1 -1 4271 if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE) { -1 4272 var assignedSlot = node.assignedSlot; -1 4273 if (HTMLSlotElement && assignedSlot instanceof HTMLSlotElement) -1 4274 return axs.dom.composedParentNode(assignedSlot); -1 4275 } -1 4276 -1 4277 // Shadow DOM v0 -1 4278 if (typeof node.getDestinationInsertionPoints === 'function') { -1 4279 var insertionPoints = node.getDestinationInsertionPoints(); -1 4280 if (insertionPoints.length > 0) -1 4281 return axs.dom.composedParentNode(insertionPoints[insertionPoints.length - 1]); -1 4282 } -1 4283 -1 4284 return null; 4505 4285 }; 4506 4286 4507 4287 /**4508 -1 * @param {Element} element4509 -1 * @param {string} kind4510 -1 * @return {Object}-1 4288 * Return the corresponding element for the given node. -1 4289 * @param {Node} node -1 4290 * @return {Element} -1 4291 * @suppress {checkTypes} 4511 4292 */4512 -1 axs.properties.getTrackElements = function(element, kind) {4513 -1 // error if resource is not available4514 -1 var trackElements = element.querySelectorAll('track[kind=' + kind + ']');4515 -1 var result = {};4516 -1 if (!trackElements.length) {4517 -1 result.valid = false;4518 -1 result.reason = { 'messageKey': 'noTracksProvided', 'args': [[kind]] };4519 -1 return result;4520 -1 }4521 -1 result.valid = true;4522 -1 var values = [];4523 -1 for (var i = 0; i < trackElements.length; i++) {4524 -1 var trackElement = {};4525 -1 var src = trackElements[i].getAttribute('src');4526 -1 var srcLang = trackElements[i].getAttribute('srcLang');4527 -1 var label = trackElements[i].getAttribute('label');4528 -1 if (!src) {4529 -1 trackElement.valid = false;4530 -1 trackElement.reason = { 'messageKey': 'noSrcProvided' };4531 -1 } else {4532 -1 trackElement.valid = true;4533 -1 trackElement.src = src;4534 -1 }4535 -1 var name = '';4536 -1 if (label) {4537 -1 name += label;4538 -1 if (srcLang)4539 -1 name += ' ';4540 -1 }4541 -1 if (srcLang)4542 -1 name += '(' + srcLang + ')';4543 -1 if (name == '')4544 -1 name = '[' + { 'messageKey': 'unnamed' } + ']';4545 -1 trackElement.name = name;4546 -1 values.push(trackElement);-1 4293 axs.dom.asElement = function(node) { -1 4294 /** @type {Element} */ var element; -1 4295 switch (node.nodeType) { -1 4296 case Node.COMMENT_NODE: -1 4297 return null; // Skip comments -1 4298 case Node.ELEMENT_NODE: -1 4299 element = /** (@type {Element}) */ node; -1 4300 if (element.localName == 'script' || -1 4301 element.localName == 'template') -1 4302 return null; // Skip script-supporting elements -1 4303 return element; -1 4304 case Node.DOCUMENT_FRAGMENT_NODE: -1 4305 return node.host; -1 4306 case Node.TEXT_NODE: -1 4307 return axs.dom.parentElement(node); -1 4308 default: -1 4309 console.warn('Unhandled node type: ', node.nodeType); 4547 4310 }4548 -1 result.values = values;4549 -1 return result;-1 4311 return null; 4550 4312 }; 4551 4313 4552 4314 /** -1 4315 * Recursively walk the composed tree from |node|, aborting if |end| is encountered. 4553 4316 * @param {Node} node4554 -1 * @return {Object.<string, Object>}-1 4317 * @param {?Node} end -1 4318 * @param {{preorder: (function (Node, Object):boolean|undefined), -1 4319 * postorder: (function (Node, Object)|undefined)}} callbacks -1 4320 * Callbacks to be called for each element traversed, excluding -1 4321 * |end|. Possible callbacks are |preorder|, called before descending into -1 4322 * child nodes, and |postorder| called after all child nodes have been -1 4323 * traversed. If |preorder| returns false, its child nodes will not be -1 4324 * traversed. -1 4325 * @param {Object} parentFlags -1 4326 * @param {ShadowRoot=} opt_shadowRoot The nearest ShadowRoot ancestor, if any. -1 4327 * @return {boolean} Whether |end| was found, if provided. 4555 4328 */4556 -1 axs.properties.getAllProperties = function(node) {4557 -1 /** @type {Element} */ var element = axs.dom.asElement(node);4558 -1 if (!element)4559 -1 return {};-1 4329 axs.dom.composedTreeSearch = function(node, end, callbacks, parentFlags, opt_shadowRoot) { -1 4330 if (node === end) -1 4331 return true; 4560 43324561 -1 var allProperties = {};4562 -1 allProperties['ariaProperties'] = axs.properties.getAriaProperties(element);4563 -1 allProperties['colorProperties'] = axs.properties.getColorProperties(element);4564 -1 allProperties['focusProperties'] = axs.properties.getFocusProperties(element);4565 -1 allProperties['textProperties'] = axs.properties.getTextProperties(node);4566 -1 allProperties['videoProperties'] = axs.properties.getVideoProperties(element);4567 -1 return allProperties;4568 -1 };-1 4333 if (node.nodeType == Node.ELEMENT_NODE) -1 4334 var element = /** @type {Element} */ (node); 4569 43354570 -1 (function() {4571 -1 /**4572 -1 * Helper for implicit semantic functionality.4573 -1 * Can be made part of the public API if need be.4574 -1 * @param {Element} element4575 -1 * @return {?axs.constants.HtmlInfo}4576 -1 */4577 -1 function getHtmlInfo(element) {4578 -1 if (!element)4579 -1 return null;4580 -1 var tagName = element.tagName;4581 -1 if (!tagName)4582 -1 return null;4583 -1 tagName = tagName.toUpperCase();4584 -1 var infos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];4585 -1 if (!infos || !infos.length)4586 -1 return null;4587 -1 var defaultInfo = null; // will contain the info with no specific selector if no others match4588 -1 for (var i = 0, len = infos.length; i < len; i++) {4589 -1 var htmlInfo = infos[i];4590 -1 if (htmlInfo.selector) {4591 -1 if (axs.browserUtils.matchSelector(element, htmlInfo.selector))4592 -1 return htmlInfo;4593 -1 } else {4594 -1 defaultInfo = htmlInfo;-1 4336 var found = false; -1 4337 var flags = Object.create(parentFlags); -1 4338 -1 4339 // Descend into node: -1 4340 // If it has a ShadowRoot, ignore all child elements - these will be picked -1 4341 // up by the <content> or <shadow> elements. Descend straight into the -1 4342 // ShadowRoot. -1 4343 if (element) { -1 4344 var localName = element.localName; -1 4345 if (flags.collectIdRefs) { -1 4346 flags.idrefs = axs.utils.getReferencedIds(element); -1 4347 } -1 4348 if (!flags.disabled || (localName === 'legend') && axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type')) { -1 4349 flags.disabled = axs.utils.isElementDisabled(element, true); -1 4350 } -1 4351 if (!flags.hidden) { -1 4352 flags.hidden = axs.utils.isElementHidden(element); -1 4353 } -1 4354 if (callbacks.preorder) { -1 4355 if (!callbacks.preorder(element, flags)) -1 4356 return found; -1 4357 } -1 4358 // NOTE: grunt qunit DOES NOT support Shadow DOM, so if changing this -1 4359 // code, be sure to run the tests in the browser before committing. -1 4360 var shadowRoot = element.shadowRoot || element.webkitShadowRoot; -1 4361 if (shadowRoot) { -1 4362 flags.level++; -1 4363 found = axs.dom.composedTreeSearch(shadowRoot, -1 4364 end, -1 4365 callbacks, -1 4366 flags, -1 4367 shadowRoot); -1 4368 if (element && callbacks.postorder && !found) -1 4369 callbacks.postorder(element, flags); -1 4370 return found; -1 4371 } -1 4372 -1 4373 // If it is a <content> element, descend into distributed elements - these -1 4374 // are elements from outside the shadow root which are rendered inside the -1 4375 // shadow DOM. -1 4376 if (localName == 'content' && typeof element.getDistributedNodes === 'function') { -1 4377 var content = /** @type {HTMLContentElement} */ (element); -1 4378 var distributedNodes = content.getDistributedNodes(); -1 4379 for (var i = 0; i < distributedNodes.length && !found; i++) { -1 4380 found = axs.dom.composedTreeSearch(distributedNodes[i], -1 4381 end, -1 4382 callbacks, -1 4383 flags, -1 4384 opt_shadowRoot); 4595 4385 } -1 4386 if (callbacks.postorder && !found) -1 4387 callbacks.postorder.call(null, element, flags); -1 4388 return found; 4596 4389 }4597 -1 return defaultInfo;4598 4390 } 4599 43914600 -1 /**4601 -1 * @param {Element} element4602 -1 * @return {string} role4603 -1 */4604 -1 axs.properties.getImplicitRole = function(element) {4605 -1 var htmlInfo = getHtmlInfo(element);4606 -1 if (htmlInfo)4607 -1 return htmlInfo.role;4608 -1 return '';4609 -1 };4610 43924611 -1 /**4612 -1 * Determine if this element can take ANY ARIA attributes including roles, state and properties.4613 -1 * If false then even global attributes should not be used.4614 -1 * @param {Element} element4615 -1 * @return {boolean}4616 -1 */4617 -1 axs.properties.canTakeAriaAttributes = function(element) {4618 -1 var htmlInfo = getHtmlInfo(element);4619 -1 if (htmlInfo)4620 -1 return !htmlInfo.reserved;4621 -1 return true;4622 -1 };4623 -1 })();4624 43934625 -1 /**4626 -1 * This lists the ARIA attributes that are supported implicitly by native properties of this element.4627 -1 *4628 -1 * @param {Element} element The element to check.4629 -1 * @return {!Array.<string>} An array of ARIA attributes.4630 -1 *4631 -1 * example:4632 -1 * var element = document.createElement("input");4633 -1 * element.setAttribute("type", "range");4634 -1 * var supported = axs.properties.getNativelySupportedAttributes(element); // an array of ARIA attributes4635 -1 * console.log(supported.indexOf("aria-valuemax") >=0); // logs 'true'4636 -1 */4637 -1 axs.properties.getNativelySupportedAttributes = function(element) {4638 -1 var result = [];4639 -1 if (!element) {4640 -1 return result;4641 -1 }4642 -1 var testElement = element.cloneNode(false); // gets rid of expandos4643 -1 var ariaAttributes = Object.keys(/** @type {!Object} */(axs.constants.ARIA_TO_HTML_ATTRIBUTE));4644 -1 for (var i = 0; i < ariaAttributes.length; i++) {4645 -1 var ariaAttribute = ariaAttributes[i];4646 -1 var nativeAttribute = axs.constants.ARIA_TO_HTML_ATTRIBUTE[ariaAttribute];4647 -1 if (nativeAttribute in testElement) {4648 -1 result[result.length] = ariaAttribute;4649 -1 }-1 4394 // If it is neither the parent of a ShadowRoot, a <content> element, nor -1 4395 // a <shadow> element recurse normally. -1 4396 var child = node.firstChild; -1 4397 while (child != null && !found) { -1 4398 found = axs.dom.composedTreeSearch(child, -1 4399 end, -1 4400 callbacks, -1 4401 flags, -1 4402 opt_shadowRoot); -1 4403 child = child.nextSibling; 4650 4404 }4651 -1 return result;-1 4405 -1 4406 if (element && callbacks.postorder && !found) -1 4407 callbacks.postorder.call(null, element, flags); -1 4408 return found; 4652 4409 }; 4653 44104654 -1 (function() {4655 -1 var roleToSelectorCache = {}; // performance optimization, cache results from getSelectorForRole-1 4411 },{}],11:[function(require,module,exports){ -1 4412 // Copyright 2012 Google Inc. -1 4413 // -1 4414 // Licensed under the Apache License, Version 2.0 (the "License"); -1 4415 // you may not use this file except in compliance with the License. -1 4416 // You may obtain a copy of the License at -1 4417 // -1 4418 // http://www.apache.org/licenses/LICENSE-2.0 -1 4419 // -1 4420 // Unless required by applicable law or agreed to in writing, software -1 4421 // distributed under the License is distributed on an "AS IS" BASIS, -1 4422 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -1 4423 // See the License for the specific language governing permissions and -1 4424 // limitations under the License. 4656 44254657 -1 /**4658 -1 * Build a selector that will match elements which implicity or explicitly have this role.4659 -1 * Note that the selector will probably not look elegant but it will work.4660 -1 * @param {string} role4661 -1 * @return {string} selector4662 -1 */4663 -1 axs.properties.getSelectorForRole = function(role) {4664 -1 if (!role)4665 -1 return '';4666 -1 if (roleToSelectorCache[role] && roleToSelectorCache.hasOwnProperty(role))4667 -1 return roleToSelectorCache[role];4668 -1 var selectors = ['[role="' + role + '"]'];4669 -1 var tagNames = Object.keys(/** @type {!Object} */(axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO));4670 -1 tagNames.forEach(function(tagName) {4671 -1 var htmlInfos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];4672 -1 if (htmlInfos && htmlInfos.length) {4673 -1 for (var i = 0; i < htmlInfos.length; i++) {4674 -1 var htmlInfo = htmlInfos[i];4675 -1 if (htmlInfo.role === role) {4676 -1 if (htmlInfo.selector) {4677 -1 selectors[selectors.length] = htmlInfo.selector;4678 -1 } else {4679 -1 selectors[selectors.length] = tagName; // Selectors API is not case sensitive.4680 -1 break; // No need to continue adding selectors since we will match the tag itself.4681 -1 }4682 -1 }4683 -1 }4684 -1 }4685 -1 });4686 -1 return (roleToSelectorCache[role] = selectors.join(','));4687 -1 };4688 -1 })();-1 4426 goog.require('axs.browserUtils'); -1 4427 goog.require('axs.color'); -1 4428 goog.require('axs.dom'); -1 4429 goog.require('axs.utils'); 4689 44304690 -1 },{}],7:[function(require,module,exports){4691 -1 var query = require('./lib/query.js');4692 -1 var name = require('./lib/name.js');-1 4431 goog.provide('axs.properties'); 4693 44324694 -1 module.exports = {4695 -1 getRole: query.getRole,4696 -1 getAttribute: query.getAttribute,4697 -1 getName: name.getName,4698 -1 getDescription: name.getDescription,-1 4433 /** -1 4434 * @const -1 4435 * @type {string} -1 4436 */ -1 4437 axs.properties.TEXT_CONTENT_XPATH = './/text()[normalize-space(.)!=""]/parent::*[name()!="script"]'; 4699 44384700 -1 matches: query.matches,4701 -1 querySelector: query.querySelector,4702 -1 querySelectorAll: query.querySelectorAll,4703 -1 closest: query.closest,-1 4439 /** -1 4440 * @param {Element} element -1 4441 * @return {Object.<string, Object>} -1 4442 */ -1 4443 axs.properties.getFocusProperties = function(element) { -1 4444 var focusProperties = {}; -1 4445 var tabindex = element.getAttribute('tabindex'); -1 4446 if (tabindex != undefined) { -1 4447 focusProperties['tabindex'] = { value: tabindex, valid: true }; -1 4448 } else { -1 4449 if (axs.utils.isElementImplicitlyFocusable(element)) -1 4450 focusProperties['implicitlyFocusable'] = { value: true, valid: true }; -1 4451 } -1 4452 if (Object.keys(focusProperties).length == 0) -1 4453 return null; -1 4454 var transparent = axs.utils.elementIsTransparent(element); -1 4455 var zeroArea = axs.utils.elementHasZeroArea(element); -1 4456 var outsideScrollArea = axs.utils.elementIsOutsideScrollArea(element); -1 4457 var overlappingElements = axs.utils.overlappingElements(element); -1 4458 if (transparent || zeroArea || outsideScrollArea || overlappingElements.length > 0) { -1 4459 var hidden = axs.utils.isElementOrAncestorHidden(element); -1 4460 var visibleProperties = { value: false, -1 4461 valid: hidden }; -1 4462 if (transparent) -1 4463 visibleProperties['transparent'] = true; -1 4464 if (zeroArea) -1 4465 visibleProperties['zeroArea'] = true; -1 4466 if (outsideScrollArea) -1 4467 visibleProperties['outsideScrollArea'] = true; -1 4468 if (overlappingElements && overlappingElements.length > 0) -1 4469 visibleProperties['overlappingElements'] = overlappingElements; -1 4470 var hiddenProperties = { value: hidden, valid: hidden }; -1 4471 if (hidden) -1 4472 hiddenProperties['reason'] = axs.properties.getHiddenReason(element); -1 4473 visibleProperties['hidden'] = hiddenProperties; -1 4474 focusProperties['visible'] = visibleProperties; -1 4475 } else { -1 4476 focusProperties['visible'] = { value: true, valid: true }; -1 4477 } -1 4478 -1 4479 return focusProperties; 4704 4480 }; 4705 44814706 -1 },{"./lib/name.js":9,"./lib/query.js":10}],8:[function(require,module,exports){4707 -1 exports.attributes = {4708 -1 // widget4709 -1 'autocomplete': 'token',4710 -1 'checked': 'tristate',4711 -1 'current': 'token',4712 -1 'disabled': 'bool',4713 -1 'expanded': 'bool-undefined',4714 -1 'haspopup': 'token',4715 -1 'hidden': 'bool', // !4716 -1 'invalid': 'token',4717 -1 'keyshortcuts': 'string',4718 -1 'label': 'string',4719 -1 'level': 'int',4720 -1 'modal': 'bool',4721 -1 'multiline': 'bool',4722 -1 'multiselectable': 'bool',4723 -1 'orientation': 'token',4724 -1 'placeholder': 'string',4725 -1 'pressed': 'tristate',4726 -1 'readonly': 'bool',4727 -1 'required': 'bool',4728 -1 'roledescription': 'string',4729 -1 'selected': 'bool-undefined',4730 -1 'valuemax': 'number',4731 -1 'valuemin': 'number',4732 -1 'valuenow': 'number',4733 -1 'valuetext': 'string',-1 4482 /** -1 4483 * @typedef {{ property: string, -1 4484 * on: Element }} -1 4485 * -1 4486 * property examples: 'display: none', 'visibility: hidden', 'aria-hidden' -1 4487 */ -1 4488 axs.properties.hiddenReason; 4734 44894735 -1 // live4736 -1 'atomic': 'bool',4737 -1 'busy': 'bool',4738 -1 'live': 'token',4739 -1 'relevant': 'token-list',-1 4490 /** -1 4491 * Determine the reason an element is not visible. -1 4492 * Will give the CSS rule or attribute and the element/ancestor it is set on. -1 4493 * @param {Element} element -1 4494 * @return {?axs.properties.hiddenReason} -1 4495 */ -1 4496 axs.properties.getHiddenReason = function(element) { -1 4497 if (!element || !(element instanceof element.ownerDocument.defaultView.HTMLElement)) -1 4498 return null; -1 4499 -1 4500 if (element.hasAttribute('chromevoxignoreariahidden')) -1 4501 var chromevoxignoreariahidden = true; -1 4502 -1 4503 var style = window.getComputedStyle(element, null); -1 4504 if (style.display == 'none') -1 4505 return { 'property': 'display: none', -1 4506 'on': element }; -1 4507 -1 4508 if (style.visibility == 'hidden') -1 4509 return { 'property': 'visibility: hidden', -1 4510 'on': element }; 4740 45114741 -1 // dragndrop4742 -1 'dropeffect': 'token-list',4743 -1 'grabbed': 'bool-undefined',-1 4512 if (element.hasAttribute('aria-hidden') && -1 4513 element.getAttribute('aria-hidden').toLowerCase() == 'true') { -1 4514 if (!chromevoxignoreariahidden) -1 4515 return { 'property': 'aria-hidden', -1 4516 'on': element }; -1 4517 } 4744 45184745 -1 // relationship4746 -1 'activedescendant': 'id',4747 -1 'colcount': 'int',4748 -1 'colindex': 'int',4749 -1 'colspan': 'int',4750 -1 'controls': 'id-list',4751 -1 'describedby': 'id-list',4752 -1 'details': 'id',4753 -1 'errormessage': 'id',4754 -1 'flowto': 'id-list',4755 -1 'labelledby': 'id-list',4756 -1 'owns': 'id-list',4757 -1 'posinset': 'int',4758 -1 'rowcount': 'int',4759 -1 'rowindex': 'int',4760 -1 'rowspan': 'int',4761 -1 'setsize': 'int',4762 -1 'sort': 'token',-1 4519 return axs.properties.getHiddenReason(axs.dom.parentElement(element)); 4763 4520 }; 4764 45214765 -1 // https://www.w3.org/TR/html-aria/#docconformance4766 -1 exports.extraSelectors = {4767 -1 article: ['article'],4768 -1 button: [4769 -1 'button',4770 -1 'input[type="button"]',4771 -1 'input[type="image"]',4772 -1 'input[type="reset"]',4773 -1 'input[type="submit"]',4774 -1 'summary',4775 -1 ],4776 -1 cell: ['td'],4777 -1 checkbox: ['input[type="checkbox"]'],4778 -1 combobox: [4779 -1 'input[type="email"][list]',4780 -1 'input[type="search"][list]',4781 -1 'input[type="tel"][list]',4782 -1 'input[type="text"][list]',4783 -1 'input[type="url"][list]',4784 -1 ],4785 -1 complementary: ['aside'],4786 -1 definition: ['dd'],4787 -1 dialog: ['dialog'],4788 -1 document: ['body'],4789 -1 figure: ['figure'],4790 -1 form: ['form[aria-label]', 'form[aria-labelledby]'],4791 -1 group: ['details', 'optgroup'],4792 -1 heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],4793 -1 img: ['img:not([alt=""])'],4794 -1 link: ['a[href]', 'area[href]', 'link[href]'],4795 -1 list: ['dl', 'ol', 'ul'],4796 -1 listitem: ['dt', 'ul > li', 'ol > li'],4797 -1 main: ['main'],4798 -1 math: ['math'],4799 -1 menuitemcheckbox: ['menuitem[type="checkbox"]'],4800 -1 menuitem: ['menuitem[type="command"]'],4801 -1 menuitemradio: ['menuitem[type="radio"]'],4802 -1 menu: ['menu[type="context"]'],4803 -1 navigation: ['nav'],4804 -1 option: ['option'],4805 -1 progressbar: ['progress'],4806 -1 radio: ['input[type="radio"]'],4807 -1 region: ['section'],4808 -1 rowgroup: ['tbody', 'thead', 'tfoot'],4809 -1 row: ['tr'],4810 -1 searchbox: ['input[type="search"]:not([list])'],4811 -1 separator: ['hr'],4812 -1 slider: ['input[type="range"]'],4813 -1 spinbutton: ['input[type="number"]'],4814 -1 status: ['output'],4815 -1 table: ['table'],4816 -1 textbox: [4817 -1 'input[type="email"]:not([list])',4818 -1 'input[type="tel"]:not([list])',4819 -1 'input[type="text"]:not([list])',4820 -1 'input[type="url"]:not([list])',4821 -1 'textarea',4822 -1 ],4823 45224824 -1 // if scope is missing, it is calculated automatically4825 -1 rowheader: ['th[scope="row"]'],4826 -1 columnheader: ['th[scope="col"]'],-1 4523 /** -1 4524 * @param {Element} element -1 4525 * @return {Object.<string, Object>} -1 4526 */ -1 4527 axs.properties.getColorProperties = function(element) { -1 4528 var colorProperties = {}; -1 4529 var contrastRatioProperties = -1 4530 axs.properties.getContrastRatioProperties(element); -1 4531 if (contrastRatioProperties) -1 4532 colorProperties['contrastRatio'] = contrastRatioProperties; -1 4533 if (Object.keys(colorProperties).length == 0) -1 4534 return null; -1 4535 return colorProperties; 4827 4536 }; 4828 45374829 -1 exports.scoped = [4830 -1 'article *', 'aside *', 'main *', 'nav *', 'section *',4831 -1 ].join(',');-1 4538 /** -1 4539 * Determines whether the given element has a text node as a direct descendant. -1 4540 * @param {Element} element -1 4541 * @return {boolean} -1 4542 */ -1 4543 axs.properties.hasDirectTextDescendant = function(element) { -1 4544 var ownerDocument; -1 4545 if (element.nodeType == Node.DOCUMENT_NODE) -1 4546 ownerDocument = element; -1 4547 else -1 4548 ownerDocument = element.ownerDocument; -1 4549 if (ownerDocument.evaluate) { -1 4550 return hasDirectTextDescendantXpath(); -1 4551 } -1 4552 return hasDirectTextDescendantTreeWalker(); 4832 45534833 -1 // https://www.w3.org/TR/wai-aria/roles4834 -1 var subRoles = {4835 -1 cell: ['gridcell', 'rowheader'],4836 -1 command: ['button', 'link', 'menuitem'],4837 -1 composite: ['grid', 'select', 'spinbutton', 'tablist'],4838 -1 img: ['doc-cover'],4839 -1 input: ['checkbox', 'option', 'radio', 'slider', 'spinbutton', 'textbox'],4840 -1 landmark: [4841 -1 'banner',4842 -1 'complementary',4843 -1 'contentinfo',4844 -1 'doc-acknowledgments',4845 -1 'doc-afterword',4846 -1 'doc-appendix',4847 -1 'doc-bibliography',4848 -1 'doc-chapter',4849 -1 'doc-conclusion',4850 -1 'doc-credits',4851 -1 'doc-endnotes',4852 -1 'doc-epilogue',4853 -1 'doc-errata',4854 -1 'doc-foreword',4855 -1 'doc-glossary',4856 -1 'doc-introduction',4857 -1 'doc-part',4858 -1 'doc-preface',4859 -1 'doc-prologue',4860 -1 'form',4861 -1 'main',4862 -1 'navigation',4863 -1 'region',4864 -1 'search',4865 -1 ],4866 -1 range: ['progressbar', 'scrollbar', 'slider', 'spinbutton'],4867 -1 roletype: ['structure', 'widget', 'window'],4868 -1 section: [4869 -1 'alert',4870 -1 'cell',4871 -1 'definition',4872 -1 'doc-abstract',4873 -1 'doc-colophon',4874 -1 'doc-credit',4875 -1 'doc-dedication',4876 -1 'doc-epigraph',4877 -1 'doc-example',4878 -1 'doc-footnote',4879 -1 'doc-qna',4880 -1 'figure',4881 -1 'group',4882 -1 'img',4883 -1 'landmark',4884 -1 'list',4885 -1 'listitem',4886 -1 'log',4887 -1 'marquee',4888 -1 'math',4889 -1 'note',4890 -1 'status',4891 -1 'table',4892 -1 'tabpanel',4893 -1 'term',4894 -1 'tooltip',4895 -1 ],4896 -1 sectionhead: [4897 -1 'columnheader',4898 -1 'doc-subtitle',4899 -1 'heading',4900 -1 'rowheader',4901 -1 'tab',4902 -1 ],4903 -1 select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'],4904 -1 separator: ['doc-pagebreak'],4905 -1 structure: [4906 -1 'application',4907 -1 'document',4908 -1 'none',4909 -1 'presentation',4910 -1 'rowgroup',4911 -1 'section',4912 -1 'sectionhead',4913 -1 'separator',4914 -1 ],4915 -1 table: ['grid'],4916 -1 textbox: ['searchbox'],4917 -1 widget: [4918 -1 'command',4919 -1 'composite',4920 -1 'gridcell',4921 -1 'input',4922 -1 'range',4923 -1 'row',4924 -1 'separator',4925 -1 'tab',4926 -1 ],4927 -1 window: ['dialog'],4928 -1 alert: ['alertdialog'],4929 -1 checkbox: ['menuitemcheckbox', 'switch'],4930 -1 dialog: ['alertdialog'],4931 -1 gridcell: ['columnheader', 'rowheader'],4932 -1 menuitem: ['menuitemcheckbox'],4933 -1 menuitemcheckbox: ['menuitemradio'],4934 -1 option: ['treeitem'],4935 -1 radio: ['menuitemradio'],4936 -1 status: ['timer'],4937 -1 grid: ['treegrid'],4938 -1 menu: ['menubar'],4939 -1 tree: ['treegrid'],4940 -1 document: ['article'],4941 -1 group: ['row', 'select', 'toolbar'],4942 -1 link: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],4943 -1 list: ['directory', 'feed'],4944 -1 listitem: ['doc-biblioentry', 'doc-endnote', 'treeitem'],4945 -1 navigation: ['doc-index', 'doc-pagelist', 'doc-toc'],4946 -1 note: ['doc-notice', 'doc-tip'],-1 4554 /** -1 4555 * Determines whether element has a text node as a direct descendant. -1 4556 * This method uses XPath on HTML DOM which is not universally supported. -1 4557 * @return {boolean} -1 4558 */ -1 4559 function hasDirectTextDescendantXpath() { -1 4560 var selectorResults = ownerDocument.evaluate(axs.properties.TEXT_CONTENT_XPATH, -1 4561 element, -1 4562 null, -1 4563 XPathResult.ANY_TYPE, -1 4564 null); -1 4565 for (var resultElement = selectorResults.iterateNext(); -1 4566 resultElement != null; -1 4567 resultElement = selectorResults.iterateNext()) { -1 4568 if (resultElement !== element) -1 4569 continue; -1 4570 return true; -1 4571 } -1 4572 return false; -1 4573 } -1 4574 -1 4575 /** -1 4576 * Determines whether element has a text node as a direct descendant. -1 4577 * This method uses TreeWalker as a fallback (at time of writing no version -1 4578 * of IE (including IE11) supports XPath in the HTML DOM). -1 4579 * @return {boolean} -1 4580 */ -1 4581 function hasDirectTextDescendantTreeWalker() { -1 4582 var treeWalker = ownerDocument.createTreeWalker(element, -1 4583 NodeFilter.SHOW_TEXT, -1 4584 null, -1 4585 false); -1 4586 while (treeWalker.nextNode()) { -1 4587 var resultElement = treeWalker.currentNode; -1 4588 var parent = resultElement.parentNode; -1 4589 // Handle elements hosted in <template>.content. -1 4590 parent = parent.host || parent; -1 4591 var tagName = parent.tagName.toLowerCase(); -1 4592 var value = resultElement.nodeValue.trim(); -1 4593 if (value && tagName !== 'script' && element !== resultElement) -1 4594 return true; -1 4595 } -1 4596 return false; -1 4597 } 4947 4598 }; 4948 45994949 -1 var getSubRoles = function(role) {4950 -1 var children = subRoles[role] || [];4951 -1 var descendents = children.map(getSubRoles);-1 4600 /** -1 4601 * @param {Element} element -1 4602 * @return {Object.<string, Object>} -1 4603 */ -1 4604 axs.properties.getContrastRatioProperties = function(element) { -1 4605 if (!axs.properties.hasDirectTextDescendant(element)) -1 4606 return null; 4952 46074953 -1 var result = [role];-1 4608 var contrastRatioProperties = {}; -1 4609 var style = window.getComputedStyle(element, null); -1 4610 var bgColor = axs.utils.getBgColor(style, element); -1 4611 if (!bgColor) -1 4612 return null; 4954 46134955 -1 descendents.forEach(function(list) {4956 -1 list.forEach(function(r) {4957 -1 if (result.indexOf(r) === -1) {4958 -1 result.push(r);4959 -1 }4960 -1 });4961 -1 });-1 4614 contrastRatioProperties['backgroundColor'] = axs.color.colorToString(bgColor); -1 4615 var fgColor = axs.utils.getFgColor(style, element, bgColor); -1 4616 contrastRatioProperties['foregroundColor'] = axs.color.colorToString(fgColor); -1 4617 var contrast = axs.utils.getContrastRatioForElementWithComputedStyle(style, element); -1 4618 if (!contrast) -1 4619 return null; -1 4620 contrastRatioProperties['value'] = contrast.toFixed(2); -1 4621 if (axs.utils.isLowContrast(contrast, style)) -1 4622 contrastRatioProperties['alert'] = true; 4962 46234963 -1 return result;-1 4624 var levelAAContrast = axs.utils.isLargeFont(style) ? 3.0 : 4.5; -1 4625 var levelAAAContrast = axs.utils.isLargeFont(style) ? 4.5 : 7.0; -1 4626 var desiredContrastRatios = {}; -1 4627 if (levelAAContrast > contrast) -1 4628 desiredContrastRatios['AA'] = levelAAContrast; -1 4629 if (levelAAAContrast > contrast) -1 4630 desiredContrastRatios['AAA'] = levelAAAContrast; -1 4631 -1 4632 if (!Object.keys(desiredContrastRatios).length) -1 4633 return contrastRatioProperties; -1 4634 -1 4635 var suggestedColors = axs.color.suggestColors(bgColor, fgColor, desiredContrastRatios); -1 4636 if (suggestedColors && Object.keys(suggestedColors).length) -1 4637 contrastRatioProperties['suggestedColors'] = suggestedColors; -1 4638 return contrastRatioProperties; 4964 4639 }; 4965 46404966 -1 exports.subRoles = {};4967 -1 for (var role in subRoles) {4968 -1 exports.subRoles[role] = getSubRoles(role);4969 -1 }4970 -1 exports.subRoles['none'] = ['none', 'presentation'];4971 -1 exports.subRoles['presentation'] = ['presentation', 'none'];-1 4641 /** -1 4642 * @param {Node} node -1 4643 * @param {!Object} textAlternatives The properties object to fill in -1 4644 * @param {boolean=} opt_recursive Whether this is a recursive call or not -1 4645 * @param {boolean=} opt_force Whether to return text alternatives for this -1 4646 * element regardless of its hidden state. -1 4647 * @return {?string} The calculated text alternative for the given element -1 4648 */ -1 4649 axs.properties.findTextAlternatives = function(node, textAlternatives, opt_recursive, opt_force) { -1 4650 var recursive = opt_recursive || false; 4972 46514973 -1 exports.nameFromContents = [4974 -1 'button',4975 -1 'checkbox',4976 -1 'columnheader',4977 -1 'doc-backlink',4978 -1 'doc-biblioref',4979 -1 'doc-glossref',4980 -1 'doc-noteref',4981 -1 'gridcell',4982 -1 'heading',4983 -1 'link',4984 -1 'menuitem',4985 -1 'menuitemcheckbox',4986 -1 'menuitemradio',4987 -1 'option',4988 -1 'radio',4989 -1 'row',4990 -1 'rowgroup',4991 -1 'rowheader',4992 -1 'sectionhead',4993 -1 'tab',4994 -1 'tooltip',4995 -1 'treeitem',4996 -1 'switch',4997 -1 ];-1 4652 /** @type {Element} */ var element = axs.dom.asElement(node); -1 4653 if (!element) -1 4654 return null; 4998 46554999 -1 exports.labelable = [5000 -1 'button',5001 -1 'input:not([type="hidden"])',5002 -1 'keygen',5003 -1 'meter',5004 -1 'output',5005 -1 'progress',5006 -1 'select',5007 -1 'textarea',5008 -1 ];-1 4656 // 1. Skip hidden elements unless the author specifies to use them via an aria-labelledby or -1 4657 // aria-describedby being used in the current computation. -1 4658 if (!opt_force && axs.utils.isElementOrAncestorHidden(element)) -1 4659 return null; 5009 46605010 -1 },{}],9:[function(require,module,exports){5011 -1 var constants = require('./constants.js');5012 -1 var query = require('./query.js');-1 4661 // if this is a text node, just return text content. -1 4662 if (node.nodeType == Node.TEXT_NODE) { -1 4663 var textContentValue = {}; -1 4664 textContentValue.type = 'text'; -1 4665 textContentValue.text = node.textContent; -1 4666 textContentValue.lastWord = axs.properties.getLastWord(textContentValue.text); -1 4667 textAlternatives['content'] = textContentValue; -1 4668 -1 4669 return node.textContent; -1 4670 } -1 4671 -1 4672 var computedName = null; -1 4673 -1 4674 if (!recursive) { -1 4675 // 2A. The aria-labelledby attribute takes precedence as the element's text alternative -1 4676 // unless this computation is already occurring as the result of a recursive aria-labelledby -1 4677 // declaration. -1 4678 computedName = axs.properties.getTextFromAriaLabelledby(element, textAlternatives); -1 4679 } -1 4680 -1 4681 // 2A. If aria-labelledby is empty or undefined, the aria-label attribute, which defines an -1 4682 // explicit text string, is used. -1 4683 if (element.hasAttribute('aria-label')) { -1 4684 var ariaLabelValue = {}; -1 4685 ariaLabelValue.type = 'text'; -1 4686 ariaLabelValue.text = element.getAttribute('aria-label'); -1 4687 ariaLabelValue.lastWord = axs.properties.getLastWord(ariaLabelValue.text); -1 4688 if (computedName) -1 4689 ariaLabelValue.unused = true; -1 4690 else if (!(recursive && axs.utils.elementIsHtmlControl(element))) -1 4691 computedName = ariaLabelValue.text; -1 4692 textAlternatives['ariaLabel'] = ariaLabelValue; -1 4693 } -1 4694 -1 4695 // 2A. If aria-labelledby and aria-label are both empty or undefined, and if the element is not -1 4696 // marked as presentational (role="presentation", check for the presence of an equivalent host -1 4697 // language attribute or element for associating a label, and use those mechanisms to determine -1 4698 // a text alternative. -1 4699 if (!element.hasAttribute('role') || element.getAttribute('role') != 'presentation') { -1 4700 computedName = axs.properties.getTextFromHostLanguageAttributes(element, -1 4701 textAlternatives, -1 4702 computedName, -1 4703 recursive); -1 4704 } -1 4705 -1 4706 // 2B (HTML version). -1 4707 if (recursive && axs.utils.elementIsHtmlControl(element)) { -1 4708 var defaultView = element.ownerDocument.defaultView; -1 4709 -1 4710 // include the value of the embedded control as part of the text alternative in the -1 4711 // following manner: -1 4712 if (element instanceof defaultView.HTMLInputElement) { -1 4713 // If the embedded control is a text field, use its value. -1 4714 var inputElement = /** @type {HTMLInputElement} */ (element); -1 4715 if (inputElement.type == 'text') { -1 4716 if (inputElement.value && inputElement.value.length > 0) -1 4717 textAlternatives['controlValue'] = { 'text': inputElement.value }; -1 4718 } -1 4719 // If the embedded control is a range (e.g. a spinbutton or slider), use the value of the -1 4720 // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow -1 4721 // attribute. -1 4722 if (inputElement.type == 'range') -1 4723 textAlternatives['controlValue'] = { 'text': inputElement.value }; -1 4724 } -1 4725 // If the embedded control is a menu, use the text alternative of the chosen menu item. -1 4726 // If the embedded control is a select or combobox, use the chosen option. -1 4727 if (element instanceof defaultView.HTMLSelectElement) { -1 4728 var inputElement = /** @type {HTMLSelectElement} */ (element); -1 4729 textAlternatives['controlValue'] = { 'text': inputElement.value }; -1 4730 } -1 4731 -1 4732 if (textAlternatives['controlValue']) { -1 4733 var controlValue = textAlternatives['controlValue']; -1 4734 if (computedName) -1 4735 controlValue.unused = true; -1 4736 else -1 4737 computedName = controlValue.text; -1 4738 } -1 4739 } -1 4740 -1 4741 // 2B (ARIA version). -1 4742 if (recursive && axs.utils.elementIsAriaWidget(element)) { -1 4743 var role = element.getAttribute('role'); -1 4744 // If the embedded control is a text field, use its value. -1 4745 if (role == 'textbox') { -1 4746 if (element.textContent && element.textContent.length > 0) -1 4747 textAlternatives['controlValue'] = { 'text': element.textContent }; -1 4748 } -1 4749 // If the embedded control is a range (e.g. a spinbutton or slider), use the value of the -1 4750 // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow -1 4751 // attribute. -1 4752 if (role == 'slider' || role == 'spinbutton') { -1 4753 if (element.hasAttribute('aria-valuetext')) -1 4754 textAlternatives['controlValue'] = { 'text': element.getAttribute('aria-valuetext') }; -1 4755 else if (element.hasAttribute('aria-valuenow')) -1 4756 textAlternatives['controlValue'] = { 'value': element.getAttribute('aria-valuenow'), -1 4757 'text': '' + element.getAttribute('aria-valuenow') }; -1 4758 } -1 4759 // If the embedded control is a menu, use the text alternative of the chosen menu item. -1 4760 if (role == 'menu') { -1 4761 var menuitems = element.querySelectorAll('[role=menuitemcheckbox], [role=menuitemradio]'); -1 4762 var selectedMenuitems = []; -1 4763 for (var i = 0; i < menuitems.length; i++) { -1 4764 if (menuitems[i].getAttribute('aria-checked') == 'true') -1 4765 selectedMenuitems.push(menuitems[i]); -1 4766 } -1 4767 if (selectedMenuitems.length > 0) { -1 4768 var selectedMenuText = ''; -1 4769 for (var i = 0; i < selectedMenuitems.length; i++) { -1 4770 selectedMenuText += axs.properties.findTextAlternatives(selectedMenuitems[i], {}, true); -1 4771 if (i < selectedMenuitems.length - 1) -1 4772 selectedMenuText += ', '; -1 4773 } -1 4774 textAlternatives['controlValue'] = { 'text': selectedMenuText }; -1 4775 } -1 4776 } -1 4777 // If the embedded control is a select or combobox, use the chosen option. -1 4778 if (role == 'combobox' || role == 'select') { -1 4779 // TODO -1 4780 textAlternatives['controlValue'] = { 'text': 'TODO' }; -1 4781 } -1 4782 -1 4783 if (textAlternatives['controlValue']) { -1 4784 var controlValue = textAlternatives['controlValue']; -1 4785 if (computedName) -1 4786 controlValue.unused = true; -1 4787 else -1 4788 computedName = controlValue.text; -1 4789 } -1 4790 } 5013 47915014 -1 var getPseudoContent = function(node, selector) {5015 -1 var styles = window.getComputedStyle(node, selector);5016 -1 var ret = styles.getPropertyValue('content');5017 -1 if (ret === 'none' || ret.substr(0, 4) === '-moz') {5018 -1 return '';5019 -1 } else {5020 -1 return ret5021 -1 .replace(/^["']/, '')5022 -1 .replace(/["']$/, '');5023 -1 }5024 -1 };-1 4792 // 2C. Otherwise, if the attributes checked in rules A and B didn't provide results, text is -1 4793 // collected from descendant content if the current element's role allows "Name From: contents." -1 4794 var hasRole = element.hasAttribute('role'); -1 4795 var canGetNameFromContents = true; -1 4796 if (hasRole) { -1 4797 var roleName = element.getAttribute('role'); -1 4798 // if element has a role, check that it allows "Name From: contents" -1 4799 var role = axs.constants.ARIA_ROLES[roleName]; -1 4800 if (role && (!role.namefrom || role.namefrom.indexOf('contents') < 0)) -1 4801 canGetNameFromContents = false; -1 4802 } -1 4803 var textFromContent = axs.properties.getTextFromDescendantContent(element, opt_force); -1 4804 if (textFromContent && canGetNameFromContents) { -1 4805 var textFromContentValue = {}; -1 4806 textFromContentValue.type = 'text'; -1 4807 textFromContentValue.text = textFromContent; -1 4808 textFromContentValue.lastWord = axs.properties.getLastWord(textFromContentValue.text); -1 4809 if (computedName) -1 4810 textFromContentValue.unused = true; -1 4811 else -1 4812 computedName = textFromContent; -1 4813 textAlternatives['content'] = textFromContentValue; -1 4814 } 5025 48155026 -1 var getContent = function(root, referenced) {5027 -1 var ret = getPseudoContent(root, ':before');5028 -1 var node = root.firstChild;5029 -1 while (node) {5030 -1 if (node.nodeType === node.TEXT_NODE) {5031 -1 ret += node.textContent;5032 -1 } else if (node.nodeType === node.ELEMENT_NODE) {5033 -1 ret += getName(node, true, referenced);5034 -1 }5035 -1 node = node.nextSibling;5036 -1 }5037 -1 ret += getPseudoContent(root, ':after');5038 -1 return ret;5039 -1 };-1 4816 // 2D. The last resort is to use text from a tooltip attribute (such as the title attribute in -1 4817 // HTML). This is used only if nothing else, including subtree content, has provided results. -1 4818 if (element.hasAttribute('title')) { -1 4819 var titleValue = {}; -1 4820 titleValue.type = 'string'; -1 4821 titleValue.valid = true; -1 4822 titleValue.text = element.getAttribute('title'); -1 4823 titleValue.lastWord = axs.properties.getLastWord(titleValue.lastWord); -1 4824 if (computedName) -1 4825 titleValue.unused = true; -1 4826 else -1 4827 computedName = titleValue.text; -1 4828 textAlternatives['title'] = titleValue; -1 4829 } 5040 48305041 -1 var allowNameFromContent = function(el) {5042 -1 var role = query.getRole(el);5043 -1 return !role || constants.nameFromContents.indexOf(role) !== -1;5044 -1 };-1 4831 if (Object.keys(textAlternatives).length == 0 && computedName == null) -1 4832 return null; 5045 48335046 -1 var isLabelable = function(el) {5047 -1 var selector = constants.labelable.join(',');5048 -1 return el.matches(selector);-1 4834 return computedName; 5049 4835 }; 5050 48365051 -1 // Control.labels is part of the standard, but not supported in most browsers5052 -1 var getLabelNode = function(node) {5053 -1 if (node.id) {5054 -1 var selector = 'label[for="' + node.id + '"]';5055 -1 var label = document.querySelector(selector);5056 -1 if (label) {5057 -1 return label;5058 -1 }5059 -1 }5060 -15061 -1 var p = node.parentElement;5062 -1 while (p) {5063 -1 if (p.tagName.toLowerCase() === 'label') {5064 -1 return p;5065 -1 }5066 -1 p = p.parentElement;5067 -1 }-1 4837 /** -1 4838 * @param {Element} element -1 4839 * @param {boolean=} opt_force Whether to return text alternatives for this -1 4840 * element regardless of its hidden state. -1 4841 * @return {?string} -1 4842 */ -1 4843 axs.properties.getTextFromDescendantContent = function(element, opt_force) { -1 4844 var children = element.childNodes; -1 4845 var childrenTextContent = []; -1 4846 for (var i = 0; i < children.length; i++) { -1 4847 var childTextContent = axs.properties.findTextAlternatives(children[i], {}, true, opt_force); -1 4848 if (childTextContent) -1 4849 childrenTextContent.push(childTextContent.trim()); -1 4850 } -1 4851 if (childrenTextContent.length) { -1 4852 var result = ''; -1 4853 // Empty children are allowed, but collapse all of them -1 4854 for (var i = 0; i < childrenTextContent.length; i++) -1 4855 result = [result, childrenTextContent[i]].join(' ').trim(); -1 4856 return result; -1 4857 } -1 4858 return null; 5068 4859 }; 5069 48605070 -1 // http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/5071 -1 // https://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te5072 -1 var getName = function(el, recursive, referenced) {5073 -1 var ret;-1 4861 /** -1 4862 * @param {Element} element -1 4863 * @param {Object} textAlternatives -1 4864 * @return {?string} -1 4865 */ -1 4866 axs.properties.getTextFromAriaLabelledby = function(element, textAlternatives) { -1 4867 var computedName = null; -1 4868 if (!element.hasAttribute('aria-labelledby')) -1 4869 return computedName; 5074 48705075 -1 if (query.getAttribute(el, 'hidden', referenced)) {5076 -1 return '';5077 -1 }5078 -1 if (query.matches(el, 'presentation')) {5079 -1 return getContent(el, referenced);5080 -1 }5081 -1 if (!recursive && el.matches('[aria-labelledby]')) {5082 -1 var ids = el.getAttribute('aria-labelledby').split(/\s+/);5083 -1 var strings = ids.map(function(id) {5084 -1 var label = document.getElementById(id);5085 -1 return getName(label, true, label);5086 -1 });5087 -1 ret = strings.join(' ');5088 -1 }5089 -1 if (!ret && el.matches('[aria-label]')) {5090 -1 ret = el.getAttribute('aria-label');5091 -1 }5092 -1 if (!query.matches(el, 'presentation')) {5093 -1 if (!ret && isLabelable(el)) {5094 -1 var label = getLabelNode(el);5095 -1 if (!recursive && label) {5096 -1 ret = getName(label, true, label);5097 -1 }5098 -1 }5099 -1 if (!ret) {5100 -1 ret = el.getAttribute('placeholder');5101 -1 }5102 -1 // figcaption5103 -1 if (!ret) {5104 -1 ret = el.getAttribute('alt');5105 -1 }5106 -1 // caption5107 -1 // table5108 -1 }5109 -1 // FIXME only if this is embedded in a label5110 -1 if (!ret && query.matches(el, 'input')) {5111 -1 // combobox5112 -1 // button5113 -1 if (query.matches(el, 'range')) {5114 -1 ret = query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value;5115 -1 } else {5116 -1 ret = el.value;5117 -1 }5118 -1 ret = '' + ret;5119 -1 }5120 -1 if (!ret && (recursive || allowNameFromContent(el))) {5121 -1 ret = getContent(el, referenced);5122 -1 }5123 -1 if (!ret) {5124 -1 ret = el.getAttribute('title');5125 -1 }-1 4871 var labelledbyAttr = element.getAttribute('aria-labelledby'); -1 4872 var labelledbyIds = labelledbyAttr.split(/\s+/); -1 4873 var labelledbyValue = {}; -1 4874 labelledbyValue.valid = true; -1 4875 var labelledbyText = []; -1 4876 var labelledbyValues = []; -1 4877 for (var i = 0; i < labelledbyIds.length; i++) { -1 4878 var labelledby = {}; -1 4879 labelledby.type = 'element'; -1 4880 var labelledbyId = labelledbyIds[i]; -1 4881 labelledby.value = labelledbyId; -1 4882 var labelledbyElement = document.getElementById(labelledbyId); -1 4883 if (!labelledbyElement) { -1 4884 labelledby.valid = false; -1 4885 labelledbyValue.valid = false; -1 4886 labelledby.errorMessage = { 'messageKey': 'noElementWithId', 'args': [labelledbyId] }; -1 4887 } else { -1 4888 labelledby.valid = true; -1 4889 labelledby.text = axs.properties.findTextAlternatives(labelledbyElement, {}, true, true); -1 4890 labelledby.lastWord = axs.properties.getLastWord(labelledby.text); -1 4891 labelledbyText.push(labelledby.text); -1 4892 labelledby.element = labelledbyElement; -1 4893 } -1 4894 labelledbyValues.push(labelledby); -1 4895 } -1 4896 if (labelledbyValues.length > 0) { -1 4897 labelledbyValues[labelledbyValues.length - 1].last = true; -1 4898 labelledbyValue.values = labelledbyValues; -1 4899 labelledbyValue.text = labelledbyText.join(' '); -1 4900 labelledbyValue.lastWord = axs.properties.getLastWord(labelledbyValue.text); -1 4901 computedName = labelledbyValue.text; -1 4902 textAlternatives['ariaLabelledby'] = labelledbyValue; -1 4903 } 5126 49045127 -1 return (ret || '').trim().replace(/\s+/g, ' ');-1 4905 return computedName; 5128 4906 }; 5129 49075130 -1 var getDescription = function(el) {5131 -1 var ret = '';5132 49085133 -1 if (el.matches('[aria-describedby]')) {5134 -1 var ids = el.getAttribute('aria-describedby').split(/\s+/);5135 -1 var strings = ids.map(function(id) {5136 -1 var label = document.getElementById(id);5137 -1 return getName(label, true, label);5138 -1 });5139 -1 ret = strings.join(' ');5140 -1 } else if (el.title) {5141 -1 ret = el.title;5142 -1 } else if (el.placeholder) {5143 -1 ret = el.placeholder;5144 -1 }-1 4909 /** -1 4910 * Determine the text description/label for an element. -1 4911 * For example will attempt to find the alt text for an image or label text for a form control. -1 4912 * @param {!Element} element -1 4913 * @param {!Object} textAlternatives An object that will be updated with information. -1 4914 * @param {?string} existingComputedname -1 4915 * @param {boolean} recursive Whether this method is being called recursively as described in -1 4916 * http://www.w3.org/TR/wai-aria/roles#textalternativecomputation section 2A. -1 4917 * @return {Object} -1 4918 */ -1 4919 axs.properties.getTextFromHostLanguageAttributes = function(element, -1 4920 textAlternatives, -1 4921 existingComputedname, -1 4922 recursive) { -1 4923 var computedName = existingComputedname; -1 4924 if (axs.browserUtils.matchSelector(element, 'img') && element.hasAttribute('alt')) { -1 4925 var altValue = {}; -1 4926 altValue.type = 'string'; -1 4927 altValue.valid = true; -1 4928 altValue.text = element.getAttribute('alt'); -1 4929 if (computedName) -1 4930 altValue.unused = true; -1 4931 else -1 4932 computedName = altValue.text; -1 4933 textAlternatives['alt'] = altValue; -1 4934 } -1 4935 -1 4936 var controlsSelector = ['input:not([type="hidden"]):not([disabled])', -1 4937 'select:not([disabled])', -1 4938 'textarea:not([disabled])', -1 4939 'button:not([disabled])', -1 4940 'video:not([disabled])'].join(', '); -1 4941 if (axs.browserUtils.matchSelector(element, controlsSelector) && !recursive) { -1 4942 if (element.hasAttribute('id')) { -1 4943 var labelForQuerySelector = 'label[for="' + element.id + '"]'; -1 4944 var labelsFor = document.querySelectorAll(labelForQuerySelector); -1 4945 var labelForValue = {}; -1 4946 var labelForValues = []; -1 4947 var labelForText = []; -1 4948 for (var i = 0; i < labelsFor.length; i++) { -1 4949 var labelFor = {}; -1 4950 labelFor.type = 'element'; -1 4951 var label = labelsFor[i]; -1 4952 var labelText = axs.properties.findTextAlternatives(label, {}, true); -1 4953 if (labelText && labelText.trim().length > 0) { -1 4954 labelFor.text = labelText.trim(); -1 4955 labelForText.push(labelText.trim()); -1 4956 } -1 4957 labelFor.element = label; -1 4958 labelForValues.push(labelFor); -1 4959 } -1 4960 if (labelForValues.length > 0) { -1 4961 labelForValues[labelForValues.length - 1].last = true; -1 4962 labelForValue.values = labelForValues; -1 4963 labelForValue.text = labelForText.join(' '); -1 4964 labelForValue.lastWord = axs.properties.getLastWord(labelForValue.text); -1 4965 if (computedName) -1 4966 labelForValue.unused = true; -1 4967 else -1 4968 computedName = labelForValue.text; -1 4969 textAlternatives['labelFor'] = labelForValue; -1 4970 } -1 4971 } 5145 49725146 -1 return (ret || '').trim().replace(/\s+/g, ' ');-1 4973 var parent = axs.dom.parentElement(element); -1 4974 var labelWrappedValue = {}; -1 4975 while (parent) { -1 4976 if (parent.tagName.toLowerCase() == 'label') { -1 4977 var parentLabel = /** @type {HTMLLabelElement} */ (parent); -1 4978 if (parentLabel.control == element) { -1 4979 labelWrappedValue.type = 'element'; -1 4980 labelWrappedValue.text = axs.properties.findTextAlternatives(parentLabel, {}, true); -1 4981 labelWrappedValue.lastWord = axs.properties.getLastWord(labelWrappedValue.text); -1 4982 labelWrappedValue.element = parentLabel; -1 4983 break; -1 4984 } -1 4985 } -1 4986 parent = axs.dom.parentElement(parent); -1 4987 } -1 4988 if (labelWrappedValue.text) { -1 4989 if (computedName) -1 4990 labelWrappedValue.unused = true; -1 4991 else -1 4992 computedName = labelWrappedValue.text; -1 4993 textAlternatives['labelWrapped'] = labelWrappedValue; -1 4994 } -1 4995 // If all else fails input of type image can fall back to its alt text -1 4996 if (axs.browserUtils.matchSelector(element, 'input[type="image"]') && element.hasAttribute('alt')) { -1 4997 var altValue = {}; -1 4998 altValue.type = 'string'; -1 4999 altValue.valid = true; -1 5000 altValue.text = element.getAttribute('alt'); -1 5001 if (computedName) -1 5002 altValue.unused = true; -1 5003 else -1 5004 computedName = altValue.text; -1 5005 textAlternatives['alt'] = altValue; -1 5006 } -1 5007 if (!Object.keys(textAlternatives).length) -1 5008 textAlternatives['noLabel'] = true; -1 5009 } -1 5010 return computedName; 5147 5011 }; 5148 50125149 -1 module.exports = {5150 -1 getName: getName,5151 -1 getDescription: getDescription,-1 5013 /** -1 5014 * @param {?string} text -1 5015 * @return {?string} -1 5016 */ -1 5017 axs.properties.getLastWord = function(text) { -1 5018 if (!text) -1 5019 return null; -1 5020 -1 5021 // TODO: this makes a lot of assumptions. -1 5022 var lastSpace = text.lastIndexOf(' ') + 1; -1 5023 var MAXLENGTH = 10; -1 5024 var cutoff = text.length - MAXLENGTH; -1 5025 var wordStart = lastSpace > cutoff ? lastSpace : cutoff; -1 5026 return text.substring(wordStart); 5152 5027 }; 5153 50285154 -1 },{"./constants.js":8,"./query.js":10}],10:[function(require,module,exports){5155 -1 var constants = require('./constants.js');5156 -1 var util = require('./util.js');-1 5029 /** -1 5030 * @param {Node} node -1 5031 * @return {Object} -1 5032 */ -1 5033 axs.properties.getTextProperties = function(node) { -1 5034 var textProperties = {}; -1 5035 var computedName = axs.properties.findTextAlternatives(node, textProperties, false, true); 5157 50365158 -1 var getSubRoles = function(roles) {5159 -1 return [].concat.apply([], roles.map(function(role) {5160 -1 return constants.subRoles[role] || [role];5161 -1 }));5162 -1 };-1 5037 if (Object.keys(textProperties).length == 0) { -1 5038 /** @type {Element} */ var element = axs.dom.asElement(node); -1 5039 if (element && axs.browserUtils.matchSelector(element, 'img')) { -1 5040 var altValue = {}; -1 5041 altValue.valid = false; -1 5042 altValue.errorMessage = 'No alt value provided'; -1 5043 textProperties['alt'] = altValue; 5163 50445164 -1 // candidates can be passed for performance optimization5165 -1 var _getRole = function(el, candidates) {5166 -1 if (el.hasAttribute('role')) {5167 -1 return el.getAttribute('role');5168 -1 }5169 -1 for (var role in constants.extraSelectors) {5170 -1 var selector = constants.extraSelectors[role].join(',');5171 -1 if ((!candidates || candidates.indexOf(role) !== -1) && el.matches(selector)) {5172 -1 return role;5173 -1 }5174 -1 }-1 5045 var src = element.src; -1 5046 if (typeof src == 'string') { -1 5047 var parts = src.split('/'); -1 5048 var filename = parts.pop(); -1 5049 var filenameValue = { text: filename }; -1 5050 textProperties['filename'] = filenameValue; -1 5051 computedName = filename; -1 5052 } -1 5053 } 5175 50545176 -1 if (!candidates ||5177 -1 candidates.indexOf('banner') !== -1 ||5178 -1 candidates.indexOf('contentinfo') !== -1) {5179 -1 var scoped = el.matches(constants.scoped);-1 5055 if (!computedName) -1 5056 return null; -1 5057 } 5180 50585181 -1 if (el.matches('header') && !scoped) {5182 -1 return 'banner';5183 -1 }5184 -1 if (el.matches('footer') && !scoped) {5185 -1 return 'contentinfo';5186 -1 }5187 -1 }-1 5059 textProperties.hasProperties = Boolean(Object.keys(textProperties).length); -1 5060 textProperties.computedText = computedName; -1 5061 textProperties.lastWord = axs.properties.getLastWord(computedName); -1 5062 return textProperties; 5188 5063 }; 5189 50645190 -1 var getAttribute = function(el, key, _hiddenRoot) {5191 -1 if (key === 'hidden' && el === _hiddenRoot) { // used for name calculation5192 -1 return false;5193 -1 }-1 5065 /** -1 5066 * Finds any ARIA attributes (roles, states and properties) explicitly set on this element. -1 5067 * @param {Element} element -1 5068 * @return {Object} -1 5069 */ -1 5070 axs.properties.getAriaProperties = function(element) { -1 5071 var ariaProperties = {}; -1 5072 var statesAndProperties = axs.properties.getGlobalAriaProperties(element); 5194 50735195 -1 var type = constants.attributes[key];5196 -1 var raw = el.getAttribute('aria-' + key);-1 5074 for (var property in axs.constants.ARIA_PROPERTIES) { -1 5075 var attributeName = 'aria-' + property; -1 5076 if (element.hasAttribute(attributeName)) { -1 5077 var propertyValue = element.getAttribute(attributeName); -1 5078 statesAndProperties[attributeName] = -1 5079 axs.utils.getAriaPropertyValue(attributeName, propertyValue, element); -1 5080 } -1 5081 } -1 5082 if (Object.keys(statesAndProperties).length > 0) -1 5083 ariaProperties['properties'] = axs.utils.values(statesAndProperties); 5197 50845198 -1 if (raw) {5199 -1 if (type === 'bool') {5200 -1 return raw === 'true';5201 -1 } else if (type === 'tristate') {5202 -1 return raw === 'true' ? true : raw === 'false' ? false : 'mixed';5203 -1 } else if (type === 'bool-undefined') {5204 -1 return raw === 'true' ? true : raw === 'false' ? false : undefined;5205 -1 } else if (type === 'id-list') {5206 -1 return raw.split(/\s+/);5207 -1 } else if (type === 'integer') {5208 -1 return parseInt(raw);5209 -1 } else if (type === 'number') {5210 -1 return parseFloat(raw);5211 -1 } else if (type === 'token-list') {5212 -1 return raw.split(/\s+/);5213 -1 } else {5214 -1 return raw;5215 -1 }5216 -1 }-1 5085 var roles = axs.utils.getRoles(element); -1 5086 if (!roles) { -1 5087 if (Object.keys(ariaProperties).length) -1 5088 return ariaProperties; -1 5089 return null; -1 5090 } -1 5091 ariaProperties['roles'] = roles; -1 5092 if (!roles.valid || !roles['roles']) -1 5093 return ariaProperties; 5217 50945218 -1 if (key === 'level') {5219 -1 for (var i = 1; i <= 6; i++) {5220 -1 if (el.tagName.toLowerCase() === 'h' + i) {5221 -1 return i;5222 -1 }5223 -1 }5224 -1 } else if (key === 'disabled') {5225 -1 return el.disabled;5226 -1 } else if (key === 'placeholder') {5227 -1 return el.placeholder;5228 -1 } else if (key === 'required') {5229 -1 return el.required;5230 -1 } else if (key === 'readonly') {5231 -1 return el.readOnly && !el.isContentEditable;5232 -1 } else if (key === 'hidden') {5233 -1 var style = window.getComputedStyle(el);5234 -1 if (el.hidden || style.display === 'none' || style.visibility === 'hidden') {5235 -1 return true;5236 -1 } else if (el.clientHeight === 0) { // rough check for performance5237 -1 return el.parentNode && getAttribute(el.parentNode, 'hidden', _hiddenRoot);5238 -1 }5239 -1 } else if (key === 'invalid' && el.checkValidity) {5240 -1 return el.checkValidity();5241 -1 }-1 5095 var roleDetails = roles['roles']; -1 5096 for (var i = 0; i < roleDetails.length; i++) { -1 5097 var role = roleDetails[i]; -1 5098 if (!role.details || !role.details.propertiesSet) -1 5099 continue; -1 5100 for (var property in role.details.propertiesSet) { -1 5101 if (property in statesAndProperties) -1 5102 continue; -1 5103 if (element.hasAttribute(property)) { -1 5104 var propertyValue = element.getAttribute(property); -1 5105 statesAndProperties[property] = -1 5106 axs.utils.getAriaPropertyValue(property, propertyValue, element); -1 5107 if ('values' in statesAndProperties[property]) { -1 5108 var values = statesAndProperties[property].values; -1 5109 values[values.length - 1].isLast = true; -1 5110 } -1 5111 } else if (role.details.requiredPropertiesSet[property]) { -1 5112 statesAndProperties[property] = -1 5113 { 'name': property, 'valid': false, 'reason': 'Required property not set' }; -1 5114 } -1 5115 } -1 5116 } -1 5117 if (Object.keys(statesAndProperties).length > 0) -1 5118 ariaProperties['properties'] = axs.utils.values(statesAndProperties); -1 5119 if (Object.keys(ariaProperties).length > 0) -1 5120 return ariaProperties; -1 5121 return null; -1 5122 }; -1 5123 -1 5124 /** -1 5125 * Gets the ARIA properties found on this element which apply to all elements, not just elements with ARIA roles. -1 5126 * @param {Element} element -1 5127 * @return {!Object} -1 5128 */ -1 5129 axs.properties.getGlobalAriaProperties = function(element) { -1 5130 var globalProperties = {}; -1 5131 for (var property in axs.constants.GLOBAL_PROPERTIES) { -1 5132 if (element.hasAttribute(property)) { -1 5133 var propertyValue = element.getAttribute(property); -1 5134 globalProperties[property] = -1 5135 axs.utils.getAriaPropertyValue(property, propertyValue, element); -1 5136 } -1 5137 } -1 5138 return globalProperties; -1 5139 }; -1 5140 -1 5141 /** -1 5142 * @param {Element} element -1 5143 * @return {Object.<string, Object>} -1 5144 */ -1 5145 axs.properties.getVideoProperties = function(element) { -1 5146 var videoSelector = 'video'; -1 5147 if (!axs.browserUtils.matchSelector(element, videoSelector)) -1 5148 return null; -1 5149 var videoProperties = {}; -1 5150 videoProperties['captionTracks'] = axs.properties.getTrackElements(element, 'captions'); -1 5151 videoProperties['descriptionTracks'] = axs.properties.getTrackElements(element, 'descriptions'); -1 5152 videoProperties['chapterTracks'] = axs.properties.getTrackElements(element, 'chapters'); -1 5153 // error if no text alternatives? -1 5154 return videoProperties; -1 5155 }; 5242 51565243 -1 if (type === 'bool' || type === 'tristate') {5244 -1 return false;5245 -1 }-1 5157 /** -1 5158 * @param {Element} element -1 5159 * @param {string} kind -1 5160 * @return {Object} -1 5161 */ -1 5162 axs.properties.getTrackElements = function(element, kind) { -1 5163 // error if resource is not available -1 5164 var trackElements = element.querySelectorAll('track[kind=' + kind + ']'); -1 5165 var result = {}; -1 5166 if (!trackElements.length) { -1 5167 result.valid = false; -1 5168 result.reason = { 'messageKey': 'noTracksProvided', 'args': [[kind]] }; -1 5169 return result; -1 5170 } -1 5171 result.valid = true; -1 5172 var values = []; -1 5173 for (var i = 0; i < trackElements.length; i++) { -1 5174 var trackElement = {}; -1 5175 var src = trackElements[i].getAttribute('src'); -1 5176 var srcLang = trackElements[i].getAttribute('srcLang'); -1 5177 var label = trackElements[i].getAttribute('label'); -1 5178 if (!src) { -1 5179 trackElement.valid = false; -1 5180 trackElement.reason = { 'messageKey': 'noSrcProvided' }; -1 5181 } else { -1 5182 trackElement.valid = true; -1 5183 trackElement.src = src; -1 5184 } -1 5185 var name = ''; -1 5186 if (label) { -1 5187 name += label; -1 5188 if (srcLang) -1 5189 name += ' '; -1 5190 } -1 5191 if (srcLang) -1 5192 name += '(' + srcLang + ')'; -1 5193 if (name == '') -1 5194 name = '[' + { 'messageKey': 'unnamed' } + ']'; -1 5195 trackElement.name = name; -1 5196 values.push(trackElement); -1 5197 } -1 5198 result.values = values; -1 5199 return result; 5246 5200 }; 5247 52015248 -1 var matches = function(el, selector) {5249 -1 var actual;-1 5202 /** -1 5203 * @param {Node} node -1 5204 * @return {Object.<string, Object>} -1 5205 */ -1 5206 axs.properties.getAllProperties = function(node) { -1 5207 /** @type {Element} */ var element = axs.dom.asElement(node); -1 5208 if (!element) -1 5209 return {}; 5250 52105251 -1 if (selector.substr(0, 1) === ':') {5252 -1 var attr = selector.substr(1);5253 -1 return getAttribute(el, attr);5254 -1 } else if (selector.substr(0, 1) === '[') {5255 -1 var match = /\[([a-z]+)="(.*)"\]/.exec(selector);5256 -1 actual = getAttribute(el, match[1]);5257 -1 var rawValue = match[2];5258 -1 return actual.toString() == rawValue;5259 -1 } else {5260 -1 var candidates = getSubRoles(selector.split(','));5261 -1 actual = _getRole(el, candidates);5262 -1 return candidates.indexOf(actual) !== -1;5263 -1 }-1 5211 var allProperties = {}; -1 5212 allProperties['ariaProperties'] = axs.properties.getAriaProperties(element); -1 5213 allProperties['colorProperties'] = axs.properties.getColorProperties(element); -1 5214 allProperties['focusProperties'] = axs.properties.getFocusProperties(element); -1 5215 allProperties['textProperties'] = axs.properties.getTextProperties(node); -1 5216 allProperties['videoProperties'] = axs.properties.getVideoProperties(element); -1 5217 return allProperties; 5264 5218 }; 5265 52195266 -1 var _querySelector = function(all) {5267 -1 return function(root, role) {5268 -1 var results = [];5269 -1 util.walkDOM(root, function(node) {5270 -1 if (node.nodeType === node.ELEMENT_NODE) {5271 -1 // FIXME: skip hidden elements5272 -1 if (matches(node, role)) {5273 -1 results.push(node);5274 -1 if (!all) {5275 -1 return false;5276 -1 }5277 -1 }5278 -1 }5279 -1 });5280 -1 return all ? results : results[0];5281 -1 };5282 -1 };-1 5220 (function() { -1 5221 /** -1 5222 * Helper for implicit semantic functionality. -1 5223 * Can be made part of the public API if need be. -1 5224 * @param {Element} element -1 5225 * @return {?axs.constants.HtmlInfo} -1 5226 */ -1 5227 function getHtmlInfo(element) { -1 5228 if (!element) -1 5229 return null; -1 5230 var tagName = element.tagName; -1 5231 if (!tagName) -1 5232 return null; -1 5233 tagName = tagName.toUpperCase(); -1 5234 var infos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName]; -1 5235 if (!infos || !infos.length) -1 5236 return null; -1 5237 var defaultInfo = null; // will contain the info with no specific selector if no others match -1 5238 for (var i = 0, len = infos.length; i < len; i++) { -1 5239 var htmlInfo = infos[i]; -1 5240 if (htmlInfo.selector) { -1 5241 if (axs.browserUtils.matchSelector(element, htmlInfo.selector)) -1 5242 return htmlInfo; -1 5243 } else { -1 5244 defaultInfo = htmlInfo; -1 5245 } -1 5246 } -1 5247 return defaultInfo; -1 5248 } 5283 52495284 -1 var closest = function(el, selector) {5285 -1 return util.searchUp(el, function(candidate) {5286 -1 return matches(candidate, selector);5287 -1 });5288 -1 };-1 5250 /** -1 5251 * @param {Element} element -1 5252 * @return {string} role -1 5253 */ -1 5254 axs.properties.getImplicitRole = function(element) { -1 5255 var htmlInfo = getHtmlInfo(element); -1 5256 if (htmlInfo) -1 5257 return htmlInfo.role; -1 5258 return ''; -1 5259 }; 5289 52605290 -1 module.exports = {5291 -1 getRole: function(el) {5292 -1 return _getRole(el);5293 -1 },5294 -1 getAttribute: getAttribute,5295 -1 matches: matches,5296 -1 querySelector: _querySelector(),5297 -1 querySelectorAll: _querySelector(true),5298 -1 closest: closest,5299 -1 };-1 5261 /** -1 5262 * Determine if this element can take ANY ARIA attributes including roles, state and properties. -1 5263 * If false then even global attributes should not be used. -1 5264 * @param {Element} element -1 5265 * @return {boolean} -1 5266 */ -1 5267 axs.properties.canTakeAriaAttributes = function(element) { -1 5268 var htmlInfo = getHtmlInfo(element); -1 5269 if (htmlInfo) -1 5270 return !htmlInfo.reserved; -1 5271 return true; -1 5272 }; -1 5273 })(); 5300 52745301 -1 },{"./constants.js":8,"./util.js":11}],11:[function(require,module,exports){5302 -1 var walkDOM = function(root, fn) {5303 -1 if (fn(root) === false) {5304 -1 return false;5305 -1 }5306 -1 var node = root.firstChild;5307 -1 while (node) {5308 -1 if (walkDOM(node, fn) === false) {5309 -1 return false;5310 -1 }5311 -1 node = node.nextSibling;5312 -1 }-1 5275 /** -1 5276 * This lists the ARIA attributes that are supported implicitly by native properties of this element. -1 5277 * -1 5278 * @param {Element} element The element to check. -1 5279 * @return {!Array.<string>} An array of ARIA attributes. -1 5280 * -1 5281 * example: -1 5282 * var element = document.createElement("input"); -1 5283 * element.setAttribute("type", "range"); -1 5284 * var supported = axs.properties.getNativelySupportedAttributes(element); // an array of ARIA attributes -1 5285 * console.log(supported.indexOf("aria-valuemax") >=0); // logs 'true' -1 5286 */ -1 5287 axs.properties.getNativelySupportedAttributes = function(element) { -1 5288 var result = []; -1 5289 if (!element) { -1 5290 return result; -1 5291 } -1 5292 var testElement = element.cloneNode(false); // gets rid of expandos -1 5293 var ariaAttributes = Object.keys(/** @type {!Object} */(axs.constants.ARIA_TO_HTML_ATTRIBUTE)); -1 5294 for (var i = 0; i < ariaAttributes.length; i++) { -1 5295 var ariaAttribute = ariaAttributes[i]; -1 5296 var nativeAttribute = axs.constants.ARIA_TO_HTML_ATTRIBUTE[ariaAttribute]; -1 5297 if (nativeAttribute in testElement) { -1 5298 result[result.length] = ariaAttribute; -1 5299 } -1 5300 } -1 5301 return result; 5313 5302 }; 5314 53035315 -1 var searchUp = function(el, test) {5316 -1 var candidate = el.parentElement;5317 -1 if (candidate) {5318 -1 if (test(candidate)) {5319 -1 return candidate;5320 -1 } else {5321 -1 return searchUp(candidate, test);5322 -1 }5323 -1 }5324 -1 };-1 5304 (function() { -1 5305 var roleToSelectorCache = {}; // performance optimization, cache results from getSelectorForRole 5325 53065326 -1 module.exports = {5327 -1 walkDOM: walkDOM,5328 -1 searchUp: searchUp,5329 -1 };-1 5307 /** -1 5308 * Build a selector that will match elements which implicity or explicitly have this role. -1 5309 * Note that the selector will probably not look elegant but it will work. -1 5310 * @param {string} role -1 5311 * @return {string} selector -1 5312 */ -1 5313 axs.properties.getSelectorForRole = function(role) { -1 5314 if (!role) -1 5315 return ''; -1 5316 if (roleToSelectorCache[role] && roleToSelectorCache.hasOwnProperty(role)) -1 5317 return roleToSelectorCache[role]; -1 5318 var selectors = ['[role="' + role + '"]']; -1 5319 var tagNames = Object.keys(/** @type {!Object} */(axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO)); -1 5320 tagNames.forEach(function(tagName) { -1 5321 var htmlInfos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName]; -1 5322 if (htmlInfos && htmlInfos.length) { -1 5323 for (var i = 0; i < htmlInfos.length; i++) { -1 5324 var htmlInfo = htmlInfos[i]; -1 5325 if (htmlInfo.role === role) { -1 5326 if (htmlInfo.selector) { -1 5327 selectors[selectors.length] = htmlInfo.selector; -1 5328 } else { -1 5329 selectors[selectors.length] = tagName; // Selectors API is not case sensitive. -1 5330 break; // No need to continue adding selectors since we will match the tag itself. -1 5331 } -1 5332 } -1 5333 } -1 5334 } -1 5335 }); -1 5336 return (roleToSelectorCache[role] = selectors.join(',')); -1 5337 }; -1 5338 })(); 5330 5339 5331 5340 },{}],12:[function(require,module,exports){ 5332 5341 /*! aXe v2.6.1 @@ -14551,7 +14560,7 @@ require('accessibility-developer-tools/src/js/Properties'); 14551 14560 module.exports = global.axs; 14552 14561 14553 14562 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})14554 -1 },{"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){-1 14563 },{"accessibility-developer-tools/src/js/AccessibilityUtils":6,"accessibility-developer-tools/src/js/BrowserUtils":7,"accessibility-developer-tools/src/js/Color":8,"accessibility-developer-tools/src/js/Constants":9,"accessibility-developer-tools/src/js/DOMUtils":10,"accessibility-developer-tools/src/js/Properties":11}],15:[function(require,module,exports){ 14555 14564 var ariaApi = require('aria-api'); 14556 14565 var accdc = require('w3c-alternative-text-computation'); 14557 14566 var axe = require('axe-core'); @@ -14649,4 +14658,4 @@ try { 14649 14658 }); 14650 14659 } 14651 1466014652 -1 },{"./axs":14,"aria-api":7,"axe-core":12,"w3c-alternative-text-computation":13}]},{},[15]);-1 14661 },{"./axs":14,"aria-api":1,"axe-core":12,"w3c-alternative-text-computation":13}]},{},[15]);