babelacc

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

commit
137aee0f416f98840dd08b9ef1fef7160a9aead7
parent
b00dfb587103535e651db71e753e19de153265af
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2018-02-09 06:55
update accdc

Diffstat

M babel.js 5505 ++++++++++++++++++++++++++++++++-----------------------------

1 files changed, 2884 insertions, 2621 deletions


diff --git a/babel.js b/babel.js

@@ -1,2291 +1,1641 @@
    1     1 (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
    2    -1 var query = require('./lib/query.js');
    3    -1 var name = require('./lib/name.js');
   -1     2 // Copyright 2012 Google Inc.
   -1     3 //
   -1     4 // Licensed under the Apache License, Version 2.0 (the "License");
   -1     5 // you may not use this file except in compliance with the License.
   -1     6 // You may obtain a copy of the License at
   -1     7 //
   -1     8 //      http://www.apache.org/licenses/LICENSE-2.0
   -1     9 //
   -1    10 // Unless required by applicable law or agreed to in writing, software
   -1    11 // distributed under the License is distributed on an "AS IS" BASIS,
   -1    12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   -1    13 // See the License for the specific language governing permissions and
   -1    14 // limitations under the License.
    4    15 
    5    -1 module.exports = {
    6    -1 	getRole: query.getRole,
    7    -1 	getAttribute: query.getAttribute,
    8    -1 	getName: name.getName,
    9    -1 	getDescription: name.getDescription,
   -1    16 goog.require('axs.browserUtils');
   -1    17 goog.require('axs.color');
   -1    18 goog.require('axs.color.Color');
   -1    19 goog.require('axs.constants');
   -1    20 goog.require('axs.dom');
   10    21 
   11    -1 	matches: query.matches,
   12    -1 	querySelector: query.querySelector,
   13    -1 	querySelectorAll: query.querySelectorAll,
   14    -1 	closest: query.closest,
   15    -1 };
   -1    22 goog.provide('axs.utils');
   16    23 
   17    -1 },{"./lib/name.js":3,"./lib/query.js":4}],2:[function(require,module,exports){
   18    -1 exports.attributes = {
   19    -1 	// widget
   20    -1 	'autocomplete': 'token',
   21    -1 	'checked': 'tristate',
   22    -1 	'current': 'token',
   23    -1 	'disabled': 'bool',
   24    -1 	'expanded': 'bool-undefined',
   25    -1 	'haspopup': 'token',
   26    -1 	'hidden': 'bool',  // !
   27    -1 	'invalid': 'token',
   28    -1 	'keyshortcuts': 'string',
   29    -1 	'label': 'string',
   30    -1 	'level': 'int',
   31    -1 	'modal': 'bool',
   32    -1 	'multiline': 'bool',
   33    -1 	'multiselectable': 'bool',
   34    -1 	'orientation': 'token',
   35    -1 	'placeholder': 'string',
   36    -1 	'pressed': 'tristate',
   37    -1 	'readonly': 'bool',
   38    -1 	'required': 'bool',
   39    -1 	'roledescription': 'string',
   40    -1 	'selected': 'bool-undefined',
   41    -1 	'valuemax': 'number',
   42    -1 	'valuemin': 'number',
   43    -1 	'valuenow': 'number',
   44    -1 	'valuetext': 'string',
   -1    24 /**
   -1    25  * @const
   -1    26  * @type {string}
   -1    27  */
   -1    28 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR =
   -1    29     'input:not([type=hidden]):not([disabled]),' +
   -1    30     'select:not([disabled]),' +
   -1    31     'textarea:not([disabled]),' +
   -1    32     'button:not([disabled]),' +
   -1    33     'a[href],' +
   -1    34     'iframe,' +
   -1    35     '[tabindex]';
   45    36 
   46    -1 	// live
   47    -1 	'atomic': 'bool',
   48    -1 	'busy': 'bool',
   49    -1 	'live': 'token',
   50    -1 	'relevant': 'token-list',
   -1    37 /**
   -1    38  * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label
   -1    39  * @const
   -1    40  * @type {string}
   -1    41  */
   -1    42 axs.utils.LABELABLE_ELEMENTS_SELECTOR =
   -1    43     'button,' +
   -1    44     'input:not([type=hidden]),' +
   -1    45     'keygen,' +
   -1    46     'meter,' +
   -1    47     'output,' +
   -1    48     'progress,' +
   -1    49     'select,' +
   -1    50     'textarea';
   51    51 
   52    -1 	// dragndrop
   53    -1 	'dropeffect': 'token-list',
   54    -1 	'grabbed': 'bool-undefined',
   55    52 
   56    -1 	// relationship
   57    -1 	'activedescendant': 'id',
   58    -1 	'colcount': 'int',
   59    -1 	'colindex': 'int',
   60    -1 	'colspan': 'int',
   61    -1 	'controls': 'id-list',
   62    -1 	'describedby': 'id-list',
   63    -1 	'details': 'id',
   64    -1 	'errormessage': 'id',
   65    -1 	'flowto': 'id-list',
   66    -1 	'labelledby': 'id-list',
   67    -1 	'owns': 'id-list',
   68    -1 	'posinset': 'int',
   69    -1 	'rowcount': 'int',
   70    -1 	'rowindex': 'int',
   71    -1 	'rowspan': 'int',
   72    -1 	'setsize': 'int',
   73    -1 	'sort': 'token',
   -1    53 /**
   -1    54  * @param {Element} element
   -1    55  * @return {boolean}
   -1    56  */
   -1    57 axs.utils.elementIsTransparent = function(element) {
   -1    58     return element.style.opacity == '0';
   74    59 };
   75    60 
   76    -1 // https://www.w3.org/TR/html-aria/#docconformance
   77    -1 exports.extraSelectors = {
   78    -1 	article: ['article'],
   79    -1 	button: [
   80    -1 		'button',
   81    -1 		'input[type="button"]',
   82    -1 		'input[type="image"]',
   83    -1 		'input[type="reset"]',
   84    -1 		'input[type="submit"]',
   85    -1 		'summary',
   86    -1 	],
   87    -1 	cell: ['td'],
   88    -1 	checkbox: ['input[type="checkbox"]'],
   89    -1 	combobox: [
   90    -1 		'input:not([type])[list]',
   91    -1 		'input[type="email"][list]',
   92    -1 		'input[type="search"][list]',
   93    -1 		'input[type="tel"][list]',
   94    -1 		'input[type="text"][list]',
   95    -1 		'input[type="url"][list]',
   96    -1 		'select:not([multiple])',
   97    -1 	],
   98    -1 	complementary: ['aside'],
   99    -1 	definition: ['dd'],
  100    -1 	dialog: ['dialog'],
  101    -1 	document: ['body'],
  102    -1 	figure: ['figure'],
  103    -1 	form: ['form[aria-label]', 'form[aria-labelledby]'],
  104    -1 	group: ['details', 'optgroup'],
  105    -1 	heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
  106    -1 	img: ['img:not([alt=""])'],
  107    -1 	link: ['a[href]', 'area[href]', 'link[href]'],
  108    -1 	list: ['dl', 'ol', 'ul'],
  109    -1 	listbox: ['select[multiple]'],
  110    -1 	listitem: ['dt', 'ul > li', 'ol > li'],
  111    -1 	main: ['main'],
  112    -1 	math: ['math'],
  113    -1 	menuitemcheckbox: ['menuitem[type="checkbox"]'],
  114    -1 	menuitem: ['menuitem[type="command"]'],
  115    -1 	menuitemradio: ['menuitem[type="radio"]'],
  116    -1 	menu: ['menu[type="context"]'],
  117    -1 	navigation: ['nav'],
  118    -1 	option: ['option'],
  119    -1 	progressbar: ['progress'],
  120    -1 	radio: ['input[type="radio"]'],
  121    -1 	region: ['section'],
  122    -1 	rowgroup: ['tbody', 'thead', 'tfoot'],
  123    -1 	row: ['tr'],
  124    -1 	searchbox: ['input[type="search"]:not([list])'],
  125    -1 	separator: ['hr'],
  126    -1 	slider: ['input[type="range"]'],
  127    -1 	spinbutton: ['input[type="number"]'],
  128    -1 	status: ['output'],
  129    -1 	table: ['table'],
  130    -1 	textbox: [
  131    -1 		'input:not([type]):not([list])',
  132    -1 		'input[type="email"]:not([list])',
  133    -1 		'input[type="tel"]:not([list])',
  134    -1 		'input[type="text"]:not([list])',
  135    -1 		'input[type="url"]:not([list])',
  136    -1 		'textarea',
  137    -1 	],
   -1    61 /**
   -1    62  * @param {Element} element
   -1    63  * @return {boolean}
   -1    64  */
   -1    65 axs.utils.elementHasZeroArea = function(element) {
   -1    66     var rect = element.getBoundingClientRect();
   -1    67     var width = rect.right - rect.left;
   -1    68     var height = rect.top - rect.bottom;
   -1    69     if (!width || !height)
   -1    70         return true;
   -1    71     return false;
   -1    72 };
  138    73 
  139    -1 	// if scope is missing, it is calculated automatically
  140    -1 	rowheader: ['th[scope="row"]'],
  141    -1 	columnheader: ['th[scope="col"]'],
   -1    74 /**
   -1    75  * @param {Element} element
   -1    76  * @return {boolean}
   -1    77  */
   -1    78 axs.utils.elementIsOutsideScrollArea = function(element) {
   -1    79     var parent = axs.dom.parentElement(element);
   -1    80 
   -1    81     var defaultView = element.ownerDocument.defaultView;
   -1    82     while (parent != defaultView.document.body) {
   -1    83         if (axs.utils.isClippedBy(element, parent))
   -1    84             return true;
   -1    85 
   -1    86         if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent))
   -1    87             return false;
   -1    88 
   -1    89         parent = axs.dom.parentElement(parent);
   -1    90     }
   -1    91 
   -1    92     return !axs.utils.canScrollTo(element, defaultView.document.body);
  142    93 };
  143    94 
  144    -1 exports.scoped = [
  145    -1 	'article *', 'aside *', 'main *', 'nav *', 'section *',
  146    -1 ].join(',');
   -1    95 /**
   -1    96  * Checks whether it's possible to scroll to the given element within the given container.
   -1    97  * Assumes that |container| is an ancestor of |element|.
   -1    98  * If |container| cannot be scrolled, returns True if the element is within its bounding client
   -1    99  * rect.
   -1   100  * @param {Element} element
   -1   101  * @param {Element} container
   -1   102  * @return {boolean} True iff it's possible to scroll to |element| within |container|.
   -1   103  */
   -1   104 axs.utils.canScrollTo = function(element, container) {
   -1   105     var rect = element.getBoundingClientRect();
   -1   106     var containerRect = container.getBoundingClientRect();
   -1   107     if (container == container.ownerDocument.body) {
   -1   108         var absoluteTop = containerRect.top;
   -1   109         var absoluteLeft = containerRect.left;
   -1   110     } else {
   -1   111         var absoluteTop = containerRect.top - container.scrollTop;
   -1   112         var absoluteLeft = containerRect.left - container.scrollLeft;
   -1   113     }
   -1   114     var containerScrollArea =
   -1   115         { top: absoluteTop,
   -1   116           bottom: absoluteTop + container.scrollHeight,
   -1   117           left: absoluteLeft,
   -1   118           right: absoluteLeft + container.scrollWidth };
  147   119 
  148    -1 // https://www.w3.org/TR/wai-aria/roles
  149    -1 var subRoles = {
  150    -1 	cell: ['gridcell', 'rowheader'],
  151    -1 	command: ['button', 'link', 'menuitem'],
  152    -1 	composite: ['grid', 'select', 'spinbutton', 'tablist'],
  153    -1 	img: ['doc-cover'],
  154    -1 	input: ['checkbox', 'option', 'radio', 'slider', 'spinbutton', 'textbox'],
  155    -1 	landmark: [
  156    -1 		'banner',
  157    -1 		'complementary',
  158    -1 		'contentinfo',
  159    -1 		'doc-acknowledgments',
  160    -1 		'doc-afterword',
  161    -1 		'doc-appendix',
  162    -1 		'doc-bibliography',
  163    -1 		'doc-chapter',
  164    -1 		'doc-conclusion',
  165    -1 		'doc-credits',
  166    -1 		'doc-endnotes',
  167    -1 		'doc-epilogue',
  168    -1 		'doc-errata',
  169    -1 		'doc-foreword',
  170    -1 		'doc-glossary',
  171    -1 		'doc-introduction',
  172    -1 		'doc-part',
  173    -1 		'doc-preface',
  174    -1 		'doc-prologue',
  175    -1 		'form',
  176    -1 		'main',
  177    -1 		'navigation',
  178    -1 		'region',
  179    -1 		'search',
  180    -1 	],
  181    -1 	range: ['progressbar', 'scrollbar', 'slider', 'spinbutton'],
  182    -1 	roletype: ['structure', 'widget', 'window'],
  183    -1 	section: [
  184    -1 		'alert',
  185    -1 		'cell',
  186    -1 		'definition',
  187    -1 		'doc-abstract',
  188    -1 		'doc-colophon',
  189    -1 		'doc-credit',
  190    -1 		'doc-dedication',
  191    -1 		'doc-epigraph',
  192    -1 		'doc-example',
  193    -1 		'doc-footnote',
  194    -1 		'doc-qna',
  195    -1 		'figure',
  196    -1 		'group',
  197    -1 		'img',
  198    -1 		'landmark',
  199    -1 		'list',
  200    -1 		'listitem',
  201    -1 		'log',
  202    -1 		'marquee',
  203    -1 		'math',
  204    -1 		'note',
  205    -1 		'status',
  206    -1 		'table',
  207    -1 		'tabpanel',
  208    -1 		'term',
  209    -1 		'tooltip',
  210    -1 	],
  211    -1 	sectionhead: [
  212    -1 		'columnheader',
  213    -1 		'doc-subtitle',
  214    -1 		'heading',
  215    -1 		'rowheader',
  216    -1 		'tab',
  217    -1 	],
  218    -1 	select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'],
  219    -1 	separator: ['doc-pagebreak'],
  220    -1 	structure: [
  221    -1 		'application',
  222    -1 		'document',
  223    -1 		'none',
  224    -1 		'presentation',
  225    -1 		'rowgroup',
  226    -1 		'section',
  227    -1 		'sectionhead',
  228    -1 		'separator',
  229    -1 	],
  230    -1 	table: ['grid'],
  231    -1 	textbox: ['searchbox'],
  232    -1 	widget: [
  233    -1 		'command',
  234    -1 		'composite',
  235    -1 		'gridcell',
  236    -1 		'input',
  237    -1 		'range',
  238    -1 		'row',
  239    -1 		'separator',
  240    -1 		'tab',
  241    -1 	],
  242    -1 	window: ['dialog'],
  243    -1 	alert: ['alertdialog'],
  244    -1 	checkbox: ['menuitemcheckbox', 'switch'],
  245    -1 	dialog: ['alertdialog'],
  246    -1 	gridcell: ['columnheader', 'rowheader'],
  247    -1 	menuitem: ['menuitemcheckbox'],
  248    -1 	menuitemcheckbox: ['menuitemradio'],
  249    -1 	option: ['treeitem'],
  250    -1 	radio: ['menuitemradio'],
  251    -1 	status: ['timer'],
  252    -1 	grid: ['treegrid'],
  253    -1 	menu: ['menubar'],
  254    -1 	tree: ['treegrid'],
  255    -1 	document: ['article'],
  256    -1 	group: ['row', 'select', 'toolbar'],
  257    -1 	link: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
  258    -1 	list: ['directory', 'feed'],
  259    -1 	listitem: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
  260    -1 	navigation: ['doc-index', 'doc-pagelist', 'doc-toc'],
  261    -1 	note: ['doc-notice', 'doc-tip'],
  262    -1 };
  263    -1 
  264    -1 var getSubRoles = function(role) {
  265    -1 	var children = subRoles[role] || [];
  266    -1 	var descendents = children.map(getSubRoles);
   -1   120     if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top ||
   -1   121         rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) {
   -1   122         return false;
   -1   123     }
  267   124 
  268    -1 	var result = [role];
   -1   125     var defaultView = element.ownerDocument.defaultView;
   -1   126     var style = defaultView.getComputedStyle(container);
  269   127 
  270    -1 	descendents.forEach(function(list) {
  271    -1 		list.forEach(function(r) {
  272    -1 			if (result.indexOf(r) === -1) {
  273    -1 				result.push(r);
  274    -1 			}
  275    -1 		});
  276    -1 	});
   -1   128     if (rect.left > containerRect.right || rect.top > containerRect.bottom) {
   -1   129         return (style.overflow == 'scroll' || style.overflow == 'auto' ||
   -1   130                 container instanceof defaultView.HTMLBodyElement);
   -1   131     }
  277   132 
  278    -1 	return result;
   -1   133     return true;
  279   134 };
  280   135 
  281    -1 exports.subRoles = {};
  282    -1 for (var role in subRoles) {
  283    -1 	exports.subRoles[role] = getSubRoles(role);
  284    -1 }
  285    -1 exports.subRoles['none'] = ['none', 'presentation'];
  286    -1 exports.subRoles['presentation'] = ['presentation', 'none'];
   -1   136 /**
   -1   137  * Checks whether the given element is clipped by the given container.
   -1   138  * Assumes that |container| is an ancestor of |element|.
   -1   139  * @param {Element} element
   -1   140  * @param {Element} container
   -1   141  * @return {boolean} True iff |element| is clipped by |container|.
   -1   142  */
   -1   143 axs.utils.isClippedBy = function(element, container) {
   -1   144     var rect = element.getBoundingClientRect();
   -1   145     var containerRect = container.getBoundingClientRect();
   -1   146     var containerTop = containerRect.top;
   -1   147     var containerLeft = containerRect.left;
   -1   148     var containerScrollArea =
   -1   149         { top: containerTop - container.scrollTop,
   -1   150           bottom: containerTop - container.scrollTop + container.scrollHeight,
   -1   151           left: containerLeft - container.scrollLeft,
   -1   152           right: containerLeft - container.scrollLeft + container.scrollWidth };
  287   153 
  288    -1 exports.nameFromContents = [
  289    -1 	'button',
  290    -1 	'checkbox',
  291    -1 	'columnheader',
  292    -1 	'doc-backlink',
  293    -1 	'doc-biblioref',
  294    -1 	'doc-glossref',
  295    -1 	'doc-noteref',
  296    -1 	'gridcell',
  297    -1 	'heading',
  298    -1 	'link',
  299    -1 	'menuitem',
  300    -1 	'menuitemcheckbox',
  301    -1 	'menuitemradio',
  302    -1 	'option',
  303    -1 	'radio',
  304    -1 	'row',
  305    -1 	'rowgroup',
  306    -1 	'rowheader',
  307    -1 	'sectionhead',
  308    -1 	'tab',
  309    -1 	'tooltip',
  310    -1 	'treeitem',
  311    -1 	'switch',
  312    -1 ];
   -1   154     var defaultView = element.ownerDocument.defaultView;
   -1   155     var style = defaultView.getComputedStyle(container);
  313   156 
  314    -1 exports.labelable = [
  315    -1 	'button',
  316    -1 	'input:not([type="hidden"])',
  317    -1 	'keygen',
  318    -1 	'meter',
  319    -1 	'output',
  320    -1 	'progress',
  321    -1 	'select',
  322    -1 	'textarea',
  323    -1 ];
   -1   157     if ((rect.right < containerRect.left || rect.bottom < containerRect.top ||
   -1   158              rect.left > containerRect.right || rect.top > containerRect.bottom) &&
   -1   159              style.overflow == 'hidden') {
   -1   160         return true;
   -1   161     }
  324   162 
  325    -1 },{}],3:[function(require,module,exports){
  326    -1 var constants = require('./constants.js');
  327    -1 var query = require('./query.js');
  328    -1 var util = require('./util.js');
   -1   163     if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top)
   -1   164         return (style.overflow != 'visible');
  329   165 
  330    -1 var getPseudoContent = function(node, selector) {
  331    -1 	var styles = window.getComputedStyle(node, selector);
  332    -1 	var ret = styles.getPropertyValue('content');
  333    -1 	if (ret === 'none' || ret.substr(0, 4) === '-moz') {
  334    -1 		return '';
  335    -1 	} else {
  336    -1 		return ret
  337    -1 			.replace(/^["']/, '')
  338    -1 			.replace(/["']$/, '');
  339    -1 	}
   -1   166     return false;
  340   167 };
  341   168 
  342    -1 var getContent = function(root, referenced) {
  343    -1 	var ret = getPseudoContent(root, ':before');
  344    -1 	var node = root.firstChild;
  345    -1 	while (node) {
  346    -1 		if (node.nodeType === node.TEXT_NODE) {
  347    -1 			ret += node.textContent;
  348    -1 		} else if (node.nodeType === node.ELEMENT_NODE) {
  349    -1 			ret += getName(node, true, referenced);
  350    -1 		}
  351    -1 		node = node.nextSibling;
  352    -1 	}
  353    -1 	ret += getPseudoContent(root, ':after');
  354    -1 	return ret;
  355    -1 };
   -1   169 /**
   -1   170  * @param {Node} ancestor A potential ancestor of |node|.
   -1   171  * @param {Node} node
   -1   172  * @return {boolean} true if |ancestor| is an ancestor of |node| (including
   -1   173  *     |ancestor| === |node|).
   -1   174  */
   -1   175 axs.utils.isAncestor = function(ancestor, node) {
   -1   176     if (node == null)
   -1   177         return false;
   -1   178     if (node === ancestor)
   -1   179         return true;
  356   180 
  357    -1 var allowNameFromContent = function(el) {
  358    -1 	var role = query.getRole(el);
  359    -1 	return !role || constants.nameFromContents.indexOf(role) !== -1;
   -1   181     var parentNode = axs.dom.composedParentNode(node);
   -1   182     return axs.utils.isAncestor(ancestor, parentNode);
  360   183 };
  361   184 
  362    -1 var isLabelable = function(el) {
  363    -1 	var selector = constants.labelable.join(',');
  364    -1 	return el.matches(selector);
  365    -1 };
   -1   185 /**
   -1   186  * @param {Element} element
   -1   187  * @return {Array.<Element>} An array of any non-transparent elements which
   -1   188  *     overlap the given element.
   -1   189  */
   -1   190 axs.utils.overlappingElements = function(element) {
   -1   191     if (axs.utils.elementHasZeroArea(element))
   -1   192         return null;
  366   193 
  367    -1 // Control.labels is part of the standard, but not supported in most browsers
  368    -1 var getLabelNodes = function(element) {
  369    -1 	var labels = [];
  370    -1 	var labelable = constants.labelable.join(',');
  371    -1 	util.walkDOM(document.body, function(node) {
  372    -1 		if (node.tagName && node.tagName.toLowerCase() === 'label') {
  373    -1 			if (node.getAttribute('for')) {
  374    -1 				if (element.id && node.getAttribute('for') === element.id) {
  375    -1 					labels.push(node);
  376    -1 				}
  377    -1 			} else if (node.querySelector(labelable) === element) {
  378    -1 				labels.push(node);
  379    -1 			}
  380    -1 		}
  381    -1 	});
  382    -1 	return labels;
  383    -1 };
   -1   194     var overlappingElements = [];
   -1   195     var clientRects = element.getClientRects();
   -1   196     for (var i = 0; i < clientRects.length; i++) {
   -1   197         var rect = clientRects[i];
   -1   198         var center_x = (rect.left + rect.right) / 2;
   -1   199         var center_y = (rect.top + rect.bottom) / 2;
   -1   200         var elementAtPoint = document.elementFromPoint(center_x, center_y);
  384   201 
  385    -1 // http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/
  386    -1 // https://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te
  387    -1 var getName = function(el, recursive, referenced) {
  388    -1 	var ret;
   -1   202         if (elementAtPoint == null || elementAtPoint == element ||
   -1   203             axs.utils.isAncestor(elementAtPoint, element) ||
   -1   204             axs.utils.isAncestor(element, elementAtPoint)) {
   -1   205             continue;
   -1   206         }
  389   207 
  390    -1 	if (query.getAttribute(el, 'hidden', referenced)) {
  391    -1 		return '';
  392    -1 	}
  393    -1 	if (query.matches(el, 'presentation')) {
  394    -1 		return getContent(el, referenced);
  395    -1 	}
  396    -1 	if (!recursive && el.matches('[aria-labelledby]')) {
  397    -1 		var ids = el.getAttribute('aria-labelledby').split(/\s+/);
  398    -1 		var strings = ids.map(function(id) {
  399    -1 			var label = document.getElementById(id);
  400    -1 			return getName(label, true, label);
  401    -1 		});
  402    -1 		ret = strings.join(' ');
  403    -1 	}
  404    -1 	if (!ret && el.matches('[aria-label]')) {
  405    -1 		ret = el.getAttribute('aria-label');
  406    -1 	}
  407    -1 	if (!query.matches(el, 'presentation')) {
  408    -1 		if (!ret && !recursive && isLabelable(el)) {
  409    -1 			var strings = getLabelNodes(el).map(function(label) {
  410    -1 				return getName(label, true, label);
  411    -1 			});
  412    -1 			ret = strings.join(' ');
  413    -1 		}
  414    -1 		if (!ret) {
  415    -1 			ret = el.getAttribute('placeholder');
  416    -1 		}
  417    -1 		if (!ret) {
  418    -1 			ret = el.getAttribute('alt');
  419    -1 		}
  420    -1 		if (!ret && el.matches('abbr,acronym') && el.title) {
  421    -1 			ret = el.title;
  422    -1 		}
  423    -1 		// figcaption
  424    -1 		// caption
  425    -1 		// table
  426    -1 	}
  427    -1 	// FIXME only if this is embedded in a label
  428    -1 	if (!ret && query.matches(el, 'textbox,button,combobox,range')) {
  429    -1 		if (query.matches(el, 'textbox,button')) {
  430    -1 			ret = el.value || el.textContent;
  431    -1 		} else if (query.matches(el, 'combobox')) {
  432    -1 			var selected = query.querySelector(el, ':selected') || query.querySelector(el, 'option');
  433    -1 			if (selected) {
  434    -1 				ret = getName(selected, recursive, referenced);
  435    -1 			}
  436    -1 		} else if (query.matches(el, 'range')) {
  437    -1 			ret = '' + (query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value);
  438    -1 		}
  439    -1 	}
  440    -1 	if (!ret && (recursive || allowNameFromContent(el))) {
  441    -1 		ret = getContent(el, referenced);
  442    -1 	}
  443    -1 	if (!ret) {
  444    -1 		ret = el.getAttribute('title');
  445    -1 	}
   -1   208         var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null);
   -1   209         if (!overlappingElementStyle)
   -1   210             continue;
  446   211 
  447    -1 	return (ret || '').trim().replace(/\s+/g, ' ');
   -1   212         var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle,
   -1   213                                                         elementAtPoint);
   -1   214         if (overlappingElementBg && overlappingElementBg.alpha > 0 &&
   -1   215             overlappingElements.indexOf(elementAtPoint) < 0) {
   -1   216             overlappingElements.push(elementAtPoint);
   -1   217         }
   -1   218     }
   -1   219 
   -1   220     return overlappingElements;
  448   221 };
  449   222 
  450    -1 var getDescription = function(el) {
  451    -1 	var ret = '';
   -1   223 /**
   -1   224  * @param {Element} element
   -1   225  * @return {boolean}
   -1   226  */
   -1   227 axs.utils.elementIsHtmlControl = function(element) {
   -1   228     var defaultView = element.ownerDocument.defaultView;
  452   229 
  453    -1 	if (el.matches('[aria-describedby]')) {
  454    -1 		var ids = el.getAttribute('aria-describedby').split(/\s+/);
  455    -1 		var strings = ids.map(function(id) {
  456    -1 			var label = document.getElementById(id);
  457    -1 			return getName(label, true, label);
  458    -1 		});
  459    -1 		ret = strings.join(' ');
  460    -1 	} else if (el.title) {
  461    -1 		ret = el.title;
  462    -1 	} else if (el.placeholder) {
  463    -1 		ret = el.placeholder;
  464    -1 	}
   -1   230     // HTML control
   -1   231     if (element instanceof defaultView.HTMLButtonElement)
   -1   232         return true;
   -1   233     if (element instanceof defaultView.HTMLInputElement)
   -1   234         return true;
   -1   235     if (element instanceof defaultView.HTMLSelectElement)
   -1   236         return true;
   -1   237     if (element instanceof defaultView.HTMLTextAreaElement)
   -1   238         return true;
  465   239 
  466    -1 	return (ret || '').trim().replace(/\s+/g, ' ');
   -1   240     return false;
  467   241 };
  468   242 
  469    -1 module.exports = {
  470    -1 	getName: getName,
  471    -1 	getDescription: getDescription,
   -1   243 /**
   -1   244  * @param {Element} element
   -1   245  * @return {boolean}
   -1   246  */
   -1   247 axs.utils.elementIsAriaWidget = function(element) {
   -1   248     if (element.hasAttribute('role')) {
   -1   249         var roleValue = element.getAttribute('role');
   -1   250         // TODO is this correct?
   -1   251         if (roleValue) {
   -1   252             var role = axs.constants.ARIA_ROLES[roleValue];
   -1   253             if (role && 'widget' in role['allParentRolesSet'])
   -1   254                 return true;
   -1   255         }
   -1   256     }
   -1   257     return false;
  472   258 };
  473   259 
  474    -1 },{"./constants.js":2,"./query.js":4,"./util.js":5}],4:[function(require,module,exports){
  475    -1 var constants = require('./constants.js');
  476    -1 var util = require('./util.js');
   -1   260 /**
   -1   261  * @param {Element} element
   -1   262  * @return {boolean}
   -1   263  */
   -1   264 axs.utils.elementIsVisible = function(element) {
   -1   265     if (axs.utils.elementIsTransparent(element))
   -1   266         return false;
   -1   267     if (axs.utils.elementHasZeroArea(element))
   -1   268         return false;
   -1   269     if (axs.utils.elementIsOutsideScrollArea(element))
   -1   270         return false;
  477   271 
  478    -1 var getSubRoles = function(roles) {
  479    -1 	return [].concat.apply([], roles.map(function(role) {
  480    -1 		return constants.subRoles[role] || [role];
  481    -1 	}));
   -1   272     var overlappingElements = axs.utils.overlappingElements(element);
   -1   273     if (overlappingElements.length)
   -1   274         return false;
   -1   275 
   -1   276     return true;
  482   277 };
  483   278 
  484    -1 // candidates can be passed for performance optimization
  485    -1 var _getRole = function(el, candidates) {
  486    -1 	if (el.hasAttribute('role')) {
  487    -1 		return el.getAttribute('role');
  488    -1 	}
  489    -1 	for (var role in constants.extraSelectors) {
  490    -1 		var selector = constants.extraSelectors[role].join(',');
  491    -1 		if ((!candidates || candidates.indexOf(role) !== -1) && el.matches(selector)) {
  492    -1 			return role;
  493    -1 		}
  494    -1 	}
   -1   279 /**
   -1   280  * @param {CSSStyleDeclaration} style
   -1   281  * @return {boolean}
   -1   282  */
   -1   283 axs.utils.isLargeFont = function(style) {
   -1   284     var fontSize = style.fontSize;
   -1   285     var bold = style.fontWeight == 'bold';
   -1   286     var matches = fontSize.match(/(\d+)px/);
   -1   287     if (matches) {
   -1   288         var fontSizePx = parseInt(matches[1], 10);
   -1   289         var bodyStyle = window.getComputedStyle(document.body, null);
   -1   290         var bodyFontSize = bodyStyle.fontSize;
   -1   291         matches = bodyFontSize.match(/(\d+)px/);
   -1   292         if (matches) {
   -1   293             var bodyFontSizePx = parseInt(matches[1], 10);
   -1   294             var boldLarge = bodyFontSizePx * 1.2;
   -1   295             var large = bodyFontSizePx * 1.5;
   -1   296         } else {
   -1   297             var boldLarge = 19.2;
   -1   298             var large = 24;
   -1   299         }
   -1   300         return (bold && fontSizePx >= boldLarge || fontSizePx >= large);
   -1   301     }
   -1   302     matches = fontSize.match(/(\d+)em/);
   -1   303     if (matches) {
   -1   304         var fontSizeEm = parseInt(matches[1], 10);
   -1   305         if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5)
   -1   306             return true;
   -1   307         return false;
   -1   308     }
   -1   309     matches = fontSize.match(/(\d+)%/);
   -1   310     if (matches) {
   -1   311         var fontSizePercent = parseInt(matches[1], 10);
   -1   312         if (bold && fontSizePercent >= 120 || fontSizePercent >= 150)
   -1   313             return true;
   -1   314         return false;
   -1   315     }
   -1   316     matches = fontSize.match(/(\d+)pt/);
   -1   317     if (matches) {
   -1   318         var fontSizePt = parseInt(matches[1], 10);
   -1   319         if (bold && fontSizePt >= 14 || fontSizePt >= 18)
   -1   320             return true;
   -1   321         return false;
   -1   322     }
   -1   323     return false;
   -1   324 };
  495   325 
  496    -1 	if (!candidates ||
  497    -1 			candidates.indexOf('banner') !== -1 ||
  498    -1 			candidates.indexOf('contentinfo') !== -1) {
  499    -1 		var scoped = el.matches(constants.scoped);
   -1   326 /**
   -1   327  * @param {CSSStyleDeclaration} style
   -1   328  * @param {Element} element
   -1   329  * @return {?axs.color.Color}
   -1   330  */
   -1   331 axs.utils.getBgColor = function(style, element) {
   -1   332     var bgColorString = style.backgroundColor;
   -1   333     var bgColor = axs.color.parseColor(bgColorString);
   -1   334     if (!bgColor)
   -1   335         return null;
  500   336 
  501    -1 		if (el.matches('header') && !scoped) {
  502    -1 			return 'banner';
  503    -1 		}
  504    -1 		if (el.matches('footer') && !scoped) {
  505    -1 			return 'contentinfo';
  506    -1 		}
  507    -1 	}
   -1   337     if (style.opacity < 1)
   -1   338         bgColor.alpha = bgColor.alpha * style.opacity;
   -1   339 
   -1   340     if (bgColor.alpha < 1) {
   -1   341         var parentBg = axs.utils.getParentBgColor(element);
   -1   342         if (parentBg == null)
   -1   343             return null;
   -1   344 
   -1   345         bgColor = axs.color.flattenColors(bgColor, parentBg);
   -1   346     }
   -1   347     return bgColor;
  508   348 };
  509   349 
  510    -1 var getAttribute = function(el, key, _hiddenRoot) {
  511    -1 	if (key === 'hidden' && el === _hiddenRoot) {  // used for name calculation
  512    -1 		return false;
  513    -1 	}
   -1   350 /**
   -1   351  * Gets the effective background color of the parent of |element|.
   -1   352  * @param {Element} element
   -1   353  * @return {?axs.color.Color}
   -1   354  */
   -1   355 axs.utils.getParentBgColor = function(element) {
   -1   356     /** @type {Element} */ var parent = element;
   -1   357     var bgStack = [];
   -1   358     var foundSolidColor = null;
   -1   359     while ((parent = axs.dom.parentElement(parent))) {
   -1   360         var computedStyle = window.getComputedStyle(parent, null);
   -1   361         if (!computedStyle)
   -1   362             continue;
  514   363 
  515    -1 	var type = constants.attributes[key];
  516    -1 	var raw = el.getAttribute('aria-' + key);
   -1   364         var parentBg = axs.color.parseColor(computedStyle.backgroundColor);
   -1   365         if (!parentBg)
   -1   366             continue;
  517   367 
  518    -1 	if (raw) {
  519    -1 		if (type === 'bool') {
  520    -1 			return raw === 'true';
  521    -1 		} else if (type === 'tristate') {
  522    -1 			return raw === 'true' ? true : raw === 'false' ? false : 'mixed';
  523    -1 		} else if (type === 'bool-undefined') {
  524    -1 			return raw === 'true' ? true : raw === 'false' ? false : undefined;
  525    -1 		} else if (type === 'id-list') {
  526    -1 			return raw.split(/\s+/);
  527    -1 		} else if (type === 'integer') {
  528    -1 			return parseInt(raw);
  529    -1 		} else if (type === 'number') {
  530    -1 			return parseFloat(raw);
  531    -1 		} else if (type === 'token-list') {
  532    -1 			return raw.split(/\s+/);
  533    -1 		} else {
  534    -1 			return raw;
  535    -1 		}
  536    -1 	}
  537    -1 
  538    -1 	if (key === 'level') {
  539    -1 		for (var i = 1; i <= 6; i++) {
  540    -1 			if (el.tagName.toLowerCase() === 'h' + i) {
  541    -1 				return i;
  542    -1 			}
  543    -1 		}
  544    -1 	} else if (key === 'disabled') {
  545    -1 		return el.disabled;
  546    -1 	} else if (key === 'placeholder') {
  547    -1 		return el.placeholder;
  548    -1 	} else if (key === 'required') {
  549    -1 		return el.required;
  550    -1 	} else if (key === 'readonly') {
  551    -1 		return el.readOnly && !el.isContentEditable;
  552    -1 	} else if (key === 'hidden') {
  553    -1 		var style = window.getComputedStyle(el);
  554    -1 		if (el.hidden || style.display === 'none' || style.visibility === 'hidden') {
  555    -1 			return true;
  556    -1 		} else if (el.clientHeight === 0) {  // rough check for performance
  557    -1 			return el.parentNode && getAttribute(el.parentNode, 'hidden', _hiddenRoot);
  558    -1 		}
  559    -1 	} else if (key === 'invalid' && el.checkValidity) {
  560    -1 		return el.checkValidity();
  561    -1 	}
   -1   368         if (computedStyle.opacity < 1)
   -1   369             parentBg.alpha = parentBg.alpha * computedStyle.opacity;
  562   370 
  563    -1 	if (type === 'bool' || type === 'tristate') {
  564    -1 		return false;
  565    -1 	}
  566    -1 };
   -1   371         if (parentBg.alpha == 0)
   -1   372             continue;
  567   373 
  568    -1 var matches = function(el, selector) {
  569    -1 	var actual;
   -1   374         bgStack.push(parentBg);
  570   375 
  571    -1 	if (selector.substr(0, 1) === ':') {
  572    -1 		var attr = selector.substr(1);
  573    -1 		return getAttribute(el, attr);
  574    -1 	} else if (selector.substr(0, 1) === '[') {
  575    -1 		var match = /\[([a-z]+)="(.*)"\]/.exec(selector);
  576    -1 		actual = getAttribute(el, match[1]);
  577    -1 		var rawValue = match[2];
  578    -1 		return actual.toString() == rawValue;
  579    -1 	} else {
  580    -1 		var candidates = getSubRoles(selector.split(','));
  581    -1 		actual = _getRole(el, candidates);
  582    -1 		return candidates.indexOf(actual) !== -1;
  583    -1 	}
  584    -1 };
   -1   376         if (parentBg.alpha == 1) {
   -1   377             foundSolidColor = true;
   -1   378             break;
   -1   379         }
   -1   380     }
  585   381 
  586    -1 var _querySelector = function(all) {
  587    -1 	return function(root, role) {
  588    -1 		var results = [];
  589    -1 		util.walkDOM(root, function(node) {
  590    -1 			if (node.nodeType === node.ELEMENT_NODE) {
  591    -1 				// FIXME: skip hidden elements
  592    -1 				if (matches(node, role)) {
  593    -1 					results.push(node);
  594    -1 					if (!all) {
  595    -1 						return false;
  596    -1 					}
  597    -1 				}
  598    -1 			}
  599    -1 		});
  600    -1 		return all ? results : results[0];
  601    -1 	};
  602    -1 };
   -1   382     if (!foundSolidColor)
   -1   383         bgStack.push(new axs.color.Color(255, 255, 255, 1));
  603   384 
  604    -1 var closest = function(el, selector) {
  605    -1 	return util.searchUp(el, function(candidate) {
  606    -1 		return matches(candidate, selector);
  607    -1 	});
   -1   385     var bg = bgStack.pop();
   -1   386     while (bgStack.length) {
   -1   387         var fg = bgStack.pop();
   -1   388         bg = axs.color.flattenColors(fg, bg);
   -1   389     }
   -1   390     return bg;
  608   391 };
  609   392 
  610    -1 module.exports = {
  611    -1 	getRole: function(el) {
  612    -1 		return _getRole(el);
  613    -1 	},
  614    -1 	getAttribute: getAttribute,
  615    -1 	matches: matches,
  616    -1 	querySelector: _querySelector(),
  617    -1 	querySelectorAll: _querySelector(true),
  618    -1 	closest: closest,
  619    -1 };
   -1   393 /**
   -1   394  * @param {CSSStyleDeclaration} style
   -1   395  * @param {Element} element
   -1   396  * @param {axs.color.Color} bgColor The background color, which may come from
   -1   397  *    another element (such as a parent element), for flattening into the
   -1   398  *    foreground color.
   -1   399  * @return {?axs.color.Color}
   -1   400  */
   -1   401 axs.utils.getFgColor = function(style, element, bgColor) {
   -1   402     var fgColorString = style.color;
   -1   403     var fgColor = axs.color.parseColor(fgColorString);
   -1   404     if (!fgColor)
   -1   405         return null;
  620   406 
  621    -1 },{"./constants.js":2,"./util.js":5}],5:[function(require,module,exports){
  622    -1 var walkDOM = function(root, fn) {
  623    -1 	if (fn(root) === false) {
  624    -1 		return false;
  625    -1 	}
  626    -1 	var node = root.firstChild;
  627    -1 	while (node) {
  628    -1 		if (walkDOM(node, fn) === false) {
  629    -1 			return false;
  630    -1 		}
  631    -1 		node = node.nextSibling;
  632    -1 	}
  633    -1 };
   -1   407     if (fgColor.alpha < 1)
   -1   408         fgColor = axs.color.flattenColors(fgColor, bgColor);
  634   409 
  635    -1 var searchUp = function(el, test) {
  636    -1 	var candidate = el.parentElement;
  637    -1 	if (candidate) {
  638    -1 		if (test(candidate)) {
  639    -1 			return candidate;
  640    -1 		} else {
  641    -1 			return searchUp(candidate, test);
  642    -1 		}
  643    -1 	}
  644    -1 };
   -1   410     if (style.opacity < 1) {
   -1   411         var parentBg = axs.utils.getParentBgColor(element);
   -1   412         fgColor.alpha = fgColor.alpha * style.opacity;
   -1   413         fgColor = axs.color.flattenColors(fgColor, parentBg);
   -1   414     }
  645   415 
  646    -1 module.exports = {
  647    -1 	walkDOM: walkDOM,
  648    -1 	searchUp: searchUp,
   -1   416     return fgColor;
  649   417 };
  650   418 
  651    -1 },{}],6:[function(require,module,exports){
  652    -1 // Copyright 2012 Google Inc.
  653    -1 //
  654    -1 // Licensed under the Apache License, Version 2.0 (the "License");
  655    -1 // you may not use this file except in compliance with the License.
  656    -1 // You may obtain a copy of the License at
  657    -1 //
  658    -1 //      http://www.apache.org/licenses/LICENSE-2.0
  659    -1 //
  660    -1 // Unless required by applicable law or agreed to in writing, software
  661    -1 // distributed under the License is distributed on an "AS IS" BASIS,
  662    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  663    -1 // See the License for the specific language governing permissions and
  664    -1 // limitations under the License.
  665    -1 
  666    -1 goog.require('axs.browserUtils');
  667    -1 goog.require('axs.color');
  668    -1 goog.require('axs.color.Color');
  669    -1 goog.require('axs.constants');
  670    -1 goog.require('axs.dom');
  671    -1 
  672    -1 goog.provide('axs.utils');
  673    -1 
  674   419 /**
  675    -1  * @const
  676    -1  * @type {string}
   -1   420  * @param {Element} element
   -1   421  * @return {?number}
  677   422  */
  678    -1 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR =
  679    -1     'input:not([type=hidden]):not([disabled]),' +
  680    -1     'select:not([disabled]),' +
  681    -1     'textarea:not([disabled]),' +
  682    -1     'button:not([disabled]),' +
  683    -1     'a[href],' +
  684    -1     'iframe,' +
  685    -1     '[tabindex]';
   -1   423 axs.utils.getContrastRatioForElement = function(element) {
   -1   424     var style = window.getComputedStyle(element, null);
   -1   425     return axs.utils.getContrastRatioForElementWithComputedStyle(style, element);
   -1   426 };
  686   427 
  687   428 /**
  688    -1  * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label
  689    -1  * @const
  690    -1  * @type {string}
   -1   429  * @param {CSSStyleDeclaration} style
   -1   430  * @param {Element} element
   -1   431  * @return {?number}
  691   432  */
  692    -1 axs.utils.LABELABLE_ELEMENTS_SELECTOR =
  693    -1     'button,' +
  694    -1     'input:not([type=hidden]),' +
  695    -1     'keygen,' +
  696    -1     'meter,' +
  697    -1     'output,' +
  698    -1     'progress,' +
  699    -1     'select,' +
  700    -1     'textarea';
   -1   433 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) {
   -1   434     if (axs.utils.isElementHidden(element))
   -1   435         return null;
  701   436 
   -1   437     var bgColor = axs.utils.getBgColor(style, element);
   -1   438     if (!bgColor)
   -1   439         return null;
  702   440 
  703    -1 /**
  704    -1  * @param {Element} element
  705    -1  * @return {boolean}
  706    -1  */
  707    -1 axs.utils.elementIsTransparent = function(element) {
  708    -1     return element.style.opacity == '0';
   -1   441     var fgColor = axs.utils.getFgColor(style, element, bgColor);
   -1   442     if (!fgColor)
   -1   443         return null;
   -1   444 
   -1   445     return axs.color.calculateContrastRatio(fgColor, bgColor);
  709   446 };
  710   447 
  711   448 /**
  712   449  * @param {Element} element
  713   450  * @return {boolean}
  714   451  */
  715    -1 axs.utils.elementHasZeroArea = function(element) {
  716    -1     var rect = element.getBoundingClientRect();
  717    -1     var width = rect.right - rect.left;
  718    -1     var height = rect.top - rect.bottom;
  719    -1     if (!width || !height)
   -1   452 axs.utils.isNativeTextElement = function(element) {
   -1   453     var tagName = element.tagName.toLowerCase();
   -1   454     var type = element.type ? element.type.toLowerCase() : '';
   -1   455     if (tagName == 'textarea')
  720   456         return true;
  721    -1     return false;
   -1   457     if (tagName != 'input')
   -1   458         return false;
   -1   459 
   -1   460     switch (type) {
   -1   461     case 'email':
   -1   462     case 'number':
   -1   463     case 'password':
   -1   464     case 'search':
   -1   465     case 'text':
   -1   466     case 'tel':
   -1   467     case 'url':
   -1   468     case '':
   -1   469         return true;
   -1   470     default:
   -1   471         return false;
   -1   472     }
  722   473 };
  723   474 
  724   475 /**
  725    -1  * @param {Element} element
   -1   476  * @param {number} contrastRatio
   -1   477  * @param {CSSStyleDeclaration} style
   -1   478  * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level
  726   479  * @return {boolean}
  727   480  */
  728    -1 axs.utils.elementIsOutsideScrollArea = function(element) {
  729    -1     var parent = axs.dom.parentElement(element);
  730    -1 
  731    -1     var defaultView = element.ownerDocument.defaultView;
  732    -1     while (parent != defaultView.document.body) {
  733    -1         if (axs.utils.isClippedBy(element, parent))
  734    -1             return true;
  735    -1 
  736    -1         if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent))
  737    -1             return false;
  738    -1 
  739    -1         parent = axs.dom.parentElement(parent);
  740    -1     }
  741    -1 
  742    -1     return !axs.utils.canScrollTo(element, defaultView.document.body);
  743    -1 };
  744    -1 
  745    -1 /**
  746    -1  * Checks whether it's possible to scroll to the given element within the given container.
  747    -1  * Assumes that |container| is an ancestor of |element|.
  748    -1  * If |container| cannot be scrolled, returns True if the element is within its bounding client
  749    -1  * rect.
  750    -1  * @param {Element} element
  751    -1  * @param {Element} container
  752    -1  * @return {boolean} True iff it's possible to scroll to |element| within |container|.
  753    -1  */
  754    -1 axs.utils.canScrollTo = function(element, container) {
  755    -1     var rect = element.getBoundingClientRect();
  756    -1     var containerRect = container.getBoundingClientRect();
  757    -1     if (container == container.ownerDocument.body) {
  758    -1         var absoluteTop = containerRect.top;
  759    -1         var absoluteLeft = containerRect.left;
   -1   481 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) {
   -1   482     // Round to nearest 0.1
   -1   483     var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10);
   -1   484     if (!opt_strict) {
   -1   485         return roundedContrastRatio < 3.0 ||
   -1   486             (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5);
  760   487     } else {
  761    -1         var absoluteTop = containerRect.top - container.scrollTop;
  762    -1         var absoluteLeft = containerRect.left - container.scrollLeft;
  763    -1     }
  764    -1     var containerScrollArea =
  765    -1         { top: absoluteTop,
  766    -1           bottom: absoluteTop + container.scrollHeight,
  767    -1           left: absoluteLeft,
  768    -1           right: absoluteLeft + container.scrollWidth };
  769    -1 
  770    -1     if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top ||
  771    -1         rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) {
  772    -1         return false;
  773    -1     }
  774    -1 
  775    -1     var defaultView = element.ownerDocument.defaultView;
  776    -1     var style = defaultView.getComputedStyle(container);
  777    -1 
  778    -1     if (rect.left > containerRect.right || rect.top > containerRect.bottom) {
  779    -1         return (style.overflow == 'scroll' || style.overflow == 'auto' ||
  780    -1                 container instanceof defaultView.HTMLBodyElement);
   -1   488         return roundedContrastRatio < 4.5 ||
   -1   489             (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0);
  781   490     }
  782    -1 
  783    -1     return true;
  784   491 };
  785   492 
  786   493 /**
  787    -1  * Checks whether the given element is clipped by the given container.
  788    -1  * Assumes that |container| is an ancestor of |element|.
  789   494  * @param {Element} element
  790    -1  * @param {Element} container
  791    -1  * @return {boolean} True iff |element| is clipped by |container|.
   -1   495  * @return {boolean}
  792   496  */
  793    -1 axs.utils.isClippedBy = function(element, container) {
  794    -1     var rect = element.getBoundingClientRect();
  795    -1     var containerRect = container.getBoundingClientRect();
  796    -1     var containerTop = containerRect.top;
  797    -1     var containerLeft = containerRect.left;
  798    -1     var containerScrollArea =
  799    -1         { top: containerTop - container.scrollTop,
  800    -1           bottom: containerTop - container.scrollTop + container.scrollHeight,
  801    -1           left: containerLeft - container.scrollLeft,
  802    -1           right: containerLeft - container.scrollLeft + container.scrollWidth };
   -1   497 axs.utils.hasLabel = function(element) {
   -1   498     var tagName = element.tagName.toLowerCase();
   -1   499     var type = element.type ? element.type.toLowerCase() : '';
  803   500 
  804    -1     var defaultView = element.ownerDocument.defaultView;
  805    -1     var style = defaultView.getComputedStyle(container);
   -1   501     if (element.hasAttribute('aria-label'))
   -1   502         return true;
   -1   503     if (element.hasAttribute('title'))
   -1   504         return true;
   -1   505     if (tagName == 'img' && element.hasAttribute('alt'))
   -1   506         return true;
   -1   507     if (tagName == 'input' && type == 'image' && element.hasAttribute('alt'))
   -1   508         return true;
   -1   509     if (tagName == 'input' && (type == 'submit' || type == 'reset'))
   -1   510         return true;
  806   511 
  807    -1     if ((rect.right < containerRect.left || rect.bottom < containerRect.top ||
  808    -1              rect.left > containerRect.right || rect.top > containerRect.bottom) &&
  809    -1              style.overflow == 'hidden') {
   -1   512     // There's a separate audit that makes sure this points to an actual element or elements.
   -1   513     if (element.hasAttribute('aria-labelledby'))
  810   514         return true;
  811    -1     }
  812   515 
  813    -1     if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top)
  814    -1         return (style.overflow != 'visible');
   -1   516     if (element.hasAttribute('id')) {
   -1   517         var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]');
   -1   518         if (labelsFor.length > 0)
   -1   519             return true;
   -1   520     }
  815   521 
   -1   522     var parent = axs.dom.parentElement(element);
   -1   523     while (parent) {
   -1   524         if (parent.tagName.toLowerCase() == 'label') {
   -1   525             var parentLabel = /** HTMLLabelElement */ parent;
   -1   526             if (parentLabel.control == element)
   -1   527                 return true;
   -1   528         }
   -1   529         parent = axs.dom.parentElement(parent);
   -1   530     }
  816   531     return false;
  817   532 };
  818   533 
  819   534 /**
  820    -1  * @param {Node} ancestor A potential ancestor of |node|.
  821    -1  * @param {Node} node
  822    -1  * @return {boolean} true if |ancestor| is an ancestor of |node| (including
  823    -1  *     |ancestor| === |node|).
   -1   535  * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute.
   -1   536  * Disabled here means that the element should be considered disabled according to specification.
   -1   537  * This element may or may not be effectively disabled in practice as this is dependent on implementation.
   -1   538  *
   -1   539  * @param {Element} element An element to check.
   -1   540  * @return {boolean} true If the element supports being natively disabled.
  824   541  */
  825    -1 axs.utils.isAncestor = function(ancestor, node) {
  826    -1     if (node == null)
  827    -1         return false;
  828    -1     if (node === ancestor)
  829    -1         return true;
  830    -1 
  831    -1     var parentNode = axs.dom.composedParentNode(node);
  832    -1     return axs.utils.isAncestor(ancestor, parentNode);
   -1   542 axs.utils.isNativelyDisableable = function(element) {
   -1   543     var tagName = element.tagName.toUpperCase();
   -1   544     return (tagName in axs.constants.NATIVELY_DISABLEABLE);
  833   545 };
  834   546 
  835   547 /**
  836    -1  * @param {Element} element
  837    -1  * @return {Array.<Element>} An array of any non-transparent elements which
  838    -1  *     overlap the given element.
   -1   548  * Determine if this element is disabled directly or indirectly by a disabled ancestor.
   -1   549  * Disabled here means that the element should be considered disabled according to specification.
   -1   550  * This element may or may not be effectively disabled in practice as this is dependent on implementation.
   -1   551  *
   -1   552  * @param {Element} element An element to check.
   -1   553  * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors.
   -1   554  * @return {boolean} true if the element or one of its ancestors is disabled.
  839   555  */
  840    -1 axs.utils.overlappingElements = function(element) {
  841    -1     if (axs.utils.elementHasZeroArea(element))
  842    -1         return null;
  843    -1 
  844    -1     var overlappingElements = [];
  845    -1     var clientRects = element.getClientRects();
  846    -1     for (var i = 0; i < clientRects.length; i++) {
  847    -1         var rect = clientRects[i];
  848    -1         var center_x = (rect.left + rect.right) / 2;
  849    -1         var center_y = (rect.top + rect.bottom) / 2;
  850    -1         var elementAtPoint = document.elementFromPoint(center_x, center_y);
  851    -1 
  852    -1         if (elementAtPoint == null || elementAtPoint == element ||
  853    -1             axs.utils.isAncestor(elementAtPoint, element) ||
  854    -1             axs.utils.isAncestor(element, elementAtPoint)) {
  855    -1             continue;
   -1   556 axs.utils.isElementDisabled = function(element, ignoreAncestors) {
   -1   557     var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *';
   -1   558     if (axs.browserUtils.matchSelector(element, selector)) {
   -1   559         return true;
   -1   560     }
   -1   561     if (!axs.utils.isNativelyDisableable(element) ||
   -1   562             axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) {
   -1   563         return false;
   -1   564     }
   -1   565     for (var next = element; next !== null; next = axs.dom.parentElement(next)) {
   -1   566         if (next.hasAttribute('disabled')) {
   -1   567             return true;
  856   568         }
  857    -1 
  858    -1         var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null);
  859    -1         if (!overlappingElementStyle)
  860    -1             continue;
  861    -1 
  862    -1         var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle,
  863    -1                                                         elementAtPoint);
  864    -1         if (overlappingElementBg && overlappingElementBg.alpha > 0 &&
  865    -1             overlappingElements.indexOf(elementAtPoint) < 0) {
  866    -1             overlappingElements.push(elementAtPoint);
   -1   569         if (ignoreAncestors) {
   -1   570             return false;
  867   571         }
  868   572     }
  869    -1 
  870    -1     return overlappingElements;
   -1   573     return false;
  871   574 };
  872   575 
  873   576 /**
  874    -1  * @param {Element} element
  875    -1  * @return {boolean}
   -1   577  * @param {Element} element An element to check.
   -1   578  * @return {boolean} True if the element is hidden from accessibility.
  876   579  */
  877    -1 axs.utils.elementIsHtmlControl = function(element) {
  878    -1     var defaultView = element.ownerDocument.defaultView;
   -1   580 axs.utils.isElementHidden = function(element) {
   -1   581     if (!(element instanceof element.ownerDocument.defaultView.HTMLElement))
   -1   582       return false;
  879   583 
  880    -1     // HTML control
  881    -1     if (element instanceof defaultView.HTMLButtonElement)
  882    -1         return true;
  883    -1     if (element instanceof defaultView.HTMLInputElement)
  884    -1         return true;
  885    -1     if (element instanceof defaultView.HTMLSelectElement)
  886    -1         return true;
  887    -1     if (element instanceof defaultView.HTMLTextAreaElement)
  888    -1         return true;
   -1   584     if (element.hasAttribute('chromevoxignoreariahidden'))
   -1   585         var chromevoxignoreariahidden = true;
  889   586 
  890    -1     return false;
  891    -1 };
   -1   587     var style = window.getComputedStyle(element, null);
   -1   588     if (style.display == 'none' || style.visibility == 'hidden')
   -1   589         return true;
  892   590 
  893    -1 /**
  894    -1  * @param {Element} element
  895    -1  * @return {boolean}
  896    -1  */
  897    -1 axs.utils.elementIsAriaWidget = function(element) {
  898    -1     if (element.hasAttribute('role')) {
  899    -1         var roleValue = element.getAttribute('role');
  900    -1         // TODO is this correct?
  901    -1         if (roleValue) {
  902    -1             var role = axs.constants.ARIA_ROLES[roleValue];
  903    -1             if (role && 'widget' in role['allParentRolesSet'])
  904    -1                 return true;
  905    -1         }
   -1   591     if (element.hasAttribute('aria-hidden') &&
   -1   592         element.getAttribute('aria-hidden').toLowerCase() == 'true') {
   -1   593         return !chromevoxignoreariahidden;
  906   594     }
   -1   595 
  907   596     return false;
  908   597 };
  909   598 
  910   599 /**
  911    -1  * @param {Element} element
  912    -1  * @return {boolean}
   -1   600  * @param {Element} element An element to check.
   -1   601  * @return {boolean} True if the element or one of its ancestors is
   -1   602  *     hidden from accessibility.
  913   603  */
  914    -1 axs.utils.elementIsVisible = function(element) {
  915    -1     if (axs.utils.elementIsTransparent(element))
  916    -1         return false;
  917    -1     if (axs.utils.elementHasZeroArea(element))
  918    -1         return false;
  919    -1     if (axs.utils.elementIsOutsideScrollArea(element))
  920    -1         return false;
   -1   604 axs.utils.isElementOrAncestorHidden = function(element) {
   -1   605     if (axs.utils.isElementHidden(element))
   -1   606         return true;
  921   607 
  922    -1     var overlappingElements = axs.utils.overlappingElements(element);
  923    -1     if (overlappingElements.length)
   -1   608     if (axs.dom.parentElement(element))
   -1   609         return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element));
   -1   610     else
  924   611         return false;
  925    -1 
  926    -1     return true;
  927   612 };
  928   613 
  929   614 /**
  930    -1  * @param {CSSStyleDeclaration} style
  931    -1  * @return {boolean}
   -1   615  * @param {Element} element An element to check
   -1   616  * @return {boolean} True if the given element is an inline element, false
   -1   617  *     otherwise.
  932   618  */
  933    -1 axs.utils.isLargeFont = function(style) {
  934    -1     var fontSize = style.fontSize;
  935    -1     var bold = style.fontWeight == 'bold';
  936    -1     var matches = fontSize.match(/(\d+)px/);
  937    -1     if (matches) {
  938    -1         var fontSizePx = parseInt(matches[1], 10);
  939    -1         var bodyStyle = window.getComputedStyle(document.body, null);
  940    -1         var bodyFontSize = bodyStyle.fontSize;
  941    -1         matches = bodyFontSize.match(/(\d+)px/);
  942    -1         if (matches) {
  943    -1             var bodyFontSizePx = parseInt(matches[1], 10);
  944    -1             var boldLarge = bodyFontSizePx * 1.2;
  945    -1             var large = bodyFontSizePx * 1.5;
  946    -1         } else {
  947    -1             var boldLarge = 19.2;
  948    -1             var large = 24;
  949    -1         }
  950    -1         return (bold && fontSizePx >= boldLarge || fontSizePx >= large);
  951    -1     }
  952    -1     matches = fontSize.match(/(\d+)em/);
  953    -1     if (matches) {
  954    -1         var fontSizeEm = parseInt(matches[1], 10);
  955    -1         if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5)
  956    -1             return true;
  957    -1         return false;
  958    -1     }
  959    -1     matches = fontSize.match(/(\d+)%/);
  960    -1     if (matches) {
  961    -1         var fontSizePercent = parseInt(matches[1], 10);
  962    -1         if (bold && fontSizePercent >= 120 || fontSizePercent >= 150)
  963    -1             return true;
  964    -1         return false;
  965    -1     }
  966    -1     matches = fontSize.match(/(\d+)pt/);
  967    -1     if (matches) {
  968    -1         var fontSizePt = parseInt(matches[1], 10);
  969    -1         if (bold && fontSizePt >= 14 || fontSizePt >= 18)
  970    -1             return true;
  971    -1         return false;
  972    -1     }
  973    -1     return false;
   -1   619 axs.utils.isInlineElement = function(element) {
   -1   620     var tagName = element.tagName.toUpperCase();
   -1   621     return axs.constants.InlineElements[tagName];
  974   622 };
  975   623 
  976   624 /**
  977    -1  * @param {CSSStyleDeclaration} style
  978    -1  * @param {Element} element
  979    -1  * @return {?axs.color.Color}
   -1   625  *
   -1   626  * Gets role details from an element.
   -1   627  * @param {Element} element The DOM element whose role we want.
   -1   628  * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute.
   -1   629  *
   -1   630  * @return {Object}
  980   631  */
  981    -1 axs.utils.getBgColor = function(style, element) {
  982    -1     var bgColorString = style.backgroundColor;
  983    -1     var bgColor = axs.color.parseColor(bgColorString);
  984    -1     if (!bgColor)
   -1   632 axs.utils.getRoles = function(element, implicit) {
   -1   633     if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit))
  985   634         return null;
  986    -1 
  987    -1     if (style.opacity < 1)
  988    -1         bgColor.alpha = bgColor.alpha * style.opacity;
  989    -1 
  990    -1     if (bgColor.alpha < 1) {
  991    -1         var parentBg = axs.utils.getParentBgColor(element);
  992    -1         if (parentBg == null)
  993    -1             return null;
  994    -1 
  995    -1         bgColor = axs.color.flattenColors(bgColor, parentBg);
   -1   635     var roleValue = element.getAttribute('role');
   -1   636     if (!roleValue && implicit)
   -1   637         roleValue = axs.properties.getImplicitRole(element);
   -1   638     if (!roleValue)  // role='' or implicit role came up empty
   -1   639         return null;
   -1   640     var roleNames = roleValue.split(' ');
   -1   641     var result = { roles: [], valid: false };
   -1   642     for (var i = 0; i < roleNames.length; i++) {
   -1   643         var role = roleNames[i];
   -1   644         var ariaRole = axs.constants.ARIA_ROLES[role];
   -1   645         var roleObject = { 'name': role };
   -1   646         if (ariaRole && !ariaRole.abstract) {
   -1   647             roleObject.details = ariaRole;
   -1   648             if (!result.applied) {
   -1   649                 result.applied = roleObject;
   -1   650             }
   -1   651             roleObject.valid = result.valid = true;
   -1   652         } else {
   -1   653             roleObject.valid = false;
   -1   654         }
   -1   655         result.roles.push(roleObject);
  996   656     }
  997    -1     return bgColor;
   -1   657 
   -1   658     return result;
  998   659 };
  999   660 
 1000   661 /**
 1001    -1  * Gets the effective background color of the parent of |element|.
 1002    -1  * @param {Element} element
 1003    -1  * @return {?axs.color.Color}
   -1   662  * @param {!string} propertyName
   -1   663  * @param {!string} value
   -1   664  * @param {!Element} element
   -1   665  * @return {!Object}
 1004   666  */
 1005    -1 axs.utils.getParentBgColor = function(element) {
 1006    -1     /** @type {Element} */ var parent = element;
 1007    -1     var bgStack = [];
 1008    -1     var foundSolidColor = null;
 1009    -1     while ((parent = axs.dom.parentElement(parent))) {
 1010    -1         var computedStyle = window.getComputedStyle(parent, null);
 1011    -1         if (!computedStyle)
 1012    -1             continue;
 1013    -1 
 1014    -1         var parentBg = axs.color.parseColor(computedStyle.backgroundColor);
 1015    -1         if (!parentBg)
 1016    -1             continue;
 1017    -1 
 1018    -1         if (computedStyle.opacity < 1)
 1019    -1             parentBg.alpha = parentBg.alpha * computedStyle.opacity;
 1020    -1 
 1021    -1         if (parentBg.alpha == 0)
 1022    -1             continue;
 1023    -1 
 1024    -1         bgStack.push(parentBg);
 1025    -1 
 1026    -1         if (parentBg.alpha == 1) {
 1027    -1             foundSolidColor = true;
 1028    -1             break;
 1029    -1         }
   -1   667 axs.utils.getAriaPropertyValue = function(propertyName, value, element) {
   -1   668     var propertyKey = propertyName.replace(/^aria-/, '');
   -1   669     var property = axs.constants.ARIA_PROPERTIES[propertyKey];
   -1   670     var result = { 'name': propertyName, 'rawValue': value };
   -1   671     if (!property) {
   -1   672         result.valid = false;
   -1   673         result.reason = '"' + propertyName + '" is not a valid ARIA property';
   -1   674         return result;
 1030   675     }
 1031   676 
 1032    -1     if (!foundSolidColor)
 1033    -1         bgStack.push(new axs.color.Color(255, 255, 255, 1));
   -1   677     var propertyType = property.valueType;
   -1   678     if (!propertyType) {
   -1   679         result.valid = false;
   -1   680         result.reason = '"' + propertyName + '" is not a valid ARIA property';
   -1   681         return result;
   -1   682     }
 1034   683 
 1035    -1     var bg = bgStack.pop();
 1036    -1     while (bgStack.length) {
 1037    -1         var fg = bgStack.pop();
 1038    -1         bg = axs.color.flattenColors(fg, bg);
   -1   684     switch (propertyType) {
   -1   685     case "idref":
   -1   686         var isValid = axs.utils.isValidIDRefValue(value, element);
   -1   687         result.valid = isValid.valid;
   -1   688         result.reason = isValid.reason;
   -1   689         result.idref = isValid.idref;
   -1   690         // falls through
   -1   691     case "idref_list":
   -1   692         var idrefValues = value.split(/\s+/);
   -1   693         result.valid = true;
   -1   694         for (var i = 0; i < idrefValues.length; i++) {
   -1   695             var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i],  element);
   -1   696             if (!refIsValid.valid)
   -1   697                 result.valid = false;
   -1   698             if (result.values)
   -1   699                 result.values.push(refIsValid);
   -1   700             else
   -1   701                 result.values = [refIsValid];
   -1   702         }
   -1   703         return result;
   -1   704     case "integer":
   -1   705         var validNumber = axs.utils.isValidNumber(value);
   -1   706         if (!validNumber.valid) {
   -1   707             result.valid = false;
   -1   708             result.reason = validNumber.reason;
   -1   709             return result;
   -1   710         }
   -1   711         if (Math.floor(validNumber.value) !== validNumber.value) {
   -1   712             result.valid = false;
   -1   713             result.reason = '' + value + ' is not a whole integer';
   -1   714         } else {
   -1   715             result.valid = true;
   -1   716             result.value = validNumber.value;
   -1   717         }
   -1   718         return result;
   -1   719     case "decimal":
   -1   720     case "number":
   -1   721         var validNumber = axs.utils.isValidNumber(value);
   -1   722         result.valid = validNumber.valid;
   -1   723         if (!validNumber.valid) {
   -1   724             result.reason = validNumber.reason;
   -1   725             return result;
   -1   726         }
   -1   727         result.value = validNumber.value;
   -1   728         return result;
   -1   729     case "string":
   -1   730         result.valid = true;
   -1   731         result.value = value;
   -1   732         return result;
   -1   733     case "token":
   -1   734         var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase());
   -1   735         if (validTokenValue.valid) {
   -1   736             result.valid = true;
   -1   737             result.value = validTokenValue.value;
   -1   738             return result;
   -1   739         } else {
   -1   740             result.valid = false;
   -1   741             result.value = value;
   -1   742             result.reason = validTokenValue.reason;
   -1   743             return result;
   -1   744         }
   -1   745         // falls through
   -1   746     case "token_list":
   -1   747         var tokenValues = value.split(/\s+/);
   -1   748         result.valid = true;
   -1   749         for (var i = 0; i < tokenValues.length; i++) {
   -1   750             var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase());
   -1   751             if (!validTokenValue.valid) {
   -1   752                 result.valid = false;
   -1   753                 if (result.reason) {
   -1   754                     result.reason = [ result.reason ];
   -1   755                     result.reason.push(validTokenValue.reason);
   -1   756                 } else {
   -1   757                     result.reason = validTokenValue.reason;
   -1   758                     result.possibleValues = validTokenValue.possibleValues;
   -1   759                 }
   -1   760             }
   -1   761             // TODO (more structured result)
   -1   762             if (result.values)
   -1   763                 result.values.push(validTokenValue.value);
   -1   764             else
   -1   765                 result.values = [validTokenValue.value];
   -1   766         }
   -1   767         return result;
   -1   768     case "tristate":
   -1   769         var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName);
   -1   770         if (validTristate.valid) {
   -1   771             result.valid = true;
   -1   772             result.value = validTristate.value;
   -1   773         } else {
   -1   774             result.valid = false;
   -1   775             result.value = value;
   -1   776             result.reason = validTristate.reason;
   -1   777         }
   -1   778         return result;
   -1   779     case "boolean":
   -1   780         var validBoolean = axs.utils.isValidBoolean(value);
   -1   781         if (validBoolean.valid) {
   -1   782             result.valid = true;
   -1   783             result.value = validBoolean.value;
   -1   784         } else {
   -1   785             result.valid = false;
   -1   786             result.value = value;
   -1   787             result.reason = validBoolean.reason;
   -1   788         }
   -1   789         return result;
 1039   790     }
 1040    -1     return bg;
   -1   791     result.valid = false;
   -1   792     result.reason = 'Not a valid ARIA property';
   -1   793     return result;
 1041   794 };
 1042   795 
 1043   796 /**
 1044    -1  * @param {CSSStyleDeclaration} style
 1045    -1  * @param {Element} element
 1046    -1  * @param {axs.color.Color} bgColor The background color, which may come from
 1047    -1  *    another element (such as a parent element), for flattening into the
 1048    -1  *    foreground color.
 1049    -1  * @return {?axs.color.Color}
   -1   797  * @param {string} propertyName The name of the property.
   -1   798  * @param {string} value The value to check.
   -1   799  * @return {!Object}
 1050   800  */
 1051    -1 axs.utils.getFgColor = function(style, element, bgColor) {
 1052    -1     var fgColorString = style.color;
 1053    -1     var fgColor = axs.color.parseColor(fgColorString);
 1054    -1     if (!fgColor)
 1055    -1         return null;
 1056    -1 
 1057    -1     if (fgColor.alpha < 1)
 1058    -1         fgColor = axs.color.flattenColors(fgColor, bgColor);
 1059    -1 
 1060    -1     if (style.opacity < 1) {
 1061    -1         var parentBg = axs.utils.getParentBgColor(element);
 1062    -1         fgColor.alpha = fgColor.alpha * style.opacity;
 1063    -1         fgColor = axs.color.flattenColors(fgColor, parentBg);
 1064    -1     }
 1065    -1 
 1066    -1     return fgColor;
   -1   801 axs.utils.isValidTokenValue = function(propertyName, value) {
   -1   802     var propertyKey = propertyName.replace(/^aria-/, '');
   -1   803     var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];
   -1   804     var possibleValues = propertyDetails.valuesSet;
   -1   805     return axs.utils.isPossibleValue(value, possibleValues, propertyName);
 1067   806 };
 1068   807 
 1069   808 /**
 1070    -1  * @param {Element} element
 1071    -1  * @return {?number}
   -1   809  * @param {string} value
   -1   810  * @param {Object.<string, boolean>} possibleValues
   -1   811  * @param {string} propertyName The name of the property.
   -1   812  * @return {!Object}
 1072   813  */
 1073    -1 axs.utils.getContrastRatioForElement = function(element) {
 1074    -1     var style = window.getComputedStyle(element, null);
 1075    -1     return axs.utils.getContrastRatioForElementWithComputedStyle(style, element);
   -1   814 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) {
   -1   815     if (!possibleValues[value])
   -1   816         return { 'valid': false,
   -1   817                  'value': value,
   -1   818                  'reason': '"' + value + '" is not a valid value for ' + propertyName,
   -1   819                  'possibleValues': Object.keys(possibleValues) };
   -1   820     return { 'valid': true, 'value': value };
 1076   821 };
 1077   822 
 1078   823 /**
 1079    -1  * @param {CSSStyleDeclaration} style
 1080    -1  * @param {Element} element
 1081    -1  * @return {?number}
   -1   824  * @param {string} value
   -1   825  * @return {!Object}
 1082   826  */
 1083    -1 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) {
 1084    -1     if (axs.utils.isElementHidden(element))
 1085    -1         return null;
 1086    -1 
 1087    -1     var bgColor = axs.utils.getBgColor(style, element);
 1088    -1     if (!bgColor)
 1089    -1         return null;
 1090    -1 
 1091    -1     var fgColor = axs.utils.getFgColor(style, element, bgColor);
 1092    -1     if (!fgColor)
 1093    -1         return null;
 1094    -1 
 1095    -1     return axs.color.calculateContrastRatio(fgColor, bgColor);
   -1   827 axs.utils.isValidBoolean = function(value) {
   -1   828     try {
   -1   829         var parsedValue = JSON.parse(value);
   -1   830     } catch (e) {
   -1   831         parsedValue = '';
   -1   832     }
   -1   833     if (typeof(parsedValue) != 'boolean')
   -1   834         return { 'valid': false,
   -1   835                  'value': value,
   -1   836                  'reason': '"' + value + '" is not a true/false value' };
   -1   837     return { 'valid': true, 'value': parsedValue };
 1096   838 };
 1097   839 
 1098   840 /**
 1099    -1  * @param {Element} element
 1100    -1  * @return {boolean}
   -1   841  * @param {string} value
   -1   842  * @param {!Element} element
   -1   843  * @return {!Object}
 1101   844  */
 1102    -1 axs.utils.isNativeTextElement = function(element) {
 1103    -1     var tagName = element.tagName.toLowerCase();
 1104    -1     var type = element.type ? element.type.toLowerCase() : '';
 1105    -1     if (tagName == 'textarea')
 1106    -1         return true;
 1107    -1     if (tagName != 'input')
 1108    -1         return false;
 1109    -1 
 1110    -1     switch (type) {
 1111    -1     case 'email':
 1112    -1     case 'number':
 1113    -1     case 'password':
 1114    -1     case 'search':
 1115    -1     case 'text':
 1116    -1     case 'tel':
 1117    -1     case 'url':
 1118    -1     case '':
 1119    -1         return true;
 1120    -1     default:
 1121    -1         return false;
 1122    -1     }
   -1   845 axs.utils.isValidIDRefValue = function(value, element) {
   -1   846     if (value.length == 0)
   -1   847         return { 'valid': true, 'idref': value };
   -1   848     if (!element.ownerDocument.getElementById(value))
   -1   849         return { 'valid': false,
   -1   850                  'idref': value,
   -1   851                  'reason': 'No element with ID "' + value + '"' };
   -1   852     return { 'valid': true, 'idref': value };
 1123   853 };
 1124   854 
 1125   855 /**
 1126    -1  * @param {number} contrastRatio
 1127    -1  * @param {CSSStyleDeclaration} style
 1128    -1  * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level
 1129    -1  * @return {boolean}
   -1   856  * Tests if a number is real number for a11y purposes.
   -1   857  * Must be a real, numerical, decimal value; heavily inspired by
   -1   858  *    http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number
   -1   859  * @param {string} value
   -1   860  * @return {!Object}
 1130   861  */
 1131    -1 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) {
 1132    -1     // Round to nearest 0.1
 1133    -1     var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10);
 1134    -1     if (!opt_strict) {
 1135    -1         return roundedContrastRatio < 3.0 ||
 1136    -1             (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5);
 1137    -1     } else {
 1138    -1         return roundedContrastRatio < 4.5 ||
 1139    -1             (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0);
   -1   862 axs.utils.isValidNumber = function(value) {
   -1   863     var failResult = {
   -1   864         'valid': false,
   -1   865         'value': value,
   -1   866         'reason': '"' + value + '" is not a number'
   -1   867     };
   -1   868     if (!value) {
   -1   869         return failResult;
   -1   870     }
   -1   871     if (/^0x/i.test(value)) {
   -1   872         failResult.reason = '"' + value + '" is not a decimal number';  // hex is not accepted
   -1   873         return failResult;
   -1   874     }
   -1   875     var parsedValue = value * 1;
   -1   876     if (!isFinite(parsedValue)) {
   -1   877         return failResult;
 1140   878     }
   -1   879     return { 'valid': true, 'value': parsedValue };
 1141   880 };
 1142   881 
 1143   882 /**
 1144   883  * @param {Element} element
 1145   884  * @return {boolean}
 1146   885  */
 1147    -1 axs.utils.hasLabel = function(element) {
 1148    -1     var tagName = element.tagName.toLowerCase();
 1149    -1     var type = element.type ? element.type.toLowerCase() : '';
 1150    -1 
 1151    -1     if (element.hasAttribute('aria-label'))
 1152    -1         return true;
 1153    -1     if (element.hasAttribute('title'))
 1154    -1         return true;
 1155    -1     if (tagName == 'img' && element.hasAttribute('alt'))
 1156    -1         return true;
 1157    -1     if (tagName == 'input' && type == 'image' && element.hasAttribute('alt'))
 1158    -1         return true;
 1159    -1     if (tagName == 'input' && (type == 'submit' || type == 'reset'))
 1160    -1         return true;
 1161    -1 
 1162    -1     // There's a separate audit that makes sure this points to an actual element or elements.
 1163    -1     if (element.hasAttribute('aria-labelledby'))
 1164    -1         return true;
 1165    -1 
 1166    -1     if (element.hasAttribute('id')) {
 1167    -1         var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]');
 1168    -1         if (labelsFor.length > 0)
 1169    -1             return true;
 1170    -1     }
   -1   886 axs.utils.isElementImplicitlyFocusable = function(element) {
   -1   887     var defaultView = element.ownerDocument.defaultView;
 1171   888 
 1172    -1     var parent = axs.dom.parentElement(element);
 1173    -1     while (parent) {
 1174    -1         if (parent.tagName.toLowerCase() == 'label') {
 1175    -1             var parentLabel = /** HTMLLabelElement */ parent;
 1176    -1             if (parentLabel.control == element)
 1177    -1                 return true;
 1178    -1         }
 1179    -1         parent = axs.dom.parentElement(parent);
 1180    -1     }
   -1   889     if (element instanceof defaultView.HTMLAnchorElement ||
   -1   890         element instanceof defaultView.HTMLAreaElement)
   -1   891         return element.hasAttribute('href');
   -1   892     if (element instanceof defaultView.HTMLInputElement ||
   -1   893         element instanceof defaultView.HTMLSelectElement ||
   -1   894         element instanceof defaultView.HTMLTextAreaElement ||
   -1   895         element instanceof defaultView.HTMLButtonElement ||
   -1   896         element instanceof defaultView.HTMLIFrameElement)
   -1   897         return !element.disabled;
 1181   898     return false;
 1182   899 };
 1183   900 
 1184   901 /**
 1185    -1  * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute.
 1186    -1  * Disabled here means that the element should be considered disabled according to specification.
 1187    -1  * This element may or may not be effectively disabled in practice as this is dependent on implementation.
 1188    -1  *
 1189    -1  * @param {Element} element An element to check.
 1190    -1  * @return {boolean} true If the element supports being natively disabled.
 1191    -1  */
 1192    -1 axs.utils.isNativelyDisableable = function(element) {
 1193    -1     var tagName = element.tagName.toUpperCase();
 1194    -1     return (tagName in axs.constants.NATIVELY_DISABLEABLE);
 1195    -1 };
 1196    -1 
 1197    -1 /**
 1198    -1  * Determine if this element is disabled directly or indirectly by a disabled ancestor.
 1199    -1  * Disabled here means that the element should be considered disabled according to specification.
 1200    -1  * This element may or may not be effectively disabled in practice as this is dependent on implementation.
 1201    -1  *
 1202    -1  * @param {Element} element An element to check.
 1203    -1  * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors.
 1204    -1  * @return {boolean} true if the element or one of its ancestors is disabled.
   -1   902  * Returns an array containing the values of the given JSON-compatible object.
   -1   903  * (Simply ignores any function values.)
   -1   904  * @param {Object} obj
   -1   905  * @return {Array}
 1205   906  */
 1206    -1 axs.utils.isElementDisabled = function(element, ignoreAncestors) {
 1207    -1     var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *';
 1208    -1     if (axs.browserUtils.matchSelector(element, selector)) {
 1209    -1         return true;
 1210    -1     }
 1211    -1     if (!axs.utils.isNativelyDisableable(element) ||
 1212    -1             axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) {
 1213    -1         return false;
 1214    -1     }
 1215    -1     for (var next = element; next !== null; next = axs.dom.parentElement(next)) {
 1216    -1         if (next.hasAttribute('disabled')) {
 1217    -1             return true;
 1218    -1         }
 1219    -1         if (ignoreAncestors) {
 1220    -1             return false;
 1221    -1         }
   -1   907 axs.utils.values = function(obj) {
   -1   908     var values = [];
   -1   909     for (var key in obj) {
   -1   910         if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')
   -1   911             values.push(obj[key]);
 1222   912     }
 1223    -1     return false;
   -1   913     return values;
 1224   914 };
 1225   915 
 1226   916 /**
 1227    -1  * @param {Element} element An element to check.
 1228    -1  * @return {boolean} True if the element is hidden from accessibility.
   -1   917  * Returns an object containing the same keys and values as the given
   -1   918  * JSON-compatible object. (Simply ignores any function values.)
   -1   919  * @param {Object} obj
   -1   920  * @return {Object}
 1229   921  */
 1230    -1 axs.utils.isElementHidden = function(element) {
 1231    -1     if (!(element instanceof element.ownerDocument.defaultView.HTMLElement))
 1232    -1       return false;
 1233    -1 
 1234    -1     if (element.hasAttribute('chromevoxignoreariahidden'))
 1235    -1         var chromevoxignoreariahidden = true;
 1236    -1 
 1237    -1     var style = window.getComputedStyle(element, null);
 1238    -1     if (style.display == 'none' || style.visibility == 'hidden')
 1239    -1         return true;
 1240    -1 
 1241    -1     if (element.hasAttribute('aria-hidden') &&
 1242    -1         element.getAttribute('aria-hidden').toLowerCase() == 'true') {
 1243    -1         return !chromevoxignoreariahidden;
   -1   922 axs.utils.namedValues = function(obj) {
   -1   923     var values = {};
   -1   924     for (var key in obj) {
   -1   925         if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')
   -1   926             values[key] = obj[key];
 1244   927     }
 1245    -1 
 1246    -1     return false;
   -1   928     return values;
 1247   929 };
 1248   930 
 1249   931 /**
 1250    -1  * @param {Element} element An element to check.
 1251    -1  * @return {boolean} True if the element or one of its ancestors is
 1252    -1  *     hidden from accessibility.
   -1   932 * Escapes a given ID to be used in a CSS selector
   -1   933 *
   -1   934 * @private
   -1   935 * @param {!string} id The ID to be escaped
   -1   936 * @return {string} The escaped ID
   -1   937 */
   -1   938 function escapeId(id) {
   -1   939     return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; });
   -1   940 }
   -1   941 
   -1   942 /** Gets a CSS selector text for a DOM object.
   -1   943  * @param {Node} obj The DOM object.
   -1   944  * @return {string} CSS selector text for the DOM object.
 1253   945  */
 1254    -1 axs.utils.isElementOrAncestorHidden = function(element) {
 1255    -1     if (axs.utils.isElementHidden(element))
 1256    -1         return true;
   -1   946 axs.utils.getQuerySelectorText = function(obj) {
   -1   947   if (obj == null || obj.tagName == 'HTML') {
   -1   948     return 'html';
   -1   949   } else if (obj.tagName == 'BODY') {
   -1   950     return 'body';
   -1   951   }
 1257   952 
 1258    -1     if (axs.dom.parentElement(element))
 1259    -1         return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element));
 1260    -1     else
 1261    -1         return false;
 1262    -1 };
   -1   953   if (obj.hasAttribute) {
   -1   954     if (obj.id) {
   -1   955       return '#' + escapeId(obj.id);
   -1   956     }
 1263   957 
 1264    -1 /**
 1265    -1  * @param {Element} element An element to check
 1266    -1  * @return {boolean} True if the given element is an inline element, false
 1267    -1  *     otherwise.
 1268    -1  */
 1269    -1 axs.utils.isInlineElement = function(element) {
 1270    -1     var tagName = element.tagName.toUpperCase();
 1271    -1     return axs.constants.InlineElements[tagName];
   -1   958     if (obj.className) {
   -1   959       var selector = '';
   -1   960       for (var i = 0; i < obj.classList.length; i++)
   -1   961         selector += '.' + obj.classList[i];
   -1   962 
   -1   963       var total = 0;
   -1   964       if (obj.parentNode) {
   -1   965         for (i = 0; i < obj.parentNode.children.length; i++) {
   -1   966           var similar = obj.parentNode.children[i];
   -1   967           if (axs.browserUtils.matchSelector(similar, selector))
   -1   968             total++;
   -1   969           if (similar === obj)
   -1   970             break;
   -1   971         }
   -1   972       } else {
   -1   973         total = 1;
   -1   974       }
   -1   975 
   -1   976       if (total == 1) {
   -1   977         return axs.utils.getQuerySelectorText(obj.parentNode) +
   -1   978                ' > ' + selector;
   -1   979       }
   -1   980     }
   -1   981 
   -1   982     if (obj.parentNode) {
   -1   983       var similarTags = obj.parentNode.children;
   -1   984       var total = 1;
   -1   985       var i = 0;
   -1   986       while (similarTags[i] !== obj) {
   -1   987         if (similarTags[i].tagName == obj.tagName) {
   -1   988           total++;
   -1   989         }
   -1   990         i++;
   -1   991       }
   -1   992 
   -1   993       var next = '';
   -1   994       if (obj.parentNode.tagName != 'BODY') {
   -1   995         next = axs.utils.getQuerySelectorText(obj.parentNode) +
   -1   996                ' > ';
   -1   997       }
   -1   998 
   -1   999       if (total == 1) {
   -1  1000         return next +
   -1  1001                obj.tagName;
   -1  1002       } else {
   -1  1003         return next +
   -1  1004                obj.tagName +
   -1  1005                ':nth-of-type(' + total + ')';
   -1  1006       }
   -1  1007     }
   -1  1008 
   -1  1009   } else if (obj.selectorText) {
   -1  1010     return obj.selectorText;
   -1  1011   }
   -1  1012 
   -1  1013   return '';
 1272  1014 };
 1273  1015 
 1274  1016 /**
 1275    -1  *
 1276    -1  * Gets role details from an element.
 1277    -1  * @param {Element} element The DOM element whose role we want.
 1278    -1  * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute.
 1279    -1  *
 1280    -1  * @return {Object}
   -1  1017  * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or
   -1  1018  * single ID reference.
   -1  1019  * @param {Element} element a potential referent.
   -1  1020  * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'.
   -1  1021  * @return {NodeList} The elements that refer to this element or null.
 1281  1022  */
 1282    -1 axs.utils.getRoles = function(element, implicit) {
 1283    -1     if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit))
   -1  1023 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) {
   -1  1024     var propertyToSelector = function(propertyKey) {
   -1  1025         var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];
   -1  1026         if (propertyDetails) {
   -1  1027             if (propertyDetails.valueType === ('idref')) {
   -1  1028                 return '[aria-' + propertyKey + '=\'' + id + '\']';
   -1  1029             } else if (propertyDetails.valueType === ('idref_list')) {
   -1  1030                 return '[aria-' + propertyKey + '~=\'' + id + '\']';
   -1  1031             }
   -1  1032         }
   -1  1033         return '';
   -1  1034     };
   -1  1035     if (!element)
 1284  1036         return null;
 1285    -1     var roleValue = element.getAttribute('role');
 1286    -1     if (!roleValue && implicit)
 1287    -1         roleValue = axs.properties.getImplicitRole(element);
 1288    -1     if (!roleValue)  // role='' or implicit role came up empty
   -1  1037     var id = element.id;
   -1  1038     if (!id)
 1289  1039         return null;
 1290    -1     var roleNames = roleValue.split(' ');
 1291    -1     var result = { roles: [], valid: false };
 1292    -1     for (var i = 0; i < roleNames.length; i++) {
 1293    -1         var role = roleNames[i];
 1294    -1         var ariaRole = axs.constants.ARIA_ROLES[role];
 1295    -1         var roleObject = { 'name': role };
 1296    -1         if (ariaRole && !ariaRole.abstract) {
 1297    -1             roleObject.details = ariaRole;
 1298    -1             if (!result.applied) {
 1299    -1                 result.applied = roleObject;
   -1  1040     id = id.replace(/'/g, "\\'");  // make it safe to use in a selector
   -1  1041 
   -1  1042     if (opt_attributeName) {
   -1  1043         var propertyKey = opt_attributeName.replace(/^aria-/, '');
   -1  1044         var referrerQuery = propertyToSelector(propertyKey);
   -1  1045         if (referrerQuery) {
   -1  1046             return element.ownerDocument.querySelectorAll(referrerQuery);
   -1  1047         }
   -1  1048     } else {
   -1  1049         var selectors = [];
   -1  1050         for (var propertyKey in axs.constants.ARIA_PROPERTIES) {
   -1  1051             var referrerQuery = propertyToSelector(propertyKey);
   -1  1052             if (referrerQuery) {
   -1  1053                 selectors.push(referrerQuery);
 1300  1054             }
 1301    -1             roleObject.valid = result.valid = true;
 1302    -1         } else {
 1303    -1             roleObject.valid = false;
 1304  1055         }
 1305    -1         result.roles.push(roleObject);
   -1  1056         return element.ownerDocument.querySelectorAll(selectors.join(','));
 1306  1057     }
 1307    -1 
 1308    -1     return result;
   -1  1058     return null;
 1309  1059 };
 1310  1060 
 1311  1061 /**
 1312    -1  * @param {!string} propertyName
 1313    -1  * @param {!string} value
 1314    -1  * @param {!Element} element
 1315    -1  * @return {!Object}
   -1  1062  * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or
   -1  1063  * single ID reference.
   -1  1064  * @param {Element} element a potential referent.
   -1  1065  * @return {NodeList} The elements that refer to this element.
 1316  1066  */
 1317    -1 axs.utils.getAriaPropertyValue = function(propertyName, value, element) {
 1318    -1     var propertyKey = propertyName.replace(/^aria-/, '');
 1319    -1     var property = axs.constants.ARIA_PROPERTIES[propertyKey];
 1320    -1     var result = { 'name': propertyName, 'rawValue': value };
 1321    -1     if (!property) {
 1322    -1         result.valid = false;
 1323    -1         result.reason = '"' + propertyName + '" is not a valid ARIA property';
 1324    -1         return result;
 1325    -1     }
 1326    -1 
 1327    -1     var propertyType = property.valueType;
 1328    -1     if (!propertyType) {
 1329    -1         result.valid = false;
 1330    -1         result.reason = '"' + propertyName + '" is not a valid ARIA property';
 1331    -1         return result;
 1332    -1     }
   -1  1067 axs.utils.getHtmlIdReferrers = function(element) {
   -1  1068     if (!element)
   -1  1069         return null;
   -1  1070     var id = element.id;
   -1  1071     if (!id)
   -1  1072         return null;
   -1  1073     id = id.replace(/'/g, "\\'");  // make it safe to use in a selector
   -1  1074     var selectorTemplates = [
   -1  1075         '[contextmenu=\'{id}\']',
   -1  1076         '[itemref~=\'{id}\']',
   -1  1077         'button[form=\'{id}\']',
   -1  1078         'button[menu=\'{id}\']',
   -1  1079         'fieldset[form=\'{id}\']',
   -1  1080         'input[form=\'{id}\']',
   -1  1081         'input[list=\'{id}\']',
   -1  1082         'keygen[form=\'{id}\']',
   -1  1083         'label[for=\'{id}\']',
   -1  1084         'label[form=\'{id}\']',
   -1  1085         'menuitem[command=\'{id}\']',
   -1  1086         'object[form=\'{id}\']',
   -1  1087         'output[for~=\'{id}\']',
   -1  1088         'output[form=\'{id}\']',
   -1  1089         'select[form=\'{id}\']',
   -1  1090         'td[headers~=\'{id}\']',
   -1  1091         'textarea[form=\'{id}\']',
   -1  1092         'tr[headers~=\'{id}\']'];
   -1  1093     var selectors = selectorTemplates.map(function(selector) {
   -1  1094         return selector.replace('\{id\}', id);
   -1  1095     });
   -1  1096     return element.ownerDocument.querySelectorAll(selectors.join(','));
   -1  1097 };
 1333  1098 
 1334    -1     switch (propertyType) {
 1335    -1     case "idref":
 1336    -1         var isValid = axs.utils.isValidIDRefValue(value, element);
 1337    -1         result.valid = isValid.valid;
 1338    -1         result.reason = isValid.reason;
 1339    -1         result.idref = isValid.idref;
 1340    -1         // falls through
 1341    -1     case "idref_list":
 1342    -1         var idrefValues = value.split(/\s+/);
 1343    -1         result.valid = true;
 1344    -1         for (var i = 0; i < idrefValues.length; i++) {
 1345    -1             var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i],  element);
 1346    -1             if (!refIsValid.valid)
 1347    -1                 result.valid = false;
 1348    -1             if (result.values)
 1349    -1                 result.values.push(refIsValid);
 1350    -1             else
 1351    -1                 result.values = [refIsValid];
 1352    -1         }
 1353    -1         return result;
 1354    -1     case "integer":
 1355    -1         var validNumber = axs.utils.isValidNumber(value);
 1356    -1         if (!validNumber.valid) {
 1357    -1             result.valid = false;
 1358    -1             result.reason = validNumber.reason;
 1359    -1             return result;
 1360    -1         }
 1361    -1         if (Math.floor(validNumber.value) !== validNumber.value) {
 1362    -1             result.valid = false;
 1363    -1             result.reason = '' + value + ' is not a whole integer';
 1364    -1         } else {
 1365    -1             result.valid = true;
 1366    -1             result.value = validNumber.value;
 1367    -1         }
 1368    -1         return result;
 1369    -1     case "decimal":
 1370    -1     case "number":
 1371    -1         var validNumber = axs.utils.isValidNumber(value);
 1372    -1         result.valid = validNumber.valid;
 1373    -1         if (!validNumber.valid) {
 1374    -1             result.reason = validNumber.reason;
 1375    -1             return result;
 1376    -1         }
 1377    -1         result.value = validNumber.value;
 1378    -1         return result;
 1379    -1     case "string":
 1380    -1         result.valid = true;
 1381    -1         result.value = value;
 1382    -1         return result;
 1383    -1     case "token":
 1384    -1         var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase());
 1385    -1         if (validTokenValue.valid) {
 1386    -1             result.valid = true;
 1387    -1             result.value = validTokenValue.value;
 1388    -1             return result;
 1389    -1         } else {
 1390    -1             result.valid = false;
 1391    -1             result.value = value;
 1392    -1             result.reason = validTokenValue.reason;
 1393    -1             return result;
 1394    -1         }
 1395    -1         // falls through
 1396    -1     case "token_list":
 1397    -1         var tokenValues = value.split(/\s+/);
 1398    -1         result.valid = true;
 1399    -1         for (var i = 0; i < tokenValues.length; i++) {
 1400    -1             var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase());
 1401    -1             if (!validTokenValue.valid) {
 1402    -1                 result.valid = false;
 1403    -1                 if (result.reason) {
 1404    -1                     result.reason = [ result.reason ];
 1405    -1                     result.reason.push(validTokenValue.reason);
   -1  1099 /**
   -1  1100  * Gets a list of all IDs this element references in either ARIA or HTML attributes.
   -1  1101  *
   -1  1102  * @param {Element} element The element to check for idref attributes.
   -1  1103  * @returns {Array.<string>} Any IDs this element references.
   -1  1104  */
   -1  1105 axs.utils.getReferencedIds = function(element) {
   -1  1106     var result = [];
   -1  1107     var addResult = function(ids) {
   -1  1108             if (ids) {
   -1  1109                 if (ids.indexOf(' ') > 0) {
   -1  1110                     result = result.concat(attrib.value.split(' '));
 1406  1111                 } else {
 1407    -1                     result.reason = validTokenValue.reason;
 1408    -1                     result.possibleValues = validTokenValue.possibleValues;
   -1  1112                     result.push(ids);
 1409  1113                 }
 1410  1114             }
 1411    -1             // TODO (more structured result)
 1412    -1             if (result.values)
 1413    -1                 result.values.push(validTokenValue.value);
 1414    -1             else
 1415    -1                 result.values = [validTokenValue.value];
 1416    -1         }
 1417    -1         return result;
 1418    -1     case "tristate":
 1419    -1         var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName);
 1420    -1         if (validTristate.valid) {
 1421    -1             result.valid = true;
 1422    -1             result.value = validTristate.value;
 1423    -1         } else {
 1424    -1             result.valid = false;
 1425    -1             result.value = value;
 1426    -1             result.reason = validTristate.reason;
 1427    -1         }
 1428    -1         return result;
 1429    -1     case "boolean":
 1430    -1         var validBoolean = axs.utils.isValidBoolean(value);
 1431    -1         if (validBoolean.valid) {
 1432    -1             result.valid = true;
 1433    -1             result.value = validBoolean.value;
 1434    -1         } else {
 1435    -1             result.valid = false;
 1436    -1             result.value = value;
 1437    -1             result.reason = validBoolean.reason;
   -1  1115         };
   -1  1116     for (var i = 0; i < element.attributes.length; i++) {
   -1  1117         var tagName = element.tagName.toLowerCase();
   -1  1118         var attrib = element.attributes[i];
   -1  1119         if (attrib.specified) {
   -1  1120             var attribName = attrib.name;
   -1  1121             var ariaAttr = attribName.match(/aria-(.+)/);
   -1  1122             if (ariaAttr) {
   -1  1123                 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]];
   -1  1124                 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) {
   -1  1125                     addResult(attrib.value);
   -1  1126                 }
   -1  1127                 continue;
   -1  1128             }
   -1  1129             switch (attribName) {
   -1  1130                 case 'contextmenu':
   -1  1131                 case 'itemref':
   -1  1132                     addResult(attrib.value);
   -1  1133                     break;
   -1  1134                 case 'form':
   -1  1135                     if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' ||
   -1  1136                             tagName == 'keygen' || tagName == 'label' || tagName == 'object' ||
   -1  1137                             tagName == 'output' || tagName == 'select' || tagName == 'textarea') {
   -1  1138                         addResult(attrib.value);
   -1  1139                     }
   -1  1140                     break;
   -1  1141                 case 'for':
   -1  1142                     if (tagName == 'label' || tagName == 'output') {
   -1  1143                         addResult(attrib.value);
   -1  1144                     }
   -1  1145                     break;
   -1  1146                 case 'menu':
   -1  1147                     if (tagName == 'button') {
   -1  1148                         addResult(attrib.value);
   -1  1149                     }
   -1  1150                     break;
   -1  1151                 case 'list':
   -1  1152                     if (tagName == 'input') {
   -1  1153                         addResult(attrib.value);
   -1  1154                     }
   -1  1155                     break;
   -1  1156                 case 'command':
   -1  1157                     if (tagName == 'menuitem') {
   -1  1158                         addResult(attrib.value);
   -1  1159                     }
   -1  1160                     break;
   -1  1161                 case 'headers':
   -1  1162                     if (tagName == 'td' || tagName == 'tr') {
   -1  1163                         addResult(attrib.value);
   -1  1164                     }
   -1  1165                     break;
   -1  1166             }
 1438  1167         }
 1439    -1         return result;
 1440  1168     }
 1441    -1     result.valid = false;
 1442    -1     result.reason = 'Not a valid ARIA property';
 1443  1169     return result;
 1444  1170 };
 1445  1171 
 1446  1172 /**
 1447    -1  * @param {string} propertyName The name of the property.
 1448    -1  * @param {string} value The value to check.
 1449    -1  * @return {!Object}
   -1  1173  * Gets elements that refer to this element in an attribute that takes an ID reference list or
   -1  1174  * single ID reference.
   -1  1175  * @param {Element} element a potential referent.
   -1  1176  * @return {Array<Element>} The elements that refer to this element.
 1450  1177  */
 1451    -1 axs.utils.isValidTokenValue = function(propertyName, value) {
 1452    -1     var propertyKey = propertyName.replace(/^aria-/, '');
 1453    -1     var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];
 1454    -1     var possibleValues = propertyDetails.valuesSet;
 1455    -1     return axs.utils.isPossibleValue(value, possibleValues, propertyName);
   -1  1178 axs.utils.getIdReferrers = function(element) {
   -1  1179     var result = [];
   -1  1180     var referrers = axs.utils.getHtmlIdReferrers(element);
   -1  1181     if (referrers) {
   -1  1182         result = result.concat(Array.prototype.slice.call(referrers));
   -1  1183     }
   -1  1184     referrers = axs.utils.getAriaIdReferrers(element);
   -1  1185     if (referrers) {
   -1  1186         result = result.concat(Array.prototype.slice.call(referrers));
   -1  1187     }
   -1  1188     return result;
 1456  1189 };
 1457  1190 
 1458  1191 /**
 1459    -1  * @param {string} value
 1460    -1  * @param {Object.<string, boolean>} possibleValues
 1461    -1  * @param {string} propertyName The name of the property.
 1462    -1  * @return {!Object}
   -1  1192  * Gets elements which this element refers to in the given attribute.
   -1  1193  * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'.
   -1  1194  * @param {Element} element The DOM element which has the ARIA attribute.
   -1  1195  * @return {!Array.<Element>} An array of elements that are referred to by this element.
   -1  1196  * @example
   -1  1197  *    var owner = document.body.appendChild(document.createElement("div"));
   -1  1198  *    var owned = document.body.appendChild(document.createElement("div"));
   -1  1199  *    owner.setAttribute("aria-owns", "kungfu");
   -1  1200  *    owned.setAttribute("id", "kungfu");
   -1  1201  *    console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned);  // This will log 'true'
 1463  1202  */
 1464    -1 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) {
 1465    -1     if (!possibleValues[value])
 1466    -1         return { 'valid': false,
 1467    -1                  'value': value,
 1468    -1                  'reason': '"' + value + '" is not a valid value for ' + propertyName,
 1469    -1                  'possibleValues': Object.keys(possibleValues) };
 1470    -1     return { 'valid': true, 'value': value };
   -1  1203 axs.utils.getIdReferents = function(attributeName, element) {
   -1  1204     var result = [];
   -1  1205     var propertyKey = attributeName.replace(/^aria-/, '');
   -1  1206     var property = axs.constants.ARIA_PROPERTIES[propertyKey];
   -1  1207     if (!property || !element.hasAttribute(attributeName))
   -1  1208         return result;
   -1  1209     var propertyType = property.valueType;
   -1  1210     if (propertyType === 'idref_list' || propertyType === 'idref') {
   -1  1211         var ownerDocument = element.ownerDocument;
   -1  1212         var ids = element.getAttribute(attributeName);
   -1  1213         ids = ids.split(/\s+/);
   -1  1214         for (var i = 0, len = ids.length; i < len; i++) {
   -1  1215             var next = ownerDocument.getElementById(ids[i]);
   -1  1216             if (next) {
   -1  1217                 result[result.length] = next;
   -1  1218             }
   -1  1219         }
   -1  1220     }
   -1  1221     return result;
 1471  1222 };
 1472  1223 
 1473  1224 /**
 1474    -1  * @param {string} value
 1475    -1  * @return {!Object}
   -1  1225  * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'.
   -1  1226  * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list'].
   -1  1227  * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match.
 1476  1228  */
 1477    -1 axs.utils.isValidBoolean = function(value) {
 1478    -1     try {
 1479    -1         var parsedValue = JSON.parse(value);
 1480    -1     } catch (e) {
 1481    -1         parsedValue = '';
   -1  1229 axs.utils.getAriaPropertiesByValueType = function(valueTypes) {
   -1  1230     var result = {};
   -1  1231     for (var propertyName in axs.constants.ARIA_PROPERTIES) {
   -1  1232         var property = axs.constants.ARIA_PROPERTIES[propertyName];
   -1  1233         if (property && valueTypes.indexOf(property.valueType) >= 0) {
   -1  1234             result[propertyName] = property;
   -1  1235         }
 1482  1236     }
 1483    -1     if (typeof(parsedValue) != 'boolean')
 1484    -1         return { 'valid': false,
 1485    -1                  'value': value,
 1486    -1                  'reason': '"' + value + '" is not a true/false value' };
 1487    -1     return { 'valid': true, 'value': parsedValue };
   -1  1237     return result;
 1488  1238 };
 1489  1239 
 1490  1240 /**
 1491    -1  * @param {string} value
 1492    -1  * @param {!Element} element
 1493    -1  * @return {!Object}
   -1  1241  * Builds a selector that matches an element with any of these ARIA properties.
   -1  1242  * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES
   -1  1243  * @return {!string} The selector.
 1494  1244  */
 1495    -1 axs.utils.isValidIDRefValue = function(value, element) {
 1496    -1     if (value.length == 0)
 1497    -1         return { 'valid': true, 'idref': value };
 1498    -1     if (!element.ownerDocument.getElementById(value))
 1499    -1         return { 'valid': false,
 1500    -1                  'idref': value,
 1501    -1                  'reason': 'No element with ID "' + value + '"' };
 1502    -1     return { 'valid': true, 'idref': value };
   -1  1245 axs.utils.getSelectorForAriaProperties = function(ariaProperties) {
   -1  1246     var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties));
   -1  1247     var result = propertyNames.map(function(propertyName) {
   -1  1248         return '[aria-' + propertyName + ']';
   -1  1249     });
   -1  1250     result.sort();  // facilitates reading long selectors and unit testing
   -1  1251     return result.join(',');
 1503  1252 };
 1504  1253 
 1505  1254 /**
 1506    -1  * Tests if a number is real number for a11y purposes.
 1507    -1  * Must be a real, numerical, decimal value; heavily inspired by
 1508    -1  *    http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number
 1509    -1  * @param {string} value
 1510    -1  * @return {!Object}
   -1  1255  * Finds descendants of this element which implement the given ARIA role.
   -1  1256  * Will look for descendants with implicit or explicit role.
   -1  1257  * @param {Element} element an HTML DOM element.
   -1  1258  * @param {string} role The role you seek.
   -1  1259  * @return {!Array.<Element>} An array of matching elements.
   -1  1260  * @example
   -1  1261  *    var container = document.createElement("div");
   -1  1262  *    var button = document.createElement("button");
   -1  1263  *    var span = document.createElement("span");
   -1  1264  *    span.setAttribute("role", "button");
   -1  1265  *    container.appendChild(button);
   -1  1266  *    container.appendChild(span);
   -1  1267  *    var result = axs.utils.findDescendantsWithRole(container, "button");  // result is an array containing both 'button' and 'span'
 1511  1268  */
 1512    -1 axs.utils.isValidNumber = function(value) {
 1513    -1     var failResult = {
 1514    -1         'valid': false,
 1515    -1         'value': value,
 1516    -1         'reason': '"' + value + '" is not a number'
 1517    -1     };
 1518    -1     if (!value) {
 1519    -1         return failResult;
 1520    -1     }
 1521    -1     if (/^0x/i.test(value)) {
 1522    -1         failResult.reason = '"' + value + '" is not a decimal number';  // hex is not accepted
 1523    -1         return failResult;
 1524    -1     }
 1525    -1     var parsedValue = value * 1;
 1526    -1     if (!isFinite(parsedValue)) {
 1527    -1         return failResult;
   -1  1269 axs.utils.findDescendantsWithRole = function(element, role) {
   -1  1270     if (!(element && role))
   -1  1271         return [];
   -1  1272     var selector = axs.properties.getSelectorForRole(role);
   -1  1273     if (!selector)
   -1  1274         return [];
   -1  1275     var result = element.querySelectorAll(selector);
   -1  1276     if (result) {  // Convert NodeList to Array; methinks 80/20 that's what callers want.
   -1  1277         result = Array.prototype.map.call(result, function(item) { return item; });
   -1  1278     } else {
   -1  1279         return [];
 1528  1280     }
 1529    -1     return { 'valid': true, 'value': parsedValue };
   -1  1281     return result;
 1530  1282 };
 1531  1283 
   -1  1284 },{}],2:[function(require,module,exports){
   -1  1285 // Copyright 2013 Google Inc.
   -1  1286 //
   -1  1287 // Licensed under the Apache License, Version 2.0 (the "License");
   -1  1288 // you may not use this file except in compliance with the License.
   -1  1289 // You may obtain a copy of the License at
   -1  1290 //
   -1  1291 //      http://www.apache.org/licenses/LICENSE-2.0
   -1  1292 //
   -1  1293 // Unless required by applicable law or agreed to in writing, software
   -1  1294 // distributed under the License is distributed on an "AS IS" BASIS,
   -1  1295 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   -1  1296 // See the License for the specific language governing permissions and
   -1  1297 // limitations under the License.
   -1  1298 
   -1  1299 goog.provide('axs.browserUtils');
   -1  1300 
 1532  1301 /**
   -1  1302  * Use Webkit matcher when matches() is not supported.
   -1  1303  * Use Firefox matcher when Webkit is not supported.
   -1  1304  * Use IE matcher when neither webkit nor Firefox supported.
 1533  1305  * @param {Element} element
 1534    -1  * @return {boolean}
   -1  1306  * @param {string} selector
   -1  1307  * @return {boolean} true if the element matches the selector
 1535  1308  */
 1536    -1 axs.utils.isElementImplicitlyFocusable = function(element) {
 1537    -1     var defaultView = element.ownerDocument.defaultView;
 1538    -1 
 1539    -1     if (element instanceof defaultView.HTMLAnchorElement ||
 1540    -1         element instanceof defaultView.HTMLAreaElement)
 1541    -1         return element.hasAttribute('href');
 1542    -1     if (element instanceof defaultView.HTMLInputElement ||
 1543    -1         element instanceof defaultView.HTMLSelectElement ||
 1544    -1         element instanceof defaultView.HTMLTextAreaElement ||
 1545    -1         element instanceof defaultView.HTMLButtonElement ||
 1546    -1         element instanceof defaultView.HTMLIFrameElement)
 1547    -1         return !element.disabled;
   -1  1309 axs.browserUtils.matchSelector = function(element, selector) {
   -1  1310     if (element.matches)
   -1  1311         return element.matches(selector);
   -1  1312     if (element.webkitMatchesSelector)
   -1  1313         return element.webkitMatchesSelector(selector);
   -1  1314     if (element.mozMatchesSelector)
   -1  1315         return element.mozMatchesSelector(selector);
   -1  1316     if (element.msMatchesSelector)
   -1  1317         return element.msMatchesSelector(selector);
 1548  1318     return false;
 1549  1319 };
 1550  1320 
 1551    -1 /**
 1552    -1  * Returns an array containing the values of the given JSON-compatible object.
 1553    -1  * (Simply ignores any function values.)
 1554    -1  * @param {Object} obj
 1555    -1  * @return {Array}
 1556    -1  */
 1557    -1 axs.utils.values = function(obj) {
 1558    -1     var values = [];
 1559    -1     for (var key in obj) {
 1560    -1         if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')
 1561    -1             values.push(obj[key]);
 1562    -1     }
 1563    -1     return values;
 1564    -1 };
   -1  1321 },{}],3:[function(require,module,exports){
   -1  1322 // Copyright 2015 Google Inc.
   -1  1323 //
   -1  1324 // Licensed under the Apache License, Version 2.0 (the "License");
   -1  1325 // you may not use this file except in compliance with the License.
   -1  1326 // You may obtain a copy of the License at
   -1  1327 //
   -1  1328 //      http://www.apache.org/licenses/LICENSE-2.0
   -1  1329 //
   -1  1330 // Unless required by applicable law or agreed to in writing, software
   -1  1331 // distributed under the License is distributed on an "AS IS" BASIS,
   -1  1332 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   -1  1333 // See the License for the specific language governing permissions and
   -1  1334 // limitations under the License.
   -1  1335 
   -1  1336 goog.provide('axs.color');
   -1  1337 goog.provide('axs.color.Color');
 1565  1338 
 1566  1339 /**
 1567    -1  * Returns an object containing the same keys and values as the given
 1568    -1  * JSON-compatible object. (Simply ignores any function values.)
 1569    -1  * @param {Object} obj
 1570    -1  * @return {Object}
   -1  1340  * @constructor
   -1  1341  * @param {number} red
   -1  1342  * @param {number} green
   -1  1343  * @param {number} blue
   -1  1344  * @param {number} alpha
 1571  1345  */
 1572    -1 axs.utils.namedValues = function(obj) {
 1573    -1     var values = {};
 1574    -1     for (var key in obj) {
 1575    -1         if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')
 1576    -1             values[key] = obj[key];
 1577    -1     }
 1578    -1     return values;
   -1  1346 axs.color.Color = function(red, green, blue, alpha) {
   -1  1347     /** @type {number} */
   -1  1348     this.red = red;
   -1  1349 
   -1  1350     /** @type {number} */
   -1  1351     this.green = green;
   -1  1352 
   -1  1353     /** @type {number} */
   -1  1354     this.blue = blue;
   -1  1355 
   -1  1356     /** @type {number} */
   -1  1357     this.alpha = alpha;
 1579  1358 };
 1580  1359 
 1581  1360 /**
 1582    -1 * Escapes a given ID to be used in a CSS selector
 1583    -1 *
 1584    -1 * @private
 1585    -1 * @param {!string} id The ID to be escaped
 1586    -1 * @return {string} The escaped ID
 1587    -1 */
 1588    -1 function escapeId(id) {
 1589    -1     return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; });
 1590    -1 }
 1591    -1 
 1592    -1 /** Gets a CSS selector text for a DOM object.
 1593    -1  * @param {Node} obj The DOM object.
 1594    -1  * @return {string} CSS selector text for the DOM object.
   -1  1361  * @constructor
   -1  1362  * See https://en.wikipedia.org/wiki/YCbCr for more information.
   -1  1363  * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr].
   -1  1364  *     All numbers are in the range [0, 1].
 1595  1365  */
 1596    -1 axs.utils.getQuerySelectorText = function(obj) {
 1597    -1   if (obj == null || obj.tagName == 'HTML') {
 1598    -1     return 'html';
 1599    -1   } else if (obj.tagName == 'BODY') {
 1600    -1     return 'body';
 1601    -1   }
   -1  1366 axs.color.YCbCr = function(coords) {
   -1  1367     /** @type {number} */
   -1  1368     this.luma = this.z = coords[0];
 1602  1369 
 1603    -1   if (obj.hasAttribute) {
 1604    -1     if (obj.id) {
 1605    -1       return '#' + escapeId(obj.id);
 1606    -1     }
   -1  1370     /** @type {number} */
   -1  1371     this.Cb = this.x = coords[1];
 1607  1372 
 1608    -1     if (obj.className) {
 1609    -1       var selector = '';
 1610    -1       for (var i = 0; i < obj.classList.length; i++)
 1611    -1         selector += '.' + obj.classList[i];
   -1  1373     /** @type {number} */
   -1  1374     this.Cr = this.y = coords[2];
   -1  1375 };
 1612  1376 
 1613    -1       var total = 0;
 1614    -1       if (obj.parentNode) {
 1615    -1         for (i = 0; i < obj.parentNode.children.length; i++) {
 1616    -1           var similar = obj.parentNode.children[i];
 1617    -1           if (axs.browserUtils.matchSelector(similar, selector))
 1618    -1             total++;
 1619    -1           if (similar === obj)
 1620    -1             break;
 1621    -1         }
 1622    -1       } else {
 1623    -1         total = 1;
 1624    -1       }
   -1  1377 axs.color.YCbCr.prototype = {
   -1  1378     /**
   -1  1379      * @param {number} scalar
   -1  1380      * @return {axs.color.YCbCr} This color multiplied by the given scalar
   -1  1381      */
   -1  1382     multiply: function(scalar) {
   -1  1383         var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ];
   -1  1384         return new axs.color.YCbCr(result);
   -1  1385     },
 1625  1386 
 1626    -1       if (total == 1) {
 1627    -1         return axs.utils.getQuerySelectorText(obj.parentNode) +
 1628    -1                ' > ' + selector;
 1629    -1       }
 1630    -1     }
   -1  1387     /**
   -1  1388      * @param {axs.color.YCbCr} other
   -1  1389      * @return {axs.color.YCbCr} This plus other
   -1  1390      */
   -1  1391     add: function(other) {
   -1  1392         var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ];
   -1  1393         return new axs.color.YCbCr(result);
   -1  1394     },
 1631  1395 
 1632    -1     if (obj.parentNode) {
 1633    -1       var similarTags = obj.parentNode.children;
 1634    -1       var total = 1;
 1635    -1       var i = 0;
 1636    -1       while (similarTags[i] !== obj) {
 1637    -1         if (similarTags[i].tagName == obj.tagName) {
 1638    -1           total++;
 1639    -1         }
 1640    -1         i++;
 1641    -1       }
   -1  1396     /**
   -1  1397      * @param {axs.color.YCbCr} other
   -1  1398      * @return {axs.color.YCbCr} This minus other
   -1  1399      */
   -1  1400     subtract: function(other) {
   -1  1401         var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ];
   -1  1402         return new axs.color.YCbCr(result);
   -1  1403     }
 1642  1404 
 1643    -1       var next = '';
 1644    -1       if (obj.parentNode.tagName != 'BODY') {
 1645    -1         next = axs.utils.getQuerySelectorText(obj.parentNode) +
 1646    -1                ' > ';
 1647    -1       }
   -1  1405 };
 1648  1406 
 1649    -1       if (total == 1) {
 1650    -1         return next +
 1651    -1                obj.tagName;
 1652    -1       } else {
 1653    -1         return next +
 1654    -1                obj.tagName +
 1655    -1                ':nth-of-type(' + total + ')';
 1656    -1       }
 1657    -1     }
 1658  1407 
 1659    -1   } else if (obj.selectorText) {
 1660    -1     return obj.selectorText;
 1661    -1   }
   -1  1408 /**
   -1  1409  * Calculate the contrast ratio between the two given colors. Returns the ratio
   -1  1410  * to 1, for example for two two colors with a contrast ratio of 21:1, this
   -1  1411  * function will return 21.
   -1  1412  * @param {axs.color.Color} fgColor
   -1  1413  * @param {axs.color.Color} bgColor
   -1  1414  * @return {!number}
   -1  1415  */
   -1  1416 axs.color.calculateContrastRatio = function(fgColor, bgColor) {
   -1  1417     if (fgColor.alpha < 1)
   -1  1418         fgColor = axs.color.flattenColors(fgColor, bgColor);
 1662  1419 
 1663    -1   return '';
   -1  1420     var fgLuminance = axs.color.calculateLuminance(fgColor);
   -1  1421     var bgLuminance = axs.color.calculateLuminance(bgColor);
   -1  1422     var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) /
   -1  1423         (Math.min(fgLuminance, bgLuminance) + 0.05);
   -1  1424     return contrastRatio;
 1664  1425 };
 1665  1426 
 1666  1427 /**
 1667    -1  * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or
 1668    -1  * single ID reference.
 1669    -1  * @param {Element} element a potential referent.
 1670    -1  * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'.
 1671    -1  * @return {NodeList} The elements that refer to this element or null.
   -1  1428  * Calculate the luminance of the given color using the WCAG algorithm.
   -1  1429  * @param {axs.color.Color} color
   -1  1430  * @return {number}
 1672  1431  */
 1673    -1 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) {
 1674    -1     var propertyToSelector = function(propertyKey) {
 1675    -1         var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];
 1676    -1         if (propertyDetails) {
 1677    -1             if (propertyDetails.valueType === ('idref')) {
 1678    -1                 return '[aria-' + propertyKey + '=\'' + id + '\']';
 1679    -1             } else if (propertyDetails.valueType === ('idref_list')) {
 1680    -1                 return '[aria-' + propertyKey + '~=\'' + id + '\']';
 1681    -1             }
 1682    -1         }
 1683    -1         return '';
 1684    -1     };
 1685    -1     if (!element)
 1686    -1         return null;
 1687    -1     var id = element.id;
 1688    -1     if (!id)
 1689    -1         return null;
 1690    -1     id = id.replace(/'/g, "\\'");  // make it safe to use in a selector
   -1  1432 axs.color.calculateLuminance = function(color) {
   -1  1433 /*    var rSRGB = color.red / 255;
   -1  1434     var gSRGB = color.green / 255;
   -1  1435     var bSRGB = color.blue / 255;
 1691  1436 
 1692    -1     if (opt_attributeName) {
 1693    -1         var propertyKey = opt_attributeName.replace(/^aria-/, '');
 1694    -1         var referrerQuery = propertyToSelector(propertyKey);
 1695    -1         if (referrerQuery) {
 1696    -1             return element.ownerDocument.querySelectorAll(referrerQuery);
 1697    -1         }
 1698    -1     } else {
 1699    -1         var selectors = [];
 1700    -1         for (var propertyKey in axs.constants.ARIA_PROPERTIES) {
 1701    -1             var referrerQuery = propertyToSelector(propertyKey);
 1702    -1             if (referrerQuery) {
 1703    -1                 selectors.push(referrerQuery);
 1704    -1             }
 1705    -1         }
 1706    -1         return element.ownerDocument.querySelectorAll(selectors.join(','));
 1707    -1     }
 1708    -1     return null;
   -1  1437     var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);
   -1  1438     var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);
   -1  1439     var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);
   -1  1440 
   -1  1441     return 0.2126 * r + 0.7152 * g + 0.0722 * b; */
   -1  1442     var ycc = axs.color.toYCbCr(color);
   -1  1443     return ycc.luma;
 1709  1444 };
 1710  1445 
 1711  1446 /**
 1712    -1  * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or
 1713    -1  * single ID reference.
 1714    -1  * @param {Element} element a potential referent.
 1715    -1  * @return {NodeList} The elements that refer to this element.
   -1  1447  * Compute the luminance ratio between two luminance values.
   -1  1448  * @param {number} luminance1
   -1  1449  * @param {number} luminance2
 1716  1450  */
 1717    -1 axs.utils.getHtmlIdReferrers = function(element) {
 1718    -1     if (!element)
 1719    -1         return null;
 1720    -1     var id = element.id;
 1721    -1     if (!id)
 1722    -1         return null;
 1723    -1     id = id.replace(/'/g, "\\'");  // make it safe to use in a selector
 1724    -1     var selectorTemplates = [
 1725    -1         '[contextmenu=\'{id}\']',
 1726    -1         '[itemref~=\'{id}\']',
 1727    -1         'button[form=\'{id}\']',
 1728    -1         'button[menu=\'{id}\']',
 1729    -1         'fieldset[form=\'{id}\']',
 1730    -1         'input[form=\'{id}\']',
 1731    -1         'input[list=\'{id}\']',
 1732    -1         'keygen[form=\'{id}\']',
 1733    -1         'label[for=\'{id}\']',
 1734    -1         'label[form=\'{id}\']',
 1735    -1         'menuitem[command=\'{id}\']',
 1736    -1         'object[form=\'{id}\']',
 1737    -1         'output[for~=\'{id}\']',
 1738    -1         'output[form=\'{id}\']',
 1739    -1         'select[form=\'{id}\']',
 1740    -1         'td[headers~=\'{id}\']',
 1741    -1         'textarea[form=\'{id}\']',
 1742    -1         'tr[headers~=\'{id}\']'];
 1743    -1     var selectors = selectorTemplates.map(function(selector) {
 1744    -1         return selector.replace('\{id\}', id);
 1745    -1     });
 1746    -1     return element.ownerDocument.querySelectorAll(selectors.join(','));
   -1  1451 axs.color.luminanceRatio = function(luminance1, luminance2) {
   -1  1452     return (Math.max(luminance1, luminance2) + 0.05) /
   -1  1453         (Math.min(luminance1, luminance2) + 0.05);
 1747  1454 };
 1748  1455 
 1749  1456 /**
 1750    -1  * Gets a list of all IDs this element references in either ARIA or HTML attributes.
 1751    -1  *
 1752    -1  * @param {Element} element The element to check for idref attributes.
 1753    -1  * @returns {Array.<string>} Any IDs this element references.
   -1  1457  * @param {string} colorString The color string from CSS.
   -1  1458  * @return {?axs.color.Color}
 1754  1459  */
 1755    -1 axs.utils.getReferencedIds = function(element) {
 1756    -1     var result = [];
 1757    -1     var addResult = function(ids) {
 1758    -1             if (ids) {
 1759    -1                 if (ids.indexOf(' ') > 0) {
 1760    -1                     result = result.concat(attrib.value.split(' '));
 1761    -1                 } else {
 1762    -1                     result.push(ids);
 1763    -1                 }
 1764    -1             }
 1765    -1         };
 1766    -1     for (var i = 0; i < element.attributes.length; i++) {
 1767    -1         var tagName = element.tagName.toLowerCase();
 1768    -1         var attrib = element.attributes[i];
 1769    -1         if (attrib.specified) {
 1770    -1             var attribName = attrib.name;
 1771    -1             var ariaAttr = attribName.match(/aria-(.+)/);
 1772    -1             if (ariaAttr) {
 1773    -1                 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]];
 1774    -1                 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) {
 1775    -1                     addResult(attrib.value);
 1776    -1                 }
 1777    -1                 continue;
 1778    -1             }
 1779    -1             switch (attribName) {
 1780    -1                 case 'contextmenu':
 1781    -1                 case 'itemref':
 1782    -1                     addResult(attrib.value);
 1783    -1                     break;
 1784    -1                 case 'form':
 1785    -1                     if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' ||
 1786    -1                             tagName == 'keygen' || tagName == 'label' || tagName == 'object' ||
 1787    -1                             tagName == 'output' || tagName == 'select' || tagName == 'textarea') {
 1788    -1                         addResult(attrib.value);
 1789    -1                     }
 1790    -1                     break;
 1791    -1                 case 'for':
 1792    -1                     if (tagName == 'label' || tagName == 'output') {
 1793    -1                         addResult(attrib.value);
 1794    -1                     }
 1795    -1                     break;
 1796    -1                 case 'menu':
 1797    -1                     if (tagName == 'button') {
 1798    -1                         addResult(attrib.value);
 1799    -1                     }
 1800    -1                     break;
 1801    -1                 case 'list':
 1802    -1                     if (tagName == 'input') {
 1803    -1                         addResult(attrib.value);
 1804    -1                     }
 1805    -1                     break;
 1806    -1                 case 'command':
 1807    -1                     if (tagName == 'menuitem') {
 1808    -1                         addResult(attrib.value);
 1809    -1                     }
 1810    -1                     break;
 1811    -1                 case 'headers':
 1812    -1                     if (tagName == 'td' || tagName == 'tr') {
 1813    -1                         addResult(attrib.value);
 1814    -1                     }
 1815    -1                     break;
 1816    -1             }
 1817    -1         }
   -1  1460 axs.color.parseColor = function(colorString) {
   -1  1461     if (colorString === "transparent") {
   -1  1462         return new axs.color.Color(0, 0, 0, 0);
 1818  1463     }
 1819    -1     return result;
 1820    -1 };
   -1  1464     var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/;
   -1  1465     var match = colorString.match(rgbRegex);
 1821  1466 
 1822    -1 /**
 1823    -1  * Gets elements that refer to this element in an attribute that takes an ID reference list or
 1824    -1  * single ID reference.
 1825    -1  * @param {Element} element a potential referent.
 1826    -1  * @return {Array<Element>} The elements that refer to this element.
 1827    -1  */
 1828    -1 axs.utils.getIdReferrers = function(element) {
 1829    -1     var result = [];
 1830    -1     var referrers = axs.utils.getHtmlIdReferrers(element);
 1831    -1     if (referrers) {
 1832    -1         result = result.concat(Array.prototype.slice.call(referrers));
   -1  1467     if (match) {
   -1  1468         var r = parseInt(match[1], 10);
   -1  1469         var g = parseInt(match[2], 10);
   -1  1470         var b = parseInt(match[3], 10);
   -1  1471         var a = 1;
   -1  1472         return new axs.color.Color(r, g, b, a);
 1833  1473     }
 1834    -1     referrers = axs.utils.getAriaIdReferrers(element);
 1835    -1     if (referrers) {
 1836    -1         result = result.concat(Array.prototype.slice.call(referrers));
   -1  1474 
   -1  1475     var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;
   -1  1476     match = colorString.match(rgbaRegex);
   -1  1477     if (match) {
   -1  1478         var r = parseInt(match[1], 10);
   -1  1479         var g = parseInt(match[2], 10);
   -1  1480         var b = parseInt(match[3], 10);
   -1  1481         var a = parseFloat(match[4]);
   -1  1482         return new axs.color.Color(r, g, b, a);
 1837  1483     }
 1838    -1     return result;
   -1  1484 
   -1  1485     return null;
 1839  1486 };
 1840  1487 
 1841  1488 /**
 1842    -1  * Gets elements which this element refers to in the given attribute.
 1843    -1  * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'.
 1844    -1  * @param {Element} element The DOM element which has the ARIA attribute.
 1845    -1  * @return {!Array.<Element>} An array of elements that are referred to by this element.
 1846    -1  * @example
 1847    -1  *    var owner = document.body.appendChild(document.createElement("div"));
 1848    -1  *    var owned = document.body.appendChild(document.createElement("div"));
 1849    -1  *    owner.setAttribute("aria-owns", "kungfu");
 1850    -1  *    owned.setAttribute("id", "kungfu");
 1851    -1  *    console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned);  // This will log 'true'
   -1  1489  * @param {number} value The value of a color channel, 0 <= value <= 0xFF
   -1  1490  * @return {!string}
 1852  1491  */
 1853    -1 axs.utils.getIdReferents = function(attributeName, element) {
 1854    -1     var result = [];
 1855    -1     var propertyKey = attributeName.replace(/^aria-/, '');
 1856    -1     var property = axs.constants.ARIA_PROPERTIES[propertyKey];
 1857    -1     if (!property || !element.hasAttribute(attributeName))
 1858    -1         return result;
 1859    -1     var propertyType = property.valueType;
 1860    -1     if (propertyType === 'idref_list' || propertyType === 'idref') {
 1861    -1         var ownerDocument = element.ownerDocument;
 1862    -1         var ids = element.getAttribute(attributeName);
 1863    -1         ids = ids.split(/\s+/);
 1864    -1         for (var i = 0, len = ids.length; i < len; i++) {
 1865    -1             var next = ownerDocument.getElementById(ids[i]);
 1866    -1             if (next) {
 1867    -1                 result[result.length] = next;
 1868    -1             }
 1869    -1         }
 1870    -1     }
 1871    -1     return result;
   -1  1492 axs.color.colorChannelToString = function(value) {
   -1  1493     value = Math.round(value);
   -1  1494     if (value <= 0xF)
   -1  1495         return '0' + value.toString(16);
   -1  1496     return value.toString(16);
 1872  1497 };
 1873  1498 
 1874  1499 /**
 1875    -1  * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'.
 1876    -1  * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list'].
 1877    -1  * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match.
   -1  1500  * @param {axs.color.Color} color
   -1  1501  * @return {!string}
 1878  1502  */
 1879    -1 axs.utils.getAriaPropertiesByValueType = function(valueTypes) {
 1880    -1     var result = {};
 1881    -1     for (var propertyName in axs.constants.ARIA_PROPERTIES) {
 1882    -1         var property = axs.constants.ARIA_PROPERTIES[propertyName];
 1883    -1         if (property && valueTypes.indexOf(property.valueType) >= 0) {
 1884    -1             result[propertyName] = property;
 1885    -1         }
   -1  1503 axs.color.colorToString = function(color) {
   -1  1504     if (color.alpha == 1) {
   -1  1505          return '#' + axs.color.colorChannelToString(color.red) +
   -1  1506          axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue);
 1886  1507     }
 1887    -1     return result;
   -1  1508     else
   -1  1509         return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')';
 1888  1510 };
 1889  1511 
 1890  1512 /**
 1891    -1  * Builds a selector that matches an element with any of these ARIA properties.
 1892    -1  * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES
 1893    -1  * @return {!string} The selector.
 1894    -1  */
 1895    -1 axs.utils.getSelectorForAriaProperties = function(ariaProperties) {
 1896    -1     var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties));
 1897    -1     var result = propertyNames.map(function(propertyName) {
 1898    -1         return '[aria-' + propertyName + ']';
 1899    -1     });
 1900    -1     result.sort();  // facilitates reading long selectors and unit testing
 1901    -1     return result.join(',');
 1902    -1 };
 1903    -1 
 1904    -1 /**
 1905    -1  * Finds descendants of this element which implement the given ARIA role.
 1906    -1  * Will look for descendants with implicit or explicit role.
 1907    -1  * @param {Element} element an HTML DOM element.
 1908    -1  * @param {string} role The role you seek.
 1909    -1  * @return {!Array.<Element>} An array of matching elements.
 1910    -1  * @example
 1911    -1  *    var container = document.createElement("div");
 1912    -1  *    var button = document.createElement("button");
 1913    -1  *    var span = document.createElement("span");
 1914    -1  *    span.setAttribute("role", "button");
 1915    -1  *    container.appendChild(button);
 1916    -1  *    container.appendChild(span);
 1917    -1  *    var result = axs.utils.findDescendantsWithRole(container, "button");  // result is an array containing both 'button' and 'span'
   -1  1513  * Compute a desired luminance given a given luminance and a desired contrast ratio.
   -1  1514  * @param {number} luminance The given luminance.
   -1  1515  * @param {number} contrast The desired contrast ratio.
   -1  1516  * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance.
   -1  1517  * @return {number} The desired luminance.
 1918  1518  */
 1919    -1 axs.utils.findDescendantsWithRole = function(element, role) {
 1920    -1     if (!(element && role))
 1921    -1         return [];
 1922    -1     var selector = axs.properties.getSelectorForRole(role);
 1923    -1     if (!selector)
 1924    -1         return [];
 1925    -1     var result = element.querySelectorAll(selector);
 1926    -1     if (result) {  // Convert NodeList to Array; methinks 80/20 that's what callers want.
 1927    -1         result = Array.prototype.map.call(result, function(item) { return item; });
   -1  1519 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) {
   -1  1520     if (higher) {
   -1  1521         var newLuminance = (luminance + 0.05) * contrast - 0.05;
   -1  1522         return newLuminance;
 1928  1523     } else {
 1929    -1         return [];
   -1  1524         var newLuminance = (luminance + 0.05) / contrast - 0.05;
   -1  1525         return newLuminance;
 1930  1526     }
 1931    -1     return result;
 1932  1527 };
 1933  1528 
 1934    -1 },{}],7:[function(require,module,exports){
 1935    -1 // Copyright 2013 Google Inc.
 1936    -1 //
 1937    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 1938    -1 // you may not use this file except in compliance with the License.
 1939    -1 // You may obtain a copy of the License at
 1940    -1 //
 1941    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1942    -1 //
 1943    -1 // Unless required by applicable law or agreed to in writing, software
 1944    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 1945    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 1946    -1 // See the License for the specific language governing permissions and
 1947    -1 // limitations under the License.
 1948    -1 
 1949    -1 goog.provide('axs.browserUtils');
 1950    -1 
 1951  1529 /**
 1952    -1  * Use Webkit matcher when matches() is not supported.
 1953    -1  * Use Firefox matcher when Webkit is not supported.
 1954    -1  * Use IE matcher when neither webkit nor Firefox supported.
 1955    -1  * @param {Element} element
 1956    -1  * @param {string} selector
 1957    -1  * @return {boolean} true if the element matches the selector
   -1  1530  * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is
   -1  1531  * as close as possible to the original color.
   -1  1532  * @param {axs.color.YCbCr} ycc The original color in YCbCr form.
   -1  1533  * @param {number} luma The desired luminance
   -1  1534  * @return {!axs.color.Color} A new color in RGB.
 1958  1535  */
 1959    -1 axs.browserUtils.matchSelector = function(element, selector) {
 1960    -1     if (element.matches)
 1961    -1         return element.matches(selector);
 1962    -1     if (element.webkitMatchesSelector)
 1963    -1         return element.webkitMatchesSelector(selector);
 1964    -1     if (element.mozMatchesSelector)
 1965    -1         return element.mozMatchesSelector(selector);
 1966    -1     if (element.msMatchesSelector)
 1967    -1         return element.msMatchesSelector(selector);
 1968    -1     return false;
 1969    -1 };
 1970    -1 
 1971    -1 },{}],8:[function(require,module,exports){
 1972    -1 // Copyright 2015 Google Inc.
 1973    -1 //
 1974    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 1975    -1 // you may not use this file except in compliance with the License.
 1976    -1 // You may obtain a copy of the License at
 1977    -1 //
 1978    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1979    -1 //
 1980    -1 // Unless required by applicable law or agreed to in writing, software
 1981    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 1982    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 1983    -1 // See the License for the specific language governing permissions and
 1984    -1 // limitations under the License.
   -1  1536 axs.color.translateColor = function(ycc, luma) {
   -1  1537     var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC;
   -1  1538     var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE
   -1  1539                                                       : axs.color.YCC_CUBE_FACES_BLACK;
 1985  1540 
 1986    -1 goog.provide('axs.color');
 1987    -1 goog.provide('axs.color.Color');
   -1  1541     var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]);
   -1  1542     var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]);
   -1  1543     var line = { a: a, b: b };
 1988  1544 
 1989    -1 /**
 1990    -1  * @constructor
 1991    -1  * @param {number} red
 1992    -1  * @param {number} green
 1993    -1  * @param {number} blue
 1994    -1  * @param {number} alpha
 1995    -1  */
 1996    -1 axs.color.Color = function(red, green, blue, alpha) {
 1997    -1     /** @type {number} */
 1998    -1     this.red = red;
   -1  1545     var intersection = null;
   -1  1546     for (var i = 0; i < cubeFaces.length; i++) {
   -1  1547         var cubeFace = cubeFaces[i];
   -1  1548         intersection = axs.color.findIntersection(line, cubeFace);
   -1  1549         // If intersection within [0, 1] in Z axis, it is within the cube.
   -1  1550         if (intersection.z >= 0 && intersection.z <= 1)
   -1  1551             break;
   -1  1552     }
   -1  1553     if (!intersection) {
   -1  1554         // Should never happen
   -1  1555         throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + ".";
   -1  1556     }
   -1  1557     if (intersection.x != ycc.x || intersection.y != ycc.y) {
   -1  1558         // Should never happen
   -1  1559         throw "Intersection has wrong Cb/Cr values.";
   -1  1560     }
 1999  1561 
 2000    -1     /** @type {number} */
 2001    -1     this.green = green;
   -1  1562     // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube
   -1  1563     // and we can immediately return new value.
   -1  1564     if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) {
   -1  1565         var translatedColor = [luma, ycc.Cb, ycc.Cr];
   -1  1566         return axs.color.fromYCbCrArray(translatedColor);
   -1  1567     }
 2002  1568 
 2003    -1     /** @type {number} */
 2004    -1     this.blue = blue;
   -1  1569     // Otherwise, translate from intersection towards white/black such that luma is correct.
   -1  1570     var dLuma = luma - intersection.luma;
   -1  1571     var scale = dLuma / (endpoint.luma - intersection.luma);
   -1  1572     var translatedColor = [ luma,
   -1  1573                             intersection.Cb - (intersection.Cb * scale),
   -1  1574                             intersection.Cr - (intersection.Cr * scale) ];
 2005  1575 
 2006    -1     /** @type {number} */
 2007    -1     this.alpha = alpha;
   -1  1576     return axs.color.fromYCbCrArray(translatedColor);
 2008  1577 };
 2009  1578 
   -1  1579 /** @typedef {{fg: string, bg: string, contrast: string}} */
   -1  1580 axs.color.SuggestedColors;
   -1  1581 
 2010  1582 /**
 2011    -1  * @constructor
 2012    -1  * See https://en.wikipedia.org/wiki/YCbCr for more information.
 2013    -1  * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr].
 2014    -1  *     All numbers are in the range [0, 1].
   -1  1583  * @param {axs.color.Color} bgColor
   -1  1584  * @param {axs.color.Color} fgColor
   -1  1585  * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio.
   -1  1586  * @return {Object.<string, axs.color.SuggestedColors>}
 2015  1587  */
 2016    -1 axs.color.YCbCr = function(coords) {
 2017    -1     /** @type {number} */
 2018    -1     this.luma = this.z = coords[0];
 2019    -1 
 2020    -1     /** @type {number} */
 2021    -1     this.Cb = this.x = coords[1];
 2022    -1 
 2023    -1     /** @type {number} */
 2024    -1     this.Cr = this.y = coords[2];
 2025    -1 };
   -1  1588 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) {
   -1  1589     var colors = {};
   -1  1590     var bgLuminance = axs.color.calculateLuminance(bgColor);
   -1  1591     var fgLuminance = axs.color.calculateLuminance(fgColor);
 2026  1592 
 2027    -1 axs.color.YCbCr.prototype = {
 2028    -1     /**
 2029    -1      * @param {number} scalar
 2030    -1      * @return {axs.color.YCbCr} This color multiplied by the given scalar
 2031    -1      */
 2032    -1     multiply: function(scalar) {
 2033    -1         var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ];
 2034    -1         return new axs.color.YCbCr(result);
 2035    -1     },
   -1  1593     var fgLuminanceIsHigher = fgLuminance > bgLuminance;
   -1  1594     var fgYCbCr = axs.color.toYCbCr(fgColor);
   -1  1595     var bgYCbCr = axs.color.toYCbCr(bgColor);
   -1  1596     for (var desiredLabel in desiredContrastRatios) {
   -1  1597         var desiredContrast = desiredContrastRatios[desiredLabel];
 2036  1598 
 2037    -1     /**
 2038    -1      * @param {axs.color.YCbCr} other
 2039    -1      * @return {axs.color.YCbCr} This plus other
 2040    -1      */
 2041    -1     add: function(other) {
 2042    -1         var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ];
 2043    -1         return new axs.color.YCbCr(result);
 2044    -1     },
   -1  1599         var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher);
   -1  1600         if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) {
   -1  1601             var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance);
   -1  1602             var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor);
   -1  1603             var suggestedColors = {};
   -1  1604             suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor));
   -1  1605             suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor));
   -1  1606             suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));
   -1  1607             colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);
   -1  1608             continue;
   -1  1609         }
 2045  1610 
 2046    -1     /**
 2047    -1      * @param {axs.color.YCbCr} other
 2048    -1      * @return {axs.color.YCbCr} This minus other
 2049    -1      */
 2050    -1     subtract: function(other) {
 2051    -1         var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ];
 2052    -1         return new axs.color.YCbCr(result);
   -1  1611         var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher);
   -1  1612         if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) {
   -1  1613             var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance);
   -1  1614             var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor);
   -1  1615             var suggestedColors = {};
   -1  1616             suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor));
   -1  1617             suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor));
   -1  1618             suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));
   -1  1619             colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);
   -1  1620         }
 2053  1621     }
 2054    -1 
   -1  1622     return colors;
 2055  1623 };
 2056  1624 
 2057    -1 
 2058  1625 /**
 2059    -1  * Calculate the contrast ratio between the two given colors. Returns the ratio
 2060    -1  * to 1, for example for two two colors with a contrast ratio of 21:1, this
 2061    -1  * function will return 21.
   -1  1626  * Combine the two given color according to alpha blending.
 2062  1627  * @param {axs.color.Color} fgColor
 2063  1628  * @param {axs.color.Color} bgColor
 2064    -1  * @return {!number}
   -1  1629  * @return {axs.color.Color}
 2065  1630  */
 2066    -1 axs.color.calculateContrastRatio = function(fgColor, bgColor) {
 2067    -1     if (fgColor.alpha < 1)
 2068    -1         fgColor = axs.color.flattenColors(fgColor, bgColor);
   -1  1631 axs.color.flattenColors = function(fgColor, bgColor) {
   -1  1632     var alpha = fgColor.alpha;
   -1  1633     var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red);
   -1  1634     var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green);
   -1  1635     var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue);
   -1  1636     var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha));
 2069  1637 
 2070    -1     var fgLuminance = axs.color.calculateLuminance(fgColor);
 2071    -1     var bgLuminance = axs.color.calculateLuminance(bgColor);
 2072    -1     var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) /
 2073    -1         (Math.min(fgLuminance, bgLuminance) + 0.05);
 2074    -1     return contrastRatio;
 2075    -1 };
 2076    -1 
 2077    -1 /**
 2078    -1  * Calculate the luminance of the given color using the WCAG algorithm.
 2079    -1  * @param {axs.color.Color} color
 2080    -1  * @return {number}
 2081    -1  */
 2082    -1 axs.color.calculateLuminance = function(color) {
 2083    -1 /*    var rSRGB = color.red / 255;
 2084    -1     var gSRGB = color.green / 255;
 2085    -1     var bSRGB = color.blue / 255;
 2086    -1 
 2087    -1     var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);
 2088    -1     var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);
 2089    -1     var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);
 2090    -1 
 2091    -1     return 0.2126 * r + 0.7152 * g + 0.0722 * b; */
 2092    -1     var ycc = axs.color.toYCbCr(color);
 2093    -1     return ycc.luma;
 2094    -1 };
 2095    -1 
 2096    -1 /**
 2097    -1  * Compute the luminance ratio between two luminance values.
 2098    -1  * @param {number} luminance1
 2099    -1  * @param {number} luminance2
 2100    -1  */
 2101    -1 axs.color.luminanceRatio = function(luminance1, luminance2) {
 2102    -1     return (Math.max(luminance1, luminance2) + 0.05) /
 2103    -1         (Math.min(luminance1, luminance2) + 0.05);
 2104    -1 };
 2105    -1 
 2106    -1 /**
 2107    -1  * @param {string} colorString The color string from CSS.
 2108    -1  * @return {?axs.color.Color}
 2109    -1  */
 2110    -1 axs.color.parseColor = function(colorString) {
 2111    -1     if (colorString === "transparent") {
 2112    -1         return new axs.color.Color(0, 0, 0, 0);
 2113    -1     }
 2114    -1     var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/;
 2115    -1     var match = colorString.match(rgbRegex);
 2116    -1 
 2117    -1     if (match) {
 2118    -1         var r = parseInt(match[1], 10);
 2119    -1         var g = parseInt(match[2], 10);
 2120    -1         var b = parseInt(match[3], 10);
 2121    -1         var a = 1;
 2122    -1         return new axs.color.Color(r, g, b, a);
 2123    -1     }
 2124    -1 
 2125    -1     var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;
 2126    -1     match = colorString.match(rgbaRegex);
 2127    -1     if (match) {
 2128    -1         var r = parseInt(match[1], 10);
 2129    -1         var g = parseInt(match[2], 10);
 2130    -1         var b = parseInt(match[3], 10);
 2131    -1         var a = parseFloat(match[4]);
 2132    -1         return new axs.color.Color(r, g, b, a);
 2133    -1     }
 2134    -1 
 2135    -1     return null;
 2136    -1 };
 2137    -1 
 2138    -1 /**
 2139    -1  * @param {number} value The value of a color channel, 0 <= value <= 0xFF
 2140    -1  * @return {!string}
 2141    -1  */
 2142    -1 axs.color.colorChannelToString = function(value) {
 2143    -1     value = Math.round(value);
 2144    -1     if (value <= 0xF)
 2145    -1         return '0' + value.toString(16);
 2146    -1     return value.toString(16);
 2147    -1 };
 2148    -1 
 2149    -1 /**
 2150    -1  * @param {axs.color.Color} color
 2151    -1  * @return {!string}
 2152    -1  */
 2153    -1 axs.color.colorToString = function(color) {
 2154    -1     if (color.alpha == 1) {
 2155    -1          return '#' + axs.color.colorChannelToString(color.red) +
 2156    -1          axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue);
 2157    -1     }
 2158    -1     else
 2159    -1         return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')';
 2160    -1 };
 2161    -1 
 2162    -1 /**
 2163    -1  * Compute a desired luminance given a given luminance and a desired contrast ratio.
 2164    -1  * @param {number} luminance The given luminance.
 2165    -1  * @param {number} contrast The desired contrast ratio.
 2166    -1  * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance.
 2167    -1  * @return {number} The desired luminance.
 2168    -1  */
 2169    -1 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) {
 2170    -1     if (higher) {
 2171    -1         var newLuminance = (luminance + 0.05) * contrast - 0.05;
 2172    -1         return newLuminance;
 2173    -1     } else {
 2174    -1         var newLuminance = (luminance + 0.05) / contrast - 0.05;
 2175    -1         return newLuminance;
 2176    -1     }
 2177    -1 };
 2178    -1 
 2179    -1 /**
 2180    -1  * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is
 2181    -1  * as close as possible to the original color.
 2182    -1  * @param {axs.color.YCbCr} ycc The original color in YCbCr form.
 2183    -1  * @param {number} luma The desired luminance
 2184    -1  * @return {!axs.color.Color} A new color in RGB.
 2185    -1  */
 2186    -1 axs.color.translateColor = function(ycc, luma) {
 2187    -1     var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC;
 2188    -1     var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE
 2189    -1                                                       : axs.color.YCC_CUBE_FACES_BLACK;
 2190    -1 
 2191    -1     var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]);
 2192    -1     var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]);
 2193    -1     var line = { a: a, b: b };
 2194    -1 
 2195    -1     var intersection = null;
 2196    -1     for (var i = 0; i < cubeFaces.length; i++) {
 2197    -1         var cubeFace = cubeFaces[i];
 2198    -1         intersection = axs.color.findIntersection(line, cubeFace);
 2199    -1         // If intersection within [0, 1] in Z axis, it is within the cube.
 2200    -1         if (intersection.z >= 0 && intersection.z <= 1)
 2201    -1             break;
 2202    -1     }
 2203    -1     if (!intersection) {
 2204    -1         // Should never happen
 2205    -1         throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + ".";
 2206    -1     }
 2207    -1     if (intersection.x != ycc.x || intersection.y != ycc.y) {
 2208    -1         // Should never happen
 2209    -1         throw "Intersection has wrong Cb/Cr values.";
 2210    -1     }
 2211    -1 
 2212    -1     // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube
 2213    -1     // and we can immediately return new value.
 2214    -1     if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) {
 2215    -1         var translatedColor = [luma, ycc.Cb, ycc.Cr];
 2216    -1         return axs.color.fromYCbCrArray(translatedColor);
 2217    -1     }
 2218    -1 
 2219    -1     // Otherwise, translate from intersection towards white/black such that luma is correct.
 2220    -1     var dLuma = luma - intersection.luma;
 2221    -1     var scale = dLuma / (endpoint.luma - intersection.luma);
 2222    -1     var translatedColor = [ luma,
 2223    -1                             intersection.Cb - (intersection.Cb * scale),
 2224    -1                             intersection.Cr - (intersection.Cr * scale) ];
 2225    -1 
 2226    -1     return axs.color.fromYCbCrArray(translatedColor);
 2227    -1 };
 2228    -1 
 2229    -1 /** @typedef {{fg: string, bg: string, contrast: string}} */
 2230    -1 axs.color.SuggestedColors;
 2231    -1 
 2232    -1 /**
 2233    -1  * @param {axs.color.Color} bgColor
 2234    -1  * @param {axs.color.Color} fgColor
 2235    -1  * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio.
 2236    -1  * @return {Object.<string, axs.color.SuggestedColors>}
 2237    -1  */
 2238    -1 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) {
 2239    -1     var colors = {};
 2240    -1     var bgLuminance = axs.color.calculateLuminance(bgColor);
 2241    -1     var fgLuminance = axs.color.calculateLuminance(fgColor);
 2242    -1 
 2243    -1     var fgLuminanceIsHigher = fgLuminance > bgLuminance;
 2244    -1     var fgYCbCr = axs.color.toYCbCr(fgColor);
 2245    -1     var bgYCbCr = axs.color.toYCbCr(bgColor);
 2246    -1     for (var desiredLabel in desiredContrastRatios) {
 2247    -1         var desiredContrast = desiredContrastRatios[desiredLabel];
 2248    -1 
 2249    -1         var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher);
 2250    -1         if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) {
 2251    -1             var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance);
 2252    -1             var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor);
 2253    -1             var suggestedColors = {};
 2254    -1             suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor));
 2255    -1             suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor));
 2256    -1             suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));
 2257    -1             colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);
 2258    -1             continue;
 2259    -1         }
 2260    -1 
 2261    -1         var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher);
 2262    -1         if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) {
 2263    -1             var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance);
 2264    -1             var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor);
 2265    -1             var suggestedColors = {};
 2266    -1             suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor));
 2267    -1             suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor));
 2268    -1             suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));
 2269    -1             colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);
 2270    -1         }
 2271    -1     }
 2272    -1     return colors;
 2273    -1 };
 2274    -1 
 2275    -1 /**
 2276    -1  * Combine the two given color according to alpha blending.
 2277    -1  * @param {axs.color.Color} fgColor
 2278    -1  * @param {axs.color.Color} bgColor
 2279    -1  * @return {axs.color.Color}
 2280    -1  */
 2281    -1 axs.color.flattenColors = function(fgColor, bgColor) {
 2282    -1     var alpha = fgColor.alpha;
 2283    -1     var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red);
 2284    -1     var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green);
 2285    -1     var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue);
 2286    -1     var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha));
 2287    -1 
 2288    -1     return new axs.color.Color(r, g, b, a);
   -1  1638     return new axs.color.Color(r, g, b, a);
 2289  1639 };
 2290  1640 
 2291  1641 /**
@@ -2510,7 +1860,7 @@ axs.color.YCC_CUBE_FACES_WHITE = [ { p0: axs.color.WHITE_YCC, p1: axs.color.CYAN
 2510  1860                                    { p0: axs.color.WHITE_YCC, p1: axs.color.MAGENTA_YCC, p2: axs.color.YELLOW_YCC },
 2511  1861                                    { p0: axs.color.WHITE_YCC, p1: axs.color.YELLOW_YCC, p2: axs.color.CYAN_YCC } ];
 2512  1862 
 2513    -1 },{}],9:[function(require,module,exports){
   -1  1863 },{}],4:[function(require,module,exports){
 2514  1864 // Copyright 2012 Google Inc.
 2515  1865 //
 2516  1866 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -4194,7 +3544,7 @@ axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO = {
 4194  3544     }]
 4195  3545 };
 4196  3546 
 4197    -1 },{}],10:[function(require,module,exports){
   -1  3547 },{}],5:[function(require,module,exports){
 4198  3548 // Copyright 2015 Google Inc.
 4199  3549 //
 4200  3550 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -4408,7 +3758,7 @@ axs.dom.composedTreeSearch = function(node, end, callbacks, parentFlags, opt_sha
 4408  3758     return found;
 4409  3759 };
 4410  3760 
 4411    -1 },{}],11:[function(require,module,exports){
   -1  3761 },{}],6:[function(require,module,exports){
 4412  3762 // Copyright 2012 Google Inc.
 4413  3763 //
 4414  3764 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -4813,529 +4163,1170 @@ axs.properties.findTextAlternatives = function(node, textAlternatives, opt_recur
 4813  4163         textAlternatives['content'] = textFromContentValue;
 4814  4164     }
 4815  4165 
 4816    -1     // 2D. The last resort is to use text from a tooltip attribute (such as the title attribute in
 4817    -1     // HTML). This is used only if nothing else, including subtree content, has provided results.
 4818    -1     if (element.hasAttribute('title')) {
 4819    -1         var titleValue = {};
 4820    -1         titleValue.type = 'string';
 4821    -1         titleValue.valid = true;
 4822    -1         titleValue.text = element.getAttribute('title');
 4823    -1         titleValue.lastWord = axs.properties.getLastWord(titleValue.lastWord);
 4824    -1         if (computedName)
 4825    -1             titleValue.unused = true;
 4826    -1         else
 4827    -1             computedName = titleValue.text;
 4828    -1         textAlternatives['title'] = titleValue;
 4829    -1     }
   -1  4166     // 2D. The last resort is to use text from a tooltip attribute (such as the title attribute in
   -1  4167     // HTML). This is used only if nothing else, including subtree content, has provided results.
   -1  4168     if (element.hasAttribute('title')) {
   -1  4169         var titleValue = {};
   -1  4170         titleValue.type = 'string';
   -1  4171         titleValue.valid = true;
   -1  4172         titleValue.text = element.getAttribute('title');
   -1  4173         titleValue.lastWord = axs.properties.getLastWord(titleValue.lastWord);
   -1  4174         if (computedName)
   -1  4175             titleValue.unused = true;
   -1  4176         else
   -1  4177             computedName = titleValue.text;
   -1  4178         textAlternatives['title'] = titleValue;
   -1  4179     }
   -1  4180 
   -1  4181     if (Object.keys(textAlternatives).length == 0 && computedName == null)
   -1  4182         return null;
   -1  4183 
   -1  4184     return computedName;
   -1  4185 };
   -1  4186 
   -1  4187 /**
   -1  4188  * @param {Element} element
   -1  4189  * @param {boolean=} opt_force Whether to return text alternatives for this
   -1  4190  *     element regardless of its hidden state.
   -1  4191  * @return {?string}
   -1  4192  */
   -1  4193 axs.properties.getTextFromDescendantContent = function(element, opt_force) {
   -1  4194     var children = element.childNodes;
   -1  4195     var childrenTextContent = [];
   -1  4196     for (var i = 0; i < children.length; i++) {
   -1  4197         var childTextContent = axs.properties.findTextAlternatives(children[i], {}, true, opt_force);
   -1  4198         if (childTextContent)
   -1  4199             childrenTextContent.push(childTextContent.trim());
   -1  4200     }
   -1  4201     if (childrenTextContent.length) {
   -1  4202         var result = '';
   -1  4203         // Empty children are allowed, but collapse all of them
   -1  4204         for (var i = 0; i < childrenTextContent.length; i++)
   -1  4205             result = [result, childrenTextContent[i]].join(' ').trim();
   -1  4206         return result;
   -1  4207     }
   -1  4208     return null;
   -1  4209 };
   -1  4210 
   -1  4211 /**
   -1  4212  * @param {Element} element
   -1  4213  * @param {Object} textAlternatives
   -1  4214  * @return {?string}
   -1  4215  */
   -1  4216 axs.properties.getTextFromAriaLabelledby = function(element, textAlternatives) {
   -1  4217     var computedName = null;
   -1  4218     if (!element.hasAttribute('aria-labelledby'))
   -1  4219         return computedName;
   -1  4220 
   -1  4221     var labelledbyAttr = element.getAttribute('aria-labelledby');
   -1  4222     var labelledbyIds = labelledbyAttr.split(/\s+/);
   -1  4223     var labelledbyValue = {};
   -1  4224     labelledbyValue.valid = true;
   -1  4225     var labelledbyText = [];
   -1  4226     var labelledbyValues = [];
   -1  4227     for (var i = 0; i < labelledbyIds.length; i++) {
   -1  4228         var labelledby = {};
   -1  4229         labelledby.type = 'element';
   -1  4230         var labelledbyId = labelledbyIds[i];
   -1  4231         labelledby.value = labelledbyId;
   -1  4232         var labelledbyElement = document.getElementById(labelledbyId);
   -1  4233         if (!labelledbyElement) {
   -1  4234             labelledby.valid = false;
   -1  4235             labelledbyValue.valid = false;
   -1  4236             labelledby.errorMessage = { 'messageKey': 'noElementWithId', 'args': [labelledbyId] };
   -1  4237         } else {
   -1  4238             labelledby.valid = true;
   -1  4239             labelledby.text = axs.properties.findTextAlternatives(labelledbyElement, {}, true, true);
   -1  4240             labelledby.lastWord = axs.properties.getLastWord(labelledby.text);
   -1  4241             labelledbyText.push(labelledby.text);
   -1  4242             labelledby.element = labelledbyElement;
   -1  4243         }
   -1  4244         labelledbyValues.push(labelledby);
   -1  4245     }
   -1  4246     if (labelledbyValues.length > 0) {
   -1  4247         labelledbyValues[labelledbyValues.length - 1].last = true;
   -1  4248         labelledbyValue.values = labelledbyValues;
   -1  4249         labelledbyValue.text = labelledbyText.join(' ');
   -1  4250         labelledbyValue.lastWord = axs.properties.getLastWord(labelledbyValue.text);
   -1  4251         computedName = labelledbyValue.text;
   -1  4252         textAlternatives['ariaLabelledby'] = labelledbyValue;
   -1  4253     }
   -1  4254 
   -1  4255     return computedName;
   -1  4256 };
   -1  4257 
   -1  4258 
   -1  4259 /**
   -1  4260  * Determine the text description/label for an element.
   -1  4261  * For example will attempt to find the alt text for an image or label text for a form control.
   -1  4262  * @param {!Element} element
   -1  4263  * @param {!Object} textAlternatives An object that will be updated with information.
   -1  4264  * @param {?string} existingComputedname
   -1  4265  * @param {boolean} recursive Whether this method is being called recursively as described in
   -1  4266  *     http://www.w3.org/TR/wai-aria/roles#textalternativecomputation section 2A.
   -1  4267  * @return {Object}
   -1  4268  */
   -1  4269 axs.properties.getTextFromHostLanguageAttributes = function(element,
   -1  4270                                                             textAlternatives,
   -1  4271                                                             existingComputedname,
   -1  4272                                                             recursive) {
   -1  4273     var computedName = existingComputedname;
   -1  4274     if (axs.browserUtils.matchSelector(element, 'img') && element.hasAttribute('alt')) {
   -1  4275         var altValue = {};
   -1  4276         altValue.type = 'string';
   -1  4277         altValue.valid = true;
   -1  4278         altValue.text = element.getAttribute('alt');
   -1  4279         if (computedName)
   -1  4280             altValue.unused = true;
   -1  4281         else
   -1  4282             computedName = altValue.text;
   -1  4283         textAlternatives['alt'] = altValue;
   -1  4284     }
   -1  4285 
   -1  4286     var controlsSelector = ['input:not([type="hidden"]):not([disabled])',
   -1  4287                             'select:not([disabled])',
   -1  4288                             'textarea:not([disabled])',
   -1  4289                             'button:not([disabled])',
   -1  4290                             'video:not([disabled])'].join(', ');
   -1  4291     if (axs.browserUtils.matchSelector(element, controlsSelector) && !recursive) {
   -1  4292         if (element.hasAttribute('id')) {
   -1  4293             var labelForQuerySelector = 'label[for="' + element.id + '"]';
   -1  4294             var labelsFor = document.querySelectorAll(labelForQuerySelector);
   -1  4295             var labelForValue = {};
   -1  4296             var labelForValues = [];
   -1  4297             var labelForText = [];
   -1  4298             for (var i = 0; i < labelsFor.length; i++) {
   -1  4299                 var labelFor = {};
   -1  4300                 labelFor.type = 'element';
   -1  4301                 var label = labelsFor[i];
   -1  4302                 var labelText = axs.properties.findTextAlternatives(label, {}, true);
   -1  4303                 if (labelText && labelText.trim().length > 0) {
   -1  4304                     labelFor.text = labelText.trim();
   -1  4305                     labelForText.push(labelText.trim());
   -1  4306                 }
   -1  4307                 labelFor.element = label;
   -1  4308                 labelForValues.push(labelFor);
   -1  4309             }
   -1  4310             if (labelForValues.length > 0) {
   -1  4311                 labelForValues[labelForValues.length - 1].last = true;
   -1  4312                 labelForValue.values = labelForValues;
   -1  4313                 labelForValue.text = labelForText.join(' ');
   -1  4314                 labelForValue.lastWord = axs.properties.getLastWord(labelForValue.text);
   -1  4315                 if (computedName)
   -1  4316                     labelForValue.unused = true;
   -1  4317                 else
   -1  4318                     computedName = labelForValue.text;
   -1  4319                 textAlternatives['labelFor'] = labelForValue;
   -1  4320             }
   -1  4321         }
   -1  4322 
   -1  4323         var parent = axs.dom.parentElement(element);
   -1  4324         var labelWrappedValue = {};
   -1  4325         while (parent) {
   -1  4326             if (parent.tagName.toLowerCase() == 'label') {
   -1  4327                 var parentLabel = /** @type {HTMLLabelElement} */ (parent);
   -1  4328                 if (parentLabel.control == element) {
   -1  4329                     labelWrappedValue.type = 'element';
   -1  4330                     labelWrappedValue.text = axs.properties.findTextAlternatives(parentLabel, {}, true);
   -1  4331                     labelWrappedValue.lastWord = axs.properties.getLastWord(labelWrappedValue.text);
   -1  4332                     labelWrappedValue.element = parentLabel;
   -1  4333                     break;
   -1  4334                 }
   -1  4335             }
   -1  4336             parent = axs.dom.parentElement(parent);
   -1  4337         }
   -1  4338         if (labelWrappedValue.text) {
   -1  4339             if (computedName)
   -1  4340                 labelWrappedValue.unused = true;
   -1  4341             else
   -1  4342                 computedName = labelWrappedValue.text;
   -1  4343             textAlternatives['labelWrapped'] = labelWrappedValue;
   -1  4344         }
   -1  4345         // If all else fails input of type image can fall back to its alt text
   -1  4346         if (axs.browserUtils.matchSelector(element, 'input[type="image"]') && element.hasAttribute('alt')) {
   -1  4347             var altValue = {};
   -1  4348             altValue.type = 'string';
   -1  4349             altValue.valid = true;
   -1  4350             altValue.text = element.getAttribute('alt');
   -1  4351             if (computedName)
   -1  4352                 altValue.unused = true;
   -1  4353             else
   -1  4354                 computedName = altValue.text;
   -1  4355             textAlternatives['alt'] = altValue;
   -1  4356         }
   -1  4357         if (!Object.keys(textAlternatives).length)
   -1  4358             textAlternatives['noLabel'] = true;
   -1  4359     }
   -1  4360     return computedName;
   -1  4361 };
   -1  4362 
   -1  4363 /**
   -1  4364  * @param {?string} text
   -1  4365  * @return {?string}
   -1  4366  */
   -1  4367 axs.properties.getLastWord = function(text) {
   -1  4368     if (!text)
   -1  4369         return null;
   -1  4370 
   -1  4371     // TODO: this makes a lot of assumptions.
   -1  4372     var lastSpace = text.lastIndexOf(' ') + 1;
   -1  4373     var MAXLENGTH = 10;
   -1  4374     var cutoff = text.length - MAXLENGTH;
   -1  4375     var wordStart = lastSpace > cutoff ? lastSpace : cutoff;
   -1  4376     return text.substring(wordStart);
   -1  4377 };
   -1  4378 
   -1  4379 /**
   -1  4380  * @param {Node} node
   -1  4381  * @return {Object}
   -1  4382  */
   -1  4383 axs.properties.getTextProperties = function(node) {
   -1  4384     var textProperties = {};
   -1  4385     var computedName = axs.properties.findTextAlternatives(node, textProperties, false, true);
   -1  4386 
   -1  4387     if (Object.keys(textProperties).length == 0) {
   -1  4388         /** @type {Element} */ var element = axs.dom.asElement(node);
   -1  4389         if (element && axs.browserUtils.matchSelector(element, 'img')) {
   -1  4390             var altValue = {};
   -1  4391             altValue.valid = false;
   -1  4392             altValue.errorMessage = 'No alt value provided';
   -1  4393             textProperties['alt'] = altValue;
   -1  4394 
   -1  4395             var src = element.src;
   -1  4396             if (typeof src == 'string') {
   -1  4397                 var parts = src.split('/');
   -1  4398                 var filename = parts.pop();
   -1  4399                 var filenameValue = { text: filename };
   -1  4400                 textProperties['filename'] = filenameValue;
   -1  4401                 computedName = filename;
   -1  4402             }
   -1  4403         }
   -1  4404 
   -1  4405         if (!computedName)
   -1  4406             return null;
   -1  4407     }
   -1  4408 
   -1  4409     textProperties.hasProperties = Boolean(Object.keys(textProperties).length);
   -1  4410     textProperties.computedText = computedName;
   -1  4411     textProperties.lastWord = axs.properties.getLastWord(computedName);
   -1  4412     return textProperties;
   -1  4413 };
   -1  4414 
   -1  4415 /**
   -1  4416  * Finds any ARIA attributes (roles, states and properties) explicitly set on this element.
   -1  4417  * @param {Element} element
   -1  4418  * @return {Object}
   -1  4419  */
   -1  4420 axs.properties.getAriaProperties = function(element) {
   -1  4421     var ariaProperties = {};
   -1  4422     var statesAndProperties = axs.properties.getGlobalAriaProperties(element);
   -1  4423 
   -1  4424     for (var property in axs.constants.ARIA_PROPERTIES) {
   -1  4425         var attributeName = 'aria-' + property;
   -1  4426         if (element.hasAttribute(attributeName)) {
   -1  4427             var propertyValue = element.getAttribute(attributeName);
   -1  4428             statesAndProperties[attributeName] =
   -1  4429                 axs.utils.getAriaPropertyValue(attributeName, propertyValue, element);
   -1  4430         }
   -1  4431     }
   -1  4432     if (Object.keys(statesAndProperties).length > 0)
   -1  4433         ariaProperties['properties'] = axs.utils.values(statesAndProperties);
   -1  4434 
   -1  4435     var roles = axs.utils.getRoles(element);
   -1  4436     if (!roles) {
   -1  4437         if (Object.keys(ariaProperties).length)
   -1  4438             return ariaProperties;
   -1  4439         return null;
   -1  4440     }
   -1  4441     ariaProperties['roles'] = roles;
   -1  4442     if (!roles.valid || !roles['roles'])
   -1  4443         return ariaProperties;
   -1  4444 
   -1  4445     var roleDetails = roles['roles'];
   -1  4446     for (var i = 0; i < roleDetails.length; i++) {
   -1  4447         var role = roleDetails[i];
   -1  4448         if (!role.details || !role.details.propertiesSet)
   -1  4449             continue;
   -1  4450         for (var property in role.details.propertiesSet) {
   -1  4451             if (property in statesAndProperties)
   -1  4452                 continue;
   -1  4453             if (element.hasAttribute(property)) {
   -1  4454                 var propertyValue = element.getAttribute(property);
   -1  4455                 statesAndProperties[property] =
   -1  4456                     axs.utils.getAriaPropertyValue(property, propertyValue, element);
   -1  4457                 if ('values' in statesAndProperties[property]) {
   -1  4458                     var values = statesAndProperties[property].values;
   -1  4459                     values[values.length - 1].isLast = true;
   -1  4460                 }
   -1  4461             } else if (role.details.requiredPropertiesSet[property]) {
   -1  4462                 statesAndProperties[property] =
   -1  4463                     { 'name': property, 'valid': false, 'reason': 'Required property not set' };
   -1  4464             }
   -1  4465         }
   -1  4466     }
   -1  4467     if (Object.keys(statesAndProperties).length > 0)
   -1  4468         ariaProperties['properties'] = axs.utils.values(statesAndProperties);
   -1  4469     if (Object.keys(ariaProperties).length > 0)
   -1  4470         return ariaProperties;
   -1  4471     return null;
   -1  4472 };
   -1  4473 
   -1  4474 /**
   -1  4475  * Gets the ARIA properties found on this element which apply to all elements, not just elements with ARIA roles.
   -1  4476  * @param {Element} element
   -1  4477  * @return {!Object}
   -1  4478  */
   -1  4479 axs.properties.getGlobalAriaProperties = function(element) {
   -1  4480     var globalProperties = {};
   -1  4481     for (var property in axs.constants.GLOBAL_PROPERTIES) {
   -1  4482         if (element.hasAttribute(property)) {
   -1  4483             var propertyValue = element.getAttribute(property);
   -1  4484             globalProperties[property] =
   -1  4485                 axs.utils.getAriaPropertyValue(property, propertyValue, element);
   -1  4486         }
   -1  4487     }
   -1  4488     return globalProperties;
   -1  4489 };
   -1  4490 
   -1  4491 /**
   -1  4492  * @param {Element} element
   -1  4493  * @return {Object.<string, Object>}
   -1  4494  */
   -1  4495 axs.properties.getVideoProperties = function(element) {
   -1  4496     var videoSelector = 'video';
   -1  4497     if (!axs.browserUtils.matchSelector(element, videoSelector))
   -1  4498         return null;
   -1  4499     var videoProperties = {};
   -1  4500     videoProperties['captionTracks'] = axs.properties.getTrackElements(element, 'captions');
   -1  4501     videoProperties['descriptionTracks'] = axs.properties.getTrackElements(element, 'descriptions');
   -1  4502     videoProperties['chapterTracks'] = axs.properties.getTrackElements(element, 'chapters');
   -1  4503     // error if no text alternatives?
   -1  4504     return videoProperties;
   -1  4505 };
   -1  4506 
   -1  4507 /**
   -1  4508  * @param {Element} element
   -1  4509  * @param {string} kind
   -1  4510  * @return {Object}
   -1  4511  */
   -1  4512 axs.properties.getTrackElements = function(element, kind) {
   -1  4513     // error if resource is not available
   -1  4514     var trackElements = element.querySelectorAll('track[kind=' + kind + ']');
   -1  4515     var result = {};
   -1  4516     if (!trackElements.length) {
   -1  4517         result.valid = false;
   -1  4518         result.reason = { 'messageKey': 'noTracksProvided', 'args': [[kind]] };
   -1  4519         return result;
   -1  4520     }
   -1  4521     result.valid = true;
   -1  4522     var values = [];
   -1  4523     for (var i = 0; i < trackElements.length; i++) {
   -1  4524         var trackElement = {};
   -1  4525         var src = trackElements[i].getAttribute('src');
   -1  4526         var srcLang = trackElements[i].getAttribute('srcLang');
   -1  4527         var label = trackElements[i].getAttribute('label');
   -1  4528         if (!src) {
   -1  4529             trackElement.valid = false;
   -1  4530             trackElement.reason = { 'messageKey': 'noSrcProvided' };
   -1  4531         } else {
   -1  4532             trackElement.valid = true;
   -1  4533             trackElement.src = src;
   -1  4534         }
   -1  4535         var name = '';
   -1  4536         if (label) {
   -1  4537             name += label;
   -1  4538             if (srcLang)
   -1  4539                 name += ' ';
   -1  4540         }
   -1  4541         if (srcLang)
   -1  4542             name += '(' + srcLang + ')';
   -1  4543         if (name == '')
   -1  4544             name = '[' + { 'messageKey': 'unnamed' } + ']';
   -1  4545         trackElement.name = name;
   -1  4546         values.push(trackElement);
   -1  4547     }
   -1  4548     result.values = values;
   -1  4549     return result;
   -1  4550 };
   -1  4551 
   -1  4552 /**
   -1  4553  * @param {Node} node
   -1  4554  * @return {Object.<string, Object>}
   -1  4555  */
   -1  4556 axs.properties.getAllProperties = function(node) {
   -1  4557     /** @type {Element} */ var element = axs.dom.asElement(node);
   -1  4558     if (!element)
   -1  4559         return {};
   -1  4560 
   -1  4561     var allProperties = {};
   -1  4562     allProperties['ariaProperties'] = axs.properties.getAriaProperties(element);
   -1  4563     allProperties['colorProperties'] = axs.properties.getColorProperties(element);
   -1  4564     allProperties['focusProperties'] = axs.properties.getFocusProperties(element);
   -1  4565     allProperties['textProperties'] = axs.properties.getTextProperties(node);
   -1  4566     allProperties['videoProperties'] = axs.properties.getVideoProperties(element);
   -1  4567     return allProperties;
   -1  4568 };
   -1  4569 
   -1  4570 (function() {
   -1  4571     /**
   -1  4572      * Helper for implicit semantic functionality.
   -1  4573      * Can be made part of the public API if need be.
   -1  4574      * @param {Element} element
   -1  4575      * @return {?axs.constants.HtmlInfo}
   -1  4576      */
   -1  4577     function getHtmlInfo(element) {
   -1  4578         if (!element)
   -1  4579             return null;
   -1  4580         var tagName = element.tagName;
   -1  4581         if (!tagName)
   -1  4582             return null;
   -1  4583         tagName = tagName.toUpperCase();
   -1  4584         var infos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];
   -1  4585         if (!infos || !infos.length)
   -1  4586             return null;
   -1  4587         var defaultInfo = null;  // will contain the info with no specific selector if no others match
   -1  4588         for (var i = 0, len = infos.length; i < len; i++) {
   -1  4589             var htmlInfo = infos[i];
   -1  4590             if (htmlInfo.selector) {
   -1  4591                 if (axs.browserUtils.matchSelector(element, htmlInfo.selector))
   -1  4592                     return htmlInfo;
   -1  4593             } else {
   -1  4594                 defaultInfo = htmlInfo;
   -1  4595             }
   -1  4596         }
   -1  4597         return defaultInfo;
   -1  4598     }
   -1  4599 
   -1  4600     /**
   -1  4601      * @param {Element} element
   -1  4602      * @return {string} role
   -1  4603      */
   -1  4604     axs.properties.getImplicitRole = function(element) {
   -1  4605         var htmlInfo = getHtmlInfo(element);
   -1  4606         if (htmlInfo)
   -1  4607             return htmlInfo.role;
   -1  4608         return '';
   -1  4609     };
   -1  4610 
   -1  4611     /**
   -1  4612      * Determine if this element can take ANY ARIA attributes including roles, state and properties.
   -1  4613      * If false then even global attributes should not be used.
   -1  4614      * @param {Element} element
   -1  4615      * @return {boolean}
   -1  4616      */
   -1  4617     axs.properties.canTakeAriaAttributes = function(element) {
   -1  4618         var htmlInfo = getHtmlInfo(element);
   -1  4619         if (htmlInfo)
   -1  4620             return !htmlInfo.reserved;
   -1  4621         return true;
   -1  4622     };
   -1  4623 })();
   -1  4624 
   -1  4625 /**
   -1  4626  * This lists the ARIA attributes that are supported implicitly by native properties of this element.
   -1  4627  *
   -1  4628  * @param {Element} element The element to check.
   -1  4629  * @return {!Array.<string>} An array of ARIA attributes.
   -1  4630  *
   -1  4631  * example:
   -1  4632  *    var element = document.createElement("input");
   -1  4633  *    element.setAttribute("type", "range");
   -1  4634  *    var supported = axs.properties.getNativelySupportedAttributes(element);  // an array of ARIA attributes
   -1  4635  *    console.log(supported.indexOf("aria-valuemax") >=0);  // logs 'true'
   -1  4636  */
   -1  4637 axs.properties.getNativelySupportedAttributes = function(element) {
   -1  4638     var result = [];
   -1  4639     if (!element) {
   -1  4640         return result;
   -1  4641     }
   -1  4642     var testElement = element.cloneNode(false);  // gets rid of expandos
   -1  4643     var ariaAttributes = Object.keys(/** @type {!Object} */(axs.constants.ARIA_TO_HTML_ATTRIBUTE));
   -1  4644     for (var i = 0; i < ariaAttributes.length; i++) {
   -1  4645         var ariaAttribute = ariaAttributes[i];
   -1  4646         var nativeAttribute = axs.constants.ARIA_TO_HTML_ATTRIBUTE[ariaAttribute];
   -1  4647         if (nativeAttribute in testElement) {
   -1  4648             result[result.length] = ariaAttribute;
   -1  4649         }
   -1  4650     }
   -1  4651     return result;
   -1  4652 };
   -1  4653 
   -1  4654 (function() {
   -1  4655     var roleToSelectorCache = {};  // performance optimization, cache results from getSelectorForRole
   -1  4656 
   -1  4657     /**
   -1  4658      * Build a selector that will match elements which implicity or explicitly have this role.
   -1  4659      * Note that the selector will probably not look elegant but it will work.
   -1  4660      * @param {string} role
   -1  4661      * @return {string} selector
   -1  4662      */
   -1  4663     axs.properties.getSelectorForRole = function(role) {
   -1  4664         if (!role)
   -1  4665             return '';
   -1  4666         if (roleToSelectorCache[role] && roleToSelectorCache.hasOwnProperty(role))
   -1  4667             return roleToSelectorCache[role];
   -1  4668         var selectors = ['[role="' + role + '"]'];
   -1  4669         var tagNames = Object.keys(/** @type {!Object} */(axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO));
   -1  4670         tagNames.forEach(function(tagName) {
   -1  4671             var htmlInfos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];
   -1  4672             if (htmlInfos && htmlInfos.length) {
   -1  4673                 for (var i = 0; i < htmlInfos.length; i++) {
   -1  4674                     var htmlInfo = htmlInfos[i];
   -1  4675                     if (htmlInfo.role === role) {
   -1  4676                         if (htmlInfo.selector) {
   -1  4677                             selectors[selectors.length] = htmlInfo.selector;
   -1  4678                         } else {
   -1  4679                             selectors[selectors.length] = tagName;  // Selectors API is not case sensitive.
   -1  4680                             break;  // No need to continue adding selectors since we will match the tag itself.
   -1  4681                         }
   -1  4682                     }
   -1  4683                 }
   -1  4684             }
   -1  4685         });
   -1  4686         return (roleToSelectorCache[role] = selectors.join(','));
   -1  4687     };
   -1  4688 })();
   -1  4689 
   -1  4690 },{}],7:[function(require,module,exports){
   -1  4691 var query = require('./lib/query.js');
   -1  4692 var name = require('./lib/name.js');
   -1  4693 
   -1  4694 module.exports = {
   -1  4695 	getRole: query.getRole,
   -1  4696 	getAttribute: query.getAttribute,
   -1  4697 	getName: name.getName,
   -1  4698 	getDescription: name.getDescription,
   -1  4699 
   -1  4700 	matches: query.matches,
   -1  4701 	querySelector: query.querySelector,
   -1  4702 	querySelectorAll: query.querySelectorAll,
   -1  4703 	closest: query.closest,
   -1  4704 };
   -1  4705 
   -1  4706 },{"./lib/name.js":9,"./lib/query.js":10}],8:[function(require,module,exports){
   -1  4707 exports.attributes = {
   -1  4708 	// widget
   -1  4709 	'autocomplete': 'token',
   -1  4710 	'checked': 'tristate',
   -1  4711 	'current': 'token',
   -1  4712 	'disabled': 'bool',
   -1  4713 	'expanded': 'bool-undefined',
   -1  4714 	'haspopup': 'token',
   -1  4715 	'hidden': 'bool',  // !
   -1  4716 	'invalid': 'token',
   -1  4717 	'keyshortcuts': 'string',
   -1  4718 	'label': 'string',
   -1  4719 	'level': 'int',
   -1  4720 	'modal': 'bool',
   -1  4721 	'multiline': 'bool',
   -1  4722 	'multiselectable': 'bool',
   -1  4723 	'orientation': 'token',
   -1  4724 	'placeholder': 'string',
   -1  4725 	'pressed': 'tristate',
   -1  4726 	'readonly': 'bool',
   -1  4727 	'required': 'bool',
   -1  4728 	'roledescription': 'string',
   -1  4729 	'selected': 'bool-undefined',
   -1  4730 	'valuemax': 'number',
   -1  4731 	'valuemin': 'number',
   -1  4732 	'valuenow': 'number',
   -1  4733 	'valuetext': 'string',
   -1  4734 
   -1  4735 	// live
   -1  4736 	'atomic': 'bool',
   -1  4737 	'busy': 'bool',
   -1  4738 	'live': 'token',
   -1  4739 	'relevant': 'token-list',
   -1  4740 
   -1  4741 	// dragndrop
   -1  4742 	'dropeffect': 'token-list',
   -1  4743 	'grabbed': 'bool-undefined',
   -1  4744 
   -1  4745 	// relationship
   -1  4746 	'activedescendant': 'id',
   -1  4747 	'colcount': 'int',
   -1  4748 	'colindex': 'int',
   -1  4749 	'colspan': 'int',
   -1  4750 	'controls': 'id-list',
   -1  4751 	'describedby': 'id-list',
   -1  4752 	'details': 'id',
   -1  4753 	'errormessage': 'id',
   -1  4754 	'flowto': 'id-list',
   -1  4755 	'labelledby': 'id-list',
   -1  4756 	'owns': 'id-list',
   -1  4757 	'posinset': 'int',
   -1  4758 	'rowcount': 'int',
   -1  4759 	'rowindex': 'int',
   -1  4760 	'rowspan': 'int',
   -1  4761 	'setsize': 'int',
   -1  4762 	'sort': 'token',
   -1  4763 };
   -1  4764 
   -1  4765 // https://www.w3.org/TR/html-aria/#docconformance
   -1  4766 exports.extraSelectors = {
   -1  4767 	article: ['article'],
   -1  4768 	button: [
   -1  4769 		'button',
   -1  4770 		'input[type="button"]',
   -1  4771 		'input[type="image"]',
   -1  4772 		'input[type="reset"]',
   -1  4773 		'input[type="submit"]',
   -1  4774 		'summary',
   -1  4775 	],
   -1  4776 	cell: ['td'],
   -1  4777 	checkbox: ['input[type="checkbox"]'],
   -1  4778 	combobox: [
   -1  4779 		'input[type="email"][list]',
   -1  4780 		'input[type="search"][list]',
   -1  4781 		'input[type="tel"][list]',
   -1  4782 		'input[type="text"][list]',
   -1  4783 		'input[type="url"][list]',
   -1  4784 	],
   -1  4785 	complementary: ['aside'],
   -1  4786 	definition: ['dd'],
   -1  4787 	dialog: ['dialog'],
   -1  4788 	document: ['body'],
   -1  4789 	figure: ['figure'],
   -1  4790 	form: ['form[aria-label]', 'form[aria-labelledby]'],
   -1  4791 	group: ['details', 'optgroup'],
   -1  4792 	heading: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
   -1  4793 	img: ['img:not([alt=""])'],
   -1  4794 	link: ['a[href]', 'area[href]', 'link[href]'],
   -1  4795 	list: ['dl', 'ol', 'ul'],
   -1  4796 	listitem: ['dt', 'ul > li', 'ol > li'],
   -1  4797 	main: ['main'],
   -1  4798 	math: ['math'],
   -1  4799 	menuitemcheckbox: ['menuitem[type="checkbox"]'],
   -1  4800 	menuitem: ['menuitem[type="command"]'],
   -1  4801 	menuitemradio: ['menuitem[type="radio"]'],
   -1  4802 	menu: ['menu[type="context"]'],
   -1  4803 	navigation: ['nav'],
   -1  4804 	option: ['option'],
   -1  4805 	progressbar: ['progress'],
   -1  4806 	radio: ['input[type="radio"]'],
   -1  4807 	region: ['section'],
   -1  4808 	rowgroup: ['tbody', 'thead', 'tfoot'],
   -1  4809 	row: ['tr'],
   -1  4810 	searchbox: ['input[type="search"]:not([list])'],
   -1  4811 	separator: ['hr'],
   -1  4812 	slider: ['input[type="range"]'],
   -1  4813 	spinbutton: ['input[type="number"]'],
   -1  4814 	status: ['output'],
   -1  4815 	table: ['table'],
   -1  4816 	textbox: [
   -1  4817 		'input[type="email"]:not([list])',
   -1  4818 		'input[type="tel"]:not([list])',
   -1  4819 		'input[type="text"]:not([list])',
   -1  4820 		'input[type="url"]:not([list])',
   -1  4821 		'textarea',
   -1  4822 	],
   -1  4823 
   -1  4824 	// if scope is missing, it is calculated automatically
   -1  4825 	rowheader: ['th[scope="row"]'],
   -1  4826 	columnheader: ['th[scope="col"]'],
   -1  4827 };
   -1  4828 
   -1  4829 exports.scoped = [
   -1  4830 	'article *', 'aside *', 'main *', 'nav *', 'section *',
   -1  4831 ].join(',');
   -1  4832 
   -1  4833 // https://www.w3.org/TR/wai-aria/roles
   -1  4834 var subRoles = {
   -1  4835 	cell: ['gridcell', 'rowheader'],
   -1  4836 	command: ['button', 'link', 'menuitem'],
   -1  4837 	composite: ['grid', 'select', 'spinbutton', 'tablist'],
   -1  4838 	img: ['doc-cover'],
   -1  4839 	input: ['checkbox', 'option', 'radio', 'slider', 'spinbutton', 'textbox'],
   -1  4840 	landmark: [
   -1  4841 		'banner',
   -1  4842 		'complementary',
   -1  4843 		'contentinfo',
   -1  4844 		'doc-acknowledgments',
   -1  4845 		'doc-afterword',
   -1  4846 		'doc-appendix',
   -1  4847 		'doc-bibliography',
   -1  4848 		'doc-chapter',
   -1  4849 		'doc-conclusion',
   -1  4850 		'doc-credits',
   -1  4851 		'doc-endnotes',
   -1  4852 		'doc-epilogue',
   -1  4853 		'doc-errata',
   -1  4854 		'doc-foreword',
   -1  4855 		'doc-glossary',
   -1  4856 		'doc-introduction',
   -1  4857 		'doc-part',
   -1  4858 		'doc-preface',
   -1  4859 		'doc-prologue',
   -1  4860 		'form',
   -1  4861 		'main',
   -1  4862 		'navigation',
   -1  4863 		'region',
   -1  4864 		'search',
   -1  4865 	],
   -1  4866 	range: ['progressbar', 'scrollbar', 'slider', 'spinbutton'],
   -1  4867 	roletype: ['structure', 'widget', 'window'],
   -1  4868 	section: [
   -1  4869 		'alert',
   -1  4870 		'cell',
   -1  4871 		'definition',
   -1  4872 		'doc-abstract',
   -1  4873 		'doc-colophon',
   -1  4874 		'doc-credit',
   -1  4875 		'doc-dedication',
   -1  4876 		'doc-epigraph',
   -1  4877 		'doc-example',
   -1  4878 		'doc-footnote',
   -1  4879 		'doc-qna',
   -1  4880 		'figure',
   -1  4881 		'group',
   -1  4882 		'img',
   -1  4883 		'landmark',
   -1  4884 		'list',
   -1  4885 		'listitem',
   -1  4886 		'log',
   -1  4887 		'marquee',
   -1  4888 		'math',
   -1  4889 		'note',
   -1  4890 		'status',
   -1  4891 		'table',
   -1  4892 		'tabpanel',
   -1  4893 		'term',
   -1  4894 		'tooltip',
   -1  4895 	],
   -1  4896 	sectionhead: [
   -1  4897 		'columnheader',
   -1  4898 		'doc-subtitle',
   -1  4899 		'heading',
   -1  4900 		'rowheader',
   -1  4901 		'tab',
   -1  4902 	],
   -1  4903 	select: ['combobox', 'listbox', 'menu', 'radiogroup', 'tree'],
   -1  4904 	separator: ['doc-pagebreak'],
   -1  4905 	structure: [
   -1  4906 		'application',
   -1  4907 		'document',
   -1  4908 		'none',
   -1  4909 		'presentation',
   -1  4910 		'rowgroup',
   -1  4911 		'section',
   -1  4912 		'sectionhead',
   -1  4913 		'separator',
   -1  4914 	],
   -1  4915 	table: ['grid'],
   -1  4916 	textbox: ['searchbox'],
   -1  4917 	widget: [
   -1  4918 		'command',
   -1  4919 		'composite',
   -1  4920 		'gridcell',
   -1  4921 		'input',
   -1  4922 		'range',
   -1  4923 		'row',
   -1  4924 		'separator',
   -1  4925 		'tab',
   -1  4926 	],
   -1  4927 	window: ['dialog'],
   -1  4928 	alert: ['alertdialog'],
   -1  4929 	checkbox: ['menuitemcheckbox', 'switch'],
   -1  4930 	dialog: ['alertdialog'],
   -1  4931 	gridcell: ['columnheader', 'rowheader'],
   -1  4932 	menuitem: ['menuitemcheckbox'],
   -1  4933 	menuitemcheckbox: ['menuitemradio'],
   -1  4934 	option: ['treeitem'],
   -1  4935 	radio: ['menuitemradio'],
   -1  4936 	status: ['timer'],
   -1  4937 	grid: ['treegrid'],
   -1  4938 	menu: ['menubar'],
   -1  4939 	tree: ['treegrid'],
   -1  4940 	document: ['article'],
   -1  4941 	group: ['row', 'select', 'toolbar'],
   -1  4942 	link: ['doc-backlink', 'doc-biblioref', 'doc-glossref', 'doc-noteref'],
   -1  4943 	list: ['directory', 'feed'],
   -1  4944 	listitem: ['doc-biblioentry', 'doc-endnote', 'treeitem'],
   -1  4945 	navigation: ['doc-index', 'doc-pagelist', 'doc-toc'],
   -1  4946 	note: ['doc-notice', 'doc-tip'],
   -1  4947 };
   -1  4948 
   -1  4949 var getSubRoles = function(role) {
   -1  4950 	var children = subRoles[role] || [];
   -1  4951 	var descendents = children.map(getSubRoles);
   -1  4952 
   -1  4953 	var result = [role];
   -1  4954 
   -1  4955 	descendents.forEach(function(list) {
   -1  4956 		list.forEach(function(r) {
   -1  4957 			if (result.indexOf(r) === -1) {
   -1  4958 				result.push(r);
   -1  4959 			}
   -1  4960 		});
   -1  4961 	});
   -1  4962 
   -1  4963 	return result;
   -1  4964 };
   -1  4965 
   -1  4966 exports.subRoles = {};
   -1  4967 for (var role in subRoles) {
   -1  4968 	exports.subRoles[role] = getSubRoles(role);
   -1  4969 }
   -1  4970 exports.subRoles['none'] = ['none', 'presentation'];
   -1  4971 exports.subRoles['presentation'] = ['presentation', 'none'];
   -1  4972 
   -1  4973 exports.nameFromContents = [
   -1  4974 	'button',
   -1  4975 	'checkbox',
   -1  4976 	'columnheader',
   -1  4977 	'doc-backlink',
   -1  4978 	'doc-biblioref',
   -1  4979 	'doc-glossref',
   -1  4980 	'doc-noteref',
   -1  4981 	'gridcell',
   -1  4982 	'heading',
   -1  4983 	'link',
   -1  4984 	'menuitem',
   -1  4985 	'menuitemcheckbox',
   -1  4986 	'menuitemradio',
   -1  4987 	'option',
   -1  4988 	'radio',
   -1  4989 	'row',
   -1  4990 	'rowgroup',
   -1  4991 	'rowheader',
   -1  4992 	'sectionhead',
   -1  4993 	'tab',
   -1  4994 	'tooltip',
   -1  4995 	'treeitem',
   -1  4996 	'switch',
   -1  4997 ];
   -1  4998 
   -1  4999 exports.labelable = [
   -1  5000 	'button',
   -1  5001 	'input:not([type="hidden"])',
   -1  5002 	'keygen',
   -1  5003 	'meter',
   -1  5004 	'output',
   -1  5005 	'progress',
   -1  5006 	'select',
   -1  5007 	'textarea',
   -1  5008 ];
 4830  5009 
 4831    -1     if (Object.keys(textAlternatives).length == 0 && computedName == null)
 4832    -1         return null;
   -1  5010 },{}],9:[function(require,module,exports){
   -1  5011 var constants = require('./constants.js');
   -1  5012 var query = require('./query.js');
 4833  5013 
 4834    -1     return computedName;
   -1  5014 var getPseudoContent = function(node, selector) {
   -1  5015 	var styles = window.getComputedStyle(node, selector);
   -1  5016 	var ret = styles.getPropertyValue('content');
   -1  5017 	if (ret === 'none' || ret.substr(0, 4) === '-moz') {
   -1  5018 		return '';
   -1  5019 	} else {
   -1  5020 		return ret
   -1  5021 			.replace(/^["']/, '')
   -1  5022 			.replace(/["']$/, '');
   -1  5023 	}
 4835  5024 };
 4836  5025 
 4837    -1 /**
 4838    -1  * @param {Element} element
 4839    -1  * @param {boolean=} opt_force Whether to return text alternatives for this
 4840    -1  *     element regardless of its hidden state.
 4841    -1  * @return {?string}
 4842    -1  */
 4843    -1 axs.properties.getTextFromDescendantContent = function(element, opt_force) {
 4844    -1     var children = element.childNodes;
 4845    -1     var childrenTextContent = [];
 4846    -1     for (var i = 0; i < children.length; i++) {
 4847    -1         var childTextContent = axs.properties.findTextAlternatives(children[i], {}, true, opt_force);
 4848    -1         if (childTextContent)
 4849    -1             childrenTextContent.push(childTextContent.trim());
 4850    -1     }
 4851    -1     if (childrenTextContent.length) {
 4852    -1         var result = '';
 4853    -1         // Empty children are allowed, but collapse all of them
 4854    -1         for (var i = 0; i < childrenTextContent.length; i++)
 4855    -1             result = [result, childrenTextContent[i]].join(' ').trim();
 4856    -1         return result;
 4857    -1     }
 4858    -1     return null;
   -1  5026 var getContent = function(root, referenced) {
   -1  5027 	var ret = getPseudoContent(root, ':before');
   -1  5028 	var node = root.firstChild;
   -1  5029 	while (node) {
   -1  5030 		if (node.nodeType === node.TEXT_NODE) {
   -1  5031 			ret += node.textContent;
   -1  5032 		} else if (node.nodeType === node.ELEMENT_NODE) {
   -1  5033 			ret += getName(node, true, referenced);
   -1  5034 		}
   -1  5035 		node = node.nextSibling;
   -1  5036 	}
   -1  5037 	ret += getPseudoContent(root, ':after');
   -1  5038 	return ret;
 4859  5039 };
 4860  5040 
 4861    -1 /**
 4862    -1  * @param {Element} element
 4863    -1  * @param {Object} textAlternatives
 4864    -1  * @return {?string}
 4865    -1  */
 4866    -1 axs.properties.getTextFromAriaLabelledby = function(element, textAlternatives) {
 4867    -1     var computedName = null;
 4868    -1     if (!element.hasAttribute('aria-labelledby'))
 4869    -1         return computedName;
 4870    -1 
 4871    -1     var labelledbyAttr = element.getAttribute('aria-labelledby');
 4872    -1     var labelledbyIds = labelledbyAttr.split(/\s+/);
 4873    -1     var labelledbyValue = {};
 4874    -1     labelledbyValue.valid = true;
 4875    -1     var labelledbyText = [];
 4876    -1     var labelledbyValues = [];
 4877    -1     for (var i = 0; i < labelledbyIds.length; i++) {
 4878    -1         var labelledby = {};
 4879    -1         labelledby.type = 'element';
 4880    -1         var labelledbyId = labelledbyIds[i];
 4881    -1         labelledby.value = labelledbyId;
 4882    -1         var labelledbyElement = document.getElementById(labelledbyId);
 4883    -1         if (!labelledbyElement) {
 4884    -1             labelledby.valid = false;
 4885    -1             labelledbyValue.valid = false;
 4886    -1             labelledby.errorMessage = { 'messageKey': 'noElementWithId', 'args': [labelledbyId] };
 4887    -1         } else {
 4888    -1             labelledby.valid = true;
 4889    -1             labelledby.text = axs.properties.findTextAlternatives(labelledbyElement, {}, true, true);
 4890    -1             labelledby.lastWord = axs.properties.getLastWord(labelledby.text);
 4891    -1             labelledbyText.push(labelledby.text);
 4892    -1             labelledby.element = labelledbyElement;
 4893    -1         }
 4894    -1         labelledbyValues.push(labelledby);
 4895    -1     }
 4896    -1     if (labelledbyValues.length > 0) {
 4897    -1         labelledbyValues[labelledbyValues.length - 1].last = true;
 4898    -1         labelledbyValue.values = labelledbyValues;
 4899    -1         labelledbyValue.text = labelledbyText.join(' ');
 4900    -1         labelledbyValue.lastWord = axs.properties.getLastWord(labelledbyValue.text);
 4901    -1         computedName = labelledbyValue.text;
 4902    -1         textAlternatives['ariaLabelledby'] = labelledbyValue;
 4903    -1     }
 4904    -1 
 4905    -1     return computedName;
   -1  5041 var allowNameFromContent = function(el) {
   -1  5042 	var role = query.getRole(el);
   -1  5043 	return !role || constants.nameFromContents.indexOf(role) !== -1;
 4906  5044 };
 4907  5045 
   -1  5046 var isLabelable = function(el) {
   -1  5047 	var selector = constants.labelable.join(',');
   -1  5048 	return el.matches(selector);
   -1  5049 };
 4908  5050 
 4909    -1 /**
 4910    -1  * Determine the text description/label for an element.
 4911    -1  * For example will attempt to find the alt text for an image or label text for a form control.
 4912    -1  * @param {!Element} element
 4913    -1  * @param {!Object} textAlternatives An object that will be updated with information.
 4914    -1  * @param {?string} existingComputedname
 4915    -1  * @param {boolean} recursive Whether this method is being called recursively as described in
 4916    -1  *     http://www.w3.org/TR/wai-aria/roles#textalternativecomputation section 2A.
 4917    -1  * @return {Object}
 4918    -1  */
 4919    -1 axs.properties.getTextFromHostLanguageAttributes = function(element,
 4920    -1                                                             textAlternatives,
 4921    -1                                                             existingComputedname,
 4922    -1                                                             recursive) {
 4923    -1     var computedName = existingComputedname;
 4924    -1     if (axs.browserUtils.matchSelector(element, 'img') && element.hasAttribute('alt')) {
 4925    -1         var altValue = {};
 4926    -1         altValue.type = 'string';
 4927    -1         altValue.valid = true;
 4928    -1         altValue.text = element.getAttribute('alt');
 4929    -1         if (computedName)
 4930    -1             altValue.unused = true;
 4931    -1         else
 4932    -1             computedName = altValue.text;
 4933    -1         textAlternatives['alt'] = altValue;
 4934    -1     }
 4935    -1 
 4936    -1     var controlsSelector = ['input:not([type="hidden"]):not([disabled])',
 4937    -1                             'select:not([disabled])',
 4938    -1                             'textarea:not([disabled])',
 4939    -1                             'button:not([disabled])',
 4940    -1                             'video:not([disabled])'].join(', ');
 4941    -1     if (axs.browserUtils.matchSelector(element, controlsSelector) && !recursive) {
 4942    -1         if (element.hasAttribute('id')) {
 4943    -1             var labelForQuerySelector = 'label[for="' + element.id + '"]';
 4944    -1             var labelsFor = document.querySelectorAll(labelForQuerySelector);
 4945    -1             var labelForValue = {};
 4946    -1             var labelForValues = [];
 4947    -1             var labelForText = [];
 4948    -1             for (var i = 0; i < labelsFor.length; i++) {
 4949    -1                 var labelFor = {};
 4950    -1                 labelFor.type = 'element';
 4951    -1                 var label = labelsFor[i];
 4952    -1                 var labelText = axs.properties.findTextAlternatives(label, {}, true);
 4953    -1                 if (labelText && labelText.trim().length > 0) {
 4954    -1                     labelFor.text = labelText.trim();
 4955    -1                     labelForText.push(labelText.trim());
 4956    -1                 }
 4957    -1                 labelFor.element = label;
 4958    -1                 labelForValues.push(labelFor);
 4959    -1             }
 4960    -1             if (labelForValues.length > 0) {
 4961    -1                 labelForValues[labelForValues.length - 1].last = true;
 4962    -1                 labelForValue.values = labelForValues;
 4963    -1                 labelForValue.text = labelForText.join(' ');
 4964    -1                 labelForValue.lastWord = axs.properties.getLastWord(labelForValue.text);
 4965    -1                 if (computedName)
 4966    -1                     labelForValue.unused = true;
 4967    -1                 else
 4968    -1                     computedName = labelForValue.text;
 4969    -1                 textAlternatives['labelFor'] = labelForValue;
 4970    -1             }
 4971    -1         }
   -1  5051 // Control.labels is part of the standard, but not supported in most browsers
   -1  5052 var getLabelNode = function(node) {
   -1  5053 	if (node.id) {
   -1  5054 		var selector = 'label[for="' + node.id + '"]';
   -1  5055 		var label = document.querySelector(selector);
   -1  5056 		if (label) {
   -1  5057 			return label;
   -1  5058 		}
   -1  5059 	}
 4972  5060 
 4973    -1         var parent = axs.dom.parentElement(element);
 4974    -1         var labelWrappedValue = {};
 4975    -1         while (parent) {
 4976    -1             if (parent.tagName.toLowerCase() == 'label') {
 4977    -1                 var parentLabel = /** @type {HTMLLabelElement} */ (parent);
 4978    -1                 if (parentLabel.control == element) {
 4979    -1                     labelWrappedValue.type = 'element';
 4980    -1                     labelWrappedValue.text = axs.properties.findTextAlternatives(parentLabel, {}, true);
 4981    -1                     labelWrappedValue.lastWord = axs.properties.getLastWord(labelWrappedValue.text);
 4982    -1                     labelWrappedValue.element = parentLabel;
 4983    -1                     break;
 4984    -1                 }
 4985    -1             }
 4986    -1             parent = axs.dom.parentElement(parent);
 4987    -1         }
 4988    -1         if (labelWrappedValue.text) {
 4989    -1             if (computedName)
 4990    -1                 labelWrappedValue.unused = true;
 4991    -1             else
 4992    -1                 computedName = labelWrappedValue.text;
 4993    -1             textAlternatives['labelWrapped'] = labelWrappedValue;
 4994    -1         }
 4995    -1         // If all else fails input of type image can fall back to its alt text
 4996    -1         if (axs.browserUtils.matchSelector(element, 'input[type="image"]') && element.hasAttribute('alt')) {
 4997    -1             var altValue = {};
 4998    -1             altValue.type = 'string';
 4999    -1             altValue.valid = true;
 5000    -1             altValue.text = element.getAttribute('alt');
 5001    -1             if (computedName)
 5002    -1                 altValue.unused = true;
 5003    -1             else
 5004    -1                 computedName = altValue.text;
 5005    -1             textAlternatives['alt'] = altValue;
 5006    -1         }
 5007    -1         if (!Object.keys(textAlternatives).length)
 5008    -1             textAlternatives['noLabel'] = true;
 5009    -1     }
 5010    -1     return computedName;
   -1  5061 	var p = node.parentElement;
   -1  5062 	while (p) {
   -1  5063 		if (p.tagName.toLowerCase() === 'label') {
   -1  5064 			return p;
   -1  5065 		}
   -1  5066 		p = p.parentElement;
   -1  5067 	}
 5011  5068 };
 5012  5069 
 5013    -1 /**
 5014    -1  * @param {?string} text
 5015    -1  * @return {?string}
 5016    -1  */
 5017    -1 axs.properties.getLastWord = function(text) {
 5018    -1     if (!text)
 5019    -1         return null;
   -1  5070 // http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/
   -1  5071 // https://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te
   -1  5072 var getName = function(el, recursive, referenced) {
   -1  5073 	var ret;
   -1  5074 
   -1  5075 	if (query.getAttribute(el, 'hidden', referenced)) {
   -1  5076 		return '';
   -1  5077 	}
   -1  5078 	if (query.matches(el, 'presentation')) {
   -1  5079 		return getContent(el, referenced);
   -1  5080 	}
   -1  5081 	if (!recursive && el.matches('[aria-labelledby]')) {
   -1  5082 		var ids = el.getAttribute('aria-labelledby').split(/\s+/);
   -1  5083 		var strings = ids.map(function(id) {
   -1  5084 			var label = document.getElementById(id);
   -1  5085 			return getName(label, true, label);
   -1  5086 		});
   -1  5087 		ret = strings.join(' ');
   -1  5088 	}
   -1  5089 	if (!ret && el.matches('[aria-label]')) {
   -1  5090 		ret = el.getAttribute('aria-label');
   -1  5091 	}
   -1  5092 	if (!query.matches(el, 'presentation')) {
   -1  5093 		if (!ret && isLabelable(el)) {
   -1  5094 			var label = getLabelNode(el);
   -1  5095 			if (!recursive && label) {
   -1  5096 				ret = getName(label, true, label);
   -1  5097 			}
   -1  5098 		}
   -1  5099 		if (!ret) {
   -1  5100 			ret = el.getAttribute('placeholder');
   -1  5101 		}
   -1  5102 		// figcaption
   -1  5103 		if (!ret) {
   -1  5104 			ret = el.getAttribute('alt');
   -1  5105 		}
   -1  5106 		// caption
   -1  5107 		// table
   -1  5108 	}
   -1  5109 	// FIXME only if this is embedded in a label
   -1  5110 	if (!ret && query.matches(el, 'input')) {
   -1  5111 		// combobox
   -1  5112 		// button
   -1  5113 		if (query.matches(el, 'range')) {
   -1  5114 			ret = query.getAttribute(el, 'valuetext') || query.getAttribute(el, 'valuenow') || el.value;
   -1  5115 		} else {
   -1  5116 			ret = el.value;
   -1  5117 		}
   -1  5118 		ret = '' + ret;
   -1  5119 	}
   -1  5120 	if (!ret && (recursive || allowNameFromContent(el))) {
   -1  5121 		ret = getContent(el, referenced);
   -1  5122 	}
   -1  5123 	if (!ret) {
   -1  5124 		ret = el.getAttribute('title');
   -1  5125 	}
 5020  5126 
 5021    -1     // TODO: this makes a lot of assumptions.
 5022    -1     var lastSpace = text.lastIndexOf(' ') + 1;
 5023    -1     var MAXLENGTH = 10;
 5024    -1     var cutoff = text.length - MAXLENGTH;
 5025    -1     var wordStart = lastSpace > cutoff ? lastSpace : cutoff;
 5026    -1     return text.substring(wordStart);
   -1  5127 	return (ret || '').trim().replace(/\s+/g, ' ');
 5027  5128 };
 5028  5129 
 5029    -1 /**
 5030    -1  * @param {Node} node
 5031    -1  * @return {Object}
 5032    -1  */
 5033    -1 axs.properties.getTextProperties = function(node) {
 5034    -1     var textProperties = {};
 5035    -1     var computedName = axs.properties.findTextAlternatives(node, textProperties, false, true);
 5036    -1 
 5037    -1     if (Object.keys(textProperties).length == 0) {
 5038    -1         /** @type {Element} */ var element = axs.dom.asElement(node);
 5039    -1         if (element && axs.browserUtils.matchSelector(element, 'img')) {
 5040    -1             var altValue = {};
 5041    -1             altValue.valid = false;
 5042    -1             altValue.errorMessage = 'No alt value provided';
 5043    -1             textProperties['alt'] = altValue;
   -1  5130 var getDescription = function(el) {
   -1  5131 	var ret = '';
 5044  5132 
 5045    -1             var src = element.src;
 5046    -1             if (typeof src == 'string') {
 5047    -1                 var parts = src.split('/');
 5048    -1                 var filename = parts.pop();
 5049    -1                 var filenameValue = { text: filename };
 5050    -1                 textProperties['filename'] = filenameValue;
 5051    -1                 computedName = filename;
 5052    -1             }
 5053    -1         }
   -1  5133 	if (el.matches('[aria-describedby]')) {
   -1  5134 		var ids = el.getAttribute('aria-describedby').split(/\s+/);
   -1  5135 		var strings = ids.map(function(id) {
   -1  5136 			var label = document.getElementById(id);
   -1  5137 			return getName(label, true, label);
   -1  5138 		});
   -1  5139 		ret = strings.join(' ');
   -1  5140 	} else if (el.title) {
   -1  5141 		ret = el.title;
   -1  5142 	} else if (el.placeholder) {
   -1  5143 		ret = el.placeholder;
   -1  5144 	}
 5054  5145 
 5055    -1         if (!computedName)
 5056    -1             return null;
 5057    -1     }
   -1  5146 	return (ret || '').trim().replace(/\s+/g, ' ');
   -1  5147 };
 5058  5148 
 5059    -1     textProperties.hasProperties = Boolean(Object.keys(textProperties).length);
 5060    -1     textProperties.computedText = computedName;
 5061    -1     textProperties.lastWord = axs.properties.getLastWord(computedName);
 5062    -1     return textProperties;
   -1  5149 module.exports = {
   -1  5150 	getName: getName,
   -1  5151 	getDescription: getDescription,
 5063  5152 };
 5064  5153 
 5065    -1 /**
 5066    -1  * Finds any ARIA attributes (roles, states and properties) explicitly set on this element.
 5067    -1  * @param {Element} element
 5068    -1  * @return {Object}
 5069    -1  */
 5070    -1 axs.properties.getAriaProperties = function(element) {
 5071    -1     var ariaProperties = {};
 5072    -1     var statesAndProperties = axs.properties.getGlobalAriaProperties(element);
   -1  5154 },{"./constants.js":8,"./query.js":10}],10:[function(require,module,exports){
   -1  5155 var constants = require('./constants.js');
   -1  5156 var util = require('./util.js');
 5073  5157 
 5074    -1     for (var property in axs.constants.ARIA_PROPERTIES) {
 5075    -1         var attributeName = 'aria-' + property;
 5076    -1         if (element.hasAttribute(attributeName)) {
 5077    -1             var propertyValue = element.getAttribute(attributeName);
 5078    -1             statesAndProperties[attributeName] =
 5079    -1                 axs.utils.getAriaPropertyValue(attributeName, propertyValue, element);
 5080    -1         }
 5081    -1     }
 5082    -1     if (Object.keys(statesAndProperties).length > 0)
 5083    -1         ariaProperties['properties'] = axs.utils.values(statesAndProperties);
   -1  5158 var getSubRoles = function(roles) {
   -1  5159 	return [].concat.apply([], roles.map(function(role) {
   -1  5160 		return constants.subRoles[role] || [role];
   -1  5161 	}));
   -1  5162 };
 5084  5163 
 5085    -1     var roles = axs.utils.getRoles(element);
 5086    -1     if (!roles) {
 5087    -1         if (Object.keys(ariaProperties).length)
 5088    -1             return ariaProperties;
 5089    -1         return null;
 5090    -1     }
 5091    -1     ariaProperties['roles'] = roles;
 5092    -1     if (!roles.valid || !roles['roles'])
 5093    -1         return ariaProperties;
   -1  5164 // candidates can be passed for performance optimization
   -1  5165 var _getRole = function(el, candidates) {
   -1  5166 	if (el.hasAttribute('role')) {
   -1  5167 		return el.getAttribute('role');
   -1  5168 	}
   -1  5169 	for (var role in constants.extraSelectors) {
   -1  5170 		var selector = constants.extraSelectors[role].join(',');
   -1  5171 		if ((!candidates || candidates.indexOf(role) !== -1) && el.matches(selector)) {
   -1  5172 			return role;
   -1  5173 		}
   -1  5174 	}
 5094  5175 
 5095    -1     var roleDetails = roles['roles'];
 5096    -1     for (var i = 0; i < roleDetails.length; i++) {
 5097    -1         var role = roleDetails[i];
 5098    -1         if (!role.details || !role.details.propertiesSet)
 5099    -1             continue;
 5100    -1         for (var property in role.details.propertiesSet) {
 5101    -1             if (property in statesAndProperties)
 5102    -1                 continue;
 5103    -1             if (element.hasAttribute(property)) {
 5104    -1                 var propertyValue = element.getAttribute(property);
 5105    -1                 statesAndProperties[property] =
 5106    -1                     axs.utils.getAriaPropertyValue(property, propertyValue, element);
 5107    -1                 if ('values' in statesAndProperties[property]) {
 5108    -1                     var values = statesAndProperties[property].values;
 5109    -1                     values[values.length - 1].isLast = true;
 5110    -1                 }
 5111    -1             } else if (role.details.requiredPropertiesSet[property]) {
 5112    -1                 statesAndProperties[property] =
 5113    -1                     { 'name': property, 'valid': false, 'reason': 'Required property not set' };
 5114    -1             }
 5115    -1         }
 5116    -1     }
 5117    -1     if (Object.keys(statesAndProperties).length > 0)
 5118    -1         ariaProperties['properties'] = axs.utils.values(statesAndProperties);
 5119    -1     if (Object.keys(ariaProperties).length > 0)
 5120    -1         return ariaProperties;
 5121    -1     return null;
 5122    -1 };
   -1  5176 	if (!candidates ||
   -1  5177 			candidates.indexOf('banner') !== -1 ||
   -1  5178 			candidates.indexOf('contentinfo') !== -1) {
   -1  5179 		var scoped = el.matches(constants.scoped);
 5123  5180 
 5124    -1 /**
 5125    -1  * Gets the ARIA properties found on this element which apply to all elements, not just elements with ARIA roles.
 5126    -1  * @param {Element} element
 5127    -1  * @return {!Object}
 5128    -1  */
 5129    -1 axs.properties.getGlobalAriaProperties = function(element) {
 5130    -1     var globalProperties = {};
 5131    -1     for (var property in axs.constants.GLOBAL_PROPERTIES) {
 5132    -1         if (element.hasAttribute(property)) {
 5133    -1             var propertyValue = element.getAttribute(property);
 5134    -1             globalProperties[property] =
 5135    -1                 axs.utils.getAriaPropertyValue(property, propertyValue, element);
 5136    -1         }
 5137    -1     }
 5138    -1     return globalProperties;
   -1  5181 		if (el.matches('header') && !scoped) {
   -1  5182 			return 'banner';
   -1  5183 		}
   -1  5184 		if (el.matches('footer') && !scoped) {
   -1  5185 			return 'contentinfo';
   -1  5186 		}
   -1  5187 	}
 5139  5188 };
 5140  5189 
 5141    -1 /**
 5142    -1  * @param {Element} element
 5143    -1  * @return {Object.<string, Object>}
 5144    -1  */
 5145    -1 axs.properties.getVideoProperties = function(element) {
 5146    -1     var videoSelector = 'video';
 5147    -1     if (!axs.browserUtils.matchSelector(element, videoSelector))
 5148    -1         return null;
 5149    -1     var videoProperties = {};
 5150    -1     videoProperties['captionTracks'] = axs.properties.getTrackElements(element, 'captions');
 5151    -1     videoProperties['descriptionTracks'] = axs.properties.getTrackElements(element, 'descriptions');
 5152    -1     videoProperties['chapterTracks'] = axs.properties.getTrackElements(element, 'chapters');
 5153    -1     // error if no text alternatives?
 5154    -1     return videoProperties;
 5155    -1 };
   -1  5190 var getAttribute = function(el, key, _hiddenRoot) {
   -1  5191 	if (key === 'hidden' && el === _hiddenRoot) {  // used for name calculation
   -1  5192 		return false;
   -1  5193 	}
 5156  5194 
 5157    -1 /**
 5158    -1  * @param {Element} element
 5159    -1  * @param {string} kind
 5160    -1  * @return {Object}
 5161    -1  */
 5162    -1 axs.properties.getTrackElements = function(element, kind) {
 5163    -1     // error if resource is not available
 5164    -1     var trackElements = element.querySelectorAll('track[kind=' + kind + ']');
 5165    -1     var result = {};
 5166    -1     if (!trackElements.length) {
 5167    -1         result.valid = false;
 5168    -1         result.reason = { 'messageKey': 'noTracksProvided', 'args': [[kind]] };
 5169    -1         return result;
 5170    -1     }
 5171    -1     result.valid = true;
 5172    -1     var values = [];
 5173    -1     for (var i = 0; i < trackElements.length; i++) {
 5174    -1         var trackElement = {};
 5175    -1         var src = trackElements[i].getAttribute('src');
 5176    -1         var srcLang = trackElements[i].getAttribute('srcLang');
 5177    -1         var label = trackElements[i].getAttribute('label');
 5178    -1         if (!src) {
 5179    -1             trackElement.valid = false;
 5180    -1             trackElement.reason = { 'messageKey': 'noSrcProvided' };
 5181    -1         } else {
 5182    -1             trackElement.valid = true;
 5183    -1             trackElement.src = src;
 5184    -1         }
 5185    -1         var name = '';
 5186    -1         if (label) {
 5187    -1             name += label;
 5188    -1             if (srcLang)
 5189    -1                 name += ' ';
 5190    -1         }
 5191    -1         if (srcLang)
 5192    -1             name += '(' + srcLang + ')';
 5193    -1         if (name == '')
 5194    -1             name = '[' + { 'messageKey': 'unnamed' } + ']';
 5195    -1         trackElement.name = name;
 5196    -1         values.push(trackElement);
 5197    -1     }
 5198    -1     result.values = values;
 5199    -1     return result;
   -1  5195 	var type = constants.attributes[key];
   -1  5196 	var raw = el.getAttribute('aria-' + key);
   -1  5197 
   -1  5198 	if (raw) {
   -1  5199 		if (type === 'bool') {
   -1  5200 			return raw === 'true';
   -1  5201 		} else if (type === 'tristate') {
   -1  5202 			return raw === 'true' ? true : raw === 'false' ? false : 'mixed';
   -1  5203 		} else if (type === 'bool-undefined') {
   -1  5204 			return raw === 'true' ? true : raw === 'false' ? false : undefined;
   -1  5205 		} else if (type === 'id-list') {
   -1  5206 			return raw.split(/\s+/);
   -1  5207 		} else if (type === 'integer') {
   -1  5208 			return parseInt(raw);
   -1  5209 		} else if (type === 'number') {
   -1  5210 			return parseFloat(raw);
   -1  5211 		} else if (type === 'token-list') {
   -1  5212 			return raw.split(/\s+/);
   -1  5213 		} else {
   -1  5214 			return raw;
   -1  5215 		}
   -1  5216 	}
   -1  5217 
   -1  5218 	if (key === 'level') {
   -1  5219 		for (var i = 1; i <= 6; i++) {
   -1  5220 			if (el.tagName.toLowerCase() === 'h' + i) {
   -1  5221 				return i;
   -1  5222 			}
   -1  5223 		}
   -1  5224 	} else if (key === 'disabled') {
   -1  5225 		return el.disabled;
   -1  5226 	} else if (key === 'placeholder') {
   -1  5227 		return el.placeholder;
   -1  5228 	} else if (key === 'required') {
   -1  5229 		return el.required;
   -1  5230 	} else if (key === 'readonly') {
   -1  5231 		return el.readOnly && !el.isContentEditable;
   -1  5232 	} else if (key === 'hidden') {
   -1  5233 		var style = window.getComputedStyle(el);
   -1  5234 		if (el.hidden || style.display === 'none' || style.visibility === 'hidden') {
   -1  5235 			return true;
   -1  5236 		} else if (el.clientHeight === 0) {  // rough check for performance
   -1  5237 			return el.parentNode && getAttribute(el.parentNode, 'hidden', _hiddenRoot);
   -1  5238 		}
   -1  5239 	} else if (key === 'invalid' && el.checkValidity) {
   -1  5240 		return el.checkValidity();
   -1  5241 	}
   -1  5242 
   -1  5243 	if (type === 'bool' || type === 'tristate') {
   -1  5244 		return false;
   -1  5245 	}
 5200  5246 };
 5201  5247 
 5202    -1 /**
 5203    -1  * @param {Node} node
 5204    -1  * @return {Object.<string, Object>}
 5205    -1  */
 5206    -1 axs.properties.getAllProperties = function(node) {
 5207    -1     /** @type {Element} */ var element = axs.dom.asElement(node);
 5208    -1     if (!element)
 5209    -1         return {};
   -1  5248 var matches = function(el, selector) {
   -1  5249 	var actual;
 5210  5250 
 5211    -1     var allProperties = {};
 5212    -1     allProperties['ariaProperties'] = axs.properties.getAriaProperties(element);
 5213    -1     allProperties['colorProperties'] = axs.properties.getColorProperties(element);
 5214    -1     allProperties['focusProperties'] = axs.properties.getFocusProperties(element);
 5215    -1     allProperties['textProperties'] = axs.properties.getTextProperties(node);
 5216    -1     allProperties['videoProperties'] = axs.properties.getVideoProperties(element);
 5217    -1     return allProperties;
   -1  5251 	if (selector.substr(0, 1) === ':') {
   -1  5252 		var attr = selector.substr(1);
   -1  5253 		return getAttribute(el, attr);
   -1  5254 	} else if (selector.substr(0, 1) === '[') {
   -1  5255 		var match = /\[([a-z]+)="(.*)"\]/.exec(selector);
   -1  5256 		actual = getAttribute(el, match[1]);
   -1  5257 		var rawValue = match[2];
   -1  5258 		return actual.toString() == rawValue;
   -1  5259 	} else {
   -1  5260 		var candidates = getSubRoles(selector.split(','));
   -1  5261 		actual = _getRole(el, candidates);
   -1  5262 		return candidates.indexOf(actual) !== -1;
   -1  5263 	}
 5218  5264 };
 5219  5265 
 5220    -1 (function() {
 5221    -1     /**
 5222    -1      * Helper for implicit semantic functionality.
 5223    -1      * Can be made part of the public API if need be.
 5224    -1      * @param {Element} element
 5225    -1      * @return {?axs.constants.HtmlInfo}
 5226    -1      */
 5227    -1     function getHtmlInfo(element) {
 5228    -1         if (!element)
 5229    -1             return null;
 5230    -1         var tagName = element.tagName;
 5231    -1         if (!tagName)
 5232    -1             return null;
 5233    -1         tagName = tagName.toUpperCase();
 5234    -1         var infos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];
 5235    -1         if (!infos || !infos.length)
 5236    -1             return null;
 5237    -1         var defaultInfo = null;  // will contain the info with no specific selector if no others match
 5238    -1         for (var i = 0, len = infos.length; i < len; i++) {
 5239    -1             var htmlInfo = infos[i];
 5240    -1             if (htmlInfo.selector) {
 5241    -1                 if (axs.browserUtils.matchSelector(element, htmlInfo.selector))
 5242    -1                     return htmlInfo;
 5243    -1             } else {
 5244    -1                 defaultInfo = htmlInfo;
 5245    -1             }
 5246    -1         }
 5247    -1         return defaultInfo;
 5248    -1     }
   -1  5266 var _querySelector = function(all) {
   -1  5267 	return function(root, role) {
   -1  5268 		var results = [];
   -1  5269 		util.walkDOM(root, function(node) {
   -1  5270 			if (node.nodeType === node.ELEMENT_NODE) {
   -1  5271 				// FIXME: skip hidden elements
   -1  5272 				if (matches(node, role)) {
   -1  5273 					results.push(node);
   -1  5274 					if (!all) {
   -1  5275 						return false;
   -1  5276 					}
   -1  5277 				}
   -1  5278 			}
   -1  5279 		});
   -1  5280 		return all ? results : results[0];
   -1  5281 	};
   -1  5282 };
 5249  5283 
 5250    -1     /**
 5251    -1      * @param {Element} element
 5252    -1      * @return {string} role
 5253    -1      */
 5254    -1     axs.properties.getImplicitRole = function(element) {
 5255    -1         var htmlInfo = getHtmlInfo(element);
 5256    -1         if (htmlInfo)
 5257    -1             return htmlInfo.role;
 5258    -1         return '';
 5259    -1     };
   -1  5284 var closest = function(el, selector) {
   -1  5285 	return util.searchUp(el, function(candidate) {
   -1  5286 		return matches(candidate, selector);
   -1  5287 	});
   -1  5288 };
 5260  5289 
 5261    -1     /**
 5262    -1      * Determine if this element can take ANY ARIA attributes including roles, state and properties.
 5263    -1      * If false then even global attributes should not be used.
 5264    -1      * @param {Element} element
 5265    -1      * @return {boolean}
 5266    -1      */
 5267    -1     axs.properties.canTakeAriaAttributes = function(element) {
 5268    -1         var htmlInfo = getHtmlInfo(element);
 5269    -1         if (htmlInfo)
 5270    -1             return !htmlInfo.reserved;
 5271    -1         return true;
 5272    -1     };
 5273    -1 })();
   -1  5290 module.exports = {
   -1  5291 	getRole: function(el) {
   -1  5292 		return _getRole(el);
   -1  5293 	},
   -1  5294 	getAttribute: getAttribute,
   -1  5295 	matches: matches,
   -1  5296 	querySelector: _querySelector(),
   -1  5297 	querySelectorAll: _querySelector(true),
   -1  5298 	closest: closest,
   -1  5299 };
 5274  5300 
 5275    -1 /**
 5276    -1  * This lists the ARIA attributes that are supported implicitly by native properties of this element.
 5277    -1  *
 5278    -1  * @param {Element} element The element to check.
 5279    -1  * @return {!Array.<string>} An array of ARIA attributes.
 5280    -1  *
 5281    -1  * example:
 5282    -1  *    var element = document.createElement("input");
 5283    -1  *    element.setAttribute("type", "range");
 5284    -1  *    var supported = axs.properties.getNativelySupportedAttributes(element);  // an array of ARIA attributes
 5285    -1  *    console.log(supported.indexOf("aria-valuemax") >=0);  // logs 'true'
 5286    -1  */
 5287    -1 axs.properties.getNativelySupportedAttributes = function(element) {
 5288    -1     var result = [];
 5289    -1     if (!element) {
 5290    -1         return result;
 5291    -1     }
 5292    -1     var testElement = element.cloneNode(false);  // gets rid of expandos
 5293    -1     var ariaAttributes = Object.keys(/** @type {!Object} */(axs.constants.ARIA_TO_HTML_ATTRIBUTE));
 5294    -1     for (var i = 0; i < ariaAttributes.length; i++) {
 5295    -1         var ariaAttribute = ariaAttributes[i];
 5296    -1         var nativeAttribute = axs.constants.ARIA_TO_HTML_ATTRIBUTE[ariaAttribute];
 5297    -1         if (nativeAttribute in testElement) {
 5298    -1             result[result.length] = ariaAttribute;
 5299    -1         }
 5300    -1     }
 5301    -1     return result;
   -1  5301 },{"./constants.js":8,"./util.js":11}],11:[function(require,module,exports){
   -1  5302 var walkDOM = function(root, fn) {
   -1  5303 	if (fn(root) === false) {
   -1  5304 		return false;
   -1  5305 	}
   -1  5306 	var node = root.firstChild;
   -1  5307 	while (node) {
   -1  5308 		if (walkDOM(node, fn) === false) {
   -1  5309 			return false;
   -1  5310 		}
   -1  5311 		node = node.nextSibling;
   -1  5312 	}
 5302  5313 };
 5303  5314 
 5304    -1 (function() {
 5305    -1     var roleToSelectorCache = {};  // performance optimization, cache results from getSelectorForRole
   -1  5315 var searchUp = function(el, test) {
   -1  5316 	var candidate = el.parentElement;
   -1  5317 	if (candidate) {
   -1  5318 		if (test(candidate)) {
   -1  5319 			return candidate;
   -1  5320 		} else {
   -1  5321 			return searchUp(candidate, test);
   -1  5322 		}
   -1  5323 	}
   -1  5324 };
 5306  5325 
 5307    -1     /**
 5308    -1      * Build a selector that will match elements which implicity or explicitly have this role.
 5309    -1      * Note that the selector will probably not look elegant but it will work.
 5310    -1      * @param {string} role
 5311    -1      * @return {string} selector
 5312    -1      */
 5313    -1     axs.properties.getSelectorForRole = function(role) {
 5314    -1         if (!role)
 5315    -1             return '';
 5316    -1         if (roleToSelectorCache[role] && roleToSelectorCache.hasOwnProperty(role))
 5317    -1             return roleToSelectorCache[role];
 5318    -1         var selectors = ['[role="' + role + '"]'];
 5319    -1         var tagNames = Object.keys(/** @type {!Object} */(axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO));
 5320    -1         tagNames.forEach(function(tagName) {
 5321    -1             var htmlInfos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];
 5322    -1             if (htmlInfos && htmlInfos.length) {
 5323    -1                 for (var i = 0; i < htmlInfos.length; i++) {
 5324    -1                     var htmlInfo = htmlInfos[i];
 5325    -1                     if (htmlInfo.role === role) {
 5326    -1                         if (htmlInfo.selector) {
 5327    -1                             selectors[selectors.length] = htmlInfo.selector;
 5328    -1                         } else {
 5329    -1                             selectors[selectors.length] = tagName;  // Selectors API is not case sensitive.
 5330    -1                             break;  // No need to continue adding selectors since we will match the tag itself.
 5331    -1                         }
 5332    -1                     }
 5333    -1                 }
 5334    -1             }
 5335    -1         });
 5336    -1         return (roleToSelectorCache[role] = selectors.join(','));
 5337    -1     };
 5338    -1 })();
   -1  5326 module.exports = {
   -1  5327 	walkDOM: walkDOM,
   -1  5328 	searchUp: searchUp,
   -1  5329 };
 5339  5330 
 5340  5331 },{}],12:[function(require,module,exports){
 5341  5332 /*! aXe v2.6.1
@@ -14199,23 +14190,249 @@ axs.properties.getNativelySupportedAttributes = function(element) {
14199 14190 })(typeof window === 'object' ? window : this);
14200 14191 },{}],13:[function(require,module,exports){
14201 14192 /*!
14202    -1 calcNames 1.2, compute the Name and Description property values for a DOM node
   -1 14193 CalcNames 1.3, compute the Name and Description property values for a DOM node
14203 14194 Returns an object with 'name' and 'desc' properties.
14204    -1 Authored by Bryan Garaventa plus contrabutions by Tobias Bengfort
   -1 14195 Functionality mirrors the steps within the W3C Accessible Name and Description computation algorithm.
   -1 14196 http://www.w3.org/TR/accname-aam-1.1/
   -1 14197 Authored by Bryan Garaventa plus refactoring contrabutions by Tobias Bengfort
   -1 14198 https://github.com/accdc/w3c-alternative-text-computation
14205 14199 Distributed under the terms of the Open Source Initiative OSI - MIT License
14206 14200 */
14207 14201 
14208    -1 var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14209    -1 	if (!node || node.nodeType !== 1) {
14210    -1 		return;
14211    -1 	}
   -1 14202 var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
   -1 14203 	if (!node || node.nodeType !== 1) {
   -1 14204 		return;
   -1 14205 	}
   -1 14206 
   -1 14207 	// Track nodes to prevent duplicate node reference parsing.
   -1 14208 	var nodes = [];
   -1 14209 
   -1 14210 	// Recursively process a DOM node to compute an accessible name in accordance with the spec
   -1 14211 	var walk = function(refNode, stop, skip, nodesToIgnoreValues) {
   -1 14212 		var fullName = '';
   -1 14213 
   -1 14214 		// Placeholder for storing CSS before and after pseudo element text values for the top level node
   -1 14215 		var cssOP = {
   -1 14216 			before: '',
   -1 14217 			after: ''
   -1 14218 		};
   -1 14219 
   -1 14220 		if (nodes.indexOf(refNode) === -1) {
   -1 14221 			// Store the before and after pseudo element 'content' values for the top level DOM node
   -1 14222 			// Note: If the pseudo element includes block level styling, a space will be added, otherwise inline is asumed and no spacing is added.
   -1 14223 			cssOP = getCSSText(refNode, null);
14212 14224 
14213    -1 	var trim = function(str) {
14214    -1 		if (typeof str !== 'string') {
14215    -1 			return '';
   -1 14225 			// Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips
   -1 14226 			if (preventVisualARIASelfCSSRef) {
   -1 14227 				if (cssOP.before.indexOf(' [ARIA] ') !== -1 || cssOP.before.indexOf(' aria-') !== -1 || cssOP.before.indexOf(' accName: ') !== -1) cssOP.before = '';
   -1 14228 				if (cssOP.after.indexOf(' [ARIA] ') !== -1 || cssOP.after.indexOf(' aria-') !== -1 || cssOP.after.indexOf(' accDescription: ') !== -1) cssOP.after = '';
   -1 14229 			}
14216 14230 		}
14217 14231 
14218    -1 		return str.replace(/^\s+|\s+$/g, '');
   -1 14232 		var blockNodeStack = [];
   -1 14233 
   -1 14234 		var hasLeftBlockNodeStack = function(node) {
   -1 14235 			var blocks = blockNodeStack.length;
   -1 14236 			for (var i = blocks; i; i--) {
   -1 14237 				if (!inParent(node, blockNodeStack[i - 1], refNode)) {
   -1 14238 					blockNodeStack.splice(i - 1, 1);
   -1 14239 				}
   -1 14240 			}
   -1 14241 			if (blockNodeStack.length < blocks) {
   -1 14242 				return true;
   -1 14243 			}
   -1 14244 			return false;
   -1 14245 		};
   -1 14246 
   -1 14247 		// Recursively apply the same naming computation to all nodes within the referenced structure
   -1 14248 		walkDOM(refNode, function(node) {
   -1 14249 
   -1 14250 			if (skip || !node || nodes.indexOf(node) !== -1 || (isHidden(node, refNode))) {
   -1 14251 				// Abort if algorithm step is already completed, or if node is a hidden child of refNode, or if this node has already been processed.
   -1 14252 				return;
   -1 14253 			}
   -1 14254 
   -1 14255 			if (nodes.indexOf(node) === -1) {
   -1 14256 				nodes.push(node);
   -1 14257 			}
   -1 14258 
   -1 14259 			// Store name for the current node.
   -1 14260 			var name = '';
   -1 14261 			// Placeholder for storing CSS before and after pseudo element text values for the current node container element
   -1 14262 			var cssO = {
   -1 14263 				before: '',
   -1 14264 				after: ''
   -1 14265 			};
   -1 14266 
   -1 14267 			var parent = refNode === node ? node : node.parentNode;
   -1 14268 			if (nodes.indexOf(parent) === -1) {
   -1 14269 				nodes.push(parent);
   -1 14270 				// Store the before and after pseudo element 'content' values for the current node container element
   -1 14271 				// Note: If the pseudo element includes block level styling, a space will be added, otherwise inline is asumed and no spacing is added.
   -1 14272 				cssO = getCSSText(parent, refNode);
   -1 14273 
   -1 14274 				// Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips
   -1 14275 				if (preventVisualARIASelfCSSRef) {
   -1 14276 					if (cssO.before.indexOf(' [ARIA] ') !== -1 || cssO.before.indexOf(' aria-') !== -1 || cssO.before.indexOf(' accName: ') !== -1) cssO.before = '';
   -1 14277 					if (cssO.after.indexOf(' [ARIA] ') !== -1 || cssO.after.indexOf(' aria-') !== -1 || cssO.after.indexOf(' accDescription: ') !== -1) cssO.after = '';
   -1 14278 				}
   -1 14279 
   -1 14280 			}
   -1 14281 
   -1 14282 			// Process standard DOM element node
   -1 14283 			if (node.nodeType === 1) {
   -1 14284 
   -1 14285 				var nodeIsBlock = isBlockLevelElement(node);
   -1 14286 				if (nodeIsBlock && blockNodeStack.indexOf(node) === -1) {
   -1 14287 					blockNodeStack.push(node);
   -1 14288 				}
   -1 14289 				if (nodeIsBlock && node !== refNode) {
   -1 14290 					// Add space at beginning of block level element if detected.
   -1 14291 					name += ' ';
   -1 14292 				}
   -1 14293 
   -1 14294 				var aLabelledby = node.getAttribute('aria-labelledby') || '';
   -1 14295 				var aLabel = node.getAttribute('aria-label') || '';
   -1 14296 				var nTitle = node.getAttribute('title') || '';
   -1 14297 				var nTag = node.nodeName.toLowerCase();
   -1 14298 				var nRole = node.getAttribute('role');
   -1 14299 				var rolePresentation = ['presentation', 'none'].indexOf(nRole) !== -1;
   -1 14300 				var isNativeFormField = ['input', 'select', 'textarea'].indexOf(nTag) !== -1;
   -1 14301 				var isSimulatedFormField = ['searchbox', 'scrollbar', 'slider', 'spinbutton', 'textbox', 'combobox', 'grid', 'listbox', 'tablist', 'tree', 'treegrid'].indexOf(nRole) !== -1;
   -1 14302 				var aOwns = node.getAttribute('aria-owns') || '';
   -1 14303 
   -1 14304 				// Check for non-empty value of aria-labelledby if current node equals reference node, follow each ID ref, then stop and process no deeper.
   -1 14305 				if (!stop && node === refNode && aLabelledby) {
   -1 14306 					if (!rolePresentation) {
   -1 14307 						var ids = aLabelledby.split(/\s+/);
   -1 14308 						var parts = [];
   -1 14309 						for (var i = 0; i < ids.length; i++) {
   -1 14310 							var element = document.getElementById(ids[i]);
   -1 14311 							// Also prevent the current form field from having its value included in the naming computation if nested as a child of label
   -1 14312 							parts.push(walk(element, true, skip, [node]));
   -1 14313 						}
   -1 14314 						// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14315 						name = addSpacing(trim(parts.join(' ')));
   -1 14316 					}
   -1 14317 
   -1 14318 					if (name || rolePresentation) {
   -1 14319 						// Abort further recursion if name is valid or if the referenced node is presentational.
   -1 14320 						skip = true;
   -1 14321 					}
   -1 14322 				}
   -1 14323 
   -1 14324 				// Otherwise, if the current node is non-presentational and is a nested widget control within the parent ref obj, then add only its value and process no deeper
   -1 14325 				if (!rolePresentation && node !== refNode && (isNativeFormField || isSimulatedFormField)) {
   -1 14326 
   -1 14327 					// Prevent the referencing node from having its value included in the case of form control labels that contain the element with focus.
   -1 14328 					if (!(nodesToIgnoreValues && nodesToIgnoreValues.length && nodesToIgnoreValues.indexOf(node) !== -1)) {
   -1 14329 
   -1 14330 						if (isSimulatedFormField && ['scrollbar', 'slider', 'spinbutton'].indexOf(nRole) !== -1) {
   -1 14331 							// For range widgets, append aria-valuetext if non-empty, or aria-valuenow if non-empty, or node.value if applicable.
   -1 14332 							name = getObjectValue(nRole, node, true);
   -1 14333 						}
   -1 14334 						else if (isSimulatedFormField && ['searchbox', 'textbox', 'combobox'].indexOf(nRole) !== -1) {
   -1 14335 							// For simulated edit widgets, append text from content if applicable, or node.value if applicable.
   -1 14336 							name = getObjectValue(nRole, node, false, true);
   -1 14337 						}
   -1 14338 						else if (isSimulatedFormField && ['grid', 'listbox', 'tablist', 'tree', 'treegrid'].indexOf(nRole) !== -1) {
   -1 14339 							// For simulated select widgets, append same naming computation algorithm for all child nodes including aria-selected="true" separated by a space when multiple.
   -1 14340 							// Also filter nodes so that only valid child roles of relevant parent role that include aria-selected="true" are included.
   -1 14341 							name = getObjectValue(nRole, node, false, false, true);
   -1 14342 						}
   -1 14343 						else if (isNativeFormField && ['input', 'textarea'].indexOf(nTag) !== -1) {
   -1 14344 							// For native edit fields, append node.value when applicable.
   -1 14345 							name = getObjectValue(nRole, node, false, false, false, true);
   -1 14346 						}
   -1 14347 						else if (isNativeFormField && nTag === 'select') {
   -1 14348 							// For native select fields, append node.value for single select, or text from content for all options with selected attribute separated by a space when multiple.
   -1 14349 							name = getObjectValue(nRole, node, false, false, true, true);
   -1 14350 						}
   -1 14351 
   -1 14352 						// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14353 						name = addSpacing(trim(name));
   -1 14354 
   -1 14355 					}
   -1 14356 				}
   -1 14357 
   -1 14358 				// Otherwise, if current node is non-presentational and has a non-empty aria-label then set as name and process no deeper.
   -1 14359 				else if (!name && !rolePresentation && aLabel) {
   -1 14360 					// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14361 					name = addSpacing(trim(aLabel));
   -1 14362 
   -1 14363 					if (name && node === refNode) {
   -1 14364 						// If name is non-empty and both the current and refObject nodes match, then don't process any deeper.
   -1 14365 						skip = true;
   -1 14366 					}
   -1 14367 				}
   -1 14368 
   -1 14369 				// Otherwise, if name is still empty and the current node is non-presentational and matches the ref node and is a standard form field with a non-empty associated label element, process label with same naming computation algorithm.
   -1 14370 				if (!name && !rolePresentation && node === refNode && isNativeFormField && node.id && document.querySelectorAll('label[for="' + node.id + '"]').length) {
   -1 14371 					var label = document.querySelector('label[for="' + node.id + '"]');
   -1 14372 					// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14373 					name = addSpacing(trim(walk(label, true, skip, [node])));
   -1 14374 				}
   -1 14375 
   -1 14376 				// Otherwise, if name is still empty and the current node is non-presentational and matches the ref node and is a standard form field with an implicit label element surrounding it, process label with same naming computation algorithm.
   -1 14377 				if (!name && !rolePresentation && node === refNode && isNativeFormField && getParent(node, 'label').nodeType === 1) {
   -1 14378 					// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14379 					name = addSpacing(trim(walk(getParent(node, 'label'), true, skip, [node])));
   -1 14380 				}
   -1 14381 
   -1 14382 				// Otherwise, if name is still empty and current node is non-presentational and is a standard img with a non-empty alt attribute, set alt attribute value as the accessible name.
   -1 14383 				else if (!name && !rolePresentation && nTag == 'img' && node.getAttribute('alt')) {
   -1 14384 					// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14385 					name = addSpacing(trim(node.getAttribute('alt')));
   -1 14386 				}
   -1 14387 
   -1 14388 				// Otherwise, if name is still empty and current node is non-presentational and includes a non-empty title attribute, set title attribute value as the accessible name.
   -1 14389 				if (!name && !rolePresentation && nTitle) {
   -1 14390 					// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14391 					name = addSpacing(trim(nTitle));
   -1 14392 				}
   -1 14393 
   -1 14394 				// Check for non-empty value of aria-owns, follow each ID ref, then process with same naming computation.
   -1 14395 				// Also abort aria-owns processing if contained on an element that does not support child elements.
   -1 14396 				if (aOwns && !isNativeFormField && nTag != 'img') {
   -1 14397 					var ids = aOwns.split(/\s+/);
   -1 14398 					var parts = [];
   -1 14399 					for (var i = 0; i < ids.length; i++) {
   -1 14400 						var element = document.getElementById(ids[i]);
   -1 14401 						// Abort processing if the referenced node is already a child DOM node
   -1 14402 						if (!inParent(element, node)) {
   -1 14403 							parts.push(trim(walk(element, true, skip)));
   -1 14404 						}
   -1 14405 					}
   -1 14406 					// Surround returned aria-owns naming computation with spaces since these will be separated visually if not already included as nested DOM nodes.
   -1 14407 					name += addSpacing(parts.join(' '));
   -1 14408 				}
   -1 14409 
   -1 14410 			}
   -1 14411 
   -1 14412 			// Otherwise, process text node
   -1 14413 			else if (node.nodeType === 3) {
   -1 14414 
   -1 14415 				// Add space at end of block level element if detected.
   -1 14416 				name = (hasLeftBlockNodeStack(node) ? ' ' : '') + node.data;
   -1 14417 
   -1 14418 			}
   -1 14419 
   -1 14420 			// Prepend and append the current CSS pseudo element text, plus normalize all whitespace such as newline characters and others into flat spaces.
   -1 14421 			name = cssO.before + name.replace(/\s+/g, ' ') + cssO.after;
   -1 14422 
   -1 14423 			if (name && !hasParentLabel(node, false, refNode)) {
   -1 14424 				fullName += name;
   -1 14425 			}
   -1 14426 
   -1 14427 		}, refNode);
   -1 14428 
   -1 14429 		// Prepend and append the refObj CSS pseudo element text, plus normalize whitespace chars into flat spaces.
   -1 14430 		fullName = cssOP.before + fullName.replace(/\s+/g, ' ') + cssOP.after;
   -1 14431 
   -1 14432 		// Clear the tracked nodes array for garbage collection.
   -1 14433 		nodes = [];
   -1 14434 
   -1 14435 		return fullName;
14219 14436 	};
14220 14437 
14221 14438 	var walkDOM = function(node, fn, refNode) {
@@ -14223,10 +14440,8 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14223 14440 			return;
14224 14441 		}
14225 14442 		fn(node);
14226    -1 
14227 14443 		if (!isException(node, refNode)) {
14228 14444 			node = node.firstChild;
14229    -1 
14230 14445 			while (node) {
14231 14446 				walkDOM(node, fn, refNode);
14232 14447 				node = node.nextSibling;
@@ -14234,9 +14449,15 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14234 14449 		}
14235 14450 	};
14236 14451 
   -1 14452 	var trim = function(str) {
   -1 14453 		if (typeof str !== 'string') {
   -1 14454 			return '';
   -1 14455 		}
   -1 14456 		return str.replace(/^\s+|\s+$/g, '');
   -1 14457 	};
   -1 14458 
14237 14459 	var isFocusable = function(node) {
14238 14460 		var nodeName = node.nodeName.toLowerCase();
14239    -1 
14240 14461 		if (node.getAttribute('tabindex')) {
14241 14462 			return true;
14242 14463 		}
@@ -14254,16 +14475,19 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14254 14475 			return false;
14255 14476 		}
14256 14477 
   -1 14478 		// Always include name from content when the referenced node matches list1, as well as when child nodes match those within list3
14257 14479 		var list1 = {
14258 14480 			roles: ['link', 'button', 'checkbox', 'option', 'radio', 'switch', 'tab', 'treeitem', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'cell', 'columnheader', 'rowheader', 'tooltip', 'heading'],
14259 14481 			tags: ['a', 'button', 'summary', 'input', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'menuitem', 'option', 'td', 'th']
14260 14482 		};
14261 14483 
   -1 14484 		// Never include name from content when current node matches list2
14262 14485 		var list2 = {
14263 14486 			roles: ['application', 'alert', 'log', 'marquee', 'timer', 'alertdialog', 'dialog', 'banner', 'complementary', 'form', 'main', 'navigation', 'region', 'search', 'article', 'document', 'feed', 'figure', 'img', 'math', 'toolbar', 'menu', 'menubar', 'grid', 'listbox', 'radiogroup', 'textbox', 'searchbox', 'spinbutton', 'scrollbar', 'slider', 'tablist', 'tabpanel', 'tree', 'treegrid', 'separator'],
14264 14487 			tags: ['article', 'aside', 'body', 'select', 'datalist', 'optgroup', 'dialog', 'figure', 'footer', 'form', 'header', 'hr', 'img', 'textarea', 'input', 'main', 'math', 'menu', 'nav', 'section']
14265 14488 		};
14266 14489 
   -1 14490 		// As an override of list2, conditionally include name from content if current node is focusable, or if the current node matches list3 while the referenced parent node matches list1.
14267 14491 		var list3 = {
14268 14492 			roles: ['combobox', 'term', 'definition', 'directory', 'list', 'group', 'note', 'status', 'table', 'rowgroup', 'row', 'contentinfo'],
14269 14493 			tags: ['dl', 'ul', 'ol', 'dd', 'details', 'output', 'table', 'thead', 'tbody', 'tfoot', 'tr']
@@ -14291,6 +14515,54 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14291 14515 		}
14292 14516 	};
14293 14517 
   -1 14518 	var getStyleObject = function(node) {
   -1 14519 		var style = {};
   -1 14520 		if (document.defaultView && document.defaultView.getComputedStyle) {
   -1 14521 			style = document.defaultView.getComputedStyle(node, '');
   -1 14522 		} else if (node.currentStyle) {
   -1 14523 			style = node.currentStyle;
   -1 14524 		}
   -1 14525 		return style;
   -1 14526 	};
   -1 14527 
   -1 14528 	var isBlockLevelElement = function(node, cssObj) {
   -1 14529 		var styleObject = cssObj || getStyleObject(node);
   -1 14530 		for (var prop in blockStyles) {
   -1 14531 			var values = blockStyles[prop];
   -1 14532 			for (var i = 0; i < values.length; i++) {
   -1 14533 				if (styleObject[prop] && ((values[i].indexOf('!') === 0 && [values[i].slice(1), 'inherit', 'initial', 'unset'].indexOf(styleObject[prop]) === -1) || styleObject[prop].indexOf(values[i]) !== -1)) {
   -1 14534 					return true;
   -1 14535 				}
   -1 14536 			}
   -1 14537 		}
   -1 14538 		if (!cssObj && node.nodeName && blockElements.indexOf(node.nodeName.toLowerCase()) !== -1) {
   -1 14539 			return true;
   -1 14540 		}
   -1 14541 		return false;
   -1 14542 	};
   -1 14543 
   -1 14544 	/*
   -1 14545 	CSS Block Styles indexed from:
   -1 14546 	https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
   -1 14547 	*/
   -1 14548 	var blockStyles = {
   -1 14549 		'display': ['block', 'grid', 'table', 'flow-root', 'flex'],
   -1 14550 		'position': ['absolute', 'fixed'],
   -1 14551 		'float': ['left', 'right', 'inline'],
   -1 14552 		'clear': ['left', 'right', 'both', 'inline'],
   -1 14553 		'overflow': ['hidden', 'scroll', 'auto'],
   -1 14554 		'column-count': ['!auto'],
   -1 14555 		'column-width': ['!auto'],
   -1 14556 		'column-span': ['all'],
   -1 14557 		'contain': ['layout', 'content', 'strict']
   -1 14558 	};
   -1 14559 
   -1 14560 	/*
   -1 14561 	HTML5 Block Elements indexed from:
   -1 14562 	https://github.com/webmodules/block-elements
   -1 14563 	*/
   -1 14564 	var blockElements = ['address', 'article', 'aside', 'blockquote', 'canvas', 'dd', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'li', 'main', 'nav', 'noscript', 'ol', 'output', 'p', 'pre', 'section', 'table', 'tfoot', 'ul', 'video'];
   -1 14565 
14294 14566 	var isHidden = function(node, refNode) {
14295 14567 		if (node.nodeType !== 1 || node == refNode) {
14296 14568 			return false;
@@ -14300,12 +14572,7 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14300 14572 			return true;
14301 14573 		}
14302 14574 
14303    -1 		var style = {};
14304    -1 		if (document.defaultView && document.defaultView.getComputedStyle) {
14305    -1 			style = document.defaultView.getComputedStyle(node, '');
14306    -1 		} else if (node.currentStyle) {
14307    -1 			style = node.currentStyle;
14308    -1 		}
   -1 14575 		var style = getStyleObject(node);
14309 14576 		if (style['display'] === 'none' || style['visibility'] === 'hidden') {
14310 14577 			return true;
14311 14578 		}
@@ -14313,20 +14580,96 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14313 14580 		return false;
14314 14581 	};
14315 14582 
14316    -1 	var getCSSText = function(node, refNode) {
14317    -1 		if (node.nodeType !== 1 || node == refNode || ['input', 'select', 'textarea', 'img', 'iframe'].indexOf(node.nodeName.toLowerCase()) !== -1) {
14318    -1 						return {before: '', after: ''};
   -1 14583 	var getObjectValue = function(role, node, isRange, isEdit, isSelect, isNative) {
   -1 14584 		var val = '';
   -1 14585 		var bypass = false;
   -1 14586 
   -1 14587 		if (isRange && !isNative) {
   -1 14588 			val = node.getAttribute('aria-valuetext') || node.getAttribute('aria-valuenow') || '';
   -1 14589 		}
   -1 14590 		else if (isEdit && !isNative) {
   -1 14591 			val = getText(node) || '';
   -1 14592 		}
   -1 14593 		else if (isSelect && !isNative) {
   -1 14594 			var childRoles = [];
   -1 14595 			if (role == 'grid' || role == 'treegrid') {
   -1 14596 				childRoles = ['gridcell', 'rowheader', 'columnheader'];
   -1 14597 			}
   -1 14598 			else if (role == 'listbox') {
   -1 14599 				childRoles = ['option'];
   -1 14600 			}
   -1 14601 			else if (role == 'tablist') {
   -1 14602 				childRoles = ['tab'];
   -1 14603 			}
   -1 14604 			else if (role == 'tree') {
   -1 14605 				childRoles = ['treeitem'];
   -1 14606 			}
   -1 14607 			val = joinSelectedParts(node, node.querySelectorAll('*[aria-selected="true"]'), false, childRoles);
   -1 14608 			bypass = true;
   -1 14609 		}
   -1 14610 		val = trim(val);
   -1 14611 		if (!val && (isRange || isEdit) && node.value) {
   -1 14612 			val = node.value;
   -1 14613 		}
   -1 14614 		if (!bypass && !val && isNative) {
   -1 14615 			val = (isSelect && node.multiple) ? joinSelectedParts(node, node.querySelectorAll('option[selected]'), true) : node.value;
14319 14616 		}
14320 14617 
14321    -1 		var getText = function(node, position) {
14322    -1 			var text = document.defaultView.getComputedStyle(node, position).getPropertyValue('content').replace(/^\"|\"$/g, '');
14323    -1 			if (!text || text === 'none') {
14324    -1 								return '';
14325    -1 			} else {
14326    -1 				return text;
   -1 14618 		return val;
   -1 14619 	};
   -1 14620 
   -1 14621 	var addSpacing = function(str) {
   -1 14622 		return str.length ? ' ' + str + ' ' : '';
   -1 14623 	};
   -1 14624 
   -1 14625 	var joinSelectedParts = function(node, nOA, isNative, childRoles) {
   -1 14626 		if (!nOA || !nOA.length) {
   -1 14627 			return '';
   -1 14628 		}
   -1 14629 		var parts = [];
   -1 14630 		for (var i = 0; i < nOA.length; i++) {
   -1 14631 			var role = nOA[i].getAttribute('role');
   -1 14632 			var isValidChildRole = !childRoles || childRoles.indexOf(role) !== -1;
   -1 14633 			if (isValidChildRole) {
   -1 14634 				parts.push(isNative ? getText(nOA[i]) : walk(nOA[i], true));
14327 14635 			}
14328    -1 		};
   -1 14636 		}
   -1 14637 		return parts.join(' ');
   -1 14638 	};
   -1 14639 
   -1 14640 	var getPseudoElStyleObj = function(node, position) {
   -1 14641 		var styleObj = {};
   -1 14642 		for (var prop in blockStyles) {
   -1 14643 			styleObj[prop] = document.defaultView.getComputedStyle(node, position).getPropertyValue(prop);
   -1 14644 		}
   -1 14645 		styleObj['content'] = document.defaultView.getComputedStyle(node, position).getPropertyValue('content').replace(/^\"|\\|\"$/g, '');
   -1 14646 		return styleObj;
   -1 14647 	};
   -1 14648 
   -1 14649 	var getText = function(node, position) {
   -1 14650 		if (!position && node.nodeType === 1) {
   -1 14651 			return node.innerText || node.textContent || '';
   -1 14652 		}
   -1 14653 		var styles = getPseudoElStyleObj(node, position);
   -1 14654 		var text = styles['content'];
   -1 14655 		if (!text || text === 'none') {
   -1 14656 			return '';
   -1 14657 		}
   -1 14658 		if (isBlockLevelElement({}, styles)) {
   -1 14659 			if (position == ':before') {
   -1 14660 				text += ' ';
   -1 14661 			}
   -1 14662 			else if (position == ':after') {
   -1 14663 				text = ' ' + text;
   -1 14664 			}
   -1 14665 		}
   -1 14666 		return text;
   -1 14667 	};
14329 14668 
   -1 14669 	var getCSSText = function(node, refNode) {
   -1 14670 		if (node.nodeType !== 1 || node == refNode || ['input', 'select', 'textarea', 'img', 'iframe'].indexOf(node.nodeName.toLowerCase()) !== -1) {
   -1 14671 			return {before: '', after: ''};
   -1 14672 		}
14330 14673 		if (document.defaultView && document.defaultView.getComputedStyle) {
14331 14674 			return {
14332 14675 				before: getText(node, ':before'),
@@ -14337,6 +14680,29 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14337 14680 		}
14338 14681 	};
14339 14682 
   -1 14683 	var inParent = function(node, parent, refNode) {
   -1 14684 		while (node) {
   -1 14685 			node = node.parentNode;
   -1 14686 			if (node == parent) {
   -1 14687 				return true;
   -1 14688 			}
   -1 14689 			else if (node == refNode) {
   -1 14690 				return false;
   -1 14691 			}
   -1 14692 		}
   -1 14693 		return false;
   -1 14694 	};
   -1 14695 
   -1 14696 	var getParent = function(node, nTag) {
   -1 14697 		while (node) {
   -1 14698 			node = node.parentNode;
   -1 14699 			if (node.nodeName.toLowerCase() == nTag) {
   -1 14700 				return node;
   -1 14701 			}
   -1 14702 		}
   -1 14703 		return {};
   -1 14704 	};
   -1 14705 
14340 14706 	var hasParentLabel = function(node, noLabel, refNode) {
14341 14707 		while (node && node !== refNode) {
14342 14708 			node = node.parentNode;
@@ -14356,146 +14722,42 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14356 14722 		return false;
14357 14723 	};
14358 14724 
14359    -1 	var walk = function(refNode, stop, skip) {
14360    -1 		var fullName = '';
14361    -1 		var nodes = [];
14362    -1 		var cssOP = {
14363    -1 			before: '',
14364    -1 			after: ''
14365    -1 		};
14366    -1 
14367    -1 		if (nodes.indexOf(refNode) === -1) {
14368    -1 			nodes.push(refNode);
14369    -1 			cssOP = getCSSText(refNode, null);
14370    -1 
14371    -1 			// Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips
14372    -1 			if (preventVisualARIASelfCSSRef) {
14373    -1 				if (cssOP.before.indexOf(' [ARIA] ') !== -1 || cssOP.before.indexOf(' aria-') !== -1) 
14374    -1 					cssOP.before = '';
14375    -1 				if (cssOP.after.indexOf(' [ARIA] ') !== -1 || cssOP.after.indexOf(' aria-') !== -1)  
14376    -1 					cssOP.after = '';
14377    -1 			}
14378    -1 		}
14379    -1 
14380    -1 		walkDOM(refNode, function(node) {
14381    -1 			if (skip || !node || (isHidden(node, refNode))) {
14382    -1 				return;
14383    -1 			}
14384    -1 
14385    -1 			var name = '';
14386    -1 			var cssO = {
14387    -1 				before: '',
14388    -1 				after: ''
14389    -1 			};
14390    -1 
14391    -1 			var parent = refNode === node ? node : node.parentNode;
14392    -1 			if (nodes.indexOf(parent) === -1) {
14393    -1 				nodes.push(parent);
14394    -1 				cssO = getCSSText(parent, refNode);
14395    -1 
14396    -1 				// Enabled in Visual ARIA to prevent self referencing by Visual ARIA tooltips
14397    -1 				if (preventVisualARIASelfCSSRef) {
14398    -1 					if (cssO.before.indexOf(' [ARIA] ') !== -1 || cssO.before.indexOf(' aria-') !== -1) 
14399    -1 						cssO.before = '';
14400    -1 					if (cssO.after.indexOf(' [ARIA] ') !== -1 || cssO.after.indexOf(' aria-') !== -1)  
14401    -1 						cssO.after = '';
14402    -1 				}
14403    -1 
14404    -1 			}
14405    -1 
14406    -1 			if (node.nodeType === 1) {
14407    -1 				var aLabelledby = node.getAttribute('aria-labelledby') || '';
14408    -1 				var aLabel = node.getAttribute('aria-label') || '';
14409    -1 				var nTitle = node.getAttribute('title') || '';
14410    -1 				var rolePresentation = ['presentation', 'none'].indexOf(node.getAttribute('role')) !== -1;
14411    -1 
14412    -1 				if (!node.firstChild || (node == refNode && (aLabelledby || aLabel)) || (node.firstChild && node != refNode && aLabel)) {
14413    -1 					if (!stop && node === refNode && aLabelledby) {
14414    -1 						if (!rolePresentation) {
14415    -1 							var ids = aLabelledby.split(/\s+/);
14416    -1 							var parts = [];
14417    -1 
14418    -1 							for (var i = 0; i < ids.length; i++) {
14419    -1 								var element = document.getElementById(ids[i]);
14420    -1 								parts.push(walk(element, true, skip));
14421    -1 							}
14422    -1 							name = parts.join(' ');
14423    -1 						}
14424    -1 
14425    -1 						if (name || rolePresentation) {
14426    -1 							skip = true;
14427    -1 						}
14428    -1 					}
14429    -1 
14430    -1 /*!@ Add values of custom controls here if recursive controls with values */
14431    -1 
14432    -1 					if (!name && !rolePresentation && aLabel) {
14433    -1 						name = aLabel;
14434    -1 
14435    -1 						if (name && node === refNode) {
14436    -1 							skip = true;
14437    -1 						}
14438    -1 					}
14439    -1 
14440    -1 					if (!name && !rolePresentation && ['input', 'select', 'textarea'].indexOf(node.nodeName.toLowerCase()) !== -1 && node.id && document.querySelectorAll('label[for="' + node.id + '"]').length) {
14441    -1 						var label = document.querySelector('label[for="' + node.id + '"]');
14442    -1 						name = walk(label, true, skip);
14443    -1 					}
14444    -1 
14445    -1 					if (!name && !rolePresentation && node.nodeName.toLowerCase() == 'img' && node.getAttribute('alt')) {
14446    -1 						name = node.getAttribute('alt');
14447    -1 					}
14448    -1 
14449    -1 					if (!name && !rolePresentation && nTitle) {
14450    -1 						name = nTitle;
14451    -1 					}
14452    -1 				}
14453    -1 			} else if (node.nodeType === 3) {
14454    -1 				name = node.data;
14455    -1 			}
14456    -1 
14457    -1 			name = cssO.before + name + cssO.after;
14458    -1 
14459    -1 			if (name && !hasParentLabel(node, false, refNode)) {
14460    -1 				fullName += name;
14461    -1 			}
14462    -1 		}, refNode);
14463    -1 
14464    -1 		fullName = cssOP.before + fullName + cssOP.after;
14465    -1 		return fullName;
14466    -1 	};
14467    -1 
14468 14725 	if (isHidden(node, document.body) || hasParentLabel(node, true, document.body)) {
14469 14726 		return;
14470 14727 	}
14471 14728 
   -1 14729 	// Compute accessible Name property value for node
14472 14730 	var accName = walk(node, false);
14473    -1 	var accDesc = '';
14474 14731 
   -1 14732 	var accDesc = '';
14475 14733 	if (['presentation', 'none'].indexOf(node.getAttribute('role')) === -1) {
14476    -1 		var desc = '';
14477    -1 
14478    -1 		var title = node.getAttribute('title') || '';
   -1 14734 		// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14735 		var title = trim(node.getAttribute('title'));
14479 14736 		if (title) {
14480 14737 			if (!accName) {
   -1 14738 				// Set accessible Name to title value as a fallback if no other labelling mechanism is available.
14481 14739 				accName = title;
14482 14740 			} else {
   -1 14741 				// Otherwise, set Description using title attribute if available and including more than whitespace characters.
14483 14742 				accDesc = title;
14484 14743 			}
14485 14744 		}
14486 14745 
   -1 14746 		// Compute accessible Description property value
14487 14747 		var describedby = node.getAttribute('aria-describedby') || '';
14488 14748 		if (describedby) {
14489 14749 			var ids = describedby.split(/\s+/);
14490 14750 			var parts = [];
14491    -1 
14492 14751 			for (var j = 0; j < ids.length; j++) {
14493 14752 				var element = document.getElementById(ids[j]);
14494 14753 				parts.push(walk(element, true));
14495 14754 			}
14496    -1 
14497    -1 			if (parts.length) {
14498    -1 				accDesc = parts.join(' ');
   -1 14755 			// Check for blank value, since whitespace chars alone are not valid as a name
   -1 14756 			var desc = trim(parts.join(' '));
   -1 14757 			if (desc) {
   -1 14758 				// Set Description if computation includes more than whitespace characters.
   -1 14759 				// Note: Setting the Description property using computation from aria-describedby will overwrite any prior Description set using the title attribute.
   -1 14760 				accDesc = desc;
14499 14761 			}
14500 14762 		}
14501 14763 	}
@@ -14504,6 +14766,7 @@ var calcNames = function(node, fnc, preventVisualARIASelfCSSRef) {
14504 14766 	accDesc = trim(accDesc.replace(/\s+/g, ' '));
14505 14767 
14506 14768 	if (accName === accDesc) {
   -1 14769 		// If both Name and Description properties match, then clear the Description property value.
14507 14770 		accDesc = '';
14508 14771 	}
14509 14772 
@@ -14560,7 +14823,7 @@ require('accessibility-developer-tools/src/js/Properties');
14560 14823 module.exports = global.axs;
14561 14824 
14562 14825 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
14563    -1 },{"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){
   -1 14826 },{"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){
14564 14827 var ariaApi = require('aria-api');
14565 14828 var accdc = require('w3c-alternative-text-computation');
14566 14829 var axe = require('axe-core');
@@ -14658,4 +14921,4 @@ try {
14658 14921 	});
14659 14922 }
14660 14923 
14661    -1 },{"./axs":14,"aria-api":1,"axe-core":12,"w3c-alternative-text-computation":13}]},{},[15]);
   -1 14924 },{"./axs":14,"aria-api":7,"axe-core":12,"w3c-alternative-text-computation":13}]},{},[15]);