babelacc

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

commit
5199cd518f15867d6a59e190226a91fae6658c1c
parent
3d70b8b1eefe06c3c0e120cb3cc97b86d856b7f9
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2025-08-06 21:43
build

Diffstat

M babel.js 4878 ++-----------------------------------------------------------

1 files changed, 83 insertions, 4795 deletions


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

@@ -264,4695 +264,6 @@ exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate :
  264   264 };
  265   265 }).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
  266   266 },{"process/browser.js":1,"timers":2}],3:[function(require,module,exports){
  267    -1 // Copyright 2012 Google Inc.
  268    -1 //
  269    -1 // Licensed under the Apache License, Version 2.0 (the "License");
  270    -1 // you may not use this file except in compliance with the License.
  271    -1 // You may obtain a copy of the License at
  272    -1 //
  273    -1 //      http://www.apache.org/licenses/LICENSE-2.0
  274    -1 //
  275    -1 // Unless required by applicable law or agreed to in writing, software
  276    -1 // distributed under the License is distributed on an "AS IS" BASIS,
  277    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  278    -1 // See the License for the specific language governing permissions and
  279    -1 // limitations under the License.
  280    -1 
  281    -1 goog.require('axs.browserUtils');
  282    -1 goog.require('axs.color');
  283    -1 goog.require('axs.color.Color');
  284    -1 goog.require('axs.constants');
  285    -1 goog.require('axs.dom');
  286    -1 
  287    -1 goog.provide('axs.utils');
  288    -1 
  289    -1 /**
  290    -1  * @const
  291    -1  * @type {string}
  292    -1  */
  293    -1 axs.utils.FOCUSABLE_ELEMENTS_SELECTOR =
  294    -1     'input:not([type=hidden]):not([disabled]),' +
  295    -1     'select:not([disabled]),' +
  296    -1     'textarea:not([disabled]),' +
  297    -1     'button:not([disabled]),' +
  298    -1     'a[href],' +
  299    -1     'iframe,' +
  300    -1     '[tabindex]';
  301    -1 
  302    -1 /**
  303    -1  * Elements that can have labels: https://html.spec.whatwg.org/multipage/forms.html#category-label
  304    -1  * @const
  305    -1  * @type {string}
  306    -1  */
  307    -1 axs.utils.LABELABLE_ELEMENTS_SELECTOR =
  308    -1     'button,' +
  309    -1     'input:not([type=hidden]),' +
  310    -1     'keygen,' +
  311    -1     'meter,' +
  312    -1     'output,' +
  313    -1     'progress,' +
  314    -1     'select,' +
  315    -1     'textarea';
  316    -1 
  317    -1 
  318    -1 /**
  319    -1  * @param {Element} element
  320    -1  * @return {boolean}
  321    -1  */
  322    -1 axs.utils.elementIsTransparent = function(element) {
  323    -1     return element.style.opacity == '0';
  324    -1 };
  325    -1 
  326    -1 /**
  327    -1  * @param {Element} element
  328    -1  * @return {boolean}
  329    -1  */
  330    -1 axs.utils.elementHasZeroArea = function(element) {
  331    -1     var rect = element.getBoundingClientRect();
  332    -1     var width = rect.right - rect.left;
  333    -1     var height = rect.top - rect.bottom;
  334    -1     if (!width || !height)
  335    -1         return true;
  336    -1     return false;
  337    -1 };
  338    -1 
  339    -1 /**
  340    -1  * @param {Element} element
  341    -1  * @return {boolean}
  342    -1  */
  343    -1 axs.utils.elementIsOutsideScrollArea = function(element) {
  344    -1     var parent = axs.dom.parentElement(element);
  345    -1 
  346    -1     var defaultView = element.ownerDocument.defaultView;
  347    -1     while (parent != defaultView.document.body) {
  348    -1         if (axs.utils.isClippedBy(element, parent))
  349    -1             return true;
  350    -1 
  351    -1         if (axs.utils.canScrollTo(element, parent) && !axs.utils.elementIsOutsideScrollArea(parent))
  352    -1             return false;
  353    -1 
  354    -1         parent = axs.dom.parentElement(parent);
  355    -1     }
  356    -1 
  357    -1     return !axs.utils.canScrollTo(element, defaultView.document.body);
  358    -1 };
  359    -1 
  360    -1 /**
  361    -1  * Checks whether it's possible to scroll to the given element within the given container.
  362    -1  * Assumes that |container| is an ancestor of |element|.
  363    -1  * If |container| cannot be scrolled, returns True if the element is within its bounding client
  364    -1  * rect.
  365    -1  * @param {Element} element
  366    -1  * @param {Element} container
  367    -1  * @return {boolean} True iff it's possible to scroll to |element| within |container|.
  368    -1  */
  369    -1 axs.utils.canScrollTo = function(element, container) {
  370    -1     var rect = element.getBoundingClientRect();
  371    -1     var containerRect = container.getBoundingClientRect();
  372    -1     if (container == container.ownerDocument.body) {
  373    -1         var absoluteTop = containerRect.top;
  374    -1         var absoluteLeft = containerRect.left;
  375    -1     } else {
  376    -1         var absoluteTop = containerRect.top - container.scrollTop;
  377    -1         var absoluteLeft = containerRect.left - container.scrollLeft;
  378    -1     }
  379    -1     var containerScrollArea =
  380    -1         { top: absoluteTop,
  381    -1           bottom: absoluteTop + container.scrollHeight,
  382    -1           left: absoluteLeft,
  383    -1           right: absoluteLeft + container.scrollWidth };
  384    -1 
  385    -1     if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top ||
  386    -1         rect.left > containerScrollArea.right || rect.top > containerScrollArea.bottom) {
  387    -1         return false;
  388    -1     }
  389    -1 
  390    -1     var defaultView = element.ownerDocument.defaultView;
  391    -1     var style = defaultView.getComputedStyle(container);
  392    -1 
  393    -1     if (rect.left > containerRect.right || rect.top > containerRect.bottom) {
  394    -1         return (style.overflow == 'scroll' || style.overflow == 'auto' ||
  395    -1                 container instanceof defaultView.HTMLBodyElement);
  396    -1     }
  397    -1 
  398    -1     return true;
  399    -1 };
  400    -1 
  401    -1 /**
  402    -1  * Checks whether the given element is clipped by the given container.
  403    -1  * Assumes that |container| is an ancestor of |element|.
  404    -1  * @param {Element} element
  405    -1  * @param {Element} container
  406    -1  * @return {boolean} True iff |element| is clipped by |container|.
  407    -1  */
  408    -1 axs.utils.isClippedBy = function(element, container) {
  409    -1     var rect = element.getBoundingClientRect();
  410    -1     var containerRect = container.getBoundingClientRect();
  411    -1     var containerTop = containerRect.top;
  412    -1     var containerLeft = containerRect.left;
  413    -1     var containerScrollArea =
  414    -1         { top: containerTop - container.scrollTop,
  415    -1           bottom: containerTop - container.scrollTop + container.scrollHeight,
  416    -1           left: containerLeft - container.scrollLeft,
  417    -1           right: containerLeft - container.scrollLeft + container.scrollWidth };
  418    -1 
  419    -1     var defaultView = element.ownerDocument.defaultView;
  420    -1     var style = defaultView.getComputedStyle(container);
  421    -1 
  422    -1     if ((rect.right < containerRect.left || rect.bottom < containerRect.top ||
  423    -1              rect.left > containerRect.right || rect.top > containerRect.bottom) &&
  424    -1              style.overflow == 'hidden') {
  425    -1         return true;
  426    -1     }
  427    -1 
  428    -1     if (rect.right < containerScrollArea.left || rect.bottom < containerScrollArea.top)
  429    -1         return (style.overflow != 'visible');
  430    -1 
  431    -1     return false;
  432    -1 };
  433    -1 
  434    -1 /**
  435    -1  * @param {Node} ancestor A potential ancestor of |node|.
  436    -1  * @param {Node} node
  437    -1  * @return {boolean} true if |ancestor| is an ancestor of |node| (including
  438    -1  *     |ancestor| === |node|).
  439    -1  */
  440    -1 axs.utils.isAncestor = function(ancestor, node) {
  441    -1     if (node == null)
  442    -1         return false;
  443    -1     if (node === ancestor)
  444    -1         return true;
  445    -1 
  446    -1     var parentNode = axs.dom.composedParentNode(node);
  447    -1     return axs.utils.isAncestor(ancestor, parentNode);
  448    -1 };
  449    -1 
  450    -1 /**
  451    -1  * @param {Element} element
  452    -1  * @return {Array.<Element>} An array of any non-transparent elements which
  453    -1  *     overlap the given element.
  454    -1  */
  455    -1 axs.utils.overlappingElements = function(element) {
  456    -1     if (axs.utils.elementHasZeroArea(element))
  457    -1         return null;
  458    -1 
  459    -1     var overlappingElements = [];
  460    -1     var clientRects = element.getClientRects();
  461    -1     for (var i = 0; i < clientRects.length; i++) {
  462    -1         var rect = clientRects[i];
  463    -1         var center_x = (rect.left + rect.right) / 2;
  464    -1         var center_y = (rect.top + rect.bottom) / 2;
  465    -1         var elementAtPoint = document.elementFromPoint(center_x, center_y);
  466    -1 
  467    -1         if (elementAtPoint == null || elementAtPoint == element ||
  468    -1             axs.utils.isAncestor(elementAtPoint, element) ||
  469    -1             axs.utils.isAncestor(element, elementAtPoint)) {
  470    -1             continue;
  471    -1         }
  472    -1 
  473    -1         var overlappingElementStyle = window.getComputedStyle(elementAtPoint, null);
  474    -1         if (!overlappingElementStyle)
  475    -1             continue;
  476    -1 
  477    -1         var overlappingElementBg = axs.utils.getBgColor(overlappingElementStyle,
  478    -1                                                         elementAtPoint);
  479    -1         if (overlappingElementBg && overlappingElementBg.alpha > 0 &&
  480    -1             overlappingElements.indexOf(elementAtPoint) < 0) {
  481    -1             overlappingElements.push(elementAtPoint);
  482    -1         }
  483    -1     }
  484    -1 
  485    -1     return overlappingElements;
  486    -1 };
  487    -1 
  488    -1 /**
  489    -1  * @param {Element} element
  490    -1  * @return {boolean}
  491    -1  */
  492    -1 axs.utils.elementIsHtmlControl = function(element) {
  493    -1     var defaultView = element.ownerDocument.defaultView;
  494    -1 
  495    -1     // HTML control
  496    -1     if (element instanceof defaultView.HTMLButtonElement)
  497    -1         return true;
  498    -1     if (element instanceof defaultView.HTMLInputElement)
  499    -1         return true;
  500    -1     if (element instanceof defaultView.HTMLSelectElement)
  501    -1         return true;
  502    -1     if (element instanceof defaultView.HTMLTextAreaElement)
  503    -1         return true;
  504    -1 
  505    -1     return false;
  506    -1 };
  507    -1 
  508    -1 /**
  509    -1  * @param {Element} element
  510    -1  * @return {boolean}
  511    -1  */
  512    -1 axs.utils.elementIsAriaWidget = function(element) {
  513    -1     if (element.hasAttribute('role')) {
  514    -1         var roleValue = element.getAttribute('role');
  515    -1         // TODO is this correct?
  516    -1         if (roleValue) {
  517    -1             var role = axs.constants.ARIA_ROLES[roleValue];
  518    -1             if (role && 'widget' in role['allParentRolesSet'])
  519    -1                 return true;
  520    -1         }
  521    -1     }
  522    -1     return false;
  523    -1 };
  524    -1 
  525    -1 /**
  526    -1  * @param {Element} element
  527    -1  * @return {boolean}
  528    -1  */
  529    -1 axs.utils.elementIsVisible = function(element) {
  530    -1     if (axs.utils.elementIsTransparent(element))
  531    -1         return false;
  532    -1     if (axs.utils.elementHasZeroArea(element))
  533    -1         return false;
  534    -1     if (axs.utils.elementIsOutsideScrollArea(element))
  535    -1         return false;
  536    -1 
  537    -1     var overlappingElements = axs.utils.overlappingElements(element);
  538    -1     if (overlappingElements.length)
  539    -1         return false;
  540    -1 
  541    -1     return true;
  542    -1 };
  543    -1 
  544    -1 /**
  545    -1  * @param {CSSStyleDeclaration} style
  546    -1  * @return {boolean}
  547    -1  */
  548    -1 axs.utils.isLargeFont = function(style) {
  549    -1     var fontSize = style.fontSize;
  550    -1     var bold = style.fontWeight == 'bold';
  551    -1     var matches = fontSize.match(/(\d+)px/);
  552    -1     if (matches) {
  553    -1         var fontSizePx = parseInt(matches[1], 10);
  554    -1         var bodyStyle = window.getComputedStyle(document.body, null);
  555    -1         var bodyFontSize = bodyStyle.fontSize;
  556    -1         matches = bodyFontSize.match(/(\d+)px/);
  557    -1         if (matches) {
  558    -1             var bodyFontSizePx = parseInt(matches[1], 10);
  559    -1             var boldLarge = bodyFontSizePx * 1.2;
  560    -1             var large = bodyFontSizePx * 1.5;
  561    -1         } else {
  562    -1             var boldLarge = 19.2;
  563    -1             var large = 24;
  564    -1         }
  565    -1         return (bold && fontSizePx >= boldLarge || fontSizePx >= large);
  566    -1     }
  567    -1     matches = fontSize.match(/(\d+)em/);
  568    -1     if (matches) {
  569    -1         var fontSizeEm = parseInt(matches[1], 10);
  570    -1         if (bold && fontSizeEm >= 1.2 || fontSizeEm >= 1.5)
  571    -1             return true;
  572    -1         return false;
  573    -1     }
  574    -1     matches = fontSize.match(/(\d+)%/);
  575    -1     if (matches) {
  576    -1         var fontSizePercent = parseInt(matches[1], 10);
  577    -1         if (bold && fontSizePercent >= 120 || fontSizePercent >= 150)
  578    -1             return true;
  579    -1         return false;
  580    -1     }
  581    -1     matches = fontSize.match(/(\d+)pt/);
  582    -1     if (matches) {
  583    -1         var fontSizePt = parseInt(matches[1], 10);
  584    -1         if (bold && fontSizePt >= 14 || fontSizePt >= 18)
  585    -1             return true;
  586    -1         return false;
  587    -1     }
  588    -1     return false;
  589    -1 };
  590    -1 
  591    -1 /**
  592    -1  * @param {CSSStyleDeclaration} style
  593    -1  * @param {Element} element
  594    -1  * @return {?axs.color.Color}
  595    -1  */
  596    -1 axs.utils.getBgColor = function(style, element) {
  597    -1     var bgColorString = style.backgroundColor;
  598    -1     var bgColor = axs.color.parseColor(bgColorString);
  599    -1     if (!bgColor)
  600    -1         return null;
  601    -1 
  602    -1     if (style.opacity < 1)
  603    -1         bgColor.alpha = bgColor.alpha * style.opacity;
  604    -1 
  605    -1     if (bgColor.alpha < 1) {
  606    -1         var parentBg = axs.utils.getParentBgColor(element);
  607    -1         if (parentBg == null)
  608    -1             return null;
  609    -1 
  610    -1         bgColor = axs.color.flattenColors(bgColor, parentBg);
  611    -1     }
  612    -1     return bgColor;
  613    -1 };
  614    -1 
  615    -1 /**
  616    -1  * Gets the effective background color of the parent of |element|.
  617    -1  * @param {Element} element
  618    -1  * @return {?axs.color.Color}
  619    -1  */
  620    -1 axs.utils.getParentBgColor = function(element) {
  621    -1     /** @type {Element} */ var parent = element;
  622    -1     var bgStack = [];
  623    -1     var foundSolidColor = null;
  624    -1     while ((parent = axs.dom.parentElement(parent))) {
  625    -1         var computedStyle = window.getComputedStyle(parent, null);
  626    -1         if (!computedStyle)
  627    -1             continue;
  628    -1 
  629    -1         var parentBg = axs.color.parseColor(computedStyle.backgroundColor);
  630    -1         if (!parentBg)
  631    -1             continue;
  632    -1 
  633    -1         if (computedStyle.opacity < 1)
  634    -1             parentBg.alpha = parentBg.alpha * computedStyle.opacity;
  635    -1 
  636    -1         if (parentBg.alpha == 0)
  637    -1             continue;
  638    -1 
  639    -1         bgStack.push(parentBg);
  640    -1 
  641    -1         if (parentBg.alpha == 1) {
  642    -1             foundSolidColor = true;
  643    -1             break;
  644    -1         }
  645    -1     }
  646    -1 
  647    -1     if (!foundSolidColor)
  648    -1         bgStack.push(new axs.color.Color(255, 255, 255, 1));
  649    -1 
  650    -1     var bg = bgStack.pop();
  651    -1     while (bgStack.length) {
  652    -1         var fg = bgStack.pop();
  653    -1         bg = axs.color.flattenColors(fg, bg);
  654    -1     }
  655    -1     return bg;
  656    -1 };
  657    -1 
  658    -1 /**
  659    -1  * @param {CSSStyleDeclaration} style
  660    -1  * @param {Element} element
  661    -1  * @param {axs.color.Color} bgColor The background color, which may come from
  662    -1  *    another element (such as a parent element), for flattening into the
  663    -1  *    foreground color.
  664    -1  * @return {?axs.color.Color}
  665    -1  */
  666    -1 axs.utils.getFgColor = function(style, element, bgColor) {
  667    -1     var fgColorString = style.color;
  668    -1     var fgColor = axs.color.parseColor(fgColorString);
  669    -1     if (!fgColor)
  670    -1         return null;
  671    -1 
  672    -1     if (fgColor.alpha < 1)
  673    -1         fgColor = axs.color.flattenColors(fgColor, bgColor);
  674    -1 
  675    -1     if (style.opacity < 1) {
  676    -1         var parentBg = axs.utils.getParentBgColor(element);
  677    -1         fgColor.alpha = fgColor.alpha * style.opacity;
  678    -1         fgColor = axs.color.flattenColors(fgColor, parentBg);
  679    -1     }
  680    -1 
  681    -1     return fgColor;
  682    -1 };
  683    -1 
  684    -1 /**
  685    -1  * @param {Element} element
  686    -1  * @return {?number}
  687    -1  */
  688    -1 axs.utils.getContrastRatioForElement = function(element) {
  689    -1     var style = window.getComputedStyle(element, null);
  690    -1     return axs.utils.getContrastRatioForElementWithComputedStyle(style, element);
  691    -1 };
  692    -1 
  693    -1 /**
  694    -1  * @param {CSSStyleDeclaration} style
  695    -1  * @param {Element} element
  696    -1  * @return {?number}
  697    -1  */
  698    -1 axs.utils.getContrastRatioForElementWithComputedStyle = function(style, element) {
  699    -1     if (axs.utils.isElementHidden(element))
  700    -1         return null;
  701    -1 
  702    -1     var bgColor = axs.utils.getBgColor(style, element);
  703    -1     if (!bgColor)
  704    -1         return null;
  705    -1 
  706    -1     var fgColor = axs.utils.getFgColor(style, element, bgColor);
  707    -1     if (!fgColor)
  708    -1         return null;
  709    -1 
  710    -1     return axs.color.calculateContrastRatio(fgColor, bgColor);
  711    -1 };
  712    -1 
  713    -1 /**
  714    -1  * @param {Element} element
  715    -1  * @return {boolean}
  716    -1  */
  717    -1 axs.utils.isNativeTextElement = function(element) {
  718    -1     var tagName = element.tagName.toLowerCase();
  719    -1     var type = element.type ? element.type.toLowerCase() : '';
  720    -1     if (tagName == 'textarea')
  721    -1         return true;
  722    -1     if (tagName != 'input')
  723    -1         return false;
  724    -1 
  725    -1     switch (type) {
  726    -1     case 'email':
  727    -1     case 'number':
  728    -1     case 'password':
  729    -1     case 'search':
  730    -1     case 'text':
  731    -1     case 'tel':
  732    -1     case 'url':
  733    -1     case '':
  734    -1         return true;
  735    -1     default:
  736    -1         return false;
  737    -1     }
  738    -1 };
  739    -1 
  740    -1 /**
  741    -1  * @param {number} contrastRatio
  742    -1  * @param {CSSStyleDeclaration} style
  743    -1  * @param {boolean=} opt_strict Whether to use AA (false) or AAA (true) level
  744    -1  * @return {boolean}
  745    -1  */
  746    -1 axs.utils.isLowContrast = function(contrastRatio, style, opt_strict) {
  747    -1     // Round to nearest 0.1
  748    -1     var roundedContrastRatio = (Math.round(contrastRatio * 10) / 10);
  749    -1     if (!opt_strict) {
  750    -1         return roundedContrastRatio < 3.0 ||
  751    -1             (!axs.utils.isLargeFont(style) && roundedContrastRatio < 4.5);
  752    -1     } else {
  753    -1         return roundedContrastRatio < 4.5 ||
  754    -1             (!axs.utils.isLargeFont(style) && roundedContrastRatio < 7.0);
  755    -1     }
  756    -1 };
  757    -1 
  758    -1 /**
  759    -1  * @param {Element} element
  760    -1  * @return {boolean}
  761    -1  */
  762    -1 axs.utils.hasLabel = function(element) {
  763    -1     var tagName = element.tagName.toLowerCase();
  764    -1     var type = element.type ? element.type.toLowerCase() : '';
  765    -1 
  766    -1     if (element.hasAttribute('aria-label'))
  767    -1         return true;
  768    -1     if (element.hasAttribute('title'))
  769    -1         return true;
  770    -1     if (tagName == 'img' && element.hasAttribute('alt'))
  771    -1         return true;
  772    -1     if (tagName == 'input' && type == 'image' && element.hasAttribute('alt'))
  773    -1         return true;
  774    -1     if (tagName == 'input' && (type == 'submit' || type == 'reset'))
  775    -1         return true;
  776    -1 
  777    -1     // There's a separate audit that makes sure this points to an actual element or elements.
  778    -1     if (element.hasAttribute('aria-labelledby'))
  779    -1         return true;
  780    -1 
  781    -1     if (element.hasAttribute('id')) {
  782    -1         var labelsFor = document.querySelectorAll('label[for="' + element.id + '"]');
  783    -1         if (labelsFor.length > 0)
  784    -1             return true;
  785    -1     }
  786    -1 
  787    -1     var parent = axs.dom.parentElement(element);
  788    -1     while (parent) {
  789    -1         if (parent.tagName.toLowerCase() == 'label') {
  790    -1             var parentLabel = /** HTMLLabelElement */ parent;
  791    -1             if (parentLabel.control == element)
  792    -1                 return true;
  793    -1         }
  794    -1         parent = axs.dom.parentElement(parent);
  795    -1     }
  796    -1     return false;
  797    -1 };
  798    -1 
  799    -1 /**
  800    -1  * Determine if this element natively supports being disabled (i.e. via the `disabled` attribute.
  801    -1  * Disabled here means that the element should be considered disabled according to specification.
  802    -1  * This element may or may not be effectively disabled in practice as this is dependent on implementation.
  803    -1  *
  804    -1  * @param {Element} element An element to check.
  805    -1  * @return {boolean} true If the element supports being natively disabled.
  806    -1  */
  807    -1 axs.utils.isNativelyDisableable = function(element) {
  808    -1     var tagName = element.tagName.toUpperCase();
  809    -1     return (tagName in axs.constants.NATIVELY_DISABLEABLE);
  810    -1 };
  811    -1 
  812    -1 /**
  813    -1  * Determine if this element is disabled directly or indirectly by a disabled ancestor.
  814    -1  * Disabled here means that the element should be considered disabled according to specification.
  815    -1  * This element may or may not be effectively disabled in practice as this is dependent on implementation.
  816    -1  *
  817    -1  * @param {Element} element An element to check.
  818    -1  * @param {boolean=} ignoreAncestors If true do not check for disabled ancestors.
  819    -1  * @return {boolean} true if the element or one of its ancestors is disabled.
  820    -1  */
  821    -1 axs.utils.isElementDisabled = function(element, ignoreAncestors) {
  822    -1     var selector = ignoreAncestors ? '[aria-disabled=true]' : '[aria-disabled=true], [aria-disabled=true] *';
  823    -1     if (axs.browserUtils.matchSelector(element, selector)) {
  824    -1         return true;
  825    -1     }
  826    -1     if (!axs.utils.isNativelyDisableable(element) ||
  827    -1             axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type *')) {
  828    -1         return false;
  829    -1     }
  830    -1     for (var next = element; next !== null; next = axs.dom.parentElement(next)) {
  831    -1         if (next.hasAttribute('disabled')) {
  832    -1             return true;
  833    -1         }
  834    -1         if (ignoreAncestors) {
  835    -1             return false;
  836    -1         }
  837    -1     }
  838    -1     return false;
  839    -1 };
  840    -1 
  841    -1 /**
  842    -1  * @param {Element} element An element to check.
  843    -1  * @return {boolean} True if the element is hidden from accessibility.
  844    -1  */
  845    -1 axs.utils.isElementHidden = function(element) {
  846    -1     if (!(element instanceof element.ownerDocument.defaultView.HTMLElement))
  847    -1       return false;
  848    -1 
  849    -1     if (element.hasAttribute('chromevoxignoreariahidden'))
  850    -1         var chromevoxignoreariahidden = true;
  851    -1 
  852    -1     var style = window.getComputedStyle(element, null);
  853    -1     if (style.display == 'none' || style.visibility == 'hidden')
  854    -1         return true;
  855    -1 
  856    -1     if (element.hasAttribute('aria-hidden') &&
  857    -1         element.getAttribute('aria-hidden').toLowerCase() == 'true') {
  858    -1         return !chromevoxignoreariahidden;
  859    -1     }
  860    -1 
  861    -1     return false;
  862    -1 };
  863    -1 
  864    -1 /**
  865    -1  * @param {Element} element An element to check.
  866    -1  * @return {boolean} True if the element or one of its ancestors is
  867    -1  *     hidden from accessibility.
  868    -1  */
  869    -1 axs.utils.isElementOrAncestorHidden = function(element) {
  870    -1     if (axs.utils.isElementHidden(element))
  871    -1         return true;
  872    -1 
  873    -1     if (axs.dom.parentElement(element))
  874    -1         return axs.utils.isElementOrAncestorHidden(axs.dom.parentElement(element));
  875    -1     else
  876    -1         return false;
  877    -1 };
  878    -1 
  879    -1 /**
  880    -1  * @param {Element} element An element to check
  881    -1  * @return {boolean} True if the given element is an inline element, false
  882    -1  *     otherwise.
  883    -1  */
  884    -1 axs.utils.isInlineElement = function(element) {
  885    -1     var tagName = element.tagName.toUpperCase();
  886    -1     return axs.constants.InlineElements[tagName];
  887    -1 };
  888    -1 
  889    -1 /**
  890    -1  *
  891    -1  * Gets role details from an element.
  892    -1  * @param {Element} element The DOM element whose role we want.
  893    -1  * @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute.
  894    -1  *
  895    -1  * @return {Object}
  896    -1  */
  897    -1 axs.utils.getRoles = function(element, implicit) {
  898    -1     if (!element || element.nodeType !== Node.ELEMENT_NODE || (!element.hasAttribute('role') && !implicit))
  899    -1         return null;
  900    -1     var roleValue = element.getAttribute('role');
  901    -1     if (!roleValue && implicit)
  902    -1         roleValue = axs.properties.getImplicitRole(element);
  903    -1     if (!roleValue)  // role='' or implicit role came up empty
  904    -1         return null;
  905    -1     var roleNames = roleValue.split(' ');
  906    -1     var result = { roles: [], valid: false };
  907    -1     for (var i = 0; i < roleNames.length; i++) {
  908    -1         var role = roleNames[i];
  909    -1         var ariaRole = axs.constants.ARIA_ROLES[role];
  910    -1         var roleObject = { 'name': role };
  911    -1         if (ariaRole && !ariaRole.abstract) {
  912    -1             roleObject.details = ariaRole;
  913    -1             if (!result.applied) {
  914    -1                 result.applied = roleObject;
  915    -1             }
  916    -1             roleObject.valid = result.valid = true;
  917    -1         } else {
  918    -1             roleObject.valid = false;
  919    -1         }
  920    -1         result.roles.push(roleObject);
  921    -1     }
  922    -1 
  923    -1     return result;
  924    -1 };
  925    -1 
  926    -1 /**
  927    -1  * @param {!string} propertyName
  928    -1  * @param {!string} value
  929    -1  * @param {!Element} element
  930    -1  * @return {!Object}
  931    -1  */
  932    -1 axs.utils.getAriaPropertyValue = function(propertyName, value, element) {
  933    -1     var propertyKey = propertyName.replace(/^aria-/, '');
  934    -1     var property = axs.constants.ARIA_PROPERTIES[propertyKey];
  935    -1     var result = { 'name': propertyName, 'rawValue': value };
  936    -1     if (!property) {
  937    -1         result.valid = false;
  938    -1         result.reason = '"' + propertyName + '" is not a valid ARIA property';
  939    -1         return result;
  940    -1     }
  941    -1 
  942    -1     var propertyType = property.valueType;
  943    -1     if (!propertyType) {
  944    -1         result.valid = false;
  945    -1         result.reason = '"' + propertyName + '" is not a valid ARIA property';
  946    -1         return result;
  947    -1     }
  948    -1 
  949    -1     switch (propertyType) {
  950    -1     case "idref":
  951    -1         var isValid = axs.utils.isValidIDRefValue(value, element);
  952    -1         result.valid = isValid.valid;
  953    -1         result.reason = isValid.reason;
  954    -1         result.idref = isValid.idref;
  955    -1         // falls through
  956    -1     case "idref_list":
  957    -1         var idrefValues = value.split(/\s+/);
  958    -1         result.valid = true;
  959    -1         for (var i = 0; i < idrefValues.length; i++) {
  960    -1             var refIsValid = axs.utils.isValidIDRefValue(idrefValues[i],  element);
  961    -1             if (!refIsValid.valid)
  962    -1                 result.valid = false;
  963    -1             if (result.values)
  964    -1                 result.values.push(refIsValid);
  965    -1             else
  966    -1                 result.values = [refIsValid];
  967    -1         }
  968    -1         return result;
  969    -1     case "integer":
  970    -1         var validNumber = axs.utils.isValidNumber(value);
  971    -1         if (!validNumber.valid) {
  972    -1             result.valid = false;
  973    -1             result.reason = validNumber.reason;
  974    -1             return result;
  975    -1         }
  976    -1         if (Math.floor(validNumber.value) !== validNumber.value) {
  977    -1             result.valid = false;
  978    -1             result.reason = '' + value + ' is not a whole integer';
  979    -1         } else {
  980    -1             result.valid = true;
  981    -1             result.value = validNumber.value;
  982    -1         }
  983    -1         return result;
  984    -1     case "decimal":
  985    -1     case "number":
  986    -1         var validNumber = axs.utils.isValidNumber(value);
  987    -1         result.valid = validNumber.valid;
  988    -1         if (!validNumber.valid) {
  989    -1             result.reason = validNumber.reason;
  990    -1             return result;
  991    -1         }
  992    -1         result.value = validNumber.value;
  993    -1         return result;
  994    -1     case "string":
  995    -1         result.valid = true;
  996    -1         result.value = value;
  997    -1         return result;
  998    -1     case "token":
  999    -1         var validTokenValue = axs.utils.isValidTokenValue(propertyName, value.toLowerCase());
 1000    -1         if (validTokenValue.valid) {
 1001    -1             result.valid = true;
 1002    -1             result.value = validTokenValue.value;
 1003    -1             return result;
 1004    -1         } else {
 1005    -1             result.valid = false;
 1006    -1             result.value = value;
 1007    -1             result.reason = validTokenValue.reason;
 1008    -1             return result;
 1009    -1         }
 1010    -1         // falls through
 1011    -1     case "token_list":
 1012    -1         var tokenValues = value.split(/\s+/);
 1013    -1         result.valid = true;
 1014    -1         for (var i = 0; i < tokenValues.length; i++) {
 1015    -1             var validTokenValue = axs.utils.isValidTokenValue(propertyName, tokenValues[i].toLowerCase());
 1016    -1             if (!validTokenValue.valid) {
 1017    -1                 result.valid = false;
 1018    -1                 if (result.reason) {
 1019    -1                     result.reason = [ result.reason ];
 1020    -1                     result.reason.push(validTokenValue.reason);
 1021    -1                 } else {
 1022    -1                     result.reason = validTokenValue.reason;
 1023    -1                     result.possibleValues = validTokenValue.possibleValues;
 1024    -1                 }
 1025    -1             }
 1026    -1             // TODO (more structured result)
 1027    -1             if (result.values)
 1028    -1                 result.values.push(validTokenValue.value);
 1029    -1             else
 1030    -1                 result.values = [validTokenValue.value];
 1031    -1         }
 1032    -1         return result;
 1033    -1     case "tristate":
 1034    -1         var validTristate = axs.utils.isPossibleValue(value.toLowerCase(), axs.constants.MIXED_VALUES, propertyName);
 1035    -1         if (validTristate.valid) {
 1036    -1             result.valid = true;
 1037    -1             result.value = validTristate.value;
 1038    -1         } else {
 1039    -1             result.valid = false;
 1040    -1             result.value = value;
 1041    -1             result.reason = validTristate.reason;
 1042    -1         }
 1043    -1         return result;
 1044    -1     case "boolean":
 1045    -1         var validBoolean = axs.utils.isValidBoolean(value);
 1046    -1         if (validBoolean.valid) {
 1047    -1             result.valid = true;
 1048    -1             result.value = validBoolean.value;
 1049    -1         } else {
 1050    -1             result.valid = false;
 1051    -1             result.value = value;
 1052    -1             result.reason = validBoolean.reason;
 1053    -1         }
 1054    -1         return result;
 1055    -1     }
 1056    -1     result.valid = false;
 1057    -1     result.reason = 'Not a valid ARIA property';
 1058    -1     return result;
 1059    -1 };
 1060    -1 
 1061    -1 /**
 1062    -1  * @param {string} propertyName The name of the property.
 1063    -1  * @param {string} value The value to check.
 1064    -1  * @return {!Object}
 1065    -1  */
 1066    -1 axs.utils.isValidTokenValue = function(propertyName, value) {
 1067    -1     var propertyKey = propertyName.replace(/^aria-/, '');
 1068    -1     var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];
 1069    -1     var possibleValues = propertyDetails.valuesSet;
 1070    -1     return axs.utils.isPossibleValue(value, possibleValues, propertyName);
 1071    -1 };
 1072    -1 
 1073    -1 /**
 1074    -1  * @param {string} value
 1075    -1  * @param {Object.<string, boolean>} possibleValues
 1076    -1  * @param {string} propertyName The name of the property.
 1077    -1  * @return {!Object}
 1078    -1  */
 1079    -1 axs.utils.isPossibleValue = function(value, possibleValues, propertyName) {
 1080    -1     if (!possibleValues[value])
 1081    -1         return { 'valid': false,
 1082    -1                  'value': value,
 1083    -1                  'reason': '"' + value + '" is not a valid value for ' + propertyName,
 1084    -1                  'possibleValues': Object.keys(possibleValues) };
 1085    -1     return { 'valid': true, 'value': value };
 1086    -1 };
 1087    -1 
 1088    -1 /**
 1089    -1  * @param {string} value
 1090    -1  * @return {!Object}
 1091    -1  */
 1092    -1 axs.utils.isValidBoolean = function(value) {
 1093    -1     try {
 1094    -1         var parsedValue = JSON.parse(value);
 1095    -1     } catch (e) {
 1096    -1         parsedValue = '';
 1097    -1     }
 1098    -1     if (typeof(parsedValue) != 'boolean')
 1099    -1         return { 'valid': false,
 1100    -1                  'value': value,
 1101    -1                  'reason': '"' + value + '" is not a true/false value' };
 1102    -1     return { 'valid': true, 'value': parsedValue };
 1103    -1 };
 1104    -1 
 1105    -1 /**
 1106    -1  * @param {string} value
 1107    -1  * @param {!Element} element
 1108    -1  * @return {!Object}
 1109    -1  */
 1110    -1 axs.utils.isValidIDRefValue = function(value, element) {
 1111    -1     if (value.length == 0)
 1112    -1         return { 'valid': true, 'idref': value };
 1113    -1     if (!element.ownerDocument.getElementById(value))
 1114    -1         return { 'valid': false,
 1115    -1                  'idref': value,
 1116    -1                  'reason': 'No element with ID "' + value + '"' };
 1117    -1     return { 'valid': true, 'idref': value };
 1118    -1 };
 1119    -1 
 1120    -1 /**
 1121    -1  * Tests if a number is real number for a11y purposes.
 1122    -1  * Must be a real, numerical, decimal value; heavily inspired by
 1123    -1  *    http://www.w3.org/TR/wai-aria/states_and_properties#valuetype_number
 1124    -1  * @param {string} value
 1125    -1  * @return {!Object}
 1126    -1  */
 1127    -1 axs.utils.isValidNumber = function(value) {
 1128    -1     var failResult = {
 1129    -1         'valid': false,
 1130    -1         'value': value,
 1131    -1         'reason': '"' + value + '" is not a number'
 1132    -1     };
 1133    -1     if (!value) {
 1134    -1         return failResult;
 1135    -1     }
 1136    -1     if (/^0x/i.test(value)) {
 1137    -1         failResult.reason = '"' + value + '" is not a decimal number';  // hex is not accepted
 1138    -1         return failResult;
 1139    -1     }
 1140    -1     var parsedValue = value * 1;
 1141    -1     if (!isFinite(parsedValue)) {
 1142    -1         return failResult;
 1143    -1     }
 1144    -1     return { 'valid': true, 'value': parsedValue };
 1145    -1 };
 1146    -1 
 1147    -1 /**
 1148    -1  * @param {Element} element
 1149    -1  * @return {boolean}
 1150    -1  */
 1151    -1 axs.utils.isElementImplicitlyFocusable = function(element) {
 1152    -1     var defaultView = element.ownerDocument.defaultView;
 1153    -1 
 1154    -1     if (element instanceof defaultView.HTMLAnchorElement ||
 1155    -1         element instanceof defaultView.HTMLAreaElement)
 1156    -1         return element.hasAttribute('href');
 1157    -1     if (element instanceof defaultView.HTMLInputElement ||
 1158    -1         element instanceof defaultView.HTMLSelectElement ||
 1159    -1         element instanceof defaultView.HTMLTextAreaElement ||
 1160    -1         element instanceof defaultView.HTMLButtonElement ||
 1161    -1         element instanceof defaultView.HTMLIFrameElement)
 1162    -1         return !element.disabled;
 1163    -1     return false;
 1164    -1 };
 1165    -1 
 1166    -1 /**
 1167    -1  * Returns an array containing the values of the given JSON-compatible object.
 1168    -1  * (Simply ignores any function values.)
 1169    -1  * @param {Object} obj
 1170    -1  * @return {Array}
 1171    -1  */
 1172    -1 axs.utils.values = function(obj) {
 1173    -1     var values = [];
 1174    -1     for (var key in obj) {
 1175    -1         if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')
 1176    -1             values.push(obj[key]);
 1177    -1     }
 1178    -1     return values;
 1179    -1 };
 1180    -1 
 1181    -1 /**
 1182    -1  * Returns an object containing the same keys and values as the given
 1183    -1  * JSON-compatible object. (Simply ignores any function values.)
 1184    -1  * @param {Object} obj
 1185    -1  * @return {Object}
 1186    -1  */
 1187    -1 axs.utils.namedValues = function(obj) {
 1188    -1     var values = {};
 1189    -1     for (var key in obj) {
 1190    -1         if (obj.hasOwnProperty(key) && typeof obj[key] != 'function')
 1191    -1             values[key] = obj[key];
 1192    -1     }
 1193    -1     return values;
 1194    -1 };
 1195    -1 
 1196    -1 /**
 1197    -1 * Escapes a given ID to be used in a CSS selector
 1198    -1 *
 1199    -1 * @private
 1200    -1 * @param {!string} id The ID to be escaped
 1201    -1 * @return {string} The escaped ID
 1202    -1 */
 1203    -1 function escapeId(id) {
 1204    -1     return id.replace(/[^a-zA-Z0-9_-]/g,function(match) { return '\\' + match; });
 1205    -1 }
 1206    -1 
 1207    -1 /** Gets a CSS selector text for a DOM object.
 1208    -1  * @param {Node} obj The DOM object.
 1209    -1  * @return {string} CSS selector text for the DOM object.
 1210    -1  */
 1211    -1 axs.utils.getQuerySelectorText = function(obj) {
 1212    -1   if (obj == null || obj.tagName == 'HTML') {
 1213    -1     return 'html';
 1214    -1   } else if (obj.tagName == 'BODY') {
 1215    -1     return 'body';
 1216    -1   }
 1217    -1 
 1218    -1   if (obj.hasAttribute) {
 1219    -1     if (obj.id) {
 1220    -1       return '#' + escapeId(obj.id);
 1221    -1     }
 1222    -1 
 1223    -1     if (obj.className) {
 1224    -1       var selector = '';
 1225    -1       for (var i = 0; i < obj.classList.length; i++)
 1226    -1         selector += '.' + obj.classList[i];
 1227    -1 
 1228    -1       var total = 0;
 1229    -1       if (obj.parentNode) {
 1230    -1         for (i = 0; i < obj.parentNode.children.length; i++) {
 1231    -1           var similar = obj.parentNode.children[i];
 1232    -1           if (axs.browserUtils.matchSelector(similar, selector))
 1233    -1             total++;
 1234    -1           if (similar === obj)
 1235    -1             break;
 1236    -1         }
 1237    -1       } else {
 1238    -1         total = 1;
 1239    -1       }
 1240    -1 
 1241    -1       if (total == 1) {
 1242    -1         return axs.utils.getQuerySelectorText(obj.parentNode) +
 1243    -1                ' > ' + selector;
 1244    -1       }
 1245    -1     }
 1246    -1 
 1247    -1     if (obj.parentNode) {
 1248    -1       var similarTags = obj.parentNode.children;
 1249    -1       var total = 1;
 1250    -1       var i = 0;
 1251    -1       while (similarTags[i] !== obj) {
 1252    -1         if (similarTags[i].tagName == obj.tagName) {
 1253    -1           total++;
 1254    -1         }
 1255    -1         i++;
 1256    -1       }
 1257    -1 
 1258    -1       var next = '';
 1259    -1       if (obj.parentNode.tagName != 'BODY') {
 1260    -1         next = axs.utils.getQuerySelectorText(obj.parentNode) +
 1261    -1                ' > ';
 1262    -1       }
 1263    -1 
 1264    -1       if (total == 1) {
 1265    -1         return next +
 1266    -1                obj.tagName;
 1267    -1       } else {
 1268    -1         return next +
 1269    -1                obj.tagName +
 1270    -1                ':nth-of-type(' + total + ')';
 1271    -1       }
 1272    -1     }
 1273    -1 
 1274    -1   } else if (obj.selectorText) {
 1275    -1     return obj.selectorText;
 1276    -1   }
 1277    -1 
 1278    -1   return '';
 1279    -1 };
 1280    -1 
 1281    -1 /**
 1282    -1  * Gets elements that refer to this element in an ARIA attribute that takes an ID reference list or
 1283    -1  * single ID reference.
 1284    -1  * @param {Element} element a potential referent.
 1285    -1  * @param {string=} opt_attributeName Name of an ARIA attribute to limit the results to, e.g. 'aria-owns'.
 1286    -1  * @return {NodeList} The elements that refer to this element or null.
 1287    -1  */
 1288    -1 axs.utils.getAriaIdReferrers = function(element, opt_attributeName) {
 1289    -1     var propertyToSelector = function(propertyKey) {
 1290    -1         var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyKey];
 1291    -1         if (propertyDetails) {
 1292    -1             if (propertyDetails.valueType === ('idref')) {
 1293    -1                 return '[aria-' + propertyKey + '=\'' + id + '\']';
 1294    -1             } else if (propertyDetails.valueType === ('idref_list')) {
 1295    -1                 return '[aria-' + propertyKey + '~=\'' + id + '\']';
 1296    -1             }
 1297    -1         }
 1298    -1         return '';
 1299    -1     };
 1300    -1     if (!element)
 1301    -1         return null;
 1302    -1     var id = element.id;
 1303    -1     if (!id)
 1304    -1         return null;
 1305    -1     id = id.replace(/'/g, "\\'");  // make it safe to use in a selector
 1306    -1 
 1307    -1     if (opt_attributeName) {
 1308    -1         var propertyKey = opt_attributeName.replace(/^aria-/, '');
 1309    -1         var referrerQuery = propertyToSelector(propertyKey);
 1310    -1         if (referrerQuery) {
 1311    -1             return element.ownerDocument.querySelectorAll(referrerQuery);
 1312    -1         }
 1313    -1     } else {
 1314    -1         var selectors = [];
 1315    -1         for (var propertyKey in axs.constants.ARIA_PROPERTIES) {
 1316    -1             var referrerQuery = propertyToSelector(propertyKey);
 1317    -1             if (referrerQuery) {
 1318    -1                 selectors.push(referrerQuery);
 1319    -1             }
 1320    -1         }
 1321    -1         return element.ownerDocument.querySelectorAll(selectors.join(','));
 1322    -1     }
 1323    -1     return null;
 1324    -1 };
 1325    -1 
 1326    -1 /**
 1327    -1  * Gets elements that refer to this element in an HTML attribute that takes an ID reference list or
 1328    -1  * single ID reference.
 1329    -1  * @param {Element} element a potential referent.
 1330    -1  * @return {NodeList} The elements that refer to this element.
 1331    -1  */
 1332    -1 axs.utils.getHtmlIdReferrers = function(element) {
 1333    -1     if (!element)
 1334    -1         return null;
 1335    -1     var id = element.id;
 1336    -1     if (!id)
 1337    -1         return null;
 1338    -1     id = id.replace(/'/g, "\\'");  // make it safe to use in a selector
 1339    -1     var selectorTemplates = [
 1340    -1         '[contextmenu=\'{id}\']',
 1341    -1         '[itemref~=\'{id}\']',
 1342    -1         'button[form=\'{id}\']',
 1343    -1         'button[menu=\'{id}\']',
 1344    -1         'fieldset[form=\'{id}\']',
 1345    -1         'input[form=\'{id}\']',
 1346    -1         'input[list=\'{id}\']',
 1347    -1         'keygen[form=\'{id}\']',
 1348    -1         'label[for=\'{id}\']',
 1349    -1         'label[form=\'{id}\']',
 1350    -1         'menuitem[command=\'{id}\']',
 1351    -1         'object[form=\'{id}\']',
 1352    -1         'output[for~=\'{id}\']',
 1353    -1         'output[form=\'{id}\']',
 1354    -1         'select[form=\'{id}\']',
 1355    -1         'td[headers~=\'{id}\']',
 1356    -1         'textarea[form=\'{id}\']',
 1357    -1         'tr[headers~=\'{id}\']'];
 1358    -1     var selectors = selectorTemplates.map(function(selector) {
 1359    -1         return selector.replace('\{id\}', id);
 1360    -1     });
 1361    -1     return element.ownerDocument.querySelectorAll(selectors.join(','));
 1362    -1 };
 1363    -1 
 1364    -1 /**
 1365    -1  * Gets a list of all IDs this element references in either ARIA or HTML attributes.
 1366    -1  *
 1367    -1  * @param {Element} element The element to check for idref attributes.
 1368    -1  * @returns {Array.<string>} Any IDs this element references.
 1369    -1  */
 1370    -1 axs.utils.getReferencedIds = function(element) {
 1371    -1     var result = [];
 1372    -1     var addResult = function(ids) {
 1373    -1             if (ids) {
 1374    -1                 if (ids.indexOf(' ') > 0) {
 1375    -1                     result = result.concat(attrib.value.split(' '));
 1376    -1                 } else {
 1377    -1                     result.push(ids);
 1378    -1                 }
 1379    -1             }
 1380    -1         };
 1381    -1     for (var i = 0; i < element.attributes.length; i++) {
 1382    -1         var tagName = element.tagName.toLowerCase();
 1383    -1         var attrib = element.attributes[i];
 1384    -1         if (attrib.specified) {
 1385    -1             var attribName = attrib.name;
 1386    -1             var ariaAttr = attribName.match(/aria-(.+)/);
 1387    -1             if (ariaAttr) {
 1388    -1                 var details = axs.constants.ARIA_PROPERTIES[ariaAttr[1]];
 1389    -1                 if (details && (details.valueType === ('idref') || details.valueType === ('idref_list'))) {
 1390    -1                     addResult(attrib.value);
 1391    -1                 }
 1392    -1                 continue;
 1393    -1             }
 1394    -1             switch (attribName) {
 1395    -1                 case 'contextmenu':
 1396    -1                 case 'itemref':
 1397    -1                     addResult(attrib.value);
 1398    -1                     break;
 1399    -1                 case 'form':
 1400    -1                     if (tagName == 'button' || tagName == 'fieldset' || tagName == 'input' ||
 1401    -1                             tagName == 'keygen' || tagName == 'label' || tagName == 'object' ||
 1402    -1                             tagName == 'output' || tagName == 'select' || tagName == 'textarea') {
 1403    -1                         addResult(attrib.value);
 1404    -1                     }
 1405    -1                     break;
 1406    -1                 case 'for':
 1407    -1                     if (tagName == 'label' || tagName == 'output') {
 1408    -1                         addResult(attrib.value);
 1409    -1                     }
 1410    -1                     break;
 1411    -1                 case 'menu':
 1412    -1                     if (tagName == 'button') {
 1413    -1                         addResult(attrib.value);
 1414    -1                     }
 1415    -1                     break;
 1416    -1                 case 'list':
 1417    -1                     if (tagName == 'input') {
 1418    -1                         addResult(attrib.value);
 1419    -1                     }
 1420    -1                     break;
 1421    -1                 case 'command':
 1422    -1                     if (tagName == 'menuitem') {
 1423    -1                         addResult(attrib.value);
 1424    -1                     }
 1425    -1                     break;
 1426    -1                 case 'headers':
 1427    -1                     if (tagName == 'td' || tagName == 'tr') {
 1428    -1                         addResult(attrib.value);
 1429    -1                     }
 1430    -1                     break;
 1431    -1             }
 1432    -1         }
 1433    -1     }
 1434    -1     return result;
 1435    -1 };
 1436    -1 
 1437    -1 /**
 1438    -1  * Gets elements that refer to this element in an attribute that takes an ID reference list or
 1439    -1  * single ID reference.
 1440    -1  * @param {Element} element a potential referent.
 1441    -1  * @return {Array<Element>} The elements that refer to this element.
 1442    -1  */
 1443    -1 axs.utils.getIdReferrers = function(element) {
 1444    -1     var result = [];
 1445    -1     var referrers = axs.utils.getHtmlIdReferrers(element);
 1446    -1     if (referrers) {
 1447    -1         result = result.concat(Array.prototype.slice.call(referrers));
 1448    -1     }
 1449    -1     referrers = axs.utils.getAriaIdReferrers(element);
 1450    -1     if (referrers) {
 1451    -1         result = result.concat(Array.prototype.slice.call(referrers));
 1452    -1     }
 1453    -1     return result;
 1454    -1 };
 1455    -1 
 1456    -1 /**
 1457    -1  * Gets elements which this element refers to in the given attribute.
 1458    -1  * @param {!string} attributeName Name of an ARIA attribute, e.g. 'aria-owns'.
 1459    -1  * @param {Element} element The DOM element which has the ARIA attribute.
 1460    -1  * @return {!Array.<Element>} An array of elements that are referred to by this element.
 1461    -1  * @example
 1462    -1  *    var owner = document.body.appendChild(document.createElement("div"));
 1463    -1  *    var owned = document.body.appendChild(document.createElement("div"));
 1464    -1  *    owner.setAttribute("aria-owns", "kungfu");
 1465    -1  *    owned.setAttribute("id", "kungfu");
 1466    -1  *    console.log(axs.utils.getIdReferents("aria-owns", owner)[0] === owned);  // This will log 'true'
 1467    -1  */
 1468    -1 axs.utils.getIdReferents = function(attributeName, element) {
 1469    -1     var result = [];
 1470    -1     var propertyKey = attributeName.replace(/^aria-/, '');
 1471    -1     var property = axs.constants.ARIA_PROPERTIES[propertyKey];
 1472    -1     if (!property || !element.hasAttribute(attributeName))
 1473    -1         return result;
 1474    -1     var propertyType = property.valueType;
 1475    -1     if (propertyType === 'idref_list' || propertyType === 'idref') {
 1476    -1         var ownerDocument = element.ownerDocument;
 1477    -1         var ids = element.getAttribute(attributeName);
 1478    -1         ids = ids.split(/\s+/);
 1479    -1         for (var i = 0, len = ids.length; i < len; i++) {
 1480    -1             var next = ownerDocument.getElementById(ids[i]);
 1481    -1             if (next) {
 1482    -1                 result[result.length] = next;
 1483    -1             }
 1484    -1         }
 1485    -1     }
 1486    -1     return result;
 1487    -1 };
 1488    -1 
 1489    -1 /**
 1490    -1  * Gets a subset of 'axs.constants.ARIA_PROPERTIES' filtered by 'valueType'.
 1491    -1  * @param {!Array.<string>} valueTypes Types to match, e.g. ['idref_list'].
 1492    -1  * @return {Object.<string, Object>} axs.constants.ARIA_PROPERTIES which match.
 1493    -1  */
 1494    -1 axs.utils.getAriaPropertiesByValueType = function(valueTypes) {
 1495    -1     var result = {};
 1496    -1     for (var propertyName in axs.constants.ARIA_PROPERTIES) {
 1497    -1         var property = axs.constants.ARIA_PROPERTIES[propertyName];
 1498    -1         if (property && valueTypes.indexOf(property.valueType) >= 0) {
 1499    -1             result[propertyName] = property;
 1500    -1         }
 1501    -1     }
 1502    -1     return result;
 1503    -1 };
 1504    -1 
 1505    -1 /**
 1506    -1  * Builds a selector that matches an element with any of these ARIA properties.
 1507    -1  * @param {Object.<string, Object>} ariaProperties axs.constants.ARIA_PROPERTIES
 1508    -1  * @return {!string} The selector.
 1509    -1  */
 1510    -1 axs.utils.getSelectorForAriaProperties = function(ariaProperties) {
 1511    -1     var propertyNames = Object.keys(/** @type {!Object} */(ariaProperties));
 1512    -1     var result = propertyNames.map(function(propertyName) {
 1513    -1         return '[aria-' + propertyName + ']';
 1514    -1     });
 1515    -1     result.sort();  // facilitates reading long selectors and unit testing
 1516    -1     return result.join(',');
 1517    -1 };
 1518    -1 
 1519    -1 /**
 1520    -1  * Finds descendants of this element which implement the given ARIA role.
 1521    -1  * Will look for descendants with implicit or explicit role.
 1522    -1  * @param {Element} element an HTML DOM element.
 1523    -1  * @param {string} role The role you seek.
 1524    -1  * @return {!Array.<Element>} An array of matching elements.
 1525    -1  * @example
 1526    -1  *    var container = document.createElement("div");
 1527    -1  *    var button = document.createElement("button");
 1528    -1  *    var span = document.createElement("span");
 1529    -1  *    span.setAttribute("role", "button");
 1530    -1  *    container.appendChild(button);
 1531    -1  *    container.appendChild(span);
 1532    -1  *    var result = axs.utils.findDescendantsWithRole(container, "button");  // result is an array containing both 'button' and 'span'
 1533    -1  */
 1534    -1 axs.utils.findDescendantsWithRole = function(element, role) {
 1535    -1     if (!(element && role))
 1536    -1         return [];
 1537    -1     var selector = axs.properties.getSelectorForRole(role);
 1538    -1     if (!selector)
 1539    -1         return [];
 1540    -1     var result = element.querySelectorAll(selector);
 1541    -1     if (result) {  // Convert NodeList to Array; methinks 80/20 that's what callers want.
 1542    -1         result = Array.prototype.map.call(result, function(item) { return item; });
 1543    -1     } else {
 1544    -1         return [];
 1545    -1     }
 1546    -1     return result;
 1547    -1 };
 1548    -1 
 1549    -1 },{}],4:[function(require,module,exports){
 1550    -1 // Copyright 2013 Google Inc.
 1551    -1 //
 1552    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 1553    -1 // you may not use this file except in compliance with the License.
 1554    -1 // You may obtain a copy of the License at
 1555    -1 //
 1556    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1557    -1 //
 1558    -1 // Unless required by applicable law or agreed to in writing, software
 1559    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 1560    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 1561    -1 // See the License for the specific language governing permissions and
 1562    -1 // limitations under the License.
 1563    -1 
 1564    -1 goog.provide('axs.browserUtils');
 1565    -1 
 1566    -1 /**
 1567    -1  * Use Webkit matcher when matches() is not supported.
 1568    -1  * Use Firefox matcher when Webkit is not supported.
 1569    -1  * Use IE matcher when neither webkit nor Firefox supported.
 1570    -1  * @param {Element} element
 1571    -1  * @param {string} selector
 1572    -1  * @return {boolean} true if the element matches the selector
 1573    -1  */
 1574    -1 axs.browserUtils.matchSelector = function(element, selector) {
 1575    -1     if (element.matches)
 1576    -1         return element.matches(selector);
 1577    -1     if (element.webkitMatchesSelector)
 1578    -1         return element.webkitMatchesSelector(selector);
 1579    -1     if (element.mozMatchesSelector)
 1580    -1         return element.mozMatchesSelector(selector);
 1581    -1     if (element.msMatchesSelector)
 1582    -1         return element.msMatchesSelector(selector);
 1583    -1     return false;
 1584    -1 };
 1585    -1 
 1586    -1 },{}],5:[function(require,module,exports){
 1587    -1 // Copyright 2015 Google Inc.
 1588    -1 //
 1589    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 1590    -1 // you may not use this file except in compliance with the License.
 1591    -1 // You may obtain a copy of the License at
 1592    -1 //
 1593    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 1594    -1 //
 1595    -1 // Unless required by applicable law or agreed to in writing, software
 1596    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 1597    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 1598    -1 // See the License for the specific language governing permissions and
 1599    -1 // limitations under the License.
 1600    -1 
 1601    -1 goog.provide('axs.color');
 1602    -1 goog.provide('axs.color.Color');
 1603    -1 
 1604    -1 /**
 1605    -1  * @constructor
 1606    -1  * @param {number} red
 1607    -1  * @param {number} green
 1608    -1  * @param {number} blue
 1609    -1  * @param {number} alpha
 1610    -1  */
 1611    -1 axs.color.Color = function(red, green, blue, alpha) {
 1612    -1     /** @type {number} */
 1613    -1     this.red = red;
 1614    -1 
 1615    -1     /** @type {number} */
 1616    -1     this.green = green;
 1617    -1 
 1618    -1     /** @type {number} */
 1619    -1     this.blue = blue;
 1620    -1 
 1621    -1     /** @type {number} */
 1622    -1     this.alpha = alpha;
 1623    -1 };
 1624    -1 
 1625    -1 /**
 1626    -1  * @constructor
 1627    -1  * See https://en.wikipedia.org/wiki/YCbCr for more information.
 1628    -1  * @param {Array.<number>} coords The YCbCr values as a 3 element array, in the order [luma, Cb, Cr].
 1629    -1  *     All numbers are in the range [0, 1].
 1630    -1  */
 1631    -1 axs.color.YCbCr = function(coords) {
 1632    -1     /** @type {number} */
 1633    -1     this.luma = this.z = coords[0];
 1634    -1 
 1635    -1     /** @type {number} */
 1636    -1     this.Cb = this.x = coords[1];
 1637    -1 
 1638    -1     /** @type {number} */
 1639    -1     this.Cr = this.y = coords[2];
 1640    -1 };
 1641    -1 
 1642    -1 axs.color.YCbCr.prototype = {
 1643    -1     /**
 1644    -1      * @param {number} scalar
 1645    -1      * @return {axs.color.YCbCr} This color multiplied by the given scalar
 1646    -1      */
 1647    -1     multiply: function(scalar) {
 1648    -1         var result = [ this.luma * scalar, this.Cb * scalar, this.Cr * scalar ];
 1649    -1         return new axs.color.YCbCr(result);
 1650    -1     },
 1651    -1 
 1652    -1     /**
 1653    -1      * @param {axs.color.YCbCr} other
 1654    -1      * @return {axs.color.YCbCr} This plus other
 1655    -1      */
 1656    -1     add: function(other) {
 1657    -1         var result = [ this.luma + other.luma, this.Cb + other.Cb, this.Cr + other.Cr ];
 1658    -1         return new axs.color.YCbCr(result);
 1659    -1     },
 1660    -1 
 1661    -1     /**
 1662    -1      * @param {axs.color.YCbCr} other
 1663    -1      * @return {axs.color.YCbCr} This minus other
 1664    -1      */
 1665    -1     subtract: function(other) {
 1666    -1         var result = [ this.luma - other.luma, this.Cb - other.Cb, this.Cr - other.Cr ];
 1667    -1         return new axs.color.YCbCr(result);
 1668    -1     }
 1669    -1 
 1670    -1 };
 1671    -1 
 1672    -1 
 1673    -1 /**
 1674    -1  * Calculate the contrast ratio between the two given colors. Returns the ratio
 1675    -1  * to 1, for example for two two colors with a contrast ratio of 21:1, this
 1676    -1  * function will return 21.
 1677    -1  * @param {axs.color.Color} fgColor
 1678    -1  * @param {axs.color.Color} bgColor
 1679    -1  * @return {!number}
 1680    -1  */
 1681    -1 axs.color.calculateContrastRatio = function(fgColor, bgColor) {
 1682    -1     if (fgColor.alpha < 1)
 1683    -1         fgColor = axs.color.flattenColors(fgColor, bgColor);
 1684    -1 
 1685    -1     var fgLuminance = axs.color.calculateLuminance(fgColor);
 1686    -1     var bgLuminance = axs.color.calculateLuminance(bgColor);
 1687    -1     var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) /
 1688    -1         (Math.min(fgLuminance, bgLuminance) + 0.05);
 1689    -1     return contrastRatio;
 1690    -1 };
 1691    -1 
 1692    -1 /**
 1693    -1  * Calculate the luminance of the given color using the WCAG algorithm.
 1694    -1  * @param {axs.color.Color} color
 1695    -1  * @return {number}
 1696    -1  */
 1697    -1 axs.color.calculateLuminance = function(color) {
 1698    -1 /*    var rSRGB = color.red / 255;
 1699    -1     var gSRGB = color.green / 255;
 1700    -1     var bSRGB = color.blue / 255;
 1701    -1 
 1702    -1     var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);
 1703    -1     var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);
 1704    -1     var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);
 1705    -1 
 1706    -1     return 0.2126 * r + 0.7152 * g + 0.0722 * b; */
 1707    -1     var ycc = axs.color.toYCbCr(color);
 1708    -1     return ycc.luma;
 1709    -1 };
 1710    -1 
 1711    -1 /**
 1712    -1  * Compute the luminance ratio between two luminance values.
 1713    -1  * @param {number} luminance1
 1714    -1  * @param {number} luminance2
 1715    -1  */
 1716    -1 axs.color.luminanceRatio = function(luminance1, luminance2) {
 1717    -1     return (Math.max(luminance1, luminance2) + 0.05) /
 1718    -1         (Math.min(luminance1, luminance2) + 0.05);
 1719    -1 };
 1720    -1 
 1721    -1 /**
 1722    -1  * @param {string} colorString The color string from CSS.
 1723    -1  * @return {?axs.color.Color}
 1724    -1  */
 1725    -1 axs.color.parseColor = function(colorString) {
 1726    -1     if (colorString === "transparent") {
 1727    -1         return new axs.color.Color(0, 0, 0, 0);
 1728    -1     }
 1729    -1     var rgbRegex = /^rgb\((\d+), (\d+), (\d+)\)$/;
 1730    -1     var match = colorString.match(rgbRegex);
 1731    -1 
 1732    -1     if (match) {
 1733    -1         var r = parseInt(match[1], 10);
 1734    -1         var g = parseInt(match[2], 10);
 1735    -1         var b = parseInt(match[3], 10);
 1736    -1         var a = 1;
 1737    -1         return new axs.color.Color(r, g, b, a);
 1738    -1     }
 1739    -1 
 1740    -1     var rgbaRegex = /^rgba\((\d+), (\d+), (\d+), (\d*(\.\d+)?)\)/;
 1741    -1     match = colorString.match(rgbaRegex);
 1742    -1     if (match) {
 1743    -1         var r = parseInt(match[1], 10);
 1744    -1         var g = parseInt(match[2], 10);
 1745    -1         var b = parseInt(match[3], 10);
 1746    -1         var a = parseFloat(match[4]);
 1747    -1         return new axs.color.Color(r, g, b, a);
 1748    -1     }
 1749    -1 
 1750    -1     return null;
 1751    -1 };
 1752    -1 
 1753    -1 /**
 1754    -1  * @param {number} value The value of a color channel, 0 <= value <= 0xFF
 1755    -1  * @return {!string}
 1756    -1  */
 1757    -1 axs.color.colorChannelToString = function(value) {
 1758    -1     value = Math.round(value);
 1759    -1     if (value <= 0xF)
 1760    -1         return '0' + value.toString(16);
 1761    -1     return value.toString(16);
 1762    -1 };
 1763    -1 
 1764    -1 /**
 1765    -1  * @param {axs.color.Color} color
 1766    -1  * @return {!string}
 1767    -1  */
 1768    -1 axs.color.colorToString = function(color) {
 1769    -1     if (color.alpha == 1) {
 1770    -1          return '#' + axs.color.colorChannelToString(color.red) +
 1771    -1          axs.color.colorChannelToString(color.green) + axs.color.colorChannelToString(color.blue);
 1772    -1     }
 1773    -1     else
 1774    -1         return 'rgba(' + [color.red, color.green, color.blue, color.alpha].join(',') + ')';
 1775    -1 };
 1776    -1 
 1777    -1 /**
 1778    -1  * Compute a desired luminance given a given luminance and a desired contrast ratio.
 1779    -1  * @param {number} luminance The given luminance.
 1780    -1  * @param {number} contrast The desired contrast ratio.
 1781    -1  * @param {boolean} higher Whether the desired luminance is higher or lower than the given luminance.
 1782    -1  * @return {number} The desired luminance.
 1783    -1  */
 1784    -1 axs.color.luminanceFromContrastRatio = function(luminance, contrast, higher) {
 1785    -1     if (higher) {
 1786    -1         var newLuminance = (luminance + 0.05) * contrast - 0.05;
 1787    -1         return newLuminance;
 1788    -1     } else {
 1789    -1         var newLuminance = (luminance + 0.05) / contrast - 0.05;
 1790    -1         return newLuminance;
 1791    -1     }
 1792    -1 };
 1793    -1 
 1794    -1 /**
 1795    -1  * Given a color in YCbCr format and a desired luminance, pick a new color with the desired luminance which is
 1796    -1  * as close as possible to the original color.
 1797    -1  * @param {axs.color.YCbCr} ycc The original color in YCbCr form.
 1798    -1  * @param {number} luma The desired luminance
 1799    -1  * @return {!axs.color.Color} A new color in RGB.
 1800    -1  */
 1801    -1 axs.color.translateColor = function(ycc, luma) {
 1802    -1     var endpoint = (luma > ycc.luma) ? axs.color.WHITE_YCC : axs.color.BLACK_YCC;
 1803    -1     var cubeFaces = (endpoint == axs.color.WHITE_YCC) ? axs.color.YCC_CUBE_FACES_WHITE
 1804    -1                                                       : axs.color.YCC_CUBE_FACES_BLACK;
 1805    -1 
 1806    -1     var a = new axs.color.YCbCr([0, ycc.Cb, ycc.Cr]);
 1807    -1     var b = new axs.color.YCbCr([1, ycc.Cb, ycc.Cr]);
 1808    -1     var line = { a: a, b: b };
 1809    -1 
 1810    -1     var intersection = null;
 1811    -1     for (var i = 0; i < cubeFaces.length; i++) {
 1812    -1         var cubeFace = cubeFaces[i];
 1813    -1         intersection = axs.color.findIntersection(line, cubeFace);
 1814    -1         // If intersection within [0, 1] in Z axis, it is within the cube.
 1815    -1         if (intersection.z >= 0 && intersection.z <= 1)
 1816    -1             break;
 1817    -1     }
 1818    -1     if (!intersection) {
 1819    -1         // Should never happen
 1820    -1         throw "Couldn't find intersection with YCbCr color cube for Cb=" + ycc.Cb + ", Cr=" + ycc.Cr + ".";
 1821    -1     }
 1822    -1     if (intersection.x != ycc.x || intersection.y != ycc.y) {
 1823    -1         // Should never happen
 1824    -1         throw "Intersection has wrong Cb/Cr values.";
 1825    -1     }
 1826    -1 
 1827    -1     // If intersection.luma is closer to endpoint than desired luma, then luma is inside cube
 1828    -1     // and we can immediately return new value.
 1829    -1     if (Math.abs(endpoint.luma - intersection.luma) < Math.abs(endpoint.luma - luma)) {
 1830    -1         var translatedColor = [luma, ycc.Cb, ycc.Cr];
 1831    -1         return axs.color.fromYCbCrArray(translatedColor);
 1832    -1     }
 1833    -1 
 1834    -1     // Otherwise, translate from intersection towards white/black such that luma is correct.
 1835    -1     var dLuma = luma - intersection.luma;
 1836    -1     var scale = dLuma / (endpoint.luma - intersection.luma);
 1837    -1     var translatedColor = [ luma,
 1838    -1                             intersection.Cb - (intersection.Cb * scale),
 1839    -1                             intersection.Cr - (intersection.Cr * scale) ];
 1840    -1 
 1841    -1     return axs.color.fromYCbCrArray(translatedColor);
 1842    -1 };
 1843    -1 
 1844    -1 /** @typedef {{fg: string, bg: string, contrast: string}} */
 1845    -1 axs.color.SuggestedColors;
 1846    -1 
 1847    -1 /**
 1848    -1  * @param {axs.color.Color} bgColor
 1849    -1  * @param {axs.color.Color} fgColor
 1850    -1  * @param {Object.<string, number>} desiredContrastRatios A map of label to desired contrast ratio.
 1851    -1  * @return {Object.<string, axs.color.SuggestedColors>}
 1852    -1  */
 1853    -1 axs.color.suggestColors = function(bgColor, fgColor, desiredContrastRatios) {
 1854    -1     var colors = {};
 1855    -1     var bgLuminance = axs.color.calculateLuminance(bgColor);
 1856    -1     var fgLuminance = axs.color.calculateLuminance(fgColor);
 1857    -1 
 1858    -1     var fgLuminanceIsHigher = fgLuminance > bgLuminance;
 1859    -1     var fgYCbCr = axs.color.toYCbCr(fgColor);
 1860    -1     var bgYCbCr = axs.color.toYCbCr(bgColor);
 1861    -1     for (var desiredLabel in desiredContrastRatios) {
 1862    -1         var desiredContrast = desiredContrastRatios[desiredLabel];
 1863    -1 
 1864    -1         var desiredFgLuminance = axs.color.luminanceFromContrastRatio(bgLuminance, desiredContrast + 0.02, fgLuminanceIsHigher);
 1865    -1         if (desiredFgLuminance <= 1 && desiredFgLuminance >= 0) {
 1866    -1             var newFgColor = axs.color.translateColor(fgYCbCr, desiredFgLuminance);
 1867    -1             var newContrastRatio = axs.color.calculateContrastRatio(newFgColor, bgColor);
 1868    -1             var suggestedColors = {};
 1869    -1             suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(newFgColor));
 1870    -1             suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(bgColor));
 1871    -1             suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));
 1872    -1             colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);
 1873    -1             continue;
 1874    -1         }
 1875    -1 
 1876    -1         var desiredBgLuminance = axs.color.luminanceFromContrastRatio(fgLuminance, desiredContrast + 0.02, !fgLuminanceIsHigher);
 1877    -1         if (desiredBgLuminance <= 1 && desiredBgLuminance >= 0) {
 1878    -1             var newBgColor = axs.color.translateColor(bgYCbCr, desiredBgLuminance);
 1879    -1             var newContrastRatio = axs.color.calculateContrastRatio(fgColor, newBgColor);
 1880    -1             var suggestedColors = {};
 1881    -1             suggestedColors.bg = /** @type {!string} */ (axs.color.colorToString(newBgColor));
 1882    -1             suggestedColors.fg = /** @type {!string} */ (axs.color.colorToString(fgColor));
 1883    -1             suggestedColors.contrast = /** @type {!string} */ (newContrastRatio.toFixed(2));
 1884    -1             colors[desiredLabel] = /** @type {axs.color.SuggestedColors} */ (suggestedColors);
 1885    -1         }
 1886    -1     }
 1887    -1     return colors;
 1888    -1 };
 1889    -1 
 1890    -1 /**
 1891    -1  * Combine the two given color according to alpha blending.
 1892    -1  * @param {axs.color.Color} fgColor
 1893    -1  * @param {axs.color.Color} bgColor
 1894    -1  * @return {axs.color.Color}
 1895    -1  */
 1896    -1 axs.color.flattenColors = function(fgColor, bgColor) {
 1897    -1     var alpha = fgColor.alpha;
 1898    -1     var r = ((1 - alpha) * bgColor.red) + (alpha * fgColor.red);
 1899    -1     var g = ((1 - alpha) * bgColor.green) + (alpha * fgColor.green);
 1900    -1     var b = ((1 - alpha) * bgColor.blue) + (alpha * fgColor.blue);
 1901    -1     var a = fgColor.alpha + (bgColor.alpha * (1 - fgColor.alpha));
 1902    -1 
 1903    -1     return new axs.color.Color(r, g, b, a);
 1904    -1 };
 1905    -1 
 1906    -1 /**
 1907    -1  * Multiply the given vector by the given matrix.
 1908    -1  * @param {Array.<Array.<number>>} matrix A 3x3 matrix
 1909    -1  * @param {Array.<number>} vector A 3-element vector
 1910    -1  * @return {Array.<number>} A 3-element vector
 1911    -1  */
 1912    -1 axs.color.multiplyMatrixVector = function(matrix, vector) {
 1913    -1     var a = matrix[0][0];
 1914    -1     var b = matrix[0][1];
 1915    -1     var c = matrix[0][2];
 1916    -1     var d = matrix[1][0];
 1917    -1     var e = matrix[1][1];
 1918    -1     var f = matrix[1][2];
 1919    -1     var g = matrix[2][0];
 1920    -1     var h = matrix[2][1];
 1921    -1     var k = matrix[2][2];
 1922    -1 
 1923    -1     var x = vector[0];
 1924    -1     var y = vector[1];
 1925    -1     var z = vector[2];
 1926    -1 
 1927    -1     return [
 1928    -1         a*x + b*y + c*z,
 1929    -1         d*x + e*y + f*z,
 1930    -1         g*x + h*y + k*z
 1931    -1     ];
 1932    -1 };
 1933    -1 
 1934    -1 /**
 1935    -1  * Convert a given RGB color to YCbCr.
 1936    -1  * @param {axs.color.Color} color
 1937    -1  * @return {axs.color.YCbCr}
 1938    -1  */
 1939    -1 axs.color.toYCbCr = function(color) {
 1940    -1     var rSRGB = color.red / 255;
 1941    -1     var gSRGB = color.green / 255;
 1942    -1     var bSRGB = color.blue / 255;
 1943    -1 
 1944    -1     var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055)/1.055), 2.4);
 1945    -1     var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055)/1.055), 2.4);
 1946    -1     var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055)/1.055), 2.4);
 1947    -1 
 1948    -1     return new axs.color.YCbCr(axs.color.multiplyMatrixVector(axs.color.YCC_MATRIX, [r, g, b]));
 1949    -1 };
 1950    -1 
 1951    -1 /**
 1952    -1  * @param {axs.color.YCbCr} ycc
 1953    -1  * @return {!axs.color.Color}
 1954    -1  */
 1955    -1 axs.color.fromYCbCr = function(ycc) {
 1956    -1     return axs.color.fromYCbCrArray([ycc.luma, ycc.Cb, ycc.Cr]);
 1957    -1 };
 1958    -1 
 1959    -1 /**
 1960    -1  * Convert a color from a YCbCr color (as a vector) to an RGB color
 1961    -1  * @param {Array.<number>} yccArray
 1962    -1  * @return {!axs.color.Color}
 1963    -1  */
 1964    -1 axs.color.fromYCbCrArray = function(yccArray) {
 1965    -1     var rgb = axs.color.multiplyMatrixVector(axs.color.INVERTED_YCC_MATRIX, yccArray);
 1966    -1 
 1967    -1     var r = rgb[0];
 1968    -1     var g = rgb[1];
 1969    -1     var b = rgb[2];
 1970    -1     var rSRGB = r <= 0.00303949 ? (r * 12.92) : (Math.pow(r, (1/2.4)) * 1.055) - 0.055;
 1971    -1     var gSRGB = g <= 0.00303949 ? (g * 12.92) : (Math.pow(g, (1/2.4)) * 1.055) - 0.055;
 1972    -1     var bSRGB = b <= 0.00303949 ? (b * 12.92) : (Math.pow(b, (1/2.4)) * 1.055) - 0.055;
 1973    -1 
 1974    -1     var red = Math.min(Math.max(Math.round(rSRGB * 255), 0), 255);
 1975    -1     var green = Math.min(Math.max(Math.round(gSRGB * 255), 0), 255);
 1976    -1     var blue = Math.min(Math.max(Math.round(bSRGB * 255), 0), 255);
 1977    -1 
 1978    -1     return new axs.color.Color(red, green, blue, 1);
 1979    -1 };
 1980    -1 
 1981    -1 /**
 1982    -1  * Returns an RGB to YCbCr conversion matrix for the given kR, kB constants.
 1983    -1  * @param {number} kR
 1984    -1  * @param {number} kB
 1985    -1  * @return {Array.<Array.<number>>}
 1986    -1  */
 1987    -1 axs.color.RGBToYCbCrMatrix = function(kR, kB) {
 1988    -1     return [
 1989    -1         [
 1990    -1             kR,
 1991    -1             (1 - kR - kB),
 1992    -1             kB
 1993    -1         ],
 1994    -1         [
 1995    -1             -kR/(2 - 2*kB),
 1996    -1             (kR + kB - 1)/(2 - 2*kB),
 1997    -1             (1 - kB)/(2 - 2*kB)
 1998    -1         ],
 1999    -1         [
 2000    -1             (1 - kR)/(2 - 2*kR),
 2001    -1             (kR + kB - 1)/(2 - 2*kR),
 2002    -1             -kB/(2 - 2*kR)
 2003    -1         ]
 2004    -1     ];
 2005    -1 };
 2006    -1 
 2007    -1 /**
 2008    -1  * Return the inverse of the given 3x3 matrix.
 2009    -1  * @param {Array.<Array.<number>>} matrix
 2010    -1  * @return Array.<Array.<number>> The inverse of the given matrix.
 2011    -1  */
 2012    -1 axs.color.invert3x3Matrix = function(matrix) {
 2013    -1     var a = matrix[0][0];
 2014    -1     var b = matrix[0][1];
 2015    -1     var c = matrix[0][2];
 2016    -1     var d = matrix[1][0];
 2017    -1     var e = matrix[1][1];
 2018    -1     var f = matrix[1][2];
 2019    -1     var g = matrix[2][0];
 2020    -1     var h = matrix[2][1];
 2021    -1     var k = matrix[2][2];
 2022    -1 
 2023    -1     var A = (e*k - f*h);
 2024    -1     var B = (f*g - d*k);
 2025    -1     var C = (d*h - e*g);
 2026    -1     var D = (c*h - b*k);
 2027    -1     var E = (a*k - c*g);
 2028    -1     var F = (g*b - a*h);
 2029    -1     var G = (b*f - c*e);
 2030    -1     var H = (c*d - a*f);
 2031    -1     var K = (a*e - b*d);
 2032    -1 
 2033    -1     var det = a * (e*k - f*h) - b * (k*d - f*g) + c * (d*h - e*g);
 2034    -1     var z = 1/det;
 2035    -1 
 2036    -1     return axs.color.scalarMultiplyMatrix([
 2037    -1         [ A, D, G ],
 2038    -1         [ B, E, H ],
 2039    -1         [ C, F, K ]
 2040    -1     ], z);
 2041    -1 };
 2042    -1 
 2043    -1 /** @typedef {{ a: axs.color.YCbCr, b: axs.color.YCbCr }} */
 2044    -1 axs.color.Line;
 2045    -1 
 2046    -1 /** @typedef {{ p0: axs.color.YCbCr, p1: axs.color.YCbCr, p2: axs.color.YCbCr }} */
 2047    -1 axs.color.Plane;
 2048    -1 
 2049    -1 /**
 2050    -1  * Find the intersection between a line and a plane using
 2051    -1  * http://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Parametric_form
 2052    -1  * @param {axs.color.Line} l
 2053    -1  * @param {axs.color.Plane} p
 2054    -1  * @return {axs.color.YCbCr}
 2055    -1  */
 2056    -1 axs.color.findIntersection = function(l, p) {
 2057    -1     var lhs = [ l.a.x - p.p0.x, l.a.y - p.p0.y, l.a.z - p.p0.z ];
 2058    -1 
 2059    -1     var matrix = [ [ l.a.x - l.b.x, p.p1.x - p.p0.x, p.p2.x - p.p0.x ],
 2060    -1                    [ l.a.y - l.b.y, p.p1.y - p.p0.y, p.p2.y - p.p0.y ],
 2061    -1                    [ l.a.z - l.b.z, p.p1.z - p.p0.z, p.p2.z - p.p0.z ] ];
 2062    -1     var invertedMatrix = axs.color.invert3x3Matrix(matrix);
 2063    -1 
 2064    -1     var tuv = axs.color.multiplyMatrixVector(invertedMatrix, lhs);
 2065    -1     var t = tuv[0];
 2066    -1 
 2067    -1     var result = l.a.add(l.b.subtract(l.a).multiply(t));
 2068    -1     return result;
 2069    -1 };
 2070    -1 
 2071    -1 /**
 2072    -1  * Multiply a matrix by a scalar.
 2073    -1  * @param {Array.<Array.<number>>} matrix A 3x3 matrix.
 2074    -1  * @param {number} scalar
 2075    -1  * @return {Array.<Array.<number>>}
 2076    -1  */
 2077    -1 axs.color.scalarMultiplyMatrix = function(matrix, scalar) {
 2078    -1     var result = [];
 2079    -1 
 2080    -1     for (var i = 0; i < 3; i++)
 2081    -1       result[i] = axs.color.scalarMultiplyVector(matrix[i], scalar);
 2082    -1 
 2083    -1     return result;
 2084    -1 };
 2085    -1 
 2086    -1 /**
 2087    -1  * Multiply a vector by a scalar.
 2088    -1  * @param {Array.<number>} vector
 2089    -1  * @param {number} scalar
 2090    -1  * @return {Array.<number>} vector
 2091    -1  */
 2092    -1 axs.color.scalarMultiplyVector = function(vector, scalar) {
 2093    -1     var result = [];
 2094    -1     for (var i = 0; i < vector.length; i++)
 2095    -1         result[i] = vector[i] * scalar;
 2096    -1     return result;
 2097    -1 };
 2098    -1 
 2099    -1 axs.color.kR = 0.2126;
 2100    -1 axs.color.kB = 0.0722;
 2101    -1 axs.color.YCC_MATRIX = axs.color.RGBToYCbCrMatrix(axs.color.kR, axs.color.kB);
 2102    -1 axs.color.INVERTED_YCC_MATRIX = axs.color.invert3x3Matrix(axs.color.YCC_MATRIX);
 2103    -1 
 2104    -1 axs.color.BLACK = new axs.color.Color(0, 0, 0, 1.0);
 2105    -1 axs.color.BLACK_YCC = axs.color.toYCbCr(axs.color.BLACK);
 2106    -1 axs.color.WHITE = new axs.color.Color(255, 255, 255, 1.0);
 2107    -1 axs.color.WHITE_YCC = axs.color.toYCbCr(axs.color.WHITE);
 2108    -1 axs.color.RED = new axs.color.Color(255, 0, 0, 1.0);
 2109    -1 axs.color.RED_YCC = axs.color.toYCbCr(axs.color.RED);
 2110    -1 axs.color.GREEN = new axs.color.Color(0, 255, 0, 1.0);
 2111    -1 axs.color.GREEN_YCC = axs.color.toYCbCr(axs.color.GREEN);
 2112    -1 axs.color.BLUE = new axs.color.Color(0, 0, 255, 1.0);
 2113    -1 axs.color.BLUE_YCC = axs.color.toYCbCr(axs.color.BLUE);
 2114    -1 axs.color.CYAN = new axs.color.Color(0, 255, 255, 1.0);
 2115    -1 axs.color.CYAN_YCC = axs.color.toYCbCr(axs.color.CYAN);
 2116    -1 axs.color.MAGENTA = new axs.color.Color(255, 0, 255, 1.0);
 2117    -1 axs.color.MAGENTA_YCC = axs.color.toYCbCr(axs.color.MAGENTA);
 2118    -1 axs.color.YELLOW = new axs.color.Color(255, 255, 0, 1.0);
 2119    -1 axs.color.YELLOW_YCC = axs.color.toYCbCr(axs.color.YELLOW);
 2120    -1 
 2121    -1 axs.color.YCC_CUBE_FACES_BLACK = [ { p0: axs.color.BLACK_YCC, p1: axs.color.RED_YCC, p2: axs.color.GREEN_YCC },
 2122    -1                                    { p0: axs.color.BLACK_YCC, p1: axs.color.GREEN_YCC, p2: axs.color.BLUE_YCC },
 2123    -1                                    { p0: axs.color.BLACK_YCC, p1: axs.color.BLUE_YCC, p2: axs.color.RED_YCC } ];
 2124    -1 axs.color.YCC_CUBE_FACES_WHITE = [ { p0: axs.color.WHITE_YCC, p1: axs.color.CYAN_YCC, p2: axs.color.MAGENTA_YCC },
 2125    -1                                    { p0: axs.color.WHITE_YCC, p1: axs.color.MAGENTA_YCC, p2: axs.color.YELLOW_YCC },
 2126    -1                                    { p0: axs.color.WHITE_YCC, p1: axs.color.YELLOW_YCC, p2: axs.color.CYAN_YCC } ];
 2127    -1 
 2128    -1 },{}],6:[function(require,module,exports){
 2129    -1 // Copyright 2012 Google Inc.
 2130    -1 //
 2131    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 2132    -1 // you may not use this file except in compliance with the License.
 2133    -1 // You may obtain a copy of the License at
 2134    -1 //
 2135    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 2136    -1 //
 2137    -1 // Unless required by applicable law or agreed to in writing, software
 2138    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 2139    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 2140    -1 // See the License for the specific language governing permissions and
 2141    -1 // limitations under the License.
 2142    -1 
 2143    -1 goog.provide('axs.constants');
 2144    -1 goog.provide('axs.constants.AuditResult');
 2145    -1 goog.provide('axs.constants.Severity');
 2146    -1 
 2147    -1 /** @type {Object.<string, Object>} */
 2148    -1 axs.constants.ARIA_ROLES = {
 2149    -1     "alert": {
 2150    -1         "namefrom": [ "author" ],
 2151    -1         "parent": [ "region" ]
 2152    -1     },
 2153    -1     "alertdialog": {
 2154    -1         "namefrom": [ "author" ],
 2155    -1         "namerequired": true,
 2156    -1         "parent": [ "alert", "dialog" ]
 2157    -1     },
 2158    -1     "application": {
 2159    -1         "namefrom": [ "author" ],
 2160    -1         "namerequired": true,
 2161    -1         "parent": [ "landmark" ]
 2162    -1     },
 2163    -1     "article": {
 2164    -1         "namefrom": [ "author" ],
 2165    -1         "parent": [ "document", "region" ]
 2166    -1     },
 2167    -1     "banner": {
 2168    -1         "namefrom": [ "author" ],
 2169    -1         "parent": [ "landmark" ]
 2170    -1     },
 2171    -1     "button": {
 2172    -1         "childpresentational": true,
 2173    -1         "namefrom": [ "contents", "author" ],
 2174    -1         "namerequired": true,
 2175    -1         "parent": [ "command" ],
 2176    -1         "properties": [ "aria-expanded", "aria-pressed" ]
 2177    -1     },
 2178    -1     "checkbox": {
 2179    -1         "namefrom": [ "contents", "author" ],
 2180    -1         "namerequired": true,
 2181    -1         "parent": [ "input" ],
 2182    -1         "requiredProperties": [ "aria-checked" ],
 2183    -1         "properties": [ "aria-checked" ]
 2184    -1     },
 2185    -1     "columnheader": {
 2186    -1         "namefrom": [ "contents", "author" ],
 2187    -1         "namerequired": true,
 2188    -1         "parent": [ "gridcell", "sectionhead", "widget" ],
 2189    -1         "properties": [ "aria-sort" ],
 2190    -1         "scope": [ "row" ]
 2191    -1     },
 2192    -1     "combobox": {
 2193    -1         "mustcontain": [ "listbox", "textbox" ],
 2194    -1         "namefrom": [ "author" ],
 2195    -1         "namerequired": true,
 2196    -1         "parent": [ "select" ],
 2197    -1         "requiredProperties": [ "aria-expanded" ],
 2198    -1         "properties": [ "aria-expanded", "aria-autocomplete", "aria-required" ]
 2199    -1     },
 2200    -1     "command": {
 2201    -1         "abstract": true,
 2202    -1         "namefrom": [ "author" ],
 2203    -1         "parent": [ "widget" ]
 2204    -1     },
 2205    -1     "complementary": {
 2206    -1         "namefrom": [ "author" ],
 2207    -1         "parent": [ "landmark" ]
 2208    -1     },
 2209    -1     "composite": {
 2210    -1         "abstract": true,
 2211    -1         "childpresentational": false,
 2212    -1         "namefrom": [ "author" ],
 2213    -1         "parent": [ "widget" ],
 2214    -1         "properties": [ "aria-activedescendant" ]
 2215    -1     },
 2216    -1     "contentinfo": {
 2217    -1         "namefrom": [ "author" ],
 2218    -1         "parent": [ "landmark" ]
 2219    -1     },
 2220    -1     "definition": {
 2221    -1         "namefrom": [ "author" ],
 2222    -1         "parent": [ "section" ]
 2223    -1     },
 2224    -1     "dialog": {
 2225    -1         "namefrom": [ "author" ],
 2226    -1         "namerequired": true,
 2227    -1         "parent": [ "window" ]
 2228    -1     },
 2229    -1     "directory": {
 2230    -1         "namefrom": [ "contents", "author" ],
 2231    -1         "parent": [ "list" ]
 2232    -1     },
 2233    -1     "document": {
 2234    -1         "namefrom": [ " author" ],
 2235    -1         "namerequired": true,
 2236    -1         "parent": [ "structure" ],
 2237    -1         "properties": [ "aria-expanded" ]
 2238    -1     },
 2239    -1     "form": {
 2240    -1         "namefrom": [ "author" ],
 2241    -1         "parent": [ "landmark" ]
 2242    -1     },
 2243    -1     "grid": {
 2244    -1         "mustcontain": [ "row", "rowgroup" ],
 2245    -1         "namefrom": [ "author" ],
 2246    -1         "namerequired": true,
 2247    -1         "parent": [ "composite", "region" ],
 2248    -1         "properties": [ "aria-level", "aria-multiselectable", "aria-readonly" ]
 2249    -1     },
 2250    -1     "gridcell": {
 2251    -1         "namefrom": [ "contents", "author" ],
 2252    -1         "namerequired": true,
 2253    -1         "parent": [ "section", "widget" ],
 2254    -1         "properties": [ "aria-readonly", "aria-required", "aria-selected" ],
 2255    -1         "scope": [ "row" ]
 2256    -1     },
 2257    -1     "group": {
 2258    -1         "namefrom": [ " author" ],
 2259    -1         "parent": [ "section" ],
 2260    -1         "properties": [ "aria-activedescendant" ]
 2261    -1     },
 2262    -1     "heading": {
 2263    -1         "namerequired": true,
 2264    -1         "parent": [ "sectionhead" ],
 2265    -1         "properties": [ "aria-level" ]
 2266    -1     },
 2267    -1     "img": {
 2268    -1         "childpresentational": true,
 2269    -1         "namefrom": [ "author" ],
 2270    -1         "namerequired": true,
 2271    -1         "parent": [ "section" ]
 2272    -1     },
 2273    -1     "input": {
 2274    -1         "abstract": true,
 2275    -1         "namefrom": [ "author" ],
 2276    -1         "parent": [ "widget" ]
 2277    -1     },
 2278    -1     "landmark": {
 2279    -1         "abstract": true,
 2280    -1         "namefrom": [ "contents", "author" ],
 2281    -1         "namerequired": false,
 2282    -1         "parent": [ "region" ]
 2283    -1     },
 2284    -1     "link": {
 2285    -1         "namefrom": [ "contents", "author" ],
 2286    -1         "namerequired": true,
 2287    -1         "parent": [ "command" ],
 2288    -1         "properties": [ "aria-expanded" ]
 2289    -1     },
 2290    -1     "list": {
 2291    -1         "mustcontain": [ "group", "listitem" ],
 2292    -1         "namefrom": [ "author" ],
 2293    -1         "parent": [ "region" ]
 2294    -1     },
 2295    -1     "listbox": {
 2296    -1         "mustcontain": [ "option" ],
 2297    -1         "namefrom": [ "author" ],
 2298    -1         "namerequired": true,
 2299    -1         "parent": [ "list", "select" ],
 2300    -1         "properties": [ "aria-multiselectable", "aria-required" ]
 2301    -1     },
 2302    -1     "listitem": {
 2303    -1         "namefrom": [ "contents", "author" ],
 2304    -1         "namerequired": true,
 2305    -1         "parent": [ "section" ],
 2306    -1         "properties": [ "aria-level", "aria-posinset", "aria-setsize" ],
 2307    -1         "scope": [ "list" ]
 2308    -1     },
 2309    -1     "log": {
 2310    -1         "namefrom": [ " author" ],
 2311    -1         "namerequired": true,
 2312    -1         "parent": [ "region" ]
 2313    -1     },
 2314    -1     "main": {
 2315    -1         "namefrom": [ "author" ],
 2316    -1         "parent": [ "landmark" ]
 2317    -1     },
 2318    -1     "marquee": {
 2319    -1         "namerequired": true,
 2320    -1         "parent": [ "section" ]
 2321    -1     },
 2322    -1     "math": {
 2323    -1         "childpresentational": true,
 2324    -1         "namefrom": [ "author" ],
 2325    -1         "parent": [ "section" ]
 2326    -1     },
 2327    -1     "menu": {
 2328    -1         "mustcontain": [
 2329    -1             "group",
 2330    -1             "menuitemradio",
 2331    -1             "menuitem",
 2332    -1             "menuitemcheckbox"
 2333    -1         ],
 2334    -1         "namefrom": [ "author" ],
 2335    -1         "namerequired": true,
 2336    -1         "parent": [ "list", "select" ]
 2337    -1     },
 2338    -1     "menubar": {
 2339    -1         "namefrom": [ "author" ],
 2340    -1         "parent": [ "menu" ]
 2341    -1     },
 2342    -1     "menuitem": {
 2343    -1         "namefrom": [ "contents", "author" ],
 2344    -1         "namerequired": true,
 2345    -1         "parent": [ "command" ],
 2346    -1         "scope": [ "menu", "menubar" ]
 2347    -1     },
 2348    -1     "menuitemcheckbox": {
 2349    -1         "namefrom": [ "contents", "author" ],
 2350    -1         "namerequired": true,
 2351    -1         "parent": [ "checkbox", "menuitem" ],
 2352    -1         "scope": [ "menu", "menubar" ]
 2353    -1     },
 2354    -1     "menuitemradio": {
 2355    -1         "namefrom": [ "contents", "author" ],
 2356    -1         "namerequired": true,
 2357    -1         "parent": [ "menuitemcheckbox", "radio" ],
 2358    -1         "scope": [ "menu", "menubar" ]
 2359    -1     },
 2360    -1     "navigation": {
 2361    -1         "namefrom": [ "author" ],
 2362    -1         "parent": [ "landmark" ]
 2363    -1     },
 2364    -1     "note": {
 2365    -1         "namefrom": [ "author" ],
 2366    -1         "parent": [ "section" ]
 2367    -1     },
 2368    -1     "option": {
 2369    -1         "namefrom": [ "contents", "author" ],
 2370    -1         "namerequired": true,
 2371    -1         "parent": [ "input" ],
 2372    -1         "properties": [
 2373    -1             "aria-checked",
 2374    -1             "aria-posinset",
 2375    -1             "aria-selected",
 2376    -1             "aria-setsize"
 2377    -1         ]
 2378    -1     },
 2379    -1     "presentation": {
 2380    -1         "parent": [ "structure" ]
 2381    -1     },
 2382    -1     "progressbar": {
 2383    -1         "childpresentational": true,
 2384    -1         "namefrom": [ "author" ],
 2385    -1         "namerequired": true,
 2386    -1         "parent": [ "range" ]
 2387    -1     },
 2388    -1     "radio": {
 2389    -1         "namefrom": [ "contents", "author" ],
 2390    -1         "namerequired": true,
 2391    -1         "parent": [ "checkbox", "option" ]
 2392    -1     },
 2393    -1     "radiogroup": {
 2394    -1         "mustcontain": [ "radio" ],
 2395    -1         "namefrom": [ "author" ],
 2396    -1         "namerequired": true,
 2397    -1         "parent": [ "select" ],
 2398    -1         "properties": [ "aria-required" ]
 2399    -1     },
 2400    -1     "range": {
 2401    -1         "abstract": true,
 2402    -1         "namefrom": [ "author" ],
 2403    -1         "parent": [ "widget" ],
 2404    -1         "properties": [
 2405    -1             "aria-valuemax",
 2406    -1             "aria-valuemin",
 2407    -1             "aria-valuenow",
 2408    -1             "aria-valuetext"
 2409    -1         ]
 2410    -1     },
 2411    -1     "region": {
 2412    -1         "namefrom": [ " author" ],
 2413    -1         "parent": [ "section" ]
 2414    -1     },
 2415    -1     "roletype": {
 2416    -1         "abstract": true,
 2417    -1         "properties": [
 2418    -1             "aria-atomic",
 2419    -1             "aria-busy",
 2420    -1             "aria-controls",
 2421    -1             "aria-describedby",
 2422    -1             "aria-disabled",
 2423    -1             "aria-dropeffect",
 2424    -1             "aria-flowto",
 2425    -1             "aria-grabbed",
 2426    -1             "aria-haspopup",
 2427    -1             "aria-hidden",
 2428    -1             "aria-invalid",
 2429    -1             "aria-label",
 2430    -1             "aria-labelledby",
 2431    -1             "aria-live",
 2432    -1             "aria-owns",
 2433    -1             "aria-relevant"
 2434    -1         ]
 2435    -1     },
 2436    -1     "row": {
 2437    -1         "mustcontain": [ "columnheader", "gridcell", "rowheader" ],
 2438    -1         "namefrom": [ "contents", "author" ],
 2439    -1         "parent": [ "group", "widget" ],
 2440    -1         "properties": [ "aria-level", "aria-selected" ],
 2441    -1         "scope": [ "grid", "rowgroup", "treegrid" ]
 2442    -1     },
 2443    -1     "rowgroup": {
 2444    -1         "mustcontain": [ "row" ],
 2445    -1         "namefrom": [ "contents", "author" ],
 2446    -1         "parent": [ "group" ],
 2447    -1         "scope": [ "grid" ]
 2448    -1     },
 2449    -1     "rowheader": {
 2450    -1         "namefrom": [ "contents", "author" ],
 2451    -1         "namerequired": true,
 2452    -1         "parent": [ "gridcell", "sectionhead", "widget" ],
 2453    -1         "properties": [ "aria-sort" ],
 2454    -1         "scope": [ "row" ]
 2455    -1     },
 2456    -1     "search": {
 2457    -1         "namefrom": [ "author" ],
 2458    -1         "parent": [ "landmark" ]
 2459    -1     },
 2460    -1     "section": {
 2461    -1         "abstract": true,
 2462    -1         "namefrom": [ "contents", "author" ],
 2463    -1         "parent": [ "structure" ],
 2464    -1         "properties": [ "aria-expanded" ]
 2465    -1     },
 2466    -1     "sectionhead": {
 2467    -1         "abstract": true,
 2468    -1         "namefrom": [ "contents", "author" ],
 2469    -1         "parent": [ "structure" ],
 2470    -1         "properties": [ "aria-expanded" ]
 2471    -1     },
 2472    -1     "select": {
 2473    -1         "abstract": true,
 2474    -1         "namefrom": [ "author" ],
 2475    -1         "parent": [ "composite", "group", "input" ]
 2476    -1     },
 2477    -1     "separator": {
 2478    -1         "childpresentational": true,
 2479    -1         "namefrom": [ "author" ],
 2480    -1         "parent": [ "structure" ],
 2481    -1         "properties": [ "aria-expanded", "aria-orientation" ]
 2482    -1     },
 2483    -1     "scrollbar": {
 2484    -1         "childpresentational": true,
 2485    -1         "namefrom": [ "author" ],
 2486    -1         "namerequired": false,
 2487    -1         "parent": [ "input", "range" ],
 2488    -1         "requiredProperties": [
 2489    -1             "aria-controls",
 2490    -1             "aria-orientation",
 2491    -1             "aria-valuemax",
 2492    -1             "aria-valuemin",
 2493    -1             "aria-valuenow"
 2494    -1         ],
 2495    -1         "properties": [
 2496    -1             "aria-controls",
 2497    -1             "aria-orientation",
 2498    -1             "aria-valuemax",
 2499    -1             "aria-valuemin",
 2500    -1             "aria-valuenow"
 2501    -1         ]
 2502    -1     },
 2503    -1     "slider": {
 2504    -1         "childpresentational": true,
 2505    -1         "namefrom": [ "author" ],
 2506    -1         "namerequired": true,
 2507    -1         "parent": [ "input", "range" ],
 2508    -1         "requiredProperties": [ "aria-valuemax", "aria-valuemin", "aria-valuenow" ],
 2509    -1         "properties": [
 2510    -1             "aria-valuemax",
 2511    -1             "aria-valuemin",
 2512    -1             "aria-valuenow",
 2513    -1             "aria-orientation"
 2514    -1         ]
 2515    -1     },
 2516    -1     "spinbutton": {
 2517    -1         "namefrom": [ "author" ],
 2518    -1         "namerequired": true,
 2519    -1         "parent": [ "input", "range" ],
 2520    -1         "requiredProperties": [ "aria-valuemax", "aria-valuemin", "aria-valuenow" ],
 2521    -1         "properties": [
 2522    -1             "aria-valuemax",
 2523    -1             "aria-valuemin",
 2524    -1             "aria-valuenow",
 2525    -1             "aria-required"
 2526    -1         ]
 2527    -1     },
 2528    -1     "status": {
 2529    -1         "parent": [ "region" ]
 2530    -1     },
 2531    -1     "structure": {
 2532    -1         "abstract": true,
 2533    -1         "parent": [ "roletype" ]
 2534    -1     },
 2535    -1     "tab": {
 2536    -1         "namefrom": [ "contents", "author" ],
 2537    -1         "parent": [ "sectionhead", "widget" ],
 2538    -1         "properties": [ "aria-selected" ],
 2539    -1         "scope": [ "tablist" ]
 2540    -1     },
 2541    -1     "tablist": {
 2542    -1         "mustcontain": [ "tab" ],
 2543    -1         "namefrom": [ "author" ],
 2544    -1         "parent": [ "composite", "directory" ],
 2545    -1         "properties": [ "aria-level" ]
 2546    -1     },
 2547    -1     "tabpanel": {
 2548    -1         "namefrom": [ "author" ],
 2549    -1         "namerequired": true,
 2550    -1         "parent": [ "region" ]
 2551    -1     },
 2552    -1     "textbox": {
 2553    -1         "namefrom": [ "author" ],
 2554    -1         "namerequired": true,
 2555    -1         "parent": [ "input" ],
 2556    -1         "properties": [
 2557    -1             "aria-activedescendant",
 2558    -1             "aria-autocomplete",
 2559    -1             "aria-multiline",
 2560    -1             "aria-readonly",
 2561    -1             "aria-required"
 2562    -1         ]
 2563    -1     },
 2564    -1     "timer": {
 2565    -1         "namefrom": [ "author" ],
 2566    -1         "namerequired": true,
 2567    -1         "parent": [ "status" ]
 2568    -1     },
 2569    -1     "toolbar": {
 2570    -1         "namefrom": [ "author" ],
 2571    -1         "parent": [ "group" ]
 2572    -1     },
 2573    -1     "tooltip": {
 2574    -1         "namerequired": true,
 2575    -1         "parent": [ "section" ]
 2576    -1     },
 2577    -1     "tree": {
 2578    -1         "mustcontain": [ "group", "treeitem" ],
 2579    -1         "namefrom": [ "author" ],
 2580    -1         "namerequired": true,
 2581    -1         "parent": [ "select" ],
 2582    -1         "properties": [ "aria-multiselectable", "aria-required" ]
 2583    -1     },
 2584    -1     "treegrid": {
 2585    -1         "mustcontain": [ "row" ],
 2586    -1         "namefrom": [ "author" ],
 2587    -1         "namerequired": true,
 2588    -1         "parent": [ "grid", "tree" ]
 2589    -1     },
 2590    -1     "treeitem": {
 2591    -1         "namefrom": [ "contents", "author" ],
 2592    -1         "namerequired": true,
 2593    -1         "parent": [ "listitem", "option" ],
 2594    -1         "scope": [ "group", "tree" ]
 2595    -1     },
 2596    -1     "widget": {
 2597    -1         "abstract": true,
 2598    -1         "parent": [ "roletype" ]
 2599    -1     },
 2600    -1     "window": {
 2601    -1         "abstract": true,
 2602    -1         "namefrom": [ " author" ],
 2603    -1         "parent": [ "roletype" ],
 2604    -1         "properties": [ "aria-expanded" ]
 2605    -1     }
 2606    -1 };
 2607    -1 
 2608    -1 axs.constants.WIDGET_ROLES = {};
 2609    -1 
 2610    -1 /**
 2611    -1  * Squashes the parent hierarchy on to role object.
 2612    -1  * @param {Object} role
 2613    -1  * @param {Object} set
 2614    -1  * @private
 2615    -1  */
 2616    -1 axs.constants.addAllParentRolesToSet_ = function(role, set) {
 2617    -1   if (!role['parent'])
 2618    -1       return;
 2619    -1   var parents = role['parent'];
 2620    -1   for (var j = 0; j < parents.length; j++) {
 2621    -1     var parentRoleName = parents[j];
 2622    -1     set[parentRoleName] = true;
 2623    -1     axs.constants.addAllParentRolesToSet_(
 2624    -1         axs.constants.ARIA_ROLES[parentRoleName], set);
 2625    -1   }
 2626    -1 };
 2627    -1 
 2628    -1 /**
 2629    -1  * Adds all properties and requiredProperties from parent hierarchy.
 2630    -1  * @param {Object} role
 2631    -1  * @param {string} propertiesName
 2632    -1  * @param {Object} propertiesSet
 2633    -1  * @private
 2634    -1  */
 2635    -1 axs.constants.addAllPropertiesToSet_ = function(role, propertiesName,
 2636    -1     propertiesSet) {
 2637    -1   var properties = role[propertiesName];
 2638    -1   if (properties) {
 2639    -1     for (var i = 0; i < properties.length; i++)
 2640    -1       propertiesSet[properties[i]] = true;
 2641    -1   }
 2642    -1   if (role['parent']) {
 2643    -1     var parents = role['parent'];
 2644    -1     for (var j = 0; j < parents.length; j++) {
 2645    -1       var parentRoleName = parents[j];
 2646    -1       axs.constants.addAllPropertiesToSet_(
 2647    -1           axs.constants.ARIA_ROLES[parentRoleName], propertiesName,
 2648    -1           propertiesSet);
 2649    -1     }
 2650    -1   }
 2651    -1 };
 2652    -1 
 2653    -1 // TODO make a AriaRole object etc.
 2654    -1 for (var roleName in axs.constants.ARIA_ROLES) {
 2655    -1     var role = axs.constants.ARIA_ROLES[roleName];
 2656    -1 
 2657    -1     var propertiesSet = {};
 2658    -1     axs.constants.addAllPropertiesToSet_(role, 'properties', propertiesSet);
 2659    -1     role['propertiesSet'] = propertiesSet;
 2660    -1 
 2661    -1     var requiredPropertiesSet = {};
 2662    -1     axs.constants.addAllPropertiesToSet_(role, 'requiredProperties', requiredPropertiesSet);
 2663    -1     role['requiredPropertiesSet'] = requiredPropertiesSet;
 2664    -1     var parentRolesSet = {};
 2665    -1     axs.constants.addAllParentRolesToSet_(role, parentRolesSet);
 2666    -1     role['allParentRolesSet'] = parentRolesSet;
 2667    -1     if ('widget' in parentRolesSet)
 2668    -1         axs.constants.WIDGET_ROLES[roleName] = role;
 2669    -1 }
 2670    -1 
 2671    -1 // BEGIN ARIA_PROPERTIES_AUTOGENERATED
 2672    -1 /** @type {Object.<string, Object>} */
 2673    -1 axs.constants.ARIA_PROPERTIES = {
 2674    -1     "activedescendant": {
 2675    -1         "type": "property",
 2676    -1         "valueType": "idref"
 2677    -1     },
 2678    -1     "atomic": {
 2679    -1         "defaultValue": "false",
 2680    -1         "type": "property",
 2681    -1         "valueType": "boolean"
 2682    -1     },
 2683    -1     "autocomplete": {
 2684    -1         "defaultValue": "none",
 2685    -1         "type": "property",
 2686    -1         "valueType": "token",
 2687    -1         "values": [
 2688    -1             "inline",
 2689    -1             "list",
 2690    -1             "both",
 2691    -1             "none"
 2692    -1         ]
 2693    -1     },
 2694    -1     "busy": {
 2695    -1         "defaultValue": "false",
 2696    -1         "type": "state",
 2697    -1         "valueType": "boolean"
 2698    -1     },
 2699    -1     "checked": {
 2700    -1         "defaultValue": "undefined",
 2701    -1         "type": "state",
 2702    -1         "valueType": "token",
 2703    -1         "values": [
 2704    -1             "true",
 2705    -1             "false",
 2706    -1             "mixed",
 2707    -1             "undefined"
 2708    -1         ]
 2709    -1     },
 2710    -1     "controls": {
 2711    -1         "type": "property",
 2712    -1         "valueType": "idref_list"
 2713    -1     },
 2714    -1     "describedby": {
 2715    -1         "type": "property",
 2716    -1         "valueType": "idref_list"
 2717    -1     },
 2718    -1     "disabled": {
 2719    -1         "defaultValue": "false",
 2720    -1         "type": "state",
 2721    -1         "valueType": "boolean"
 2722    -1     },
 2723    -1     "dropeffect": {
 2724    -1         "defaultValue": "none",
 2725    -1         "type": "property",
 2726    -1         "valueType": "token_list",
 2727    -1         "values": [
 2728    -1             "copy",
 2729    -1             "move",
 2730    -1             "link",
 2731    -1             "execute",
 2732    -1             "popup",
 2733    -1             "none"
 2734    -1         ]
 2735    -1     },
 2736    -1     "expanded": {
 2737    -1         "defaultValue": "undefined",
 2738    -1         "type": "state",
 2739    -1         "valueType": "token",
 2740    -1         "values": [
 2741    -1             "true",
 2742    -1             "false",
 2743    -1             "undefined"
 2744    -1         ]
 2745    -1     },
 2746    -1     "flowto": {
 2747    -1         "type": "property",
 2748    -1         "valueType": "idref_list"
 2749    -1     },
 2750    -1     "grabbed": {
 2751    -1         "defaultValue": "undefined",
 2752    -1         "type": "state",
 2753    -1         "valueType": "token",
 2754    -1         "values": [
 2755    -1             "true",
 2756    -1             "false",
 2757    -1             "undefined"
 2758    -1         ]
 2759    -1     },
 2760    -1     "haspopup": {
 2761    -1         "defaultValue": "false",
 2762    -1         "type": "property",
 2763    -1         "valueType": "boolean"
 2764    -1     },
 2765    -1     "hidden": {
 2766    -1         "defaultValue": "false",
 2767    -1         "type": "state",
 2768    -1         "valueType": "boolean"
 2769    -1     },
 2770    -1     "invalid": {
 2771    -1         "defaultValue": "false",
 2772    -1         "type": "state",
 2773    -1         "valueType": "token",
 2774    -1         "values": [
 2775    -1             "grammar",
 2776    -1             "false",
 2777    -1             "spelling",
 2778    -1             "true"
 2779    -1         ]
 2780    -1     },
 2781    -1     "label": {
 2782    -1         "type": "property",
 2783    -1         "valueType": "string"
 2784    -1     },
 2785    -1     "labelledby": {
 2786    -1         "type": "property",
 2787    -1         "valueType": "idref_list"
 2788    -1     },
 2789    -1     "level": {
 2790    -1         "type": "property",
 2791    -1         "valueType": "integer"
 2792    -1     },
 2793    -1     "live": {
 2794    -1         "defaultValue": "off",
 2795    -1         "type": "property",
 2796    -1         "valueType": "token",
 2797    -1         "values": [
 2798    -1             "off",
 2799    -1             "polite",
 2800    -1             "assertive"
 2801    -1         ]
 2802    -1     },
 2803    -1     "multiline": {
 2804    -1         "defaultValue": "false",
 2805    -1         "type": "property",
 2806    -1         "valueType": "boolean"
 2807    -1     },
 2808    -1     "multiselectable": {
 2809    -1         "defaultValue": "false",
 2810    -1         "type": "property",
 2811    -1         "valueType": "boolean"
 2812    -1     },
 2813    -1     "orientation": {
 2814    -1         "defaultValue": "vertical",
 2815    -1         "type": "property",
 2816    -1         "valueType": "token",
 2817    -1         "values": [
 2818    -1             "horizontal",
 2819    -1             "vertical"
 2820    -1         ]
 2821    -1     },
 2822    -1     "owns": {
 2823    -1         "type": "property",
 2824    -1         "valueType": "idref_list"
 2825    -1     },
 2826    -1     "posinset": {
 2827    -1         "type": "property",
 2828    -1         "valueType": "integer"
 2829    -1     },
 2830    -1     "pressed": {
 2831    -1         "defaultValue": "undefined",
 2832    -1         "type": "state",
 2833    -1         "valueType": "token",
 2834    -1         "values": [
 2835    -1             "true",
 2836    -1             "false",
 2837    -1             "mixed",
 2838    -1             "undefined"
 2839    -1         ]
 2840    -1     },
 2841    -1     "readonly": {
 2842    -1         "defaultValue": "false",
 2843    -1         "type": "property",
 2844    -1         "valueType": "boolean"
 2845    -1     },
 2846    -1     "relevant": {
 2847    -1         "defaultValue": "additions text",
 2848    -1         "type": "property",
 2849    -1         "valueType": "token_list",
 2850    -1         "values": [
 2851    -1             "additions",
 2852    -1             "removals",
 2853    -1             "text",
 2854    -1             "all"
 2855    -1         ]
 2856    -1     },
 2857    -1     "required": {
 2858    -1         "defaultValue": "false",
 2859    -1         "type": "property",
 2860    -1         "valueType": "boolean"
 2861    -1     },
 2862    -1     "selected": {
 2863    -1         "defaultValue": "undefined",
 2864    -1         "type": "state",
 2865    -1         "valueType": "token",
 2866    -1         "values": [
 2867    -1             "true",
 2868    -1             "false",
 2869    -1             "undefined"
 2870    -1         ]
 2871    -1     },
 2872    -1     "setsize": {
 2873    -1         "type": "property",
 2874    -1         "valueType": "integer"
 2875    -1     },
 2876    -1     "sort": {
 2877    -1         "defaultValue": "none",
 2878    -1         "type": "property",
 2879    -1         "valueType": "token",
 2880    -1         "values": [
 2881    -1             "ascending",
 2882    -1             "descending",
 2883    -1             "none",
 2884    -1             "other"
 2885    -1         ]
 2886    -1     },
 2887    -1     "valuemax": {
 2888    -1         "type": "property",
 2889    -1         "valueType": "decimal"
 2890    -1     },
 2891    -1     "valuemin": {
 2892    -1         "type": "property",
 2893    -1         "valueType": "decimal"
 2894    -1     },
 2895    -1     "valuenow": {
 2896    -1         "type": "property",
 2897    -1         "valueType": "decimal"
 2898    -1     },
 2899    -1     "valuetext": {
 2900    -1         "type": "property",
 2901    -1         "valueType": "string"
 2902    -1     }
 2903    -1 };
 2904    -1 // END ARIA_PROPERTIES_AUTOGENERATED
 2905    -1 
 2906    -1 (function() {
 2907    -1 // pull values lists into sets
 2908    -1 for (var propertyName in axs.constants.ARIA_PROPERTIES) {
 2909    -1     var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyName];
 2910    -1     if (!propertyDetails.values)
 2911    -1         continue;
 2912    -1     var valuesSet = {};
 2913    -1     for (var i = 0; i < propertyDetails.values.length; i++)
 2914    -1         valuesSet[propertyDetails.values[i]] = true;
 2915    -1     propertyDetails.valuesSet = valuesSet;
 2916    -1 }
 2917    -1 })();
 2918    -1 
 2919    -1 /**
 2920    -1  * All of the states and properties which apply globally.
 2921    -1  * @type {Object<!string, !boolean>}
 2922    -1  */
 2923    -1 axs.constants.GLOBAL_PROPERTIES = axs.constants.ARIA_ROLES['roletype'].propertiesSet;
 2924    -1 
 2925    -1 /**
 2926    -1  * A constant indicating no role name.
 2927    -1  * @type {string}
 2928    -1  */
 2929    -1 axs.constants.NO_ROLE_NAME = ' ';
 2930    -1 
 2931    -1 /**
 2932    -1  * A mapping from ARIA role names to their message ids.
 2933    -1  * Copied from ChromeVox:
 2934    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js
 2935    -1  * @type {Object.<string, string>}
 2936    -1  */
 2937    -1 axs.constants.WIDGET_ROLE_TO_NAME = {
 2938    -1   'alert' : 'aria_role_alert',
 2939    -1   'alertdialog' : 'aria_role_alertdialog',
 2940    -1   'button' : 'aria_role_button',
 2941    -1   'checkbox' : 'aria_role_checkbox',
 2942    -1   'columnheader' : 'aria_role_columnheader',
 2943    -1   'combobox' : 'aria_role_combobox',
 2944    -1   'dialog' : 'aria_role_dialog',
 2945    -1   'grid' : 'aria_role_grid',
 2946    -1   'gridcell' : 'aria_role_gridcell',
 2947    -1   'link' : 'aria_role_link',
 2948    -1   'listbox' : 'aria_role_listbox',
 2949    -1   'log' : 'aria_role_log',
 2950    -1   'marquee' : 'aria_role_marquee',
 2951    -1   'menu' : 'aria_role_menu',
 2952    -1   'menubar' : 'aria_role_menubar',
 2953    -1   'menuitem' : 'aria_role_menuitem',
 2954    -1   'menuitemcheckbox' : 'aria_role_menuitemcheckbox',
 2955    -1   'menuitemradio' : 'aria_role_menuitemradio',
 2956    -1   'option' : axs.constants.NO_ROLE_NAME,
 2957    -1   'progressbar' : 'aria_role_progressbar',
 2958    -1   'radio' : 'aria_role_radio',
 2959    -1   'radiogroup' : 'aria_role_radiogroup',
 2960    -1   'rowheader' : 'aria_role_rowheader',
 2961    -1   'scrollbar' : 'aria_role_scrollbar',
 2962    -1   'slider' : 'aria_role_slider',
 2963    -1   'spinbutton' : 'aria_role_spinbutton',
 2964    -1   'status' : 'aria_role_status',
 2965    -1   'tab' : 'aria_role_tab',
 2966    -1   'tabpanel' : 'aria_role_tabpanel',
 2967    -1   'textbox' : 'aria_role_textbox',
 2968    -1   'timer' : 'aria_role_timer',
 2969    -1   'toolbar' : 'aria_role_toolbar',
 2970    -1   'tooltip' : 'aria_role_tooltip',
 2971    -1   'treeitem' : 'aria_role_treeitem'
 2972    -1 };
 2973    -1 
 2974    -1 
 2975    -1 /**
 2976    -1  * @type {Object.<string, string>}
 2977    -1  * Copied from ChromeVox:
 2978    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js
 2979    -1  */
 2980    -1 axs.constants.STRUCTURE_ROLE_TO_NAME = {
 2981    -1   'article' : 'aria_role_article',
 2982    -1   'application' : 'aria_role_application',
 2983    -1   'banner' : 'aria_role_banner',
 2984    -1   'columnheader' : 'aria_role_columnheader',
 2985    -1   'complementary' : 'aria_role_complementary',
 2986    -1   'contentinfo' : 'aria_role_contentinfo',
 2987    -1   'definition' : 'aria_role_definition',
 2988    -1   'directory' : 'aria_role_directory',
 2989    -1   'document' : 'aria_role_document',
 2990    -1   'form' : 'aria_role_form',
 2991    -1   'group' : 'aria_role_group',
 2992    -1   'heading' : 'aria_role_heading',
 2993    -1   'img' : 'aria_role_img',
 2994    -1   'list' : 'aria_role_list',
 2995    -1   'listitem' : 'aria_role_listitem',
 2996    -1   'main' : 'aria_role_main',
 2997    -1   'math' : 'aria_role_math',
 2998    -1   'navigation' : 'aria_role_navigation',
 2999    -1   'note' : 'aria_role_note',
 3000    -1   'region' : 'aria_role_region',
 3001    -1   'rowheader' : 'aria_role_rowheader',
 3002    -1   'search' : 'aria_role_search',
 3003    -1   'separator' : 'aria_role_separator'
 3004    -1 };
 3005    -1 
 3006    -1 
 3007    -1 /**
 3008    -1  * @type {Array.<Object>}
 3009    -1  * Copied from ChromeVox:
 3010    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/aria_util.js
 3011    -1  */
 3012    -1 axs.constants.ATTRIBUTE_VALUE_TO_STATUS = [
 3013    -1   { name: 'aria-autocomplete', values:
 3014    -1       {'inline' : 'aria_autocomplete_inline',
 3015    -1        'list' : 'aria_autocomplete_list',
 3016    -1        'both' : 'aria_autocomplete_both'} },
 3017    -1   { name: 'aria-checked', values:
 3018    -1       {'true' : 'aria_checked_true',
 3019    -1        'false' : 'aria_checked_false',
 3020    -1        'mixed' : 'aria_checked_mixed'} },
 3021    -1   { name: 'aria-disabled', values:
 3022    -1       {'true' : 'aria_disabled_true'} },
 3023    -1   { name: 'aria-expanded', values:
 3024    -1       {'true' : 'aria_expanded_true',
 3025    -1        'false' : 'aria_expanded_false'} },
 3026    -1   { name: 'aria-invalid', values:
 3027    -1       {'true' : 'aria_invalid_true',
 3028    -1        'grammar' : 'aria_invalid_grammar',
 3029    -1        'spelling' : 'aria_invalid_spelling'} },
 3030    -1   { name: 'aria-multiline', values:
 3031    -1       {'true' : 'aria_multiline_true'} },
 3032    -1   { name: 'aria-multiselectable', values:
 3033    -1       {'true' : 'aria_multiselectable_true'} },
 3034    -1   { name: 'aria-pressed', values:
 3035    -1       {'true' : 'aria_pressed_true',
 3036    -1        'false' : 'aria_pressed_false',
 3037    -1        'mixed' : 'aria_pressed_mixed'} },
 3038    -1   { name: 'aria-readonly', values:
 3039    -1       {'true' : 'aria_readonly_true'} },
 3040    -1   { name: 'aria-required', values:
 3041    -1       {'true' : 'aria_required_true'} },
 3042    -1   { name: 'aria-selected', values:
 3043    -1       {'true' : 'aria_selected_true',
 3044    -1        'false' : 'aria_selected_false'} }
 3045    -1 ];
 3046    -1 
 3047    -1 /**
 3048    -1  * Copied from ChromeVox:
 3049    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js
 3050    -1  * @type {Object}
 3051    -1  */
 3052    -1 axs.constants.INPUT_TYPE_TO_INFORMATION_TABLE_MSG = {
 3053    -1   'button' : 'input_type_button',
 3054    -1   'checkbox' : 'input_type_checkbox',
 3055    -1   'color' : 'input_type_color',
 3056    -1   'datetime' : 'input_type_datetime',
 3057    -1   'datetime-local' : 'input_type_datetime_local',
 3058    -1   'date' : 'input_type_date',
 3059    -1   'email' : 'input_type_email',
 3060    -1   'file' : 'input_type_file',
 3061    -1   'image' : 'input_type_image',
 3062    -1   'month' : 'input_type_month',
 3063    -1   'number' : 'input_type_number',
 3064    -1   'password' : 'input_type_password',
 3065    -1   'radio' : 'input_type_radio',
 3066    -1   'range' : 'input_type_range',
 3067    -1   'reset' : 'input_type_reset',
 3068    -1   'search' : 'input_type_search',
 3069    -1   'submit' : 'input_type_submit',
 3070    -1   'tel' : 'input_type_tel',
 3071    -1   'text' : 'input_type_text',
 3072    -1   'url' : 'input_type_url',
 3073    -1   'week' : 'input_type_week'
 3074    -1 };
 3075    -1 
 3076    -1 
 3077    -1 /**
 3078    -1  * Copied from ChromeVox:
 3079    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js
 3080    -1  * @type {Object}
 3081    -1  */
 3082    -1 axs.constants.TAG_TO_INFORMATION_TABLE_VERBOSE_MSG = {
 3083    -1   'A' : 'tag_link',
 3084    -1   'BUTTON' : 'tag_button',
 3085    -1   'H1' : 'tag_h1',
 3086    -1   'H2' : 'tag_h2',
 3087    -1   'H3' : 'tag_h3',
 3088    -1   'H4' : 'tag_h4',
 3089    -1   'H5' : 'tag_h5',
 3090    -1   'H6' : 'tag_h6',
 3091    -1   'LI' : 'tag_li',
 3092    -1   'OL' : 'tag_ol',
 3093    -1   'SELECT' : 'tag_select',
 3094    -1   'TEXTAREA' : 'tag_textarea',
 3095    -1   'UL' : 'tag_ul',
 3096    -1   'SECTION' : 'tag_section',
 3097    -1   'NAV' : 'tag_nav',
 3098    -1   'ARTICLE' : 'tag_article',
 3099    -1   'ASIDE' : 'tag_aside',
 3100    -1   'HGROUP' : 'tag_hgroup',
 3101    -1   'HEADER' : 'tag_header',
 3102    -1   'FOOTER' : 'tag_footer',
 3103    -1   'TIME' : 'tag_time',
 3104    -1   'MARK' : 'tag_mark'
 3105    -1 };
 3106    -1 
 3107    -1 /**
 3108    -1  * Copied from ChromeVox:
 3109    -1  * http://code.google.com/p/google-axs-chrome/source/browse/trunk/chromevox/common/dom_util.js
 3110    -1  * @type {Object}
 3111    -1  */
 3112    -1 axs.constants.TAG_TO_INFORMATION_TABLE_BRIEF_MSG = {
 3113    -1   'BUTTON' : 'tag_button',
 3114    -1   'SELECT' : 'tag_select',
 3115    -1   'TEXTAREA' : 'tag_textarea'
 3116    -1 };
 3117    -1 
 3118    -1 axs.constants.MIXED_VALUES = {
 3119    -1     "true": true,
 3120    -1     "false": true,
 3121    -1     "mixed": true
 3122    -1 };
 3123    -1 
 3124    -1 /** @enum {string} */
 3125    -1 axs.constants.Severity = {
 3126    -1     INFO: 'Info',
 3127    -1     WARNING: 'Warning',
 3128    -1     SEVERE: 'Severe'
 3129    -1 };
 3130    -1 
 3131    -1 /** @enum {string} */
 3132    -1 axs.constants.AuditResult = {
 3133    -1     PASS: 'PASS',
 3134    -1     FAIL: 'FAIL',
 3135    -1     NA: 'NA'
 3136    -1 };
 3137    -1 
 3138    -1 /** @enum {boolean} */
 3139    -1 axs.constants.InlineElements = {
 3140    -1     // fontstyle
 3141    -1     'TT': true,
 3142    -1     'I': true,
 3143    -1     'B': true,
 3144    -1     'BIG': true,
 3145    -1     'SMALL': true,
 3146    -1 
 3147    -1     // phrase
 3148    -1     'EM': true,
 3149    -1     'STRONG': true,
 3150    -1     'DFN': true,
 3151    -1     'CODE': true,
 3152    -1     'SAMP': true,
 3153    -1     'KBD': true,
 3154    -1     'VAR': true,
 3155    -1     'CITE': true,
 3156    -1     'ABBR': true,
 3157    -1     'ACRONYM': true,
 3158    -1 
 3159    -1     // special
 3160    -1     'A': true,
 3161    -1     'IMG': true,
 3162    -1     'OBJECT': true,
 3163    -1     'BR': true,
 3164    -1     'SCRIPT': true,
 3165    -1     'MAP': true,
 3166    -1     'Q': true,
 3167    -1     'SUB': true,
 3168    -1     'SUP': true,
 3169    -1     'SPAN': true,
 3170    -1     'BDO': true,
 3171    -1 
 3172    -1     // formctrl
 3173    -1     'INPUT': true,
 3174    -1     'SELECT': true,
 3175    -1     'TEXTAREA': true,
 3176    -1     'LABEL': true,
 3177    -1     'BUTTON': true
 3178    -1  };
 3179    -1 
 3180    -1  /** @enum {boolean} */
 3181    -1 axs.constants.NATIVELY_DISABLEABLE = {
 3182    -1     // W3C and WHATWG https://html.spec.whatwg.org/#enabling-and-disabling-form-controls:-the-disabled-attribute
 3183    -1     'BUTTON': true,
 3184    -1     'INPUT': true,
 3185    -1     'SELECT': true,
 3186    -1     'TEXTAREA': true,
 3187    -1     'FIELDSET': true,
 3188    -1 
 3189    -1     // W3C http://www.w3.org/TR/html5/disabled-elements.html#disabled-elements
 3190    -1     'OPTGROUP': true,
 3191    -1     'OPTION': true
 3192    -1 };
 3193    -1 
 3194    -1 /**
 3195    -1  * Maps ARIA attributes to their exactly equivalent HTML attributes.
 3196    -1  * @type {Object.<string, string>}
 3197    -1  */
 3198    -1 axs.constants.ARIA_TO_HTML_ATTRIBUTE = {
 3199    -1   'aria-checked' : 'checked',
 3200    -1   'aria-disabled' : 'disabled',
 3201    -1   'aria-hidden' : 'hidden',
 3202    -1   'aria-expanded' : 'open',
 3203    -1   'aria-valuemax' : 'max',
 3204    -1   'aria-valuemin' : 'min',
 3205    -1   'aria-readonly' : 'readonly',
 3206    -1   'aria-required' : 'required',
 3207    -1   'aria-selected' : 'selected',
 3208    -1   'aria-valuenow' : 'value'
 3209    -1 };
 3210    -1 
 3211    -1 /**
 3212    -1  * Holds information about implicit ARIA semantics for a given HTML element type.
 3213    -1  * This object has the following properties:
 3214    -1  * <ul>
 3215    -1  * <li>`role` will contain the implicit role if it exists, otherwise empty string.</li>
 3216    -1  * <li>`allowed` contains the roles that can reasonably be applied to this element.
 3217    -1  *    Note: A tag that can take any role is signified by a '*' wildcard in the array. It is not
 3218    -1  *    an error if the array contains other roles but currently this has no meaning. In future it may
 3219    -1  *    be used to indicate recommended roles.
 3220    -1  * </li>
 3221    -1  * <li>`selector` is present if this is a 'subclass' of the base HTML element, i.e. its semantics are
 3222    -1  *    modified by context or attributes. It can be used with the selectors API to find and/or match
 3223    -1  *    elements.
 3224    -1  * </li>
 3225    -1  * <li>`reserved` will be true if this is a semantically strong element that you may not modify with any
 3226    -1  *    ARIA attributes, including role or global attributes.
 3227    -1  * </li>
 3228    -1  * </ul>
 3229    -1  *
 3230    -1  * @typedef {{ role: string,
 3231    -1  *             allowed: Array.<string>,
 3232    -1  *             selector: string,
 3233    -1  *             reserved:  boolean }}
 3234    -1  */
 3235    -1 axs.constants.HtmlInfo;
 3236    -1 /**
 3237    -1  * A lookup table which maps uppercase tagName to information about implicit ARIA semantics.
 3238    -1  * This table is based on the document: http://w3c.github.io/aria-in-html/
 3239    -1  * It is not complete and never can be. Complex scenarios require specific handling not provided here.
 3240    -1  * Any element not listed here:
 3241    -1  *    - has no implicit role
 3242    -1  *    - can take any role
 3243    -1  *    e.g. em,strong,small,s,cite,q,dfn,abbr,time,code,var,samp,kbd,sub and sup,i,b,u,mark ,ruby,rt,rp,bdi,bdo,br,wbr
 3244    -1  *
 3245    -1  * Where there is any ambiguity this table will endeavor to provide for the most broad case (to avoid
 3246    -1  *    false failures in conformance checking).
 3247    -1  *
 3248    -1  * For example 'table' can take any role however in practice it should only be given the role 'grid' when
 3249    -1  *    being used as a data grid or 'presentation' when used for layout. This lookup ignores these nuances and
 3250    -1  *    allows all roles.
 3251    -1  *
 3252    -1  * @type {Object.<string, Array.<axs.constants.HtmlInfo>>}
 3253    -1  */
 3254    -1 axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO = {
 3255    -1     'A': [{
 3256    -1         role: 'link',
 3257    -1         allowed: [
 3258    -1         'button',
 3259    -1         'checkbox',
 3260    -1         'menuitem',
 3261    -1         'menuitemcheckbox',
 3262    -1         'menuitemradio',
 3263    -1         'tab',
 3264    -1         'treeitem'],
 3265    -1         selector: 'a[href]'
 3266    -1     }],
 3267    -1     'ADDRESS': [{
 3268    -1         role: '',
 3269    -1         allowed: [
 3270    -1         'contentinfo',
 3271    -1         'presentation']
 3272    -1     }],
 3273    -1     'AREA': [{
 3274    -1         role: 'link',
 3275    -1         selector: 'area[href]'
 3276    -1     }],
 3277    -1     'ARTICLE': [{
 3278    -1         role: 'article',
 3279    -1         allowed: [
 3280    -1         'presentation',
 3281    -1         'article',
 3282    -1         'document',
 3283    -1         'application',
 3284    -1         'main']
 3285    -1     }],
 3286    -1     'ASIDE': [{
 3287    -1         role: 'complementary',
 3288    -1         allowed: [
 3289    -1         'note',
 3290    -1         'complementary',
 3291    -1         'search',
 3292    -1         'presentation']
 3293    -1     }],
 3294    -1     'AUDIO': [{
 3295    -1         role: '',
 3296    -1         allowed: ['application', 'presentation']
 3297    -1     }],
 3298    -1     'BASE': [{
 3299    -1         role: '',
 3300    -1         reserved: true
 3301    -1     }],
 3302    -1     'BODY': [{
 3303    -1         role: 'document',
 3304    -1         allowed: ['presentation']
 3305    -1     }],
 3306    -1     'BUTTON': [{
 3307    -1         role: 'button',
 3308    -1         allowed: [
 3309    -1         'link',
 3310    -1         'menuitem',
 3311    -1         'menuitemcheckbox',
 3312    -1         'menuitemradio',
 3313    -1         'radio'],
 3314    -1         selector: 'button:not([aria-pressed]):not([type="menu"])'
 3315    -1     }, {
 3316    -1         role: 'button',
 3317    -1         allowed: ['button'],
 3318    -1         selector: 'button[aria-pressed]'
 3319    -1     }, {
 3320    -1         role: 'button',
 3321    -1         attributes: {
 3322    -1             'aria-haspopup': true
 3323    -1         },
 3324    -1         allowed: [
 3325    -1         'link',
 3326    -1         'menuitem',
 3327    -1         'menuitemcheckbox',
 3328    -1         'menuitemradio',
 3329    -1         'radio'],
 3330    -1         selector: 'button[type="menu"]'
 3331    -1     }],
 3332    -1     'CAPTION': [{
 3333    -1         role: '',
 3334    -1         allowed: ['presentation']
 3335    -1     }],
 3336    -1     'COL': [{
 3337    -1         role: '',
 3338    -1         reserved: true
 3339    -1     }],
 3340    -1     'COLGROUP': [{
 3341    -1         role: '',
 3342    -1         reserved: true
 3343    -1     }],
 3344    -1     'DATALIST': [{
 3345    -1         role: 'listbox',
 3346    -1         attributes: {
 3347    -1             'aria-multiselectable': false
 3348    -1         },
 3349    -1         allowed: ['presentation']
 3350    -1     }],
 3351    -1     'DEL': [{
 3352    -1         role: '',
 3353    -1         allowed: ['*']
 3354    -1     }],
 3355    -1     'DD': [{
 3356    -1         role: '',
 3357    -1         allowed: ['presentation']
 3358    -1     }],
 3359    -1     'DT': [{
 3360    -1         role: '',
 3361    -1         allowed: ['presentation']
 3362    -1     }],
 3363    -1     'DETAILS': [{
 3364    -1         role: 'group',
 3365    -1         allowed: [
 3366    -1         'group',
 3367    -1         'presentation']
 3368    -1     }],
 3369    -1     'DIALOG': [{  // updated 'allowed' from: http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-dialog-element
 3370    -1         role: 'dialog',
 3371    -1         allowed: ['dialog', 'alert', 'alertdialog', 'application', 'log', 'marquee', 'status'],
 3372    -1         selector: 'dialog[open]'
 3373    -1     }, {
 3374    -1         role: 'dialog',
 3375    -1         attributes: {
 3376    -1             'aria-hidden': true
 3377    -1         },
 3378    -1         allowed: ['dialog', 'alert', 'alertdialog', 'application', 'log', 'marquee', 'status'],
 3379    -1         selector: 'dialog:not([open])'
 3380    -1     }],
 3381    -1     'DIV': [{
 3382    -1         role: '',
 3383    -1         allowed: ['*']
 3384    -1     }],
 3385    -1     'DL': [{
 3386    -1         role: 'list',
 3387    -1         allowed: ['presentation']
 3388    -1     }],
 3389    -1     'EMBED': [{
 3390    -1         role: '',
 3391    -1         allowed: [
 3392    -1         'application',
 3393    -1         'document',
 3394    -1         'img',
 3395    -1         'presentation']
 3396    -1     }],
 3397    -1     'FIGURE': [{
 3398    -1         role: '',
 3399    -1         allowed: ['*']
 3400    -1     }],
 3401    -1     'FOOTER': [{
 3402    -1         role: '',
 3403    -1         allowed: ['contentinfo', 'presentation']
 3404    -1     }],
 3405    -1     'FORM': [{
 3406    -1         role: 'form',
 3407    -1         allowed: ['presentation']
 3408    -1     }],
 3409    -1     'P': [{
 3410    -1         role: '',
 3411    -1         allowed: ['*']
 3412    -1     }],
 3413    -1     'PRE': [{
 3414    -1         role: '',
 3415    -1         allowed: ['*']
 3416    -1     }],
 3417    -1     'BLOCKQUOTE': [{
 3418    -1         role: '',
 3419    -1         allowed: ['*']
 3420    -1     }],
 3421    -1     H1: [{
 3422    -1         role: 'heading'
 3423    -1     }],
 3424    -1     H2: [{
 3425    -1         role: 'heading'
 3426    -1     }],
 3427    -1     H3: [{
 3428    -1         role: 'heading'
 3429    -1     }],
 3430    -1     H4: [{
 3431    -1         role: 'heading'
 3432    -1     }],
 3433    -1     H5: [{
 3434    -1         role: 'heading'
 3435    -1     }],
 3436    -1     H6: [{
 3437    -1         role: 'heading'
 3438    -1     }],
 3439    -1     'HEAD': [{
 3440    -1         role: '',
 3441    -1         reserved: true
 3442    -1     }],
 3443    -1     'HEADER': [{
 3444    -1         role: '',
 3445    -1         allowed: [
 3446    -1         'banner',
 3447    -1         'presentation']
 3448    -1     }],
 3449    -1     'HR': [{
 3450    -1         role: 'separator',
 3451    -1         allowed: ['presentation']
 3452    -1     }],
 3453    -1     'HTML': [{
 3454    -1         role: '',
 3455    -1         reserved: true
 3456    -1     }],
 3457    -1     'IFRAME': [{
 3458    -1         role: '',
 3459    -1         allowed: [
 3460    -1         'application',
 3461    -1         'document',
 3462    -1         'img',
 3463    -1         'presentation'],
 3464    -1         selector: 'iframe:not([seamless])'
 3465    -1     }, {
 3466    -1         role: '',
 3467    -1         allowed: [
 3468    -1         'application',
 3469    -1         'document',
 3470    -1         'img',
 3471    -1         'presentation',
 3472    -1         'group'],
 3473    -1         selector: 'iframe[seamless]'
 3474    -1     }],
 3475    -1     'IMG': [{
 3476    -1         role: 'presentation',
 3477    -1         reserved: true,
 3478    -1         selector: 'img[alt=""]'
 3479    -1     }, {
 3480    -1         role: 'img',
 3481    -1         allowed: ['*'],
 3482    -1         selector: 'img[alt]:not([alt=""])'
 3483    -1     }],
 3484    -1     'INPUT': [{
 3485    -1         role: 'button',
 3486    -1         allowed: [
 3487    -1         'link',
 3488    -1         'menuitem',
 3489    -1         'menuitemcheckbox',
 3490    -1         'menuitemradio',
 3491    -1         'radio'],
 3492    -1         selector: 'input[type="button"]:not([aria-pressed])'
 3493    -1     }, {
 3494    -1         role: 'button',
 3495    -1         allowed: ['button'],
 3496    -1         selector: 'input[type="button"][aria-pressed]'
 3497    -1     }, {
 3498    -1         role: 'checkbox',
 3499    -1         allowed: ['checkbox'],
 3500    -1         selector: 'input[type="checkbox"]'
 3501    -1     }, {
 3502    -1         role: '',
 3503    -1         selector: 'input[type="color"]'
 3504    -1     }, {
 3505    -1         role: '',
 3506    -1         selector: 'input[type="date"]'
 3507    -1     }, {
 3508    -1         role: '',
 3509    -1         selector: 'input[type="datetime"]'
 3510    -1     }, {
 3511    -1         role: 'textbox',
 3512    -1         selector: 'input[type="email"]:not([list])'
 3513    -1     }, {
 3514    -1         role: '',
 3515    -1         selector: 'input[type="file"]'
 3516    -1     }, {
 3517    -1         role: '',
 3518    -1         reserved: true,
 3519    -1         selector: 'input[type="hidden"]'
 3520    -1     }, {
 3521    -1         role: 'button',
 3522    -1         allowed: ['button'],
 3523    -1         selector: 'input[type="image"][aria-pressed]'
 3524    -1     }, {
 3525    -1         role: 'button',
 3526    -1         allowed: [
 3527    -1         'link',
 3528    -1         'menuitem',
 3529    -1         'menuitemcheckbox',
 3530    -1         'menuitemradio',
 3531    -1         'radio'],
 3532    -1         selector: 'input[type="image"]:not([aria-pressed])'
 3533    -1     }, {
 3534    -1         role: '',
 3535    -1         selector: 'input[type="month"]'
 3536    -1     }, {
 3537    -1         role: '',
 3538    -1         selector: 'input[type="number"]'
 3539    -1     }, {
 3540    -1         role: 'textbox',
 3541    -1         selector: 'input[type="password"]'
 3542    -1     }, {
 3543    -1         role: 'radio',
 3544    -1         allowed: ['menuitemradio'],
 3545    -1         selector: 'input[type="radio"]'
 3546    -1     }, {
 3547    -1         role: 'slider',
 3548    -1         selector: 'input[type="range"]'
 3549    -1     }, {
 3550    -1         role: 'button',
 3551    -1         selector: 'input[type="reset"]'
 3552    -1     }, {
 3553    -1         role: 'combobox',  // aria-owns is set to the same value as the list attribute
 3554    -1         selector: 'input[type="search"][list]'
 3555    -1     }, {
 3556    -1         role: 'textbox',
 3557    -1         selector: 'input[type="search"]:not([list])'
 3558    -1     }, {
 3559    -1         role: 'button',
 3560    -1         selector: 'input[type="submit"]'
 3561    -1     }, {
 3562    -1         role: 'combobox',  // aria-owns is set to the same value as the list attribute
 3563    -1         selector: 'input[type="tel"][list]'
 3564    -1     }, {
 3565    -1         role: 'textbox',
 3566    -1         selector: 'input[type="tel"]:not([list])'
 3567    -1     }, {
 3568    -1         role: 'combobox',  // aria-owns is set to the same value as the list attribute
 3569    -1         selector: 'input[type="text"][list]'
 3570    -1     }, {
 3571    -1         role: 'textbox',
 3572    -1         selector: 'input[type="text"]:not([list])'
 3573    -1     }, {
 3574    -1         role: 'textbox',
 3575    -1         selector: 'input:not([type])'
 3576    -1     }, {
 3577    -1         role: '',
 3578    -1         selector: 'input[type="time"]'
 3579    -1     }, {
 3580    -1         role: 'combobox',  // aria-owns is set to the same value as the list attribute
 3581    -1         selector: 'input[type="url"][list]'
 3582    -1     }, {
 3583    -1         role: 'textbox',
 3584    -1         selector: 'input[type="url"]:not([list])'
 3585    -1     }, {
 3586    -1         role: '',
 3587    -1         selector: 'input[type="week"]'
 3588    -1     }],
 3589    -1     'INS': [{
 3590    -1         role: '',
 3591    -1         allowed: ['*']
 3592    -1     }],
 3593    -1     'KEYGEN': [{
 3594    -1         role: ''
 3595    -1     }],
 3596    -1     'LABEL': [{
 3597    -1         role: '',
 3598    -1         allowed: ['presentation']
 3599    -1     }],
 3600    -1     'LI': [{
 3601    -1         role: 'listitem',
 3602    -1         allowed: [
 3603    -1         'menuitem',
 3604    -1         'menuitemcheckbox',
 3605    -1         'menuitemradio',
 3606    -1         'option',
 3607    -1         'tab',
 3608    -1         'treeitem',
 3609    -1         'presentation'],
 3610    -1         selector: 'ol:not([role="presentation"])>li, ul:not([role="presentation"])>li'
 3611    -1     }, {
 3612    -1         role: 'listitem',
 3613    -1         allowed: [
 3614    -1         'listitem',
 3615    -1         'menuitem',
 3616    -1         'menuitemcheckbox',
 3617    -1         'menuitemradio',
 3618    -1         'option',
 3619    -1         'tab',
 3620    -1         'treeitem',
 3621    -1         'presentation'],
 3622    -1         selector: 'ol[role="presentation"]>li, ul[role="presentation"]>li'
 3623    -1     }],
 3624    -1     'LINK': [{
 3625    -1         role: 'link',
 3626    -1         reserved: true,
 3627    -1         selector: 'link[href]'
 3628    -1     }],
 3629    -1     'MAIN': [{
 3630    -1         role: '',
 3631    -1         allowed: [
 3632    -1         'main',
 3633    -1         'presentation']
 3634    -1     }],
 3635    -1     'MAP': [{
 3636    -1         role: '',
 3637    -1         reserved: true
 3638    -1     }],
 3639    -1     'MATH': [{
 3640    -1         role: '',
 3641    -1         allowed: ['presentation']
 3642    -1     }],
 3643    -1     'MENU': [{
 3644    -1         role: 'toolbar',
 3645    -1         selector: 'menu[type="toolbar"]'
 3646    -1     }],
 3647    -1     'MENUITEM': [{
 3648    -1         role: 'menuitem',
 3649    -1         selector: 'menuitem[type="command"]'
 3650    -1     }, {
 3651    -1         role: 'menuitemcheckbox',
 3652    -1         selector: 'menuitem[type="checkbox"]'
 3653    -1     }, {
 3654    -1         role: 'menuitemradio',
 3655    -1         selector: 'menuitem[type="radio"]'
 3656    -1     }],
 3657    -1     'META': [{
 3658    -1         role: '',
 3659    -1         reserved: true
 3660    -1     }],
 3661    -1     'METER': [{
 3662    -1         role: 'progressbar',
 3663    -1         allowed: ['presentation']
 3664    -1     }],
 3665    -1     'NAV': [{
 3666    -1         role: 'navigation',
 3667    -1         allowed: ['navigation', 'presentation']
 3668    -1     }],
 3669    -1     'NOSCRIPT': [{
 3670    -1         role: '',
 3671    -1         reserved: true
 3672    -1     }],
 3673    -1     'OBJECT': [{
 3674    -1         role: '',
 3675    -1         allowed: ['application', 'document', 'img', 'presentation']
 3676    -1     }],
 3677    -1     'OL': [{
 3678    -1         role: 'list',
 3679    -1         allowed: ['directory', 'group', 'listbox', 'menu', 'menubar', 'tablist', 'toolbar', 'tree', 'presentation']
 3680    -1     }],
 3681    -1     'OPTGROUP': [{
 3682    -1         role: '',
 3683    -1         allowed: ['presentation']
 3684    -1     }],
 3685    -1     'OPTION': [{
 3686    -1         role: 'option'
 3687    -1     }],
 3688    -1     'OUTPUT': [{
 3689    -1         role: 'status',
 3690    -1         allowed: ['*']
 3691    -1     }],
 3692    -1     'PARAM': [{
 3693    -1         role: '',
 3694    -1         reserved: true
 3695    -1     }],
 3696    -1     'PICTURE': [{
 3697    -1         role: '',
 3698    -1         reserved: true
 3699    -1     }],
 3700    -1     'PROGRESS': [{
 3701    -1         role: 'progressbar',
 3702    -1         allowed: ['presentation']
 3703    -1     }],
 3704    -1     'SCRIPT': [{
 3705    -1         role: '',
 3706    -1         reserved: true
 3707    -1     }],
 3708    -1     'SECTION': [{
 3709    -1         role: 'region',
 3710    -1         allowed: [
 3711    -1         'alert',
 3712    -1         'alertdialog',
 3713    -1         'application',
 3714    -1         'contentinfo',
 3715    -1         'dialog',
 3716    -1         'document',
 3717    -1         'log',
 3718    -1         'marquee',
 3719    -1         'search',
 3720    -1         'status',
 3721    -1         'presentation']
 3722    -1     }],
 3723    -1     'SELECT': [{
 3724    -1         role: 'listbox'
 3725    -1     }],
 3726    -1     'SOURCE': [{
 3727    -1         role: '',
 3728    -1         reserved: true
 3729    -1     }],
 3730    -1     'SPAN': [{
 3731    -1         role: '',
 3732    -1         allowed: ['*']
 3733    -1     }],
 3734    -1     'STYLE': [{
 3735    -1         role: '',
 3736    -1         reserved: true
 3737    -1     }],
 3738    -1     'SVG': [{
 3739    -1         role: '',
 3740    -1         allowed: [
 3741    -1         'application',
 3742    -1         'document',
 3743    -1         'img',
 3744    -1         'presentation']
 3745    -1     }],
 3746    -1     'SUMMARY': [{
 3747    -1         role: '',
 3748    -1         allowed: ['presentation']
 3749    -1     }],
 3750    -1     'TABLE': [{
 3751    -1         role: '',
 3752    -1         allowed: ['*']
 3753    -1     }],
 3754    -1     'TEMPLATE': [{
 3755    -1         role: '',
 3756    -1         reserved: true
 3757    -1     }],
 3758    -1     'TEXTAREA': [{
 3759    -1         role: 'textbox'
 3760    -1     }],
 3761    -1     'TBODY': [{
 3762    -1         role: 'rowgroup',
 3763    -1         allowed: ['*']
 3764    -1     }],
 3765    -1     'THEAD': [{
 3766    -1         role: 'rowgroup',
 3767    -1         allowed: ['*']
 3768    -1     }],
 3769    -1     'TFOOT': [{
 3770    -1         role: 'rowgroup',
 3771    -1         allowed: ['*']
 3772    -1     }],
 3773    -1     'TITLE': [{
 3774    -1         role: '',
 3775    -1         reserved: true
 3776    -1     }],
 3777    -1     'TD': [{
 3778    -1         role: '',
 3779    -1         allowed: ['*']
 3780    -1     }],
 3781    -1     'TH': [{
 3782    -1         role: '',
 3783    -1         allowed: ['*']
 3784    -1     }],
 3785    -1     'TR': [{
 3786    -1         role: '',
 3787    -1         allowed: ['*']
 3788    -1     }],
 3789    -1     'TRACK': [{
 3790    -1         role: '',
 3791    -1         reserved: true
 3792    -1     }],
 3793    -1     'UL': [{
 3794    -1         role: 'list',
 3795    -1         allowed: [
 3796    -1         'directory',
 3797    -1         'group',
 3798    -1         'listbox',
 3799    -1         'menu',
 3800    -1         'menubar',
 3801    -1         'tablist',
 3802    -1         'toolbar',
 3803    -1         'tree',
 3804    -1         'presentation']
 3805    -1     }],
 3806    -1     'VIDEO': [{
 3807    -1         role: '',
 3808    -1         allowed: ['application', 'presentation']
 3809    -1     }]
 3810    -1 };
 3811    -1 
 3812    -1 },{}],7:[function(require,module,exports){
 3813    -1 // Copyright 2015 Google Inc.
 3814    -1 //
 3815    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 3816    -1 // you may not use this file except in compliance with the License.
 3817    -1 // You may obtain a copy of the License at
 3818    -1 //
 3819    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 3820    -1 //
 3821    -1 // Unless required by applicable law or agreed to in writing, software
 3822    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 3823    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 3824    -1 // See the License for the specific language governing permissions and
 3825    -1 // limitations under the License.
 3826    -1 
 3827    -1 goog.provide('axs.dom');
 3828    -1 
 3829    -1 /**
 3830    -1  * Returns the nearest ancestor which is an Element.
 3831    -1  * @param {Node} node
 3832    -1  * @return {?Element}
 3833    -1  */
 3834    -1 axs.dom.parentElement = function(node) {
 3835    -1     if (!node)
 3836    -1         return null;
 3837    -1 
 3838    -1     var parentNode = axs.dom.composedParentNode(node);
 3839    -1     if (!parentNode)
 3840    -1         return null;
 3841    -1 
 3842    -1     switch (parentNode.nodeType) {
 3843    -1     case Node.ELEMENT_NODE:
 3844    -1         return /** @type {Element} */ (parentNode);
 3845    -1     default:
 3846    -1         return axs.dom.parentElement(parentNode);
 3847    -1     }
 3848    -1 };
 3849    -1 
 3850    -1 /**
 3851    -1  * Returns the shadow host of a document fragment if it is a Shadow DOM fragment
 3852    -1  * otherwise returns `null`.
 3853    -1  * @param {DocumentFragment} fragment
 3854    -1  * @return {?Element}
 3855    -1  */
 3856    -1 axs.dom.shadowHost = function(fragment) {
 3857    -1     // If host exists, this is a Shadow DOM fragment.
 3858    -1     if ('host' in fragment)
 3859    -1         return fragment.host;
 3860    -1     else
 3861    -1     return null;
 3862    -1 };
 3863    -1 
 3864    -1 /**
 3865    -1  * Returns the given Node's parent in the composed tree.
 3866    -1  * @param {Node} node
 3867    -1  * @return {?Node}
 3868    -1  */
 3869    -1 axs.dom.composedParentNode = function(node) {
 3870    -1     if (!node)
 3871    -1         return null;
 3872    -1     if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE)
 3873    -1         return axs.dom.shadowHost(/** @type {DocumentFragment} */ (node));
 3874    -1 
 3875    -1     var parentNode = node.parentNode;
 3876    -1     if (!parentNode)
 3877    -1         return null;
 3878    -1 
 3879    -1     if (parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE)
 3880    -1         return axs.dom.shadowHost(/** @type {DocumentFragment} */ (parentNode));
 3881    -1 
 3882    -1     if (!parentNode.shadowRoot)
 3883    -1         return parentNode;
 3884    -1 
 3885    -1     // Shadow DOM v1
 3886    -1     if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE) {
 3887    -1       var assignedSlot = node.assignedSlot;
 3888    -1       if (HTMLSlotElement && assignedSlot instanceof HTMLSlotElement)
 3889    -1           return axs.dom.composedParentNode(assignedSlot);
 3890    -1     }
 3891    -1 
 3892    -1     // Shadow DOM v0
 3893    -1     if (typeof node.getDestinationInsertionPoints === 'function') {
 3894    -1         var insertionPoints = node.getDestinationInsertionPoints();
 3895    -1         if (insertionPoints.length > 0)
 3896    -1             return axs.dom.composedParentNode(insertionPoints[insertionPoints.length - 1]);
 3897    -1     }
 3898    -1 
 3899    -1     return null;
 3900    -1 };
 3901    -1 
 3902    -1 /**
 3903    -1  * Return the corresponding element for the given node.
 3904    -1  * @param {Node} node
 3905    -1  * @return {Element}
 3906    -1  * @suppress {checkTypes}
 3907    -1  */
 3908    -1 axs.dom.asElement = function(node) {
 3909    -1     /** @type {Element} */ var element;
 3910    -1     switch (node.nodeType) {
 3911    -1     case Node.COMMENT_NODE:
 3912    -1         return null;  // Skip comments
 3913    -1     case Node.ELEMENT_NODE:
 3914    -1         element = /** (@type {Element}) */ node;
 3915    -1         if (element.localName == 'script' ||
 3916    -1             element.localName == 'template')
 3917    -1             return null;  // Skip script-supporting elements
 3918    -1         return element;
 3919    -1     case Node.DOCUMENT_FRAGMENT_NODE:
 3920    -1         return node.host;
 3921    -1     case Node.TEXT_NODE:
 3922    -1         return axs.dom.parentElement(node);
 3923    -1     default:
 3924    -1         console.warn('Unhandled node type: ', node.nodeType);
 3925    -1     }
 3926    -1     return null;
 3927    -1 };
 3928    -1 
 3929    -1 /**
 3930    -1  * Recursively walk the composed tree from |node|, aborting if |end| is encountered.
 3931    -1  * @param {Node} node
 3932    -1  * @param {?Node} end
 3933    -1  * @param {{preorder: (function (Node, Object):boolean|undefined),
 3934    -1  *          postorder: (function (Node, Object)|undefined)}} callbacks
 3935    -1  *     Callbacks to be called for each element traversed, excluding
 3936    -1  *     |end|. Possible callbacks are |preorder|, called before descending into
 3937    -1  *     child nodes, and |postorder| called after all child nodes have been
 3938    -1  *     traversed. If |preorder| returns false, its child nodes will not be
 3939    -1  *     traversed.
 3940    -1  * @param {Object} parentFlags
 3941    -1  * @param {ShadowRoot=} opt_shadowRoot The nearest ShadowRoot ancestor, if any.
 3942    -1  * @return {boolean} Whether |end| was found, if provided.
 3943    -1  */
 3944    -1 axs.dom.composedTreeSearch = function(node, end, callbacks, parentFlags, opt_shadowRoot) {
 3945    -1     if (node === end)
 3946    -1         return true;
 3947    -1 
 3948    -1     if (node.nodeType == Node.ELEMENT_NODE)
 3949    -1         var element = /** @type {Element} */ (node);
 3950    -1 
 3951    -1     var found = false;
 3952    -1     var flags = Object.create(parentFlags);
 3953    -1 
 3954    -1     // Descend into node:
 3955    -1     // If it has a ShadowRoot, ignore all child elements - these will be picked
 3956    -1     // up by the <content> or <shadow> elements. Descend straight into the
 3957    -1     // ShadowRoot.
 3958    -1     if (element) {
 3959    -1         var localName = element.localName;
 3960    -1         if (flags.collectIdRefs) {
 3961    -1             flags.idrefs = axs.utils.getReferencedIds(element);
 3962    -1         }
 3963    -1         if (!flags.disabled || (localName === 'legend') && axs.browserUtils.matchSelector(element, 'fieldset>legend:first-of-type')) {
 3964    -1             flags.disabled = axs.utils.isElementDisabled(element, true);
 3965    -1         }
 3966    -1         if (!flags.hidden) {
 3967    -1             flags.hidden = axs.utils.isElementHidden(element);
 3968    -1         }
 3969    -1         if (callbacks.preorder) {
 3970    -1             if (!callbacks.preorder(element, flags))
 3971    -1                 return found;
 3972    -1         }
 3973    -1         // NOTE: grunt qunit DOES NOT support Shadow DOM, so if changing this
 3974    -1         // code, be sure to run the tests in the browser before committing.
 3975    -1         var shadowRoot = element.shadowRoot || element.webkitShadowRoot;
 3976    -1         if (shadowRoot) {
 3977    -1             flags.level++;
 3978    -1             found = axs.dom.composedTreeSearch(shadowRoot,
 3979    -1                                                end,
 3980    -1                                                callbacks,
 3981    -1                                                flags,
 3982    -1                                                shadowRoot);
 3983    -1             if (element && callbacks.postorder && !found)
 3984    -1                 callbacks.postorder(element, flags);
 3985    -1             return found;
 3986    -1         }
 3987    -1 
 3988    -1         // If it is a <content> element, descend into distributed elements - these
 3989    -1         // are elements from outside the shadow root which are rendered inside the
 3990    -1         // shadow DOM.
 3991    -1         if (localName == 'content' && typeof element.getDistributedNodes === 'function') {
 3992    -1             var content = /** @type {HTMLContentElement} */ (element);
 3993    -1             var distributedNodes = content.getDistributedNodes();
 3994    -1             for (var i = 0; i < distributedNodes.length && !found; i++) {
 3995    -1                 found = axs.dom.composedTreeSearch(distributedNodes[i],
 3996    -1                                                        end,
 3997    -1                                                        callbacks,
 3998    -1                                                        flags,
 3999    -1                                                        opt_shadowRoot);
 4000    -1             }
 4001    -1             if (callbacks.postorder && !found)
 4002    -1                 callbacks.postorder.call(null, element, flags);
 4003    -1             return found;
 4004    -1         }
 4005    -1     }
 4006    -1 
 4007    -1 
 4008    -1 
 4009    -1     // If it is neither the parent of a ShadowRoot, a <content> element, nor
 4010    -1     // a <shadow> element recurse normally.
 4011    -1     var child = node.firstChild;
 4012    -1     while (child != null && !found) {
 4013    -1         found = axs.dom.composedTreeSearch(child,
 4014    -1                                            end,
 4015    -1                                            callbacks,
 4016    -1                                            flags,
 4017    -1                                            opt_shadowRoot);
 4018    -1         child = child.nextSibling;
 4019    -1     }
 4020    -1 
 4021    -1     if (element && callbacks.postorder && !found)
 4022    -1         callbacks.postorder.call(null, element, flags);
 4023    -1     return found;
 4024    -1 };
 4025    -1 
 4026    -1 },{}],8:[function(require,module,exports){
 4027    -1 // Copyright 2012 Google Inc.
 4028    -1 //
 4029    -1 // Licensed under the Apache License, Version 2.0 (the "License");
 4030    -1 // you may not use this file except in compliance with the License.
 4031    -1 // You may obtain a copy of the License at
 4032    -1 //
 4033    -1 //      http://www.apache.org/licenses/LICENSE-2.0
 4034    -1 //
 4035    -1 // Unless required by applicable law or agreed to in writing, software
 4036    -1 // distributed under the License is distributed on an "AS IS" BASIS,
 4037    -1 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 4038    -1 // See the License for the specific language governing permissions and
 4039    -1 // limitations under the License.
 4040    -1 
 4041    -1 goog.require('axs.browserUtils');
 4042    -1 goog.require('axs.color');
 4043    -1 goog.require('axs.dom');
 4044    -1 goog.require('axs.utils');
 4045    -1 
 4046    -1 goog.provide('axs.properties');
 4047    -1 
 4048    -1 /**
 4049    -1  * @const
 4050    -1  * @type {string}
 4051    -1  */
 4052    -1 axs.properties.TEXT_CONTENT_XPATH = './/text()[normalize-space(.)!=""]/parent::*[name()!="script"]';
 4053    -1 
 4054    -1 /**
 4055    -1  * @param {Element} element
 4056    -1  * @return {Object.<string, Object>}
 4057    -1  */
 4058    -1 axs.properties.getFocusProperties = function(element) {
 4059    -1     var focusProperties = {};
 4060    -1     var tabindex = element.getAttribute('tabindex');
 4061    -1     if (tabindex != undefined) {
 4062    -1         focusProperties['tabindex'] = { value: tabindex, valid: true };
 4063    -1     } else {
 4064    -1         if (axs.utils.isElementImplicitlyFocusable(element))
 4065    -1             focusProperties['implicitlyFocusable'] = { value: true, valid: true };
 4066    -1     }
 4067    -1     if (Object.keys(focusProperties).length == 0)
 4068    -1         return null;
 4069    -1     var transparent = axs.utils.elementIsTransparent(element);
 4070    -1     var zeroArea = axs.utils.elementHasZeroArea(element);
 4071    -1     var outsideScrollArea = axs.utils.elementIsOutsideScrollArea(element);
 4072    -1     var overlappingElements = axs.utils.overlappingElements(element);
 4073    -1     if (transparent || zeroArea || outsideScrollArea || overlappingElements.length > 0) {
 4074    -1         var hidden = axs.utils.isElementOrAncestorHidden(element);
 4075    -1         var visibleProperties = { value: false,
 4076    -1                                   valid: hidden };
 4077    -1         if (transparent)
 4078    -1             visibleProperties['transparent'] = true;
 4079    -1         if (zeroArea)
 4080    -1             visibleProperties['zeroArea'] = true;
 4081    -1         if (outsideScrollArea)
 4082    -1             visibleProperties['outsideScrollArea'] = true;
 4083    -1         if (overlappingElements && overlappingElements.length > 0)
 4084    -1             visibleProperties['overlappingElements'] = overlappingElements;
 4085    -1         var hiddenProperties = { value: hidden, valid: hidden };
 4086    -1         if (hidden)
 4087    -1             hiddenProperties['reason'] = axs.properties.getHiddenReason(element);
 4088    -1         visibleProperties['hidden'] = hiddenProperties;
 4089    -1         focusProperties['visible'] = visibleProperties;
 4090    -1     } else {
 4091    -1         focusProperties['visible'] = { value: true, valid: true };
 4092    -1     }
 4093    -1 
 4094    -1     return focusProperties;
 4095    -1 };
 4096    -1 
 4097    -1 /**
 4098    -1  * @typedef {{ property: string,
 4099    -1  *             on: Element }}
 4100    -1  *
 4101    -1  * property examples: 'display: none', 'visibility: hidden', 'aria-hidden'
 4102    -1  */
 4103    -1 axs.properties.hiddenReason;
 4104    -1 
 4105    -1 /**
 4106    -1  * Determine the reason an element is not visible.
 4107    -1  * Will give the CSS rule or attribute and the element/ancestor it is set on.
 4108    -1  * @param {Element} element
 4109    -1  * @return {?axs.properties.hiddenReason}
 4110    -1  */
 4111    -1 axs.properties.getHiddenReason = function(element) {
 4112    -1     if (!element || !(element instanceof element.ownerDocument.defaultView.HTMLElement))
 4113    -1       return null;
 4114    -1 
 4115    -1     if (element.hasAttribute('chromevoxignoreariahidden'))
 4116    -1         var chromevoxignoreariahidden = true;
 4117    -1 
 4118    -1     var style = window.getComputedStyle(element, null);
 4119    -1     if (style.display == 'none')
 4120    -1         return { 'property': 'display: none',
 4121    -1                  'on': element };
 4122    -1 
 4123    -1     if (style.visibility == 'hidden')
 4124    -1         return { 'property': 'visibility: hidden',
 4125    -1                  'on': element };
 4126    -1 
 4127    -1     if (element.hasAttribute('aria-hidden') &&
 4128    -1         element.getAttribute('aria-hidden').toLowerCase() == 'true') {
 4129    -1         if (!chromevoxignoreariahidden)
 4130    -1             return { 'property': 'aria-hidden',
 4131    -1                      'on': element };
 4132    -1     }
 4133    -1 
 4134    -1     return axs.properties.getHiddenReason(axs.dom.parentElement(element));
 4135    -1 };
 4136    -1 
 4137    -1 
 4138    -1 /**
 4139    -1  * @param {Element} element
 4140    -1  * @return {Object.<string, Object>}
 4141    -1  */
 4142    -1 axs.properties.getColorProperties = function(element) {
 4143    -1     var colorProperties = {};
 4144    -1     var contrastRatioProperties =
 4145    -1         axs.properties.getContrastRatioProperties(element);
 4146    -1     if (contrastRatioProperties)
 4147    -1         colorProperties['contrastRatio'] = contrastRatioProperties;
 4148    -1     if (Object.keys(colorProperties).length == 0)
 4149    -1         return null;
 4150    -1     return colorProperties;
 4151    -1 };
 4152    -1 
 4153    -1 /**
 4154    -1  * Determines whether the given element has a text node as a direct descendant.
 4155    -1  * @param {Element} element
 4156    -1  * @return {boolean}
 4157    -1  */
 4158    -1 axs.properties.hasDirectTextDescendant = function(element) {
 4159    -1     var ownerDocument;
 4160    -1     if (element.nodeType == Node.DOCUMENT_NODE)
 4161    -1         ownerDocument = element;
 4162    -1     else
 4163    -1         ownerDocument = element.ownerDocument;
 4164    -1     if (ownerDocument.evaluate) {
 4165    -1         return hasDirectTextDescendantXpath();
 4166    -1     }
 4167    -1     return hasDirectTextDescendantTreeWalker();
 4168    -1 
 4169    -1     /**
 4170    -1      * Determines whether element has a text node as a direct descendant.
 4171    -1      * This method uses XPath on HTML DOM which is not universally supported.
 4172    -1      * @return {boolean}
 4173    -1      */
 4174    -1     function hasDirectTextDescendantXpath() {
 4175    -1         var selectorResults = ownerDocument.evaluate(axs.properties.TEXT_CONTENT_XPATH,
 4176    -1                                                      element,
 4177    -1                                                      null,
 4178    -1                                                      XPathResult.ANY_TYPE,
 4179    -1                                                      null);
 4180    -1         for (var resultElement = selectorResults.iterateNext();
 4181    -1              resultElement != null;
 4182    -1              resultElement = selectorResults.iterateNext()) {
 4183    -1             if (resultElement !== element)
 4184    -1                 continue;
 4185    -1             return true;
 4186    -1         }
 4187    -1         return false;
 4188    -1     }
 4189    -1 
 4190    -1     /**
 4191    -1      * Determines whether element has a text node as a direct descendant.
 4192    -1      * This method uses TreeWalker as a fallback (at time of writing no version
 4193    -1      * of IE (including IE11) supports XPath in the HTML DOM).
 4194    -1      * @return {boolean}
 4195    -1      */
 4196    -1     function hasDirectTextDescendantTreeWalker() {
 4197    -1         var treeWalker = ownerDocument.createTreeWalker(element,
 4198    -1                                                         NodeFilter.SHOW_TEXT,
 4199    -1                                                         null,
 4200    -1                                                         false);
 4201    -1         while (treeWalker.nextNode()) {
 4202    -1             var resultElement = treeWalker.currentNode;
 4203    -1             var parent = resultElement.parentNode;
 4204    -1             // Handle elements hosted in <template>.content.
 4205    -1             parent = parent.host || parent;
 4206    -1             var tagName = parent.tagName.toLowerCase();
 4207    -1             var value = resultElement.nodeValue.trim();
 4208    -1             if (value && tagName !== 'script' && element !== resultElement)
 4209    -1                 return true;
 4210    -1         }
 4211    -1         return false;
 4212    -1     }
 4213    -1 };
 4214    -1 
 4215    -1 /**
 4216    -1  * @param {Element} element
 4217    -1  * @return {Object.<string, Object>}
 4218    -1  */
 4219    -1 axs.properties.getContrastRatioProperties = function(element) {
 4220    -1     if (!axs.properties.hasDirectTextDescendant(element))
 4221    -1         return null;
 4222    -1 
 4223    -1     var contrastRatioProperties = {};
 4224    -1     var style = window.getComputedStyle(element, null);
 4225    -1     var bgColor = axs.utils.getBgColor(style, element);
 4226    -1     if (!bgColor)
 4227    -1         return null;
 4228    -1 
 4229    -1     contrastRatioProperties['backgroundColor'] = axs.color.colorToString(bgColor);
 4230    -1     var fgColor = axs.utils.getFgColor(style, element, bgColor);
 4231    -1     contrastRatioProperties['foregroundColor'] = axs.color.colorToString(fgColor);
 4232    -1     var contrast = axs.utils.getContrastRatioForElementWithComputedStyle(style, element);
 4233    -1     if (!contrast)
 4234    -1         return null;
 4235    -1     contrastRatioProperties['value'] = contrast.toFixed(2);
 4236    -1     if (axs.utils.isLowContrast(contrast, style))
 4237    -1         contrastRatioProperties['alert'] = true;
 4238    -1 
 4239    -1     var levelAAContrast = axs.utils.isLargeFont(style) ? 3.0 : 4.5;
 4240    -1     var levelAAAContrast = axs.utils.isLargeFont(style) ? 4.5 : 7.0;
 4241    -1     var desiredContrastRatios = {};
 4242    -1     if (levelAAContrast > contrast)
 4243    -1         desiredContrastRatios['AA'] = levelAAContrast;
 4244    -1     if (levelAAAContrast > contrast)
 4245    -1         desiredContrastRatios['AAA'] = levelAAAContrast;
 4246    -1 
 4247    -1     if (!Object.keys(desiredContrastRatios).length)
 4248    -1         return contrastRatioProperties;
 4249    -1 
 4250    -1     var suggestedColors = axs.color.suggestColors(bgColor, fgColor, desiredContrastRatios);
 4251    -1     if (suggestedColors && Object.keys(suggestedColors).length)
 4252    -1         contrastRatioProperties['suggestedColors'] = suggestedColors;
 4253    -1     return contrastRatioProperties;
 4254    -1 };
 4255    -1 
 4256    -1 /**
 4257    -1  * @param {Node} node
 4258    -1  * @param {!Object} textAlternatives The properties object to fill in
 4259    -1  * @param {boolean=} opt_recursive Whether this is a recursive call or not
 4260    -1  * @param {boolean=} opt_force Whether to return text alternatives for this
 4261    -1  *     element regardless of its hidden state.
 4262    -1  * @return {?string} The calculated text alternative for the given element
 4263    -1  */
 4264    -1 axs.properties.findTextAlternatives = function(node, textAlternatives, opt_recursive, opt_force) {
 4265    -1     var recursive = opt_recursive || false;
 4266    -1 
 4267    -1     /** @type {Element} */ var element = axs.dom.asElement(node);
 4268    -1     if (!element)
 4269    -1         return null;
 4270    -1 
 4271    -1     // 1. Skip hidden elements unless the author specifies to use them via an aria-labelledby or
 4272    -1     // aria-describedby being used in the current computation.
 4273    -1     if (!opt_force && axs.utils.isElementOrAncestorHidden(element))
 4274    -1         return null;
 4275    -1 
 4276    -1     // if this is a text node, just return text content.
 4277    -1     if (node.nodeType == Node.TEXT_NODE) {
 4278    -1         var textContentValue = {};
 4279    -1         textContentValue.type = 'text';
 4280    -1         textContentValue.text = node.textContent;
 4281    -1         textContentValue.lastWord = axs.properties.getLastWord(textContentValue.text);
 4282    -1         textAlternatives['content'] = textContentValue;
 4283    -1 
 4284    -1         return node.textContent;
 4285    -1     }
 4286    -1 
 4287    -1     var computedName = null;
 4288    -1 
 4289    -1     if (!recursive) {
 4290    -1         // 2A. The aria-labelledby attribute takes precedence as the element's text alternative
 4291    -1         // unless this computation is already occurring as the result of a recursive aria-labelledby
 4292    -1         // declaration.
 4293    -1         computedName = axs.properties.getTextFromAriaLabelledby(element, textAlternatives);
 4294    -1     }
 4295    -1 
 4296    -1     // 2A. If aria-labelledby is empty or undefined, the aria-label attribute, which defines an
 4297    -1     // explicit text string, is used.
 4298    -1     if (element.hasAttribute('aria-label')) {
 4299    -1         var ariaLabelValue = {};
 4300    -1         ariaLabelValue.type = 'text';
 4301    -1         ariaLabelValue.text = element.getAttribute('aria-label');
 4302    -1         ariaLabelValue.lastWord = axs.properties.getLastWord(ariaLabelValue.text);
 4303    -1         if (computedName)
 4304    -1             ariaLabelValue.unused = true;
 4305    -1         else if (!(recursive && axs.utils.elementIsHtmlControl(element)))
 4306    -1             computedName = ariaLabelValue.text;
 4307    -1         textAlternatives['ariaLabel'] = ariaLabelValue;
 4308    -1     }
 4309    -1 
 4310    -1     // 2A. If aria-labelledby and aria-label are both empty or undefined, and if the element is not
 4311    -1     // marked as presentational (role="presentation", check for the presence of an equivalent host
 4312    -1     // language attribute or element for associating a label, and use those mechanisms to determine
 4313    -1     // a text alternative.
 4314    -1     if (!element.hasAttribute('role') || element.getAttribute('role') != 'presentation') {
 4315    -1         computedName = axs.properties.getTextFromHostLanguageAttributes(element,
 4316    -1                                                                         textAlternatives,
 4317    -1                                                                         computedName,
 4318    -1                                                                         recursive);
 4319    -1     }
 4320    -1 
 4321    -1     // 2B (HTML version).
 4322    -1     if (recursive && axs.utils.elementIsHtmlControl(element)) {
 4323    -1         var defaultView = element.ownerDocument.defaultView;
 4324    -1 
 4325    -1         // include the value of the embedded control as part of the text alternative in the
 4326    -1         // following manner:
 4327    -1         if (element instanceof defaultView.HTMLInputElement) {
 4328    -1             // If the embedded control is a text field, use its value.
 4329    -1             var inputElement = /** @type {HTMLInputElement} */ (element);
 4330    -1             if (inputElement.type == 'text') {
 4331    -1                 if (inputElement.value && inputElement.value.length > 0)
 4332    -1                     textAlternatives['controlValue'] = { 'text': inputElement.value };
 4333    -1             }
 4334    -1             // If the embedded control is a range (e.g. a spinbutton or slider), use the value of the
 4335    -1             // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow
 4336    -1             // attribute.
 4337    -1             if (inputElement.type == 'range')
 4338    -1                 textAlternatives['controlValue'] = { 'text': inputElement.value };
 4339    -1         }
 4340    -1         // If the embedded control is a menu, use the text alternative of the chosen menu item.
 4341    -1         // If the embedded control is a select or combobox, use the chosen option.
 4342    -1         if (element instanceof defaultView.HTMLSelectElement) {
 4343    -1             var inputElement = /** @type {HTMLSelectElement} */ (element);
 4344    -1             textAlternatives['controlValue'] = { 'text': inputElement.value };
 4345    -1         }
 4346    -1 
 4347    -1         if (textAlternatives['controlValue']) {
 4348    -1             var controlValue = textAlternatives['controlValue'];
 4349    -1             if (computedName)
 4350    -1                 controlValue.unused = true;
 4351    -1             else
 4352    -1                 computedName = controlValue.text;
 4353    -1         }
 4354    -1     }
 4355    -1 
 4356    -1     // 2B (ARIA version).
 4357    -1     if (recursive && axs.utils.elementIsAriaWidget(element)) {
 4358    -1         var role = element.getAttribute('role');
 4359    -1         // If the embedded control is a text field, use its value.
 4360    -1         if (role == 'textbox') {
 4361    -1             if (element.textContent && element.textContent.length > 0)
 4362    -1                 textAlternatives['controlValue'] = { 'text': element.textContent };
 4363    -1         }
 4364    -1         // If the embedded control is a range (e.g. a spinbutton or slider), use the value of the
 4365    -1         // aria-valuetext attribute if available, or otherwise the value of the aria-valuenow
 4366    -1         // attribute.
 4367    -1         if (role == 'slider' || role == 'spinbutton') {
 4368    -1             if (element.hasAttribute('aria-valuetext'))
 4369    -1                 textAlternatives['controlValue'] = { 'text': element.getAttribute('aria-valuetext') };
 4370    -1             else if (element.hasAttribute('aria-valuenow'))
 4371    -1                 textAlternatives['controlValue'] = { 'value': element.getAttribute('aria-valuenow'),
 4372    -1                                                      'text': '' + element.getAttribute('aria-valuenow') };
 4373    -1         }
 4374    -1         // If the embedded control is a menu, use the text alternative of the chosen menu item.
 4375    -1         if (role == 'menu') {
 4376    -1             var menuitems = element.querySelectorAll('[role=menuitemcheckbox], [role=menuitemradio]');
 4377    -1             var selectedMenuitems = [];
 4378    -1             for (var i = 0; i < menuitems.length; i++) {
 4379    -1                 if (menuitems[i].getAttribute('aria-checked') == 'true')
 4380    -1                     selectedMenuitems.push(menuitems[i]);
 4381    -1             }
 4382    -1             if (selectedMenuitems.length > 0) {
 4383    -1                 var selectedMenuText = '';
 4384    -1                 for (var i = 0; i < selectedMenuitems.length; i++) {
 4385    -1                     selectedMenuText += axs.properties.findTextAlternatives(selectedMenuitems[i], {}, true);
 4386    -1                     if (i < selectedMenuitems.length - 1)
 4387    -1                         selectedMenuText += ', ';
 4388    -1                 }
 4389    -1                 textAlternatives['controlValue'] = { 'text': selectedMenuText };
 4390    -1             }
 4391    -1         }
 4392    -1         // If the embedded control is a select or combobox, use the chosen option.
 4393    -1         if (role == 'combobox' || role == 'select') {
 4394    -1             // TODO
 4395    -1             textAlternatives['controlValue'] = { 'text': 'TODO' };
 4396    -1         }
 4397    -1 
 4398    -1         if (textAlternatives['controlValue']) {
 4399    -1             var controlValue = textAlternatives['controlValue'];
 4400    -1             if (computedName)
 4401    -1                 controlValue.unused = true;
 4402    -1             else
 4403    -1                 computedName = controlValue.text;
 4404    -1         }
 4405    -1     }
 4406    -1 
 4407    -1     // 2C. Otherwise, if the attributes checked in rules A and B didn't provide results, text is
 4408    -1     // collected from descendant content if the current element's role allows "Name From: contents."
 4409    -1     var hasRole = element.hasAttribute('role');
 4410    -1     var canGetNameFromContents = true;
 4411    -1     if (hasRole) {
 4412    -1         var roleName = element.getAttribute('role');
 4413    -1         // if element has a role, check that it allows "Name From: contents"
 4414    -1         var role = axs.constants.ARIA_ROLES[roleName];
 4415    -1         if (role && (!role.namefrom || role.namefrom.indexOf('contents') < 0))
 4416    -1             canGetNameFromContents = false;
 4417    -1     }
 4418    -1     var textFromContent = axs.properties.getTextFromDescendantContent(element, opt_force);
 4419    -1     if (textFromContent && canGetNameFromContents) {
 4420    -1         var textFromContentValue = {};
 4421    -1         textFromContentValue.type = 'text';
 4422    -1         textFromContentValue.text = textFromContent;
 4423    -1         textFromContentValue.lastWord = axs.properties.getLastWord(textFromContentValue.text);
 4424    -1         if (computedName)
 4425    -1             textFromContentValue.unused = true;
 4426    -1         else
 4427    -1             computedName = textFromContent;
 4428    -1         textAlternatives['content'] = textFromContentValue;
 4429    -1     }
 4430    -1 
 4431    -1     // 2D. The last resort is to use text from a tooltip attribute (such as the title attribute in
 4432    -1     // HTML). This is used only if nothing else, including subtree content, has provided results.
 4433    -1     if (element.hasAttribute('title')) {
 4434    -1         var titleValue = {};
 4435    -1         titleValue.type = 'string';
 4436    -1         titleValue.valid = true;
 4437    -1         titleValue.text = element.getAttribute('title');
 4438    -1         titleValue.lastWord = axs.properties.getLastWord(titleValue.lastWord);
 4439    -1         if (computedName)
 4440    -1             titleValue.unused = true;
 4441    -1         else
 4442    -1             computedName = titleValue.text;
 4443    -1         textAlternatives['title'] = titleValue;
 4444    -1     }
 4445    -1 
 4446    -1     if (Object.keys(textAlternatives).length == 0 && computedName == null)
 4447    -1         return null;
 4448    -1 
 4449    -1     return computedName;
 4450    -1 };
 4451    -1 
 4452    -1 /**
 4453    -1  * @param {Element} element
 4454    -1  * @param {boolean=} opt_force Whether to return text alternatives for this
 4455    -1  *     element regardless of its hidden state.
 4456    -1  * @return {?string}
 4457    -1  */
 4458    -1 axs.properties.getTextFromDescendantContent = function(element, opt_force) {
 4459    -1     var children = element.childNodes;
 4460    -1     var childrenTextContent = [];
 4461    -1     for (var i = 0; i < children.length; i++) {
 4462    -1         var childTextContent = axs.properties.findTextAlternatives(children[i], {}, true, opt_force);
 4463    -1         if (childTextContent)
 4464    -1             childrenTextContent.push(childTextContent.trim());
 4465    -1     }
 4466    -1     if (childrenTextContent.length) {
 4467    -1         var result = '';
 4468    -1         // Empty children are allowed, but collapse all of them
 4469    -1         for (var i = 0; i < childrenTextContent.length; i++)
 4470    -1             result = [result, childrenTextContent[i]].join(' ').trim();
 4471    -1         return result;
 4472    -1     }
 4473    -1     return null;
 4474    -1 };
 4475    -1 
 4476    -1 /**
 4477    -1  * @param {Element} element
 4478    -1  * @param {Object} textAlternatives
 4479    -1  * @return {?string}
 4480    -1  */
 4481    -1 axs.properties.getTextFromAriaLabelledby = function(element, textAlternatives) {
 4482    -1     var computedName = null;
 4483    -1     if (!element.hasAttribute('aria-labelledby'))
 4484    -1         return computedName;
 4485    -1 
 4486    -1     var labelledbyAttr = element.getAttribute('aria-labelledby');
 4487    -1     var labelledbyIds = labelledbyAttr.split(/\s+/);
 4488    -1     var labelledbyValue = {};
 4489    -1     labelledbyValue.valid = true;
 4490    -1     var labelledbyText = [];
 4491    -1     var labelledbyValues = [];
 4492    -1     for (var i = 0; i < labelledbyIds.length; i++) {
 4493    -1         var labelledby = {};
 4494    -1         labelledby.type = 'element';
 4495    -1         var labelledbyId = labelledbyIds[i];
 4496    -1         labelledby.value = labelledbyId;
 4497    -1         var labelledbyElement = document.getElementById(labelledbyId);
 4498    -1         if (!labelledbyElement) {
 4499    -1             labelledby.valid = false;
 4500    -1             labelledbyValue.valid = false;
 4501    -1             labelledby.errorMessage = { 'messageKey': 'noElementWithId', 'args': [labelledbyId] };
 4502    -1         } else {
 4503    -1             labelledby.valid = true;
 4504    -1             labelledby.text = axs.properties.findTextAlternatives(labelledbyElement, {}, true, true);
 4505    -1             labelledby.lastWord = axs.properties.getLastWord(labelledby.text);
 4506    -1             labelledbyText.push(labelledby.text);
 4507    -1             labelledby.element = labelledbyElement;
 4508    -1         }
 4509    -1         labelledbyValues.push(labelledby);
 4510    -1     }
 4511    -1     if (labelledbyValues.length > 0) {
 4512    -1         labelledbyValues[labelledbyValues.length - 1].last = true;
 4513    -1         labelledbyValue.values = labelledbyValues;
 4514    -1         labelledbyValue.text = labelledbyText.join(' ');
 4515    -1         labelledbyValue.lastWord = axs.properties.getLastWord(labelledbyValue.text);
 4516    -1         computedName = labelledbyValue.text;
 4517    -1         textAlternatives['ariaLabelledby'] = labelledbyValue;
 4518    -1     }
 4519    -1 
 4520    -1     return computedName;
 4521    -1 };
 4522    -1 
 4523    -1 
 4524    -1 /**
 4525    -1  * Determine the text description/label for an element.
 4526    -1  * For example will attempt to find the alt text for an image or label text for a form control.
 4527    -1  * @param {!Element} element
 4528    -1  * @param {!Object} textAlternatives An object that will be updated with information.
 4529    -1  * @param {?string} existingComputedname
 4530    -1  * @param {boolean} recursive Whether this method is being called recursively as described in
 4531    -1  *     http://www.w3.org/TR/wai-aria/roles#textalternativecomputation section 2A.
 4532    -1  * @return {Object}
 4533    -1  */
 4534    -1 axs.properties.getTextFromHostLanguageAttributes = function(element,
 4535    -1                                                             textAlternatives,
 4536    -1                                                             existingComputedname,
 4537    -1                                                             recursive) {
 4538    -1     var computedName = existingComputedname;
 4539    -1     if (axs.browserUtils.matchSelector(element, 'img') && element.hasAttribute('alt')) {
 4540    -1         var altValue = {};
 4541    -1         altValue.type = 'string';
 4542    -1         altValue.valid = true;
 4543    -1         altValue.text = element.getAttribute('alt');
 4544    -1         if (computedName)
 4545    -1             altValue.unused = true;
 4546    -1         else
 4547    -1             computedName = altValue.text;
 4548    -1         textAlternatives['alt'] = altValue;
 4549    -1     }
 4550    -1 
 4551    -1     var controlsSelector = ['input:not([type="hidden"]):not([disabled])',
 4552    -1                             'select:not([disabled])',
 4553    -1                             'textarea:not([disabled])',
 4554    -1                             'button:not([disabled])',
 4555    -1                             'video:not([disabled])'].join(', ');
 4556    -1     if (axs.browserUtils.matchSelector(element, controlsSelector) && !recursive) {
 4557    -1         if (element.hasAttribute('id')) {
 4558    -1             var labelForQuerySelector = 'label[for="' + element.id + '"]';
 4559    -1             var labelsFor = document.querySelectorAll(labelForQuerySelector);
 4560    -1             var labelForValue = {};
 4561    -1             var labelForValues = [];
 4562    -1             var labelForText = [];
 4563    -1             for (var i = 0; i < labelsFor.length; i++) {
 4564    -1                 var labelFor = {};
 4565    -1                 labelFor.type = 'element';
 4566    -1                 var label = labelsFor[i];
 4567    -1                 var labelText = axs.properties.findTextAlternatives(label, {}, true);
 4568    -1                 if (labelText && labelText.trim().length > 0) {
 4569    -1                     labelFor.text = labelText.trim();
 4570    -1                     labelForText.push(labelText.trim());
 4571    -1                 }
 4572    -1                 labelFor.element = label;
 4573    -1                 labelForValues.push(labelFor);
 4574    -1             }
 4575    -1             if (labelForValues.length > 0) {
 4576    -1                 labelForValues[labelForValues.length - 1].last = true;
 4577    -1                 labelForValue.values = labelForValues;
 4578    -1                 labelForValue.text = labelForText.join(' ');
 4579    -1                 labelForValue.lastWord = axs.properties.getLastWord(labelForValue.text);
 4580    -1                 if (computedName)
 4581    -1                     labelForValue.unused = true;
 4582    -1                 else
 4583    -1                     computedName = labelForValue.text;
 4584    -1                 textAlternatives['labelFor'] = labelForValue;
 4585    -1             }
 4586    -1         }
 4587    -1 
 4588    -1         var parent = axs.dom.parentElement(element);
 4589    -1         var labelWrappedValue = {};
 4590    -1         while (parent) {
 4591    -1             if (parent.tagName.toLowerCase() == 'label') {
 4592    -1                 var parentLabel = /** @type {HTMLLabelElement} */ (parent);
 4593    -1                 if (parentLabel.control == element) {
 4594    -1                     labelWrappedValue.type = 'element';
 4595    -1                     labelWrappedValue.text = axs.properties.findTextAlternatives(parentLabel, {}, true);
 4596    -1                     labelWrappedValue.lastWord = axs.properties.getLastWord(labelWrappedValue.text);
 4597    -1                     labelWrappedValue.element = parentLabel;
 4598    -1                     break;
 4599    -1                 }
 4600    -1             }
 4601    -1             parent = axs.dom.parentElement(parent);
 4602    -1         }
 4603    -1         if (labelWrappedValue.text) {
 4604    -1             if (computedName)
 4605    -1                 labelWrappedValue.unused = true;
 4606    -1             else
 4607    -1                 computedName = labelWrappedValue.text;
 4608    -1             textAlternatives['labelWrapped'] = labelWrappedValue;
 4609    -1         }
 4610    -1         // If all else fails input of type image can fall back to its alt text
 4611    -1         if (axs.browserUtils.matchSelector(element, 'input[type="image"]') && element.hasAttribute('alt')) {
 4612    -1             var altValue = {};
 4613    -1             altValue.type = 'string';
 4614    -1             altValue.valid = true;
 4615    -1             altValue.text = element.getAttribute('alt');
 4616    -1             if (computedName)
 4617    -1                 altValue.unused = true;
 4618    -1             else
 4619    -1                 computedName = altValue.text;
 4620    -1             textAlternatives['alt'] = altValue;
 4621    -1         }
 4622    -1         if (!Object.keys(textAlternatives).length)
 4623    -1             textAlternatives['noLabel'] = true;
 4624    -1     }
 4625    -1     return computedName;
 4626    -1 };
 4627    -1 
 4628    -1 /**
 4629    -1  * @param {?string} text
 4630    -1  * @return {?string}
 4631    -1  */
 4632    -1 axs.properties.getLastWord = function(text) {
 4633    -1     if (!text)
 4634    -1         return null;
 4635    -1 
 4636    -1     // TODO: this makes a lot of assumptions.
 4637    -1     var lastSpace = text.lastIndexOf(' ') + 1;
 4638    -1     var MAXLENGTH = 10;
 4639    -1     var cutoff = text.length - MAXLENGTH;
 4640    -1     var wordStart = lastSpace > cutoff ? lastSpace : cutoff;
 4641    -1     return text.substring(wordStart);
 4642    -1 };
 4643    -1 
 4644    -1 /**
 4645    -1  * @param {Node} node
 4646    -1  * @return {Object}
 4647    -1  */
 4648    -1 axs.properties.getTextProperties = function(node) {
 4649    -1     var textProperties = {};
 4650    -1     var computedName = axs.properties.findTextAlternatives(node, textProperties, false, true);
 4651    -1 
 4652    -1     if (Object.keys(textProperties).length == 0) {
 4653    -1         /** @type {Element} */ var element = axs.dom.asElement(node);
 4654    -1         if (element && axs.browserUtils.matchSelector(element, 'img')) {
 4655    -1             var altValue = {};
 4656    -1             altValue.valid = false;
 4657    -1             altValue.errorMessage = 'No alt value provided';
 4658    -1             textProperties['alt'] = altValue;
 4659    -1 
 4660    -1             var src = element.src;
 4661    -1             if (typeof src == 'string') {
 4662    -1                 var parts = src.split('/');
 4663    -1                 var filename = parts.pop();
 4664    -1                 var filenameValue = { text: filename };
 4665    -1                 textProperties['filename'] = filenameValue;
 4666    -1                 computedName = filename;
 4667    -1             }
 4668    -1         }
 4669    -1 
 4670    -1         if (!computedName)
 4671    -1             return null;
 4672    -1     }
 4673    -1 
 4674    -1     textProperties.hasProperties = Boolean(Object.keys(textProperties).length);
 4675    -1     textProperties.computedText = computedName;
 4676    -1     textProperties.lastWord = axs.properties.getLastWord(computedName);
 4677    -1     return textProperties;
 4678    -1 };
 4679    -1 
 4680    -1 /**
 4681    -1  * Finds any ARIA attributes (roles, states and properties) explicitly set on this element.
 4682    -1  * @param {Element} element
 4683    -1  * @return {Object}
 4684    -1  */
 4685    -1 axs.properties.getAriaProperties = function(element) {
 4686    -1     var ariaProperties = {};
 4687    -1     var statesAndProperties = axs.properties.getGlobalAriaProperties(element);
 4688    -1 
 4689    -1     for (var property in axs.constants.ARIA_PROPERTIES) {
 4690    -1         var attributeName = 'aria-' + property;
 4691    -1         if (element.hasAttribute(attributeName)) {
 4692    -1             var propertyValue = element.getAttribute(attributeName);
 4693    -1             statesAndProperties[attributeName] =
 4694    -1                 axs.utils.getAriaPropertyValue(attributeName, propertyValue, element);
 4695    -1         }
 4696    -1     }
 4697    -1     if (Object.keys(statesAndProperties).length > 0)
 4698    -1         ariaProperties['properties'] = axs.utils.values(statesAndProperties);
 4699    -1 
 4700    -1     var roles = axs.utils.getRoles(element);
 4701    -1     if (!roles) {
 4702    -1         if (Object.keys(ariaProperties).length)
 4703    -1             return ariaProperties;
 4704    -1         return null;
 4705    -1     }
 4706    -1     ariaProperties['roles'] = roles;
 4707    -1     if (!roles.valid || !roles['roles'])
 4708    -1         return ariaProperties;
 4709    -1 
 4710    -1     var roleDetails = roles['roles'];
 4711    -1     for (var i = 0; i < roleDetails.length; i++) {
 4712    -1         var role = roleDetails[i];
 4713    -1         if (!role.details || !role.details.propertiesSet)
 4714    -1             continue;
 4715    -1         for (var property in role.details.propertiesSet) {
 4716    -1             if (property in statesAndProperties)
 4717    -1                 continue;
 4718    -1             if (element.hasAttribute(property)) {
 4719    -1                 var propertyValue = element.getAttribute(property);
 4720    -1                 statesAndProperties[property] =
 4721    -1                     axs.utils.getAriaPropertyValue(property, propertyValue, element);
 4722    -1                 if ('values' in statesAndProperties[property]) {
 4723    -1                     var values = statesAndProperties[property].values;
 4724    -1                     values[values.length - 1].isLast = true;
 4725    -1                 }
 4726    -1             } else if (role.details.requiredPropertiesSet[property]) {
 4727    -1                 statesAndProperties[property] =
 4728    -1                     { 'name': property, 'valid': false, 'reason': 'Required property not set' };
 4729    -1             }
 4730    -1         }
 4731    -1     }
 4732    -1     if (Object.keys(statesAndProperties).length > 0)
 4733    -1         ariaProperties['properties'] = axs.utils.values(statesAndProperties);
 4734    -1     if (Object.keys(ariaProperties).length > 0)
 4735    -1         return ariaProperties;
 4736    -1     return null;
 4737    -1 };
 4738    -1 
 4739    -1 /**
 4740    -1  * Gets the ARIA properties found on this element which apply to all elements, not just elements with ARIA roles.
 4741    -1  * @param {Element} element
 4742    -1  * @return {!Object}
 4743    -1  */
 4744    -1 axs.properties.getGlobalAriaProperties = function(element) {
 4745    -1     var globalProperties = {};
 4746    -1     for (var property in axs.constants.GLOBAL_PROPERTIES) {
 4747    -1         if (element.hasAttribute(property)) {
 4748    -1             var propertyValue = element.getAttribute(property);
 4749    -1             globalProperties[property] =
 4750    -1                 axs.utils.getAriaPropertyValue(property, propertyValue, element);
 4751    -1         }
 4752    -1     }
 4753    -1     return globalProperties;
 4754    -1 };
 4755    -1 
 4756    -1 /**
 4757    -1  * @param {Element} element
 4758    -1  * @return {Object.<string, Object>}
 4759    -1  */
 4760    -1 axs.properties.getVideoProperties = function(element) {
 4761    -1     var videoSelector = 'video';
 4762    -1     if (!axs.browserUtils.matchSelector(element, videoSelector))
 4763    -1         return null;
 4764    -1     var videoProperties = {};
 4765    -1     videoProperties['captionTracks'] = axs.properties.getTrackElements(element, 'captions');
 4766    -1     videoProperties['descriptionTracks'] = axs.properties.getTrackElements(element, 'descriptions');
 4767    -1     videoProperties['chapterTracks'] = axs.properties.getTrackElements(element, 'chapters');
 4768    -1     // error if no text alternatives?
 4769    -1     return videoProperties;
 4770    -1 };
 4771    -1 
 4772    -1 /**
 4773    -1  * @param {Element} element
 4774    -1  * @param {string} kind
 4775    -1  * @return {Object}
 4776    -1  */
 4777    -1 axs.properties.getTrackElements = function(element, kind) {
 4778    -1     // error if resource is not available
 4779    -1     var trackElements = element.querySelectorAll('track[kind=' + kind + ']');
 4780    -1     var result = {};
 4781    -1     if (!trackElements.length) {
 4782    -1         result.valid = false;
 4783    -1         result.reason = { 'messageKey': 'noTracksProvided', 'args': [[kind]] };
 4784    -1         return result;
 4785    -1     }
 4786    -1     result.valid = true;
 4787    -1     var values = [];
 4788    -1     for (var i = 0; i < trackElements.length; i++) {
 4789    -1         var trackElement = {};
 4790    -1         var src = trackElements[i].getAttribute('src');
 4791    -1         var srcLang = trackElements[i].getAttribute('srcLang');
 4792    -1         var label = trackElements[i].getAttribute('label');
 4793    -1         if (!src) {
 4794    -1             trackElement.valid = false;
 4795    -1             trackElement.reason = { 'messageKey': 'noSrcProvided' };
 4796    -1         } else {
 4797    -1             trackElement.valid = true;
 4798    -1             trackElement.src = src;
 4799    -1         }
 4800    -1         var name = '';
 4801    -1         if (label) {
 4802    -1             name += label;
 4803    -1             if (srcLang)
 4804    -1                 name += ' ';
 4805    -1         }
 4806    -1         if (srcLang)
 4807    -1             name += '(' + srcLang + ')';
 4808    -1         if (name == '')
 4809    -1             name = '[' + { 'messageKey': 'unnamed' } + ']';
 4810    -1         trackElement.name = name;
 4811    -1         values.push(trackElement);
 4812    -1     }
 4813    -1     result.values = values;
 4814    -1     return result;
 4815    -1 };
 4816    -1 
 4817    -1 /**
 4818    -1  * @param {Node} node
 4819    -1  * @return {Object.<string, Object>}
 4820    -1  */
 4821    -1 axs.properties.getAllProperties = function(node) {
 4822    -1     /** @type {Element} */ var element = axs.dom.asElement(node);
 4823    -1     if (!element)
 4824    -1         return {};
 4825    -1 
 4826    -1     var allProperties = {};
 4827    -1     allProperties['ariaProperties'] = axs.properties.getAriaProperties(element);
 4828    -1     allProperties['colorProperties'] = axs.properties.getColorProperties(element);
 4829    -1     allProperties['focusProperties'] = axs.properties.getFocusProperties(element);
 4830    -1     allProperties['textProperties'] = axs.properties.getTextProperties(node);
 4831    -1     allProperties['videoProperties'] = axs.properties.getVideoProperties(element);
 4832    -1     return allProperties;
 4833    -1 };
 4834    -1 
 4835    -1 (function() {
 4836    -1     /**
 4837    -1      * Helper for implicit semantic functionality.
 4838    -1      * Can be made part of the public API if need be.
 4839    -1      * @param {Element} element
 4840    -1      * @return {?axs.constants.HtmlInfo}
 4841    -1      */
 4842    -1     function getHtmlInfo(element) {
 4843    -1         if (!element)
 4844    -1             return null;
 4845    -1         var tagName = element.tagName;
 4846    -1         if (!tagName)
 4847    -1             return null;
 4848    -1         tagName = tagName.toUpperCase();
 4849    -1         var infos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];
 4850    -1         if (!infos || !infos.length)
 4851    -1             return null;
 4852    -1         var defaultInfo = null;  // will contain the info with no specific selector if no others match
 4853    -1         for (var i = 0, len = infos.length; i < len; i++) {
 4854    -1             var htmlInfo = infos[i];
 4855    -1             if (htmlInfo.selector) {
 4856    -1                 if (axs.browserUtils.matchSelector(element, htmlInfo.selector))
 4857    -1                     return htmlInfo;
 4858    -1             } else {
 4859    -1                 defaultInfo = htmlInfo;
 4860    -1             }
 4861    -1         }
 4862    -1         return defaultInfo;
 4863    -1     }
 4864    -1 
 4865    -1     /**
 4866    -1      * @param {Element} element
 4867    -1      * @return {string} role
 4868    -1      */
 4869    -1     axs.properties.getImplicitRole = function(element) {
 4870    -1         var htmlInfo = getHtmlInfo(element);
 4871    -1         if (htmlInfo)
 4872    -1             return htmlInfo.role;
 4873    -1         return '';
 4874    -1     };
 4875    -1 
 4876    -1     /**
 4877    -1      * Determine if this element can take ANY ARIA attributes including roles, state and properties.
 4878    -1      * If false then even global attributes should not be used.
 4879    -1      * @param {Element} element
 4880    -1      * @return {boolean}
 4881    -1      */
 4882    -1     axs.properties.canTakeAriaAttributes = function(element) {
 4883    -1         var htmlInfo = getHtmlInfo(element);
 4884    -1         if (htmlInfo)
 4885    -1             return !htmlInfo.reserved;
 4886    -1         return true;
 4887    -1     };
 4888    -1 })();
 4889    -1 
 4890    -1 /**
 4891    -1  * This lists the ARIA attributes that are supported implicitly by native properties of this element.
 4892    -1  *
 4893    -1  * @param {Element} element The element to check.
 4894    -1  * @return {!Array.<string>} An array of ARIA attributes.
 4895    -1  *
 4896    -1  * example:
 4897    -1  *    var element = document.createElement("input");
 4898    -1  *    element.setAttribute("type", "range");
 4899    -1  *    var supported = axs.properties.getNativelySupportedAttributes(element);  // an array of ARIA attributes
 4900    -1  *    console.log(supported.indexOf("aria-valuemax") >=0);  // logs 'true'
 4901    -1  */
 4902    -1 axs.properties.getNativelySupportedAttributes = function(element) {
 4903    -1     var result = [];
 4904    -1     if (!element) {
 4905    -1         return result;
 4906    -1     }
 4907    -1     var testElement = element.cloneNode(false);  // gets rid of expandos
 4908    -1     var ariaAttributes = Object.keys(/** @type {!Object} */(axs.constants.ARIA_TO_HTML_ATTRIBUTE));
 4909    -1     for (var i = 0; i < ariaAttributes.length; i++) {
 4910    -1         var ariaAttribute = ariaAttributes[i];
 4911    -1         var nativeAttribute = axs.constants.ARIA_TO_HTML_ATTRIBUTE[ariaAttribute];
 4912    -1         if (nativeAttribute in testElement) {
 4913    -1             result[result.length] = ariaAttribute;
 4914    -1         }
 4915    -1     }
 4916    -1     return result;
 4917    -1 };
 4918    -1 
 4919    -1 (function() {
 4920    -1     var roleToSelectorCache = {};  // performance optimization, cache results from getSelectorForRole
 4921    -1 
 4922    -1     /**
 4923    -1      * Build a selector that will match elements which implicity or explicitly have this role.
 4924    -1      * Note that the selector will probably not look elegant but it will work.
 4925    -1      * @param {string} role
 4926    -1      * @return {string} selector
 4927    -1      */
 4928    -1     axs.properties.getSelectorForRole = function(role) {
 4929    -1         if (!role)
 4930    -1             return '';
 4931    -1         if (roleToSelectorCache[role] && roleToSelectorCache.hasOwnProperty(role))
 4932    -1             return roleToSelectorCache[role];
 4933    -1         var selectors = ['[role="' + role + '"]'];
 4934    -1         var tagNames = Object.keys(/** @type {!Object} */(axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO));
 4935    -1         tagNames.forEach(function(tagName) {
 4936    -1             var htmlInfos = axs.constants.TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName];
 4937    -1             if (htmlInfos && htmlInfos.length) {
 4938    -1                 for (var i = 0; i < htmlInfos.length; i++) {
 4939    -1                     var htmlInfo = htmlInfos[i];
 4940    -1                     if (htmlInfo.role === role) {
 4941    -1                         if (htmlInfo.selector) {
 4942    -1                             selectors[selectors.length] = htmlInfo.selector;
 4943    -1                         } else {
 4944    -1                             selectors[selectors.length] = tagName;  // Selectors API is not case sensitive.
 4945    -1                             break;  // No need to continue adding selectors since we will match the tag itself.
 4946    -1                         }
 4947    -1                     }
 4948    -1                 }
 4949    -1             }
 4950    -1         });
 4951    -1         return (roleToSelectorCache[role] = selectors.join(','));
 4952    -1     };
 4953    -1 })();
 4954    -1 
 4955    -1 },{}],9:[function(require,module,exports){
 4956   267 (function (global, factory) {
 4957   268 	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
 4958   269 	typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -6247,10 +1558,10 @@ axs.properties.getNativelySupportedAttributes = function(element) {
 6247  1558 
 6248  1559 }));
 6249  1560 
 6250    -1 },{}],10:[function(require,module,exports){
   -1  1561 },{}],4:[function(require,module,exports){
 6251  1562 (function (process,setImmediate){(function (){
 6252    -1 /*! axe v4.10.2
 6253    -1  * Copyright (c) 2015 - 2024 Deque Systems, Inc.
   -1  1563 /*! axe v4.10.3
   -1  1564  * Copyright (c) 2015 - 2025 Deque Systems, Inc.
 6254  1565  *
 6255  1566  * Your use of this Source Code Form is subject to the terms of the Mozilla Public
 6256  1567  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6273,7 +1584,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
 6273  1584     }, _typeof(o);
 6274  1585   }
 6275  1586   var axe = axe || {};
 6276    -1   axe.version = '4.10.2';
   -1  1587   axe.version = '4.10.3';
 6277  1588   if (typeof define === 'function' && define.amd) {
 6278  1589     define('axe-core', [], function() {
 6279  1590       return axe;
@@ -13194,6 +8505,9 @@ axs.properties.getNativelySupportedAttributes = function(element) {
13194  8505       parseStylesheet: function parseStylesheet() {
13195  8506         return parse_stylesheet_default;
13196  8507       },
   -1  8508       parseTabindex: function parseTabindex() {
   -1  8509         return parse_tabindex_default;
   -1  8510       },
13197  8511       performanceTimer: function performanceTimer() {
13198  8512         return performance_timer_default;
13199  8513       },
@@ -16550,9 +11864,8 @@ axs.properties.getNativelySupportedAttributes = function(element) {
16550 11864       var nodeAndDescendents = query_selector_all_default(virtualNode, '*');
16551 11865       var tabbableElements = nodeAndDescendents.filter(function(vNode) {
16552 11866         var isFocusable2 = vNode.isFocusable;
16553    -1         var tabIndex = vNode.actualNode.getAttribute('tabindex');
16554    -1         tabIndex = tabIndex && !isNaN(parseInt(tabIndex, 10)) ? parseInt(tabIndex) : null;
16555    -1         return tabIndex ? isFocusable2 && tabIndex >= 0 : isFocusable2;
   -1 11867         var tabIndex = parse_tabindex_default(vNode.actualNode.getAttribute('tabindex'));
   -1 11868         return tabIndex !== null ? isFocusable2 && tabIndex >= 0 : isFocusable2;
16556 11869       });
16557 11870       return tabbableElements;
16558 11871     }
@@ -16595,18 +11908,15 @@ axs.properties.getNativelySupportedAttributes = function(element) {
16595 11908       } else if (is_natively_focusable_default(vNode)) {
16596 11909         return true;
16597 11910       }
16598    -1       var tabindex = vNode.attr('tabindex');
16599    -1       if (tabindex && !isNaN(parseInt(tabindex, 10))) {
16600    -1         return true;
16601    -1       }
16602    -1       return false;
   -1 11911       var tabindex = parse_tabindex_default(vNode.attr('tabindex'));
   -1 11912       return tabindex !== null;
16603 11913     }
16604 11914     function _isInTabOrder(el) {
16605 11915       var _nodeLookup6 = _nodeLookup(el), vNode = _nodeLookup6.vNode;
16606 11916       if (vNode.props.nodeType !== 1) {
16607 11917         return false;
16608 11918       }
16609    -1       var tabindex = parseInt(vNode.attr('tabindex', 10));
   -1 11919       var tabindex = parse_tabindex_default(vNode.attr('tabindex'));
16610 11920       if (tabindex <= -1) {
16611 11921         return false;
16612 11922       }
@@ -17507,7 +12817,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
17507 12817         superclassRole: [ 'composite' ]
17508 12818       },
17509 12819       tabpanel: {
17510    -1         type: 'widget',
   -1 12820         type: 'structure',
17511 12821         allowedAttrs: [ 'aria-expanded' ],
17512 12822         superclassRole: [ 'section' ],
17513 12823         accessibleNameRequired: false
@@ -18086,7 +13396,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
18086 13396             }, {
18087 13397               hasAccessibleName: true
18088 13398             } ],
18089    -1             allowedRoles: [ 'button', 'checkbox', 'link', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'meter', 'option', 'progressbar', 'radio', 'scrollbar', 'separator', 'slider', 'switch', 'tab', 'treeitem', 'doc-cover' ]
   -1 13399             allowedRoles: [ 'button', 'checkbox', 'link', 'math', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'meter', 'option', 'progressbar', 'radio', 'scrollbar', 'separator', 'slider', 'switch', 'tab', 'treeitem', 'doc-cover' ]
18090 13400           },
18091 13401           usemap: {
18092 13402             matches: '[usemap]',
@@ -20144,7 +15454,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
20144 15454       });
20145 15455     }
20146 15456     function insertedIntoFocusOrder(el) {
20147    -1       var tabIndex = parseInt(el.getAttribute('tabindex'), 10);
   -1 15457       var tabIndex = parse_tabindex_default(el.getAttribute('tabindex'));
20148 15458       return tabIndex > -1 && _isFocusable(el) && !is_natively_focusable_default(el);
20149 15459     }
20150 15460     var inserted_into_focus_order_default = insertedIntoFocusOrder;
@@ -24867,12 +20177,8 @@ axs.properties.getNativelySupportedAttributes = function(element) {
24867 20177       };
24868 20178     }
24869 20179     function frameFocusable(frame) {
24870    -1       var tabIndex = frame.getAttribute('tabindex');
24871    -1       if (!tabIndex) {
24872    -1         return true;
24873    -1       }
24874    -1       var _int = parseInt(tabIndex, 10);
24875    -1       return isNaN(_int) || _int >= 0;
   -1 20180       var tabIndex = parse_tabindex_default(frame.getAttribute('tabindex'));
   -1 20181       return tabIndex === null || tabIndex >= 0;
24876 20182     }
24877 20183     function getBoundingSize(domNode) {
24878 20184       var width = parseInt(domNode.getAttribute('width'), 10);
@@ -25447,6 +20753,17 @@ axs.properties.getNativelySupportedAttributes = function(element) {
25447 20753       });
25448 20754     }
25449 20755     var parse_crossorigin_stylesheet_default = parseCrossOriginStylesheet;
   -1 20756     function parseTabindex(value) {
   -1 20757       if (typeof value !== 'string') {
   -1 20758         return null;
   -1 20759       }
   -1 20760       var match = value.trim().match(/^([-+]?\d+)/);
   -1 20761       if (match) {
   -1 20762         return Number(match[1]);
   -1 20763       }
   -1 20764       return null;
   -1 20765     }
   -1 20766     var parse_tabindex_default = parseTabindex;
25450 20767     var performanceTimer = function() {
25451 20768       function now() {
25452 20769         if (window.performance && window.performance) {
@@ -25752,8 +21069,14 @@ axs.properties.getNativelySupportedAttributes = function(element) {
25752 21069     }
25753 21070     function preloadMedia(_ref74) {
25754 21071       var _ref74$treeRoot = _ref74.treeRoot, treeRoot = _ref74$treeRoot === void 0 ? axe._tree[0] : _ref74$treeRoot;
25755    -1       var mediaVirtualNodes = query_selector_all_filter_default(treeRoot, 'video, audio', function(_ref75) {
   -1 21072       var mediaVirtualNodes = query_selector_all_filter_default(treeRoot, 'video[autoplay], audio[autoplay]', function(_ref75) {
25756 21073         var actualNode = _ref75.actualNode;
   -1 21074         if (actualNode.preload === 'none' && actualNode.readyState === 0 && actualNode.networkState !== actualNode.NETWORK_LOADING) {
   -1 21075           return false;
   -1 21076         }
   -1 21077         if (actualNode.hasAttribute('paused') || actualNode.hasAttribute('muted')) {
   -1 21078           return false;
   -1 21079         }
25757 21080         if (actualNode.hasAttribute('src')) {
25758 21081           return !!actualNode.getAttribute('src');
25759 21082         }
@@ -31109,16 +26432,20 @@ axs.properties.getNativelySupportedAttributes = function(element) {
31109 26432     }
31110 26433     var css_orientation_lock_evaluate_default = cssOrientationLockEvaluate;
31111 26434     function noAutoplayAudioEvaluate(node, options) {
   -1 26435       var hasControls = node.hasAttribute('controls');
   -1 26436       if (node.hasAttribute('loop')) {
   -1 26437         return hasControls;
   -1 26438       }
31112 26439       if (!node.duration) {
31113 26440         console.warn('axe.utils.preloadMedia did not load metadata');
31114 26441         return void 0;
31115 26442       }
31116 26443       var _options$allowedDurat = options.allowedDuration, allowedDuration = _options$allowedDurat === void 0 ? 3 : _options$allowedDurat;
31117 26444       var playableDuration = getPlayableDuration(node);
31118    -1       if (playableDuration <= allowedDuration && !node.hasAttribute('loop')) {
   -1 26445       if (playableDuration <= allowedDuration) {
31119 26446         return true;
31120 26447       }
31121    -1       if (!node.hasAttribute('controls')) {
   -1 26448       if (!hasControls) {
31122 26449         return false;
31123 26450       }
31124 26451       return true;
@@ -31673,8 +27000,8 @@ axs.properties.getNativelySupportedAttributes = function(element) {
31673 27000     }
31674 27001     var alt_space_value_evaluate_default = altSpaceValueEvaluate;
31675 27002     function tabindexEvaluate(node, options, virtualNode) {
31676    -1       var tabIndex = parseInt(virtualNode.attr('tabindex'), 10);
31677    -1       return isNaN(tabIndex) ? true : tabIndex <= 0;
   -1 27003       var tabIndex = parse_tabindex_default(virtualNode.attr('tabindex'));
   -1 27004       return tabIndex === null || tabIndex <= 0;
31678 27005     }
31679 27006     var tabindex_evaluate_default = tabindexEvaluate;
31680 27007     function noFocusableContentEvaluate(node, options, virtualNode) {
@@ -31718,8 +27045,8 @@ axs.properties.getNativelySupportedAttributes = function(element) {
31718 27045       return retVal;
31719 27046     }
31720 27047     function usesUnreliableHidingStrategy(vNode) {
31721    -1       var tabIndex = parseInt(vNode.attr('tabindex'), 10);
31722    -1       return !isNaN(tabIndex) && tabIndex < 0;
   -1 27048       var tabIndex = parse_tabindex_default(vNode.attr('tabindex'));
   -1 27049       return tabIndex !== null && tabIndex < 0;
31723 27050     }
31724 27051     function landmarkIsTopLevelEvaluate(node) {
31725 27052       var landmarks = get_aria_roles_by_type_default('landmark');
@@ -31791,9 +27118,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
31791 27118     }
31792 27119     var focusable_not_tabbable_evaluate_default = focusableNotTabbableEvaluate;
31793 27120     function focusableNoNameEvaluate(node, options, virtualNode) {
31794    -1       var tabIndex = virtualNode.attr('tabindex');
31795    -1       var inFocusOrder = _isFocusable(virtualNode) && tabIndex > -1;
31796    -1       if (!inFocusOrder) {
   -1 27121       if (!_isInTabOrder(virtualNode)) {
31797 27122         return false;
31798 27123       }
31799 27124       try {
@@ -32355,7 +27680,8 @@ axs.properties.getNativelySupportedAttributes = function(element) {
32355 27680       navigation: true,
32356 27681       region: true,
32357 27682       search: false,
32358    -1       status: true
   -1 27683       status: true,
   -1 27684       tabpanel: true
32359 27685     };
32360 27686     function validScrollableTagName(node) {
32361 27687       var nodeName2 = node.nodeName.toUpperCase();
@@ -33258,8 +28584,8 @@ axs.properties.getNativelySupportedAttributes = function(element) {
33258 28584     }
33259 28585     var no_role_matches_default = noRoleMatches;
33260 28586     function noNegativeTabindexMatches(node, virtualNode) {
33261    -1       var tabindex = parseInt(virtualNode.attr('tabindex'), 10);
33262    -1       return isNaN(tabindex) || tabindex >= 0;
   -1 28587       var tabindex = parse_tabindex_default(virtualNode.attr('tabindex'));
   -1 28588       return tabindex === null || tabindex >= 0;
33263 28589     }
33264 28590     var no_negative_tabindex_matches_default = noNegativeTabindexMatches;
33265 28591     function noNamingMethodMatches(node, virtualNode) {
@@ -33589,14 +28915,14 @@ axs.properties.getNativelySupportedAttributes = function(element) {
33589 28915         return false;
33590 28916       }
33591 28917       var role = virtualNode.attr('role');
33592    -1       var tabIndex = virtualNode.attr('tabindex');
33593    -1       if (tabIndex === '-1' && role) {
   -1 28918       var tabIndex = parse_tabindex_default(virtualNode.attr('tabindex'));
   -1 28919       if (tabIndex < 0 && role) {
33594 28920         var roleDef = standards_default.ariaRoles[role];
33595 28921         if (roleDef === void 0 || roleDef.type !== 'widget') {
33596 28922           return false;
33597 28923         }
33598 28924       }
33599    -1       if (tabIndex === '-1' && virtualNode.actualNode && !_isVisibleOnScreen(virtualNode) && !_isVisibleToScreenReaders(virtualNode)) {
   -1 28925       if (tabIndex < 0 && virtualNode.actualNode && !_isVisibleOnScreen(virtualNode) && !_isVisibleToScreenReaders(virtualNode)) {
33600 28926         return false;
33601 28927       }
33602 28928       return true;
@@ -34087,16 +29413,18 @@ axs.properties.getNativelySupportedAttributes = function(element) {
34087 29413           });
34088 29414         });
34089 29415       });
34090    -1       q.defer(function(res) {
34091    -1         return setTimeout(res, 0);
34092    -1       });
34093    -1       if (options.performanceTimer) {
34094    -1         this._logRulePerformance();
34095    -1       }
34096 29416       q.then(function() {
34097    -1         return resolve(ruleResult);
   -1 29417         if (options.performanceTimer) {
   -1 29418           _this9._logRulePerformance();
   -1 29419         }
   -1 29420         setTimeout(function() {
   -1 29421           resolve(ruleResult);
   -1 29422         }, 0);
34098 29423       })['catch'](function(error) {
34099    -1         return reject(error);
   -1 29424         if (options.performanceTimer) {
   -1 29425           _this9._logRulePerformance();
   -1 29426         }
   -1 29427         reject(error);
34100 29428       });
34101 29429     };
34102 29430     Rule.prototype.runSync = function runSync2(context) {
@@ -36511,7 +31839,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
36511 31839           impact: 'critical',
36512 31840           messages: {
36513 31841             pass: 'The multimedia element has a captions track',
36514    -1             incomplete: 'Check that captions is available for the element'
   -1 31842             incomplete: 'Check that captions are available for the element'
36515 31843           }
36516 31844         },
36517 31845         'frame-tested': {
@@ -38953,7 +34281,7 @@ axs.properties.getNativelySupportedAttributes = function(element) {
38953 34281   });
38954 34282 })(typeof window === 'object' ? window : this);
38955 34283 }).call(this)}).call(this,require('_process'),require("timers").setImmediate)
38956    -1 },{"_process":1,"timers":2}],11:[function(require,module,exports){
   -1 34284 },{"_process":1,"timers":2}],5:[function(require,module,exports){
38957 34285 "use strict";
38958 34286 
38959 34287 exports.__esModule = true;
@@ -38999,7 +34327,7 @@ function computeAccessibleDescription(root) {
38999 34327   return description;
39000 34328 }
39001 34329 
39002    -1 },{"./accessible-name-and-description":12,"./util":20}],12:[function(require,module,exports){
   -1 34330 },{"./accessible-name-and-description":6,"./util":14}],6:[function(require,module,exports){
39003 34331 "use strict";
39004 34332 
39005 34333 exports.__esModule = true;
@@ -39562,7 +34890,7 @@ function computeTextAlternative(root) {
39562 34890   }));
39563 34891 }
39564 34892 
39565    -1 },{"./polyfills/SetLike":18,"./polyfills/array.from":19,"./util":20}],13:[function(require,module,exports){
   -1 34893 },{"./polyfills/SetLike":12,"./polyfills/array.from":13,"./util":14}],7:[function(require,module,exports){
39566 34894 "use strict";
39567 34895 
39568 34896 exports.__esModule = true;
@@ -39590,7 +34918,7 @@ function computeAccessibleName(root) {
39590 34918   return (0, _accessibleNameAndDescription.computeTextAlternative)(root, options);
39591 34919 }
39592 34920 
39593    -1 },{"./accessible-name-and-description":12,"./util":20}],14:[function(require,module,exports){
   -1 34921 },{"./accessible-name-and-description":6,"./util":14}],8:[function(require,module,exports){
39594 34922 "use strict";
39595 34923 
39596 34924 exports.__esModule = true;
@@ -39783,7 +35111,7 @@ function getExplicitRole(element) {
39783 35111   return null;
39784 35112 }
39785 35113 
39786    -1 },{"./util":20}],15:[function(require,module,exports){
   -1 35114 },{"./util":14}],9:[function(require,module,exports){
39787 35115 "use strict";
39788 35116 
39789 35117 exports.__esModule = true;
@@ -39811,7 +35139,7 @@ var _isDisabled = require("./is-disabled");
39811 35139 exports.isDisabled = _isDisabled.isDisabled;
39812 35140 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
39813 35141 
39814    -1 },{"./accessible-description":11,"./accessible-name":13,"./getRole":14,"./is-disabled":16,"./is-inaccessible":17}],16:[function(require,module,exports){
   -1 35142 },{"./accessible-description":5,"./accessible-name":7,"./getRole":8,"./is-disabled":10,"./is-inaccessible":11}],10:[function(require,module,exports){
39815 35143 "use strict";
39816 35144 
39817 35145 exports.__esModule = true;
@@ -39832,7 +35160,7 @@ function isDisabled(element) {
39832 35160   return elementsSupportingDisabledAttribute.has(localName) && element.hasAttribute("disabled") ? true : element.getAttribute("aria-disabled") === "true";
39833 35161 }
39834 35162 
39835    -1 },{"./getRole":14}],17:[function(require,module,exports){
   -1 35163 },{"./getRole":8}],11:[function(require,module,exports){
39836 35164 "use strict";
39837 35165 
39838 35166 exports.__esModule = true;
@@ -39901,7 +35229,7 @@ function isSubtreeInaccessible(element) {
39901 35229   return false;
39902 35230 }
39903 35231 
39904    -1 },{}],18:[function(require,module,exports){
   -1 35232 },{}],12:[function(require,module,exports){
39905 35233 "use strict";
39906 35234 
39907 35235 exports.__esModule = true;
@@ -39965,7 +35293,7 @@ var SetLike = /*#__PURE__*/function () {
39965 35293 }();
39966 35294 var _default = exports.default = typeof Set === "undefined" ? Set : SetLike;
39967 35295 
39968    -1 },{}],19:[function(require,module,exports){
   -1 35296 },{}],13:[function(require,module,exports){
39969 35297 "use strict";
39970 35298 
39971 35299 exports.__esModule = true;
@@ -40057,7 +35385,7 @@ function arrayFrom(arrayLike, mapFn) {
40057 35385   return A;
40058 35386 }
40059 35387 
40060    -1 },{}],20:[function(require,module,exports){
   -1 35388 },{}],14:[function(require,module,exports){
40061 35389 "use strict";
40062 35390 
40063 35391 function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
@@ -40162,7 +35490,7 @@ function hasAnyConcreteRoles(node, roles) {
40162 35490   return false;
40163 35491 }
40164 35492 
40165    -1 },{"./getRole":14}],21:[function(require,module,exports){
   -1 35493 },{"./getRole":8}],15:[function(require,module,exports){
40166 35494 /*@license
40167 35495 CalcNames: The AccName Computation Prototype, compute the Name and Description property values for a DOM node
40168 35496 Returns an object with 'name' and 'desc' properties.
@@ -40179,7 +35507,7 @@ Distributed under the terms of the Open Source Initiative OSI - MIT License
40179 35507     window[nameSpace] = {};
40180 35508     nameSpace = window[nameSpace];
40181 35509   }
40182    -1   nameSpace.getAccNameVersion = "2.62";
   -1 35510   nameSpace.getAccNameVersion = "2.64";
40183 35511   // AccName Computation Prototype
40184 35512   nameSpace.getAccName = nameSpace.calcNames = function (
40185 35513     node,
@@ -40726,7 +36054,8 @@ Plus roles extended for the Role Parity project.
40726 36054                   false;
40727 36055 
40728 36056                 var nAlt =
40729    -1                   rolePresentation && nTag === "img"
   -1 36057                   (btnType !== "image" && nTag !== "img" && nTag !== "area") ||
   -1 36058                   (rolePresentation && nTag === "img")
40730 36059                     ? ""
40731 36060                     : trim(node.alt || node.getAttribute("alt"));
40732 36061 
@@ -41869,36 +37198,10 @@ Plus roles extended for the Role Parity project.
41869 37198   }
41870 37199 })();
41871 37200 
41872    -1 },{}],22:[function(require,module,exports){
41873    -1 (function (global){(function (){
41874    -1 global.goog = {
41875    -1 	provide: function() {},
41876    -1 	require: function() {},
41877    -1 };
41878    -1 global.axs = {
41879    -1 	browserUtils: {},
41880    -1 	color: {},
41881    -1 	constants: {},
41882    -1 	dom: {},
41883    -1 	utils: {},
41884    -1 	properties: {},
41885    -1 };
41886    -1 
41887    -1 require('accessibility-developer-tools/src/js/Constants');
41888    -1 require('accessibility-developer-tools/src/js/AccessibilityUtils');
41889    -1 require('accessibility-developer-tools/src/js/BrowserUtils');
41890    -1 require('accessibility-developer-tools/src/js/Color');
41891    -1 require('accessibility-developer-tools/src/js/DOMUtils');
41892    -1 require('accessibility-developer-tools/src/js/Properties');
41893    -1 
41894    -1 module.exports = global.axs;
41895    -1 
41896    -1 }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
41897    -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}],23:[function(require,module,exports){
41898    -1 var ariaApi = require('aria-api');
   -1 37201 },{}],16:[function(require,module,exports){
   -1 37202 var ariaApi = require('aria-api/dist/aria.js');
41899 37203 var accdc = require('w3c-alternative-text-computation');
41900 37204 var axe = require('axe-core');
41901    -1 var axs = require('./axs');
41902 37205 var domAccessibilityApi = require('dom-accessibility-api');
41903 37206 
41904 37207 var form = document.querySelector('#ba-form');
@@ -41924,7 +37227,7 @@ var implementations = [{
41924 37227 		};
41925 37228 	},
41926 37229 }, {
41927    -1 	name: 'WhatSock (2.62)',
   -1 37230 	name: 'WhatSock (2.64)',
41928 37231 	url: 'https://github.com/WhatSock/w3c-alternative-text-computation',
41929 37232 	fn: accdc.calcNames,
41930 37233 }, {
@@ -41938,7 +37241,7 @@ var implementations = [{
41938 37241 		};
41939 37242 	},
41940 37243 }, {
41941    -1 	name: 'axe (4.10.2)',
   -1 37244 	name: 'axe (4.10.3)',
41942 37245 	url: 'https://github.com/dequelabs/axe-core',
41943 37246 	fn: function(el) {
41944 37247 		axe._tree = axe.utils.getFlattenedTree(document.body);
@@ -41948,21 +37251,6 @@ var implementations = [{
41948 37251 			role: ex(axe.commons.aria.getRole, [el]),
41949 37252 		};
41950 37253 	},
41951    -1 }, {
41952    -1 	name: 'axs (2.12.0)',
41953    -1 	url: 'https://github.com/GoogleChrome/accessibility-developer-tools',
41954    -1 	fn: function(el) {
41955    -1 		return {
41956    -1 			name: ex(axs.properties.findTextAlternatives, [el, {}]),
41957    -1 			desc: '-',
41958    -1 			role: ex(function() {
41959    -1 				var roles = axs.utils.getRoles(el, true);
41960    -1 				if (roles) {
41961    -1 					return roles.roles.map(x => x.name).join(' ');
41962    -1 				}
41963    -1 			}),
41964    -1 		};
41965    -1 	},
41966 37254 }];
41967 37255 
41968 37256 var createTd = function(text, url) {
@@ -42022,4 +37310,4 @@ try {
42022 37310 	});
42023 37311 }
42024 37312 
42025    -1 },{"./axs":22,"aria-api":9,"axe-core":10,"dom-accessibility-api":15,"w3c-alternative-text-computation":21}]},{},[23]);
   -1 37313 },{"aria-api/dist/aria.js":3,"axe-core":4,"dom-accessibility-api":9,"w3c-alternative-text-computation":15}]},{},[16]);