babelacc

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

commit
c8f3b94515ea3dd34c3599130f9d9620e2aa250f
parent
f329fa2407c271d5eb3cde33ab0b12617ce5e095
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2018-02-08 14:53
build

Diffstat

M babel.js 3420 ++++++++++++++++++++++++++++++------------------------------

1 files changed, 1710 insertions, 1710 deletions


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

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