babelacc

compare different implementations of the text alternative computation  https://p.ce9e.org/babelacc/
git clone https://git.ce9e.org/babelacc.git

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 at
    7    -1 //
    8    -1 //      http://www.apache.org/licenses/LICENSE-2.0
    9    -1 //
   10    -1 // Unless required by applicable law or agreed to in writing, software
   11    -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 and
   14    -1 // limitations under the License.
   15    -1 
   16    -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    -1 
   22    -1 goog.provide('axs.utils');
   23    -1 
   24    -1 /**
   25    -1  * @const
   26    -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    -1 
   37    -1 /**
   38    -1  * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label
   39    -1  * @const
   40    -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     4 
   53    -1 /**
   54    -1  * @param {Element} element
   55    -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    10 
   61    -1 /**
   62    -1  * @param {Element} element
   63    -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    16 
   74    -1 /**
   75    -1  * @param {Element} element
   76    -1  * @return {boolean}
   77    -1  */
   78    -1 axs.utils.elementIsOutsideScrollArea = function(element) {
   79    -1     var parent = axs.dom.parentElement(element);
   80    -1 
   81    -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    45 
   86    -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    51 
   89    -1         parent = axs.dom.parentElement(parent);
   90    -1     }
   -1    52 	// dragndrop
   -1    53 	'dropeffect': 'token-list',
   -1    54 	'grabbed': 'bool-undefined',
   91    55 
   92    -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    75 
   95    -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 client
   99    -1  * rect.
  100    -1  * @param {Element} element
  101    -1  * @param {Element} container
  102    -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    -1 
  120    -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    -1 
  125    -1     var defaultView = element.ownerDocument.defaultView;
  126    -1     var style = defaultView.getComputedStyle(container);
  127    -1 
  128    -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   138 
  133    -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   143 
  136    -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} element
  140    -1  * @param {Element} container
  141    -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    -1 
  154    -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   147 
  157    -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   263 
  163    -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   267 
  166    -1     return false;
  167    -1 };
   -1   268 	var result = [role];
  168   269 
  169    -1 /**
  170    -1  * @param {Node} ancestor A potential ancestor of |node|.
  171    -1  * @param {Node} node
  172    -1  * @return {boolean} true if |ancestor| is an ancestor of |node| (including
  173    -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   277 
  181    -1     var parentNode = axs.dom.composedParentNode(node);
  182    -1     return axs.utils.isAncestor(ancestor, parentNode);
   -1   278 	return result;
  183   279 };
  184   280 
  185    -1 /**
  186    -1  * @param {Element} element
  187    -1  * @return {Array.<Element>} An array of any non-transparent elements which
  188    -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    -1 
  194    -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   287 
  202    -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   313 
  208    -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   324 
  212    -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   329 
  220    -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   341 
  223    -1 /**
  224    -1  * @param {Element} element
  225    -1  * @return {boolean}
  226    -1  */
  227    -1 axs.utils.elementIsHtmlControl = function(element) {
  228    -1     var defaultView = element.ownerDocument.defaultView;
  229    -1 
  230    -1     // HTML control
  231    -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   356 
  240    -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   361 
  243    -1 /**
  244    -1  * @param {Element} element
  245    -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   366 
  260    -1 /**
  261    -1  * @param {Element} element
  262    -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   384 
  272    -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   389 
  276    -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   446 
  279    -1 /**
  280    -1  * @param {CSSStyleDeclaration} style
  281    -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   449 
  326    -1 /**
  327    -1  * @param {CSSStyleDeclaration} style
  328    -1  * @param {Element} element
  329    -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   452 
  337    -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   465 
  340    -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   468 
  345    -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   473 
  350    -1 /**
  351    -1  * Gets the effective background color of the parent of |element|.
  352    -1  * @param {Element} element
  353    -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   477 
  364    -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   483 
  368    -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   495 
  371    -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   500 
  374    -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   509 
  376    -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   514 
  382    -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   517 
  385    -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} style
  395    -1  * @param {Element} element
  396    -1  * @param {axs.color.Color} bgColor The background color, which may come from
  397    -1  *    another element (such as a parent element), for flattening into the
  398    -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   686 
  407    -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   701 
  410    -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   702 
  416    -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} element
  421    -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} style
  430   725  * @param {Element} element
  431    -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   730 
  437    -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   735 
  441    -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   738 
  445    -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} element
  450    -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   769 
  460    -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   774 
  475    -1 /**
  476    -1  * @param {number} contrastRatio
  477    -1  * @param {CSSStyleDeclaration} style
  478    -1  * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level
  479    -1  * @return {boolean}
  480    -1  */
  481    -1 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) {
  482    -1     // Round to nearest 0.1
  483    -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} element
  495    -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   803 
  501    -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   806 
  512    -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    -1 
  516    -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   812 
  522    -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    -1 
  584    -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   879 
  587    -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   889 
  591    -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 is
  602    -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   921 
  608    -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 check
  616    -1  * @return {boolean} True if the given element is an inline element, false
  617    -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   986 
  658    -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} propertyName
  663    -1  * @param {!string} value
  664    -1  * @param {!Element} element
  665    -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  1013 
  677    -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  1017 
  684    -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 through
  691    -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             else
  701    -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 through
  746    -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             else
  765    -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} value
  810    -1  * @param {Object.<string, boolean>} possibleValues
  811    -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} value
  825    -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} value
  842    -1  * @param {!Element} element
  843    -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 by
  858    -1  *    http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number
  859    -1  * @param {string} value
  860    -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 accepted
  873    -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  1150 
  889    -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} obj
  905    -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 given
  918    -1  * JSON-compatible object. (Simply ignores any function values.)
  919    -1  * @param {Object} obj
  920    -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 selector
  933    -1 *
  934    -1 * @private
  935    -1 * @param {!string} id The ID to be escaped
  936    -1 * @return {string} The escaped ID
  937    -1 */
  938    -1 function escapeId(id) {
  939    -1     return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; });
  940    -1 }
  941    -1 
  942    -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    -1 
  953    -1   if (obj.hasAttribute) {
  954    -1     if (obj.id) {
  955    -1       return '#' + escapeId(obj.id);
  956    -1     }
  957    -1 
  958    -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    -1 
  963    -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    -1 
  976    -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  1233 
  982    -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  1236 
  993    -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  1240 
  999    -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  1245 
 1009    -1   } else if (obj.selectorText) {
 1010    -1     return obj.selectorText;
 1011    -1   }
 1012    -1 
 1013    -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 or
 1018    -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  1257 
 1042    -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 or
 1063    -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 selector
 1074    -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 or
 1174    -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  1333 
 1191    -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  * @example
 1197    -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_PROPERTIES
 1243    -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 testing
 1251    -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  * @example
 1261    -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  1489 
 1284    -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 at
 1290    -1 //
 1291    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1292    -1 //
 1293    -1 // Unless required by applicable law or agreed to in writing, software
 1294    -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 and
 1297    -1 // limitations under the License.
 1298    -1 
 1299    -1 goog.provide('axs.browserUtils');
 1300    -1 
 1301  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} element
 1306    -1  * @param {string} selector
 1307    -1  * @return {boolean} true if the element matches the selector
 1308    -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    -1 
 1321    -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 at
 1327    -1 //
 1328    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1329    -1 //
 1330    -1 // Unless required by applicable law or agreed to in writing, software
 1331    -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 and
 1334    -1 // limitations under the License.
 1335    -1 
 1336    -1 goog.provide('axs.color');
 1337    -1 goog.provide('axs.color.Color');
 1338    -1 
 1339    -1 /**
 1340    -1  * @constructor
 1341    -1  * @param {number} red
 1342    -1  * @param {number} green
 1343    -1  * @param {number} blue
 1344    -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    -1 
 1350    -1     /** @type {number} */
 1351    -1     this.green = green;
 1352    -1 
 1353    -1     /** @type {number} */
 1354    -1     this.blue = blue;
 1355    -1 
 1356    -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  * @constructor
 1362    -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    -1 
 1370    -1     /** @type {number} */
 1371    -1     this.Cb = this.x = coords[1];
 1372    -1 
 1373    -1     /** @type {number} */
 1374    -1     this.Cr = this.y = coords[2];
 1375    -1 };
 1376    -1 
 1377    -1 axs.color.YCbCr.prototype = {
 1378    -1     /**
 1379    -1      * @param {number} scalar
 1380    -1      * @return {axs.color.YCbCr} This color multiplied by the given scalar
 1381    -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    -1 
 1387    -1     /**
 1388    -1      * @param {axs.color.YCbCr} other
 1389    -1      * @return {axs.color.YCbCr} This plus other
 1390    -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    -1 
 1396    -1     /**
 1397    -1      * @param {axs.color.YCbCr} other
 1398    -1      * @return {axs.color.YCbCr} This minus other
 1399    -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  1531 
 1407    -1 
 1408  1532 /**
 1409    -1  * Calculate the contrast ratio between the two given colors. Returns the ratio
 1410    -1  * to 1, for example for two two colors with a contrast ratio of 21:1, this
 1411    -1  * function will return 21.
 1412    -1  * @param {axs.color.Color} fgColor
 1413    -1  * @param {axs.color.Color} bgColor
 1414    -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  1538 
 1420    -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} color
 1430    -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    -1 
 1437    -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    -1 
 1441    -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} luminance1
 1449    -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  1591 
 1467    -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  1602 
 1475    -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  1607 
 1485    -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  1612 
 1488    -1 /**
 1489    -1  * @param {number} value The value of a color channel, 0 <= value <= 0xFF
 1490    -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    -1 
 1499    -1 /**
 1500    -1  * @param {axs.color.Color} color
 1501    -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     else
 1509    -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  1625 
 1512    -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    -1 
 1529    -1 /**
 1530    -1  * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is
 1531    -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 luminance
 1534    -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_WHITE
 1539    -1                                                       : axs.color.YCC_CUBE_FACES_BLACK;
 1540  1631 
 1541    -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  1642 
 1545    -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 happen
 1555    -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 happen
 1559    -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  1648 
 1562    -1     // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube
 1563    -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  1658 
 1569    -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  1662 
 1576    -1     return axs.color.fromYCbCrArray(translatedColor);
   -1  1663   return '';
 1577  1664 };
 1578  1665 
 1579    -1 /** @typedef {{fg: string, bg: string, contrast: string}} */
 1580    -1 axs.color.SuggestedColors;
 1581    -1 
 1582  1666 /**
 1583    -1  * @param {axs.color.Color} bgColor
 1584    -1  * @param {axs.color.Color} fgColor
 1585    -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    -1 
 1593    -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    -1 
 1599    -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  1691 
 1611    -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} fgColor
 1628    -1  * @param {axs.color.Color} bgColor
 1629    -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    -1 
 1638    -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 matrix
 1644    -1  * @param {Array.<number>} vector A 3-element vector
 1645    -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    -1 
 1658    -1     var x = vector[0];
 1659    -1     var y = vector[1];
 1660    -1     var z = vector[2];
 1661    -1 
 1662    -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*z
 1666    -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} color
 1672    -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    -1 
 1679    -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    -1 
 1683    -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} ycc
 1688    -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 color
 1696    -1  * @param {Array.<number>} yccArray
 1697    -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    -1 
 1702    -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    -1 
 1709    -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    -1 
 1713    -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} kR
 1719    -1  * @param {number} kB
 1720    -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             kB
 1728    -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>>} matrix
 1745    -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    -1 
 1758    -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    -1 
 1768    -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  1933 
 1771    -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  1970 
 1778    -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  1985 
 1781    -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 using
 1786    -1  * http://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Parametric_form
 1787    -1  * @param {axs.color.Line} l
 1788    -1  * @param {axs.color.Plane} p
 1789    -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  1999 
 1794    -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  2002 
 1799    -1     var tuv = axs.color.multiplyMatrixVector(invertedMatrix, lhs);
 1800    -1     var t = tuv[0];
   -1  2003     /** @type {number} */
   -1  2004     this.blue = blue;
 1801  2005 
 1802    -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} scalar
 1810    -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  2019 
 1815    -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  2054 
 1818    -1     return result;
 1819  2055 };
 1820  2056 
   -1  2057 
 1821  2058 /**
 1822    -1  * Multiply a vector by a scalar.
 1823    -1  * @param {Array.<number>} vector
 1824    -1  * @param {number} scalar
 1825    -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  2076 
 1834    -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  2086 
 1839    -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  2090 
 1856    -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  2095 
 1863    -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 at
 1869    -1 //
 1870    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1871    -1 //
 1872    -1 // Unless required by applicable law or agreed to in writing, software
 1873    -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 and
 1876    -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  2105 
 1878    -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  2116 
 1882    -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    -1 
 2343    -1 axs.constants.WIDGET_ROLES = {};
 2344    -1 
 2345    -1 /**
 2346    -1  * Squashes the parent hierarchy on to role object.
 2347    -1  * @param {Object} role
 2348    -1  * @param {Object} set
 2349    -1  * @private
 2350    -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    -1 
 2363    -1 /**
 2364    -1  * Adds all properties and requiredProperties from parent hierarchy.
 2365    -1  * @param {Object} role
 2366    -1  * @param {string} propertiesName
 2367    -1  * @param {Object} propertiesSet
 2368    -1  * @private
 2369    -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    -1 
 2388    -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    -1 
 2392    -1     var propertiesSet = {};
 2393    -1     axs.constants.addAllPropertiesToSet_(role, 'properties', propertiesSet);
 2394    -1     role['propertiesSet'] = propertiesSet;
 2395    -1 
 2396    -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    -1 
 2406    -1 // BEGIN ARIA_PROPERTIES_AUTOGENERATED
 2407    -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_AUTOGENERATED
 2640    -1 
 2641    -1 (function() {
 2642    -1 // pull values lists into sets
 2643    -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    -1 
 2654    -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    -1 
 2660    -1 /**
 2661    -1  * A constant indicating no role name.
 2662    -1  * @type {string}
 2663    -1  */
 2664    -1 axs.constants.NO_ROLE_NAME = ' ';
 2665    -1 
 2666    -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.js
 2670    -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    -1 
 2709    -1 
 2710    -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.js
 2714    -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    -1 
 2741    -1 
 2742    -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.js
 2746    -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    -1 
 2782    -1 /**
 2783    -1  * Copied from ChromeVox:
 2784    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js
 2785    -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    -1 
 2811    -1 
 2812    -1 /**
 2813    -1  * Copied from ChromeVox:
 2814    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js
 2815    -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    -1 
 2842    -1 /**
 2843    -1  * Copied from ChromeVox:
 2844    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js
 2845    -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    -1 
 2853    -1 axs.constants.MIXED_VALUES = {
 2854    -1     "true": true,
 2855    -1     "false": true,
 2856    -1     "mixed": true
 2857    -1 };
 2858    -1 
 2859    -1 /** @enum {string} */
 2860    -1 axs.constants.Severity = {
 2861    -1     INFO: 'Info',
 2862    -1     WARNING: 'Warning',
 2863    -1     SEVERE: 'Severe'
 2864    -1 };
 2865    -1 
 2866    -1 /** @enum {string} */
 2867    -1 axs.constants.AuditResult = {
 2868    -1     PASS: 'PASS',
 2869    -1     FAIL: 'FAIL',
 2870    -1     NA: 'NA'
 2871    -1 };
 2872    -1 
 2873    -1 /** @enum {boolean} */
 2874    -1 axs.constants.InlineElements = {
 2875    -1     // fontstyle
 2876    -1     'TT': true,
 2877    -1     'I': true,
 2878    -1     'B': true,
 2879    -1     'BIG': true,
 2880    -1     'SMALL': true,
 2881    -1 
 2882    -1     // phrase
 2883    -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    -1 
 2894    -1     // special
 2895    -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    -1 
 2907    -1     // formctrl
 2908    -1     'INPUT': true,
 2909    -1     'SELECT': true,
 2910    -1     'TEXTAREA': true,
 2911    -1     'LABEL': true,
 2912    -1     'BUTTON': true
 2913    -1  };
 2914    -1 
 2915    -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-attribute
 2918    -1     'BUTTON': true,
 2919    -1     'INPUT': true,
 2920    -1     'SELECT': true,
 2921    -1     'TEXTAREA': true,
 2922    -1     'FIELDSET': true,
 2923    -1 
 2924    -1     // W3C http://www.w3.org/TR/html5/disabled-elements.html#disabled-elements
 2925    -1     'OPTGROUP': true,
 2926    -1     'OPTION': true
 2927    -1 };
 2928    -1 
 2929    -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    -1 
 2946    -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 not
 2953    -1  *    an error if the array contains other roles but currently this has no meaning. In future it may
 2954    -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 are
 2957    -1  *    modified by context or attributes. It can be used with the selectors API to find and/or match
 2958    -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 any
 2961    -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 role
 2977    -1  *    - can take any role
 2978    -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,wbr
 2979    -1  *
 2980    -1  * Where there is any ambiguity this table will endeavor to provide for the most broad case (to avoid
 2981    -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' when
 2984    -1  *    being used as a data grid or 'presentation' when used for layout. This lookup ignores these nuances and
 2985    -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: true
 3036    -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': true
 3058    -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: true
 3074    -1     }],
 3075    -1     'COLGROUP': [{
 3076    -1         role: '',
 3077    -1         reserved: true
 3078    -1     }],
 3079    -1     'DATALIST': [{
 3080    -1         role: 'listbox',
 3081    -1         attributes: {
 3082    -1             'aria-multiselectable': false
 3083    -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-element
 3105    -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': true
 3112    -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: true
 3177    -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: true
 3191    -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 attribute
 3289    -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 attribute
 3298    -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 attribute
 3304    -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 attribute
 3316    -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: true
 3373    -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: true
 3395    -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: true
 3407    -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: true
 3430    -1     }],
 3431    -1     'PICTURE': [{
 3432    -1         role: '',
 3433    -1         reserved: true
 3434    -1     }],
 3435    -1     'PROGRESS': [{
 3436    -1         role: 'progressbar',
 3437    -1         allowed: ['presentation']
 3438    -1     }],
 3439    -1     'SCRIPT': [{
 3440    -1         role: '',
 3441    -1         reserved: true
 3442    -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: true
 3464    -1     }],
 3465    -1     'SPAN': [{
 3466    -1         role: '',
 3467    -1         allowed: ['*']
 3468    -1     }],
 3469    -1     'STYLE': [{
 3470    -1         role: '',
 3471    -1         reserved: true
 3472    -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: true
 3492    -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: true
 3511    -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: true
 3527    -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  2137 
 3547    -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 at
 3553    -1 //
 3554    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 3555    -1 //
 3556    -1 // Unless required by applicable law or agreed to in writing, software
 3557    -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 and
 3560    -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  2148 
 3562    -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} node
 3567    -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  2351 
 3573    -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  2358 
 3577    -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 fragment
 3587    -1  * otherwise returns `null`.
 3588    -1  * @param {DocumentFragment} fragment
 3589    -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     else
 3596    -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} node
 3602    -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    -1 
 3610    -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  2407 
 3614    -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  2417 
 3617    -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  2420 
 3620    -1     // Shadow DOM v1
 3621    -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  2427 
 3627    -1     // Shadow DOM v0
 3628    -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  2430 
 3634    -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} node
 3640    -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 comments
 3648    -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 elements
 3653    -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} node
 3667    -1  * @param {?Node} end
 3668    -1  * @param {{preorder: (function (Node, Object):boolean|undefined),
 3669    -1  *          postorder: (function (Node, Object)|undefined)}} callbacks
 3670    -1  *     Callbacks to be called for each element traversed, excluding
 3671    -1  *     |end|. Possible callbacks are |preorder|, called before descending into
 3672    -1  *     child nodes, and |postorder| called after all child nodes have been
 3673    -1  *     traversed. If |preorder| returns false, its child nodes will not be
 3674    -1  *     traversed.
 3675    -1  * @param {Object} parentFlags
 3676    -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    -1 
 3683    -1     if (node.nodeType == Node.ELEMENT_NODE)
 3684    -1         var element = /** @type {Element} */ (node);
 3685    -1 
 3686    -1     var found = false;
 3687    -1     var flags = Object.create(parentFlags);
   -1  2462 axs.color.scalarMultiplyMatrix = function(matrix, scalar) {
   -1  2463     var result = [];
 3688  2464 
 3689    -1     // Descend into node:
 3690    -1     // If it has a ShadowRoot, ignore all child elements - these will be picked
 3691    -1     // up by the <content> or <shadow> elements. Descend straight into the
 3692    -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 this
 3709    -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  2467 
 3723    -1         // If it is a <content> element, descend into distributed elements - these
 3724    -1         // are elements from outside the shadow root which are rendered inside the
 3725    -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  2488 
 3744    -1     // If it is neither the parent of a ShadowRoot, a <content> element, nor
 3745    -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  2505 
 3756    -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  2512 
 3761    -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  2527 
 3776    -1 goog.require('axs.browserUtils');
 3777    -1 goog.require('axs.color');
 3778    -1 goog.require('axs.dom');
 3779    -1 goog.require('axs.utils');
 3780    -1 
 3781    -1 goog.provide('axs.properties');
 3782    -1 
 3783    -1 /**
 3784    -1  * @const
 3785    -1  * @type {string}
 3786    -1  */
 3787    -1 axs.properties.TEXT_CONTENT_XPATH = './/text()[normalize-space(.)!=""]/parent::*[name()!="script"]';
 3788    -1 
 3789    -1 /**
 3790    -1  * @param {Element} element
 3791    -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    -1 
 3829    -1     return focusProperties;
 3830    -1 };
 3831    -1 
 3832    -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    -1 
 3840    -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} element
 3844    -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    -1 
 3850    -1     if (element.hasAttribute('chromevoxignoreariahidden'))
 3851    -1         var chromevoxignoreariahidden = true;
 3852    -1 
 3853    -1     var style = window.getComputedStyle(element, null);
 3854    -1     if (style.display == 'none')
 3855    -1         return { 'property': 'display: none',
 3856    -1                  'on': element };
 3857    -1 
 3858    -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  2531 
 3862    -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    -1 
 3869    -1     return axs.properties.getHiddenReason(axs.dom.parentElement(element));
 3870    -1 };
 3871    -1 
 3872    -1 
 3873    -1 /**
 3874    -1  * @param {Element} element
 3875    -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  2992 
 3888    -1 /**
 3889    -1  * Determines whether the given element has a text node as a direct descendant.
 3890    -1  * @param {Element} element
 3891    -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     else
 3898    -1         ownerDocument = element.ownerDocument;
 3899    -1     if (ownerDocument.evaluate) {
 3900    -1         return hasDirectTextDescendantXpath();
 3901    -1     }
 3902    -1     return hasDirectTextDescendantTreeWalker();
 3903    -1 
 3904    -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    -1 
 3925    -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 version
 3928    -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} element
 3952    -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    -1 
 3958    -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    -1 
 3964    -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    -1 
 3974    -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    -1 
 3982    -1     if (!Object.keys(desiredContrastRatios).length)
 3983    -1         return contrastRatioProperties;
 3984    -1 
 3985    -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} node
 3993    -1  * @param {!Object} textAlternatives The properties object to fill in
 3994    -1  * @param {boolean=} opt_recursive Whether this is a recursive call or not
 3995    -1  * @param {boolean=} opt_force Whether to return text alternatives for this
 3996    -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    -1 
 4002    -1     /** @type {Element} */ var element = axs.dom.asElement(node);
 4003    -1     if (!element)
 4004    -1         return null;
 4005    -1 
 4006    -1     // 1. Skip hidden elements unless the author specifies to use them via an aria-labelledby or
 4007    -1     // aria-describedby being used in the current computation.
 4008    -1     if (!opt_force && axs.utils.isElementOrAncestorHidden(element))
 4009    -1         return null;
 4010    -1 
 4011    -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    -1 
 4019    -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  3037 
 4022    -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  3041 
 4024    -1     if (!recursive) {
 4025    -1         // 2A. The aria-labelledby attribute takes precedence as the element's text alternative
 4026    -1         // unless this computation is already occurring as the result of a recursive aria-labelledby
 4027    -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  3045 
 4031    -1     // 2A. If aria-labelledby is empty or undefined, the aria-label attribute, which defines an
 4032    -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  3055 
 4045    -1     // 2A. If aria-labelledby and aria-label are both empty or undefined, and if the element is not
 4046    -1     // marked as presentational (role="presentation", check for the presence of an equivalent host
 4047    -1     // language attribute or element for associating a label, and use those mechanisms to determine
 4048    -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  3290 
 4056    -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  3303 
 4060    -1         // include the value of the embedded control as part of the text alternative in the
 4061    -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 the
 4070    -1             // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow
 4071    -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  3309 
 4082    -1         if (textAlternatives['controlValue']) {
 4083    -1             var controlValue = textAlternatives['controlValue'];
 4084    -1             if (computedName)
 4085    -1                 controlValue.unused = true;
 4086    -1             else
 4087    -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  3315 
 4091    -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 the
 4100    -1         // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow
 4101    -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             // TODO
 4130    -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  3358 
 4133    -1         if (textAlternatives['controlValue']) {
 4134    -1             var controlValue = textAlternatives['controlValue'];
 4135    -1             if (computedName)
 4136    -1                 controlValue.unused = true;
 4137    -1             else
 4138    -1                 computedName = controlValue.text;
 4139    -1         }
 4140    -1     }
 4141  3359 
 4142    -1     // 2C. Otherwise, if the attributes checked in rules A and B didn't provide results, text is
 4143    -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         else
 4162    -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  3390 
 4166    -1     // 2D. The last resort is to use text from a tooltip attribute (such as the title attribute in
 4167    -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         else
 4177    -1             computedName = titleValue.text;
 4178    -1         textAlternatives['title'] = titleValue;
 4179    -1     }
 4180  3391 
 4181    -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  3431 
 4184    -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} element
 4189    -1  * @param {boolean=} opt_force Whether to return text alternatives for this
 4190    -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 them
 4204    -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} element
 4213    -1  * @param {Object} textAlternatives
 4214    -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  3502 
 4221    -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  3508 
 4255    -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} element
 4263    -1  * @param {!Object} textAlternatives An object that will be updated with information.
 4264    -1  * @param {?string} existingComputedname
 4265    -1  * @param {boolean} recursive Whether this method is being called recursively as described in
 4266    -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         else
 4282    -1             computedName = altValue.text;
 4283    -1         textAlternatives['alt'] = altValue;
 4284    -1     }
 4285    -1 
 4286    -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                 else
 4318    -1                     computedName = labelForValue.text;
 4319    -1                 textAlternatives['labelFor'] = labelForValue;
 4320    -1             }
 4321    -1         }
 4322    -1 
 4323    -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             else
 4342    -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 text
 4346    -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             else
 4354    -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  4196 
 4363    -1 /**
 4364    -1  * @param {?string} text
 4365    -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  4211 
 4371    -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} node
 4381    -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    -1 
 4387    -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    -1 
 4395    -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    -1 
 4405    -1         if (!computedName)
 4406    -1             return null;
 4407    -1     }
 4408    -1 
 4409    -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    -1 
 4415    -1 /**
 4416    -1  * Finds any ARIA attributes (roles, states and properties) explicitly set on this element.
 4417    -1  * @param {Element} element
 4418    -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    -1 
 4424    -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  4222 
 4435    -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  4226 
 4445    -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} element
 4477    -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} element
 4493    -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} element
 4509    -1  * @param {string} kind
 4510    -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 available
 4514    -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} node
 4554    -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  4332 
 4561    -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  4335 
 4570    -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} element
 4575    -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 match
 4588    -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  4391 
 4600    -1     /**
 4601    -1      * @param {Element} element
 4602    -1      * @return {string} role
 4603    -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  4392 
 4611    -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} element
 4615    -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  4393 
 4625    -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 attributes
 4635    -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 expandos
 4643    -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  4410 
 4654    -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  4425 
 4657    -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} role
 4661    -1      * @return {string} selector
 4662    -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  4430 
 4690    -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  4432 
 4694    -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  4438 
 4700    -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  4481 
 4706    -1 },{"./lib/name.js":9,"./lib/query.js":10}],8:[function(require,module,exports){
 4707    -1 exports.attributes = {
 4708    -1 	// widget
 4709    -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  4489 
 4735    -1 	// live
 4736    -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  4511 
 4741    -1 	// dragndrop
 4742    -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  4518 
 4745    -1 	// relationship
 4746    -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  4521 
 4765    -1 // https://www.w3.org/TR/html-aria/#docconformance
 4766    -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  4522 
 4824    -1 	// if scope is missing, it is calculated automatically
 4825    -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  4537 
 4829    -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  4553 
 4833    -1 // https://www.w3.org/TR/wai-aria/roles
 4834    -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  4599 
 4949    -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  4607 
 4953    -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  4613 
 4955    -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  4623 
 4963    -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  4640 
 4966    -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  4651 
 4973    -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  4655 
 4999    -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  4660 
 5010    -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  4791 
 5014    -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 ret
 5021    -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  4815 
 5026    -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  4830 
 5041    -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  4833 
 5046    -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  4836 
 5051    -1 // Control.labels is part of the standard, but not supported in most browsers
 5052    -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    -1 
 5061    -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  4860 
 5070    -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_te
 5072    -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  4870 
 5075    -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 		// figcaption
 5103    -1 		if (!ret) {
 5104    -1 			ret = el.getAttribute('alt');
 5105    -1 		}
 5106    -1 		// caption
 5107    -1 		// table
 5108    -1 	}
 5109    -1 	// FIXME only if this is embedded in a label
 5110    -1 	if (!ret && query.matches(el, 'input')) {
 5111    -1 		// combobox
 5112    -1 		// button
 5113    -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  4904 
 5127    -1 	return (ret || '').trim().replace(/\s+/g, ' ');
   -1  4905     return computedName;
 5128  4906 };
 5129  4907 
 5130    -1 var getDescription = function(el) {
 5131    -1 	var ret = '';
 5132  4908 
 5133    -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  4972 
 5146    -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  5012 
 5149    -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  5028 
 5154    -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  5036 
 5158    -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  5044 
 5164    -1 // candidates can be passed for performance optimization
 5165    -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  5054 
 5176    -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  5058 
 5181    -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  5064 
 5190    -1 var getAttribute = function(el, key, _hiddenRoot) {
 5191    -1 	if (key === 'hidden' && el === _hiddenRoot) {  // used for name calculation
 5192    -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  5073 
 5195    -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  5084 
 5198    -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  5094 
 5218    -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 performance
 5237    -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  5156 
 5243    -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  5201 
 5248    -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  5210 
 5251    -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  5219 
 5266    -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 elements
 5272    -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  5249 
 5284    -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  5260 
 5290    -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  5274 
 5301    -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  5303 
 5315    -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  5306 
 5326    -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 14660 
14652    -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]);