Home | History | Annotate | Download | only in common
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * @fileoverview A bunch of predicates that take as input an array of
      7  * nodes with the unique ancestors of a node. They output true if a
      8  * certain category of node has been found.
      9  *
     10  */
     11 
     12 goog.provide('cvox.DomPredicates');
     13 
     14 
     15 /**
     16  * Checkbox.
     17  * @param {Array.<Node>} nodes An array of nodes to check.
     18  * @return {?Node} Node in the array that is a checkbox.
     19  */
     20 cvox.DomPredicates.checkboxPredicate = function(nodes) {
     21   for (var i = 0; i < nodes.length; i++) {
     22     if ((nodes[i].getAttribute &&
     23          nodes[i].getAttribute('role') == 'checkbox') ||
     24         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'checkbox')) {
     25       return nodes[i];
     26     }
     27   }
     28   return null;
     29 };
     30 
     31 
     32 /**
     33  * Radio button.
     34  * @param {Array.<Node>} nodes An array of nodes to check.
     35  * @return {?Node} Node in the array that is a radio button.
     36  */
     37 cvox.DomPredicates.radioPredicate = function(nodes) {
     38   for (var i = 0; i < nodes.length; i++) {
     39     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'radio') ||
     40         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'radio')) {
     41       return nodes[i];
     42     }
     43   }
     44   return null;
     45 };
     46 
     47 
     48 /**
     49  * Slider.
     50  * @param {Array.<Node>} nodes An array of nodes to check.
     51  * @return {?Node} Node in the array that is a slider.
     52  */
     53 cvox.DomPredicates.sliderPredicate = function(nodes) {
     54   for (var i = 0; i < nodes.length; i++) {
     55     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'slider') ||
     56         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'range')) {
     57       return nodes[i];
     58     }
     59   }
     60   return null;
     61 };
     62 
     63 
     64 /**
     65  * Graphic.
     66  * @param {Array.<Node>} nodes An array of nodes to check.
     67  * @return {?Node} Node in the array that is a graphic.
     68  */
     69 cvox.DomPredicates.graphicPredicate = function(nodes) {
     70   for (var i = 0; i < nodes.length; i++) {
     71     if (nodes[i].tagName == 'IMG' ||
     72         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'img')) {
     73       return nodes[i];
     74     }
     75   }
     76   return null;
     77 };
     78 
     79 
     80 /**
     81  * Button.
     82  * @param {Array.<Node>} nodes An array of nodes to check.
     83  * @return {?Node} Node in the array that is a button.
     84  */
     85 cvox.DomPredicates.buttonPredicate = function(nodes) {
     86   for (var i = 0; i < nodes.length; i++) {
     87     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'button') ||
     88         nodes[i].tagName == 'BUTTON' ||
     89         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'submit') ||
     90         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'button') ||
     91         (nodes[i].tagName == 'INPUT' && nodes[i].type == 'reset')) {
     92       return nodes[i];
     93     }
     94   }
     95   return null;
     96 };
     97 
     98 
     99 /**
    100  * Combo box.
    101  * @param {Array.<Node>} nodes An array of nodes to check.
    102  * @return {?Node} Node in the array that is a combo box.
    103  */
    104 cvox.DomPredicates.comboBoxPredicate = function(nodes) {
    105   for (var i = 0; i < nodes.length; i++) {
    106     if ((nodes[i].getAttribute &&
    107          nodes[i].getAttribute('role') == 'combobox') ||
    108         nodes[i].tagName == 'SELECT') {
    109       return nodes[i];
    110     }
    111   }
    112   return null;
    113 };
    114 
    115 
    116 /**
    117  * Editable text field.
    118  * @param {Array.<Node>} nodes An array of nodes to check.
    119  * @return {?Node} Node in the array that is an editable text field.
    120  */
    121 cvox.DomPredicates.editTextPredicate = function(nodes) {
    122   for (var i = 0; i < nodes.length; i++) {
    123     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'textbox') ||
    124         nodes[i].tagName == 'TEXTAREA' ||
    125         nodes[i].isContentEditable ||
    126         (nodes[i].tagName == 'INPUT' &&
    127         cvox.DomUtil.isInputTypeText(nodes[i]))) {
    128       return nodes[i];
    129     }
    130   }
    131   return null;
    132 };
    133 
    134 
    135 /**
    136  * Heading.
    137  * @param {Array.<Node>} nodes An array of nodes to check.
    138  * @return {?Node} Node in the array that is a heading.
    139  */
    140 cvox.DomPredicates.headingPredicate = function(nodes) {
    141   for (var i = 0; i < nodes.length; i++) {
    142     if (nodes[i].getAttribute &&
    143         nodes[i].getAttribute('role') == 'heading') {
    144       return nodes[i];
    145     }
    146     switch (nodes[i].tagName) {
    147       case 'H1':
    148       case 'H2':
    149       case 'H3':
    150       case 'H4':
    151       case 'H5':
    152       case 'H6':
    153         return nodes[i];
    154     }
    155   }
    156   return null;
    157 };
    158 
    159 
    160 /**
    161  * Heading level 1.
    162  * @param {Array.<Node>} nodes An array of nodes to check.
    163  * @return {?Node} Node in the array that is a heading level 1.
    164  * TODO: handle ARIA headings with ARIA heading levels?
    165  */
    166 cvox.DomPredicates.heading1Predicate = function(nodes) {
    167   return cvox.DomPredicates.containsTagName_(nodes, 'H1');
    168 };
    169 
    170 
    171 /**
    172  * Heading level 2.
    173  * @param {Array.<Node>} nodes An array of nodes to check.
    174  * @return {?Node} Node in the array that is a heading level 2.
    175  */
    176 cvox.DomPredicates.heading2Predicate = function(nodes) {
    177   return cvox.DomPredicates.containsTagName_(nodes, 'H2');
    178 };
    179 
    180 
    181 /**
    182  * Heading level 3.
    183  * @param {Array.<Node>} nodes An array of nodes to check.
    184  * @return {?Node} Node in the array that is a heading level 3.
    185  */
    186 cvox.DomPredicates.heading3Predicate = function(nodes) {
    187   return cvox.DomPredicates.containsTagName_(nodes, 'H3');
    188 };
    189 
    190 
    191 /**
    192  * Heading level 4.
    193  * @param {Array.<Node>} nodes An array of nodes to check.
    194  * @return {?Node} Node in the array that is a heading level 4.
    195  */
    196 cvox.DomPredicates.heading4Predicate = function(nodes) {
    197   return cvox.DomPredicates.containsTagName_(nodes, 'H4');
    198 };
    199 
    200 
    201 /**
    202  * Heading level 5.
    203  * @param {Array.<Node>} nodes An array of nodes to check.
    204  * @return {?Node} Node in the array that is a heading level 5.
    205  */
    206 cvox.DomPredicates.heading5Predicate = function(nodes) {
    207   return cvox.DomPredicates.containsTagName_(nodes, 'H5');
    208 };
    209 
    210 
    211 /**
    212  * Heading level 6.
    213  * @param {Array.<Node>} nodes An array of nodes to check.
    214  * @return {?Node} Node in the array that is a heading level 6.
    215  */
    216 cvox.DomPredicates.heading6Predicate = function(nodes) {
    217   return cvox.DomPredicates.containsTagName_(nodes, 'H6');
    218 };
    219 
    220 
    221 /**
    222  * Link.
    223  * @param {Array.<Node>} nodes An array of nodes to check.
    224  * @return {?Node} Node in the array that is a link.
    225  */
    226 cvox.DomPredicates.linkPredicate = function(nodes) {
    227   for (var i = 0; i < nodes.length; i++) {
    228     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'link') ||
    229         (nodes[i].tagName == 'A' && nodes[i].href)) {
    230       return nodes[i];
    231     }
    232   }
    233   return null;
    234 };
    235 
    236 
    237 /**
    238  * Table.
    239  * @param {Array.<Node>} nodes An array of nodes to check.
    240  * @return {?Node} Node in the array that is a data table.
    241  */
    242 cvox.DomPredicates.tablePredicate = function(nodes) {
    243   // TODO(stoarca): Captions should always be allowed!!
    244   var node = cvox.DomUtil.findTableNodeInList(nodes, {allowCaptions: true});
    245   if (node && !cvox.DomUtil.isLayoutTable(node)) {
    246     return node;
    247   } else {
    248     return null;
    249   }
    250 };
    251 
    252 /**
    253  * Table Cell.
    254  * @param {Array.<Node>} nodes An array of nodes to check.
    255  * @return {?Node} Node in the array that is a table cell.
    256  */
    257 cvox.DomPredicates.cellPredicate = function(nodes) {
    258   for (var i = nodes.length - 1; i >= 0; --i) {
    259     var node = nodes[i];
    260     if (node.tagName == 'TD' ||
    261         node.tagName == 'TH' ||
    262         (node.getAttribute && node.getAttribute('role') == 'gridcell')) {
    263       return node;
    264     }
    265   }
    266   return null;
    267 };
    268 
    269 
    270 /**
    271  * Visited link.
    272  * @param {Array.<Node>} nodes An array of nodes to check.
    273  * @return {?Node} Node in the array that is a visited link.
    274  */
    275 cvox.DomPredicates.visitedLinkPredicate = function(nodes) {
    276   for (var i = nodes.length - 1; i >= 0; --i) {
    277     if (cvox.DomPredicates.linkPredicate([nodes[i]]) &&
    278         cvox.ChromeVox.visitedUrls[nodes[i].href]) {
    279       return nodes[i];
    280     }
    281   }
    282 };
    283 
    284 
    285 /**
    286  * List.
    287  * @param {Array.<Node>} nodes An array of nodes to check.
    288  * @return {?Node} Node in the array that is a list.
    289  */
    290 cvox.DomPredicates.listPredicate = function(nodes) {
    291   for (var i = 0; i < nodes.length; i++) {
    292     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'list') ||
    293         nodes[i].tagName == 'UL' ||
    294         nodes[i].tagName == 'OL') {
    295       return nodes[i];
    296     }
    297   }
    298   return null;
    299 };
    300 
    301 
    302 /**
    303  * List item.
    304  * @param {Array.<Node>} nodes An array of nodes to check.
    305  * @return {?Node} Node in the array that is a list item.
    306  */
    307 cvox.DomPredicates.listItemPredicate = function(nodes) {
    308   for (var i = 0; i < nodes.length; i++) {
    309     if ((nodes[i].getAttribute &&
    310          nodes[i].getAttribute('role') == 'listitem') ||
    311         nodes[i].tagName == 'LI') {
    312       return nodes[i];
    313     }
    314   }
    315   return null;
    316 };
    317 
    318 
    319 /**
    320  * Blockquote.
    321  * @param {Array.<Node>} nodes An array of nodes to check.
    322  * @return {?Node} Node in the array that is a blockquote.
    323  */
    324 cvox.DomPredicates.blockquotePredicate = function(nodes) {
    325   return cvox.DomPredicates.containsTagName_(nodes, 'BLOCKQUOTE');
    326 };
    327 
    328 
    329 /**
    330  * Form field.
    331  * @param {Array.<Node>} nodes An array of nodes to check.
    332  * @return {?Node} Node in the array that is any type of form field.
    333  */
    334 cvox.DomPredicates.formFieldPredicate = function(nodes) {
    335   for (var i = 0; i < nodes.length; i++) {
    336     if (cvox.DomUtil.isControl(nodes[i])) {
    337       return nodes[i];
    338     }
    339   }
    340   return null;
    341 };
    342 
    343 
    344 /**
    345  * ARIA landmark.
    346  * @param {Array.<Node>} nodes An array of nodes to check.
    347  * @return {?Node} Node in the array that is an ARIA landmark.
    348  */
    349 cvox.DomPredicates.landmarkPredicate = function(nodes) {
    350   for (var i = 0; i < nodes.length; i++) {
    351     if (cvox.AriaUtil.isLandmark(nodes[i])) {
    352       return nodes[i];
    353     }
    354   }
    355   return null;
    356 };
    357 
    358 
    359 /**
    360  * @param {Array} arr Array of nodes.
    361  * @param {string} tagName The name of the tag.
    362  * @return {?Node} Node if obj is in the array.
    363  * @private
    364  */
    365 cvox.DomPredicates.containsTagName_ = function(arr, tagName) {
    366   var i = arr.length;
    367   while (i--) {
    368     if (arr[i].tagName == tagName) {
    369       return arr[i];
    370     }
    371   }
    372   return null;
    373 };
    374 
    375 
    376 /**
    377  * MathML expression
    378  * @param {Array.<Node>} nodes An array of nodes to check.
    379  * @return {?Node} Node in the array that is a math expression.
    380  */
    381 cvox.DomPredicates.mathPredicate = function(nodes) {
    382   return cvox.DomUtil.findMathNodeInList(nodes);
    383 };
    384 
    385 /**
    386  * SECTION: A section is anything that indicates a new section. This includes
    387  * headings and landmarks.
    388  * @param {Array.<Node>} nodes An array of nodes to check.
    389  * @return {?Node} Node in the array that is considered a section marker.
    390  */
    391 cvox.DomPredicates.sectionPredicate = function(nodes) {
    392   for (var i = 0; i < nodes.length; i++) {
    393     if (cvox.DomUtil.isSemanticElt(nodes[i])) {
    394       return nodes[i];
    395     }
    396     if (cvox.AriaUtil.isLandmark(nodes[i])) {
    397       return nodes[i];
    398     }
    399     if (nodes[i].getAttribute &&
    400         nodes[i].getAttribute('role') == 'heading') {
    401       return nodes[i];
    402     }
    403     switch (nodes[i].tagName) {
    404       case 'H1':
    405       case 'H2':
    406       case 'H3':
    407       case 'H4':
    408       case 'H5':
    409       case 'H6':
    410         return nodes[i];
    411     }
    412   }
    413   return null;
    414 };
    415 
    416 /**
    417  * CONTROL: A control is anything that the user can interact with. This includes
    418  * form fields and links.
    419  * @param {Array.<Node>} nodes An array of nodes to check.
    420  * @return {?Node} Node in the array that is considered a control.
    421  */
    422 cvox.DomPredicates.controlPredicate = function(nodes) {
    423   for (var i = 0; i < nodes.length; i++) {
    424     if (cvox.DomUtil.isControl(nodes[i])) {
    425       return nodes[i];
    426     }
    427     if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'link') ||
    428         (nodes[i].tagName == 'A' && nodes[i].href)) {
    429       return nodes[i];
    430     }
    431   }
    432   return null;
    433 };
    434 
    435 /**
    436  * Caption.
    437  * @param {Array.<Node>} nodes An array of nodes to check.
    438  * @return {?Node} Node in the array that is a caption.
    439  */
    440 cvox.DomPredicates.captionPredicate = function(nodes) {
    441   for (var i = 0; i < nodes.length; i++) {
    442     if (nodes[i].tagName == 'CAPTION') {
    443       return nodes[i];
    444     }
    445   }
    446   return null;
    447 };
    448 
    449 /**
    450  * Article.
    451  * @param {Array.<Node>} nodes An array of nodes to check.
    452  * @return {?Node} Node in the array that is a article.
    453  */
    454 cvox.DomPredicates.articlePredicate = function(nodes) {
    455   for (var i = 0; i < nodes.length; i++) {
    456     if ((nodes[i].getAttribute &&
    457             nodes[i].getAttribute('role') == 'article') ||
    458         nodes[i].tagName == 'ARTICLE') {
    459       return nodes[i];
    460     }
    461   }
    462   return null;
    463 };
    464 
    465 /**
    466  * Media.
    467  * @param {Array.<Node>} nodes An array of nodes to check.
    468  * @return {?Node} Node in the array that is a media widget (video or audio).
    469  */
    470 cvox.DomPredicates.mediaPredicate = function(nodes) {
    471   for (var i = 0; i < nodes.length; i++) {
    472     if (nodes[i].tagName == 'AUDIO' ||
    473         nodes[i].tagName == 'VIDEO') {
    474       return nodes[i];
    475     }
    476   }
    477   return null;
    478 };
    479 
    480 
    481 /**
    482  * Ordered List.
    483  * @param {Array.<Node>} nodes An array of nodes to check.
    484  * @return {?Node} Node in the array that is a ordered list.
    485  */
    486 cvox.DomPredicates.orderedListPredicate = function(nodes) {
    487   for (var i = 0; i < nodes.length; i++) {
    488     if (nodes[i].tagName == 'OL') {
    489       return nodes[i];
    490     }
    491   }
    492   return null;
    493 };
    494