blob: 3674383a8653105b1f53ba8e2a66b6f29f0bceca [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview A bunch of predicates that take as input an array of
* nodes with the unique ancestors of a node. They output true if a
* certain category of node has been found.
*
*/
goog.provide('cvox.DomPredicates');
/**
* Checkbox.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a checkbox.
*/
cvox.DomPredicates.checkboxPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute &&
nodes[i].getAttribute('role') == 'checkbox') ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'checkbox')) {
return nodes[i];
}
}
return null;
};
/**
* Radio button.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a radio button.
*/
cvox.DomPredicates.radioPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'radio') ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'radio')) {
return nodes[i];
}
}
return null;
};
/**
* Slider.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a slider.
*/
cvox.DomPredicates.sliderPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'slider') ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'range')) {
return nodes[i];
}
}
return null;
};
/**
* Graphic.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a graphic.
*/
cvox.DomPredicates.graphicPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].tagName == 'IMG' ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'img')) {
return nodes[i];
}
}
return null;
};
/**
* Button.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a button.
*/
cvox.DomPredicates.buttonPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'button') ||
nodes[i].tagName == 'BUTTON' ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'submit') ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'button') ||
(nodes[i].tagName == 'INPUT' && nodes[i].type == 'reset')) {
return nodes[i];
}
}
return null;
};
/**
* Combo box.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a combo box.
*/
cvox.DomPredicates.comboBoxPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute &&
nodes[i].getAttribute('role') == 'combobox') ||
nodes[i].tagName == 'SELECT') {
return nodes[i];
}
}
return null;
};
/**
* Editable text field.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is an editable text field.
*/
cvox.DomPredicates.editTextPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'textbox') ||
nodes[i].tagName == 'TEXTAREA' || nodes[i].isContentEditable ||
(nodes[i].tagName == 'INPUT' &&
cvox.DomUtil.isInputTypeText(nodes[i]))) {
return nodes[i];
}
}
return null;
};
/**
* Heading.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading.
*/
cvox.DomPredicates.headingPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].getAttribute && nodes[i].getAttribute('role') == 'heading') {
return nodes[i];
}
switch (nodes[i].tagName) {
case 'H1':
case 'H2':
case 'H3':
case 'H4':
case 'H5':
case 'H6':
return nodes[i];
}
}
return null;
};
/**
* Heading level 1.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading level 1.
* TODO: handle ARIA headings with ARIA heading levels?
*/
cvox.DomPredicates.heading1Predicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'H1');
};
/**
* Heading level 2.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading level 2.
*/
cvox.DomPredicates.heading2Predicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'H2');
};
/**
* Heading level 3.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading level 3.
*/
cvox.DomPredicates.heading3Predicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'H3');
};
/**
* Heading level 4.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading level 4.
*/
cvox.DomPredicates.heading4Predicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'H4');
};
/**
* Heading level 5.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading level 5.
*/
cvox.DomPredicates.heading5Predicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'H5');
};
/**
* Heading level 6.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a heading level 6.
*/
cvox.DomPredicates.heading6Predicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'H6');
};
/**
* Link.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a link.
*/
cvox.DomPredicates.linkPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'link') ||
(nodes[i].tagName == 'A' && nodes[i].href)) {
return nodes[i];
}
}
return null;
};
/**
* Table.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a data table.
*/
cvox.DomPredicates.tablePredicate = function(nodes) {
// TODO(stoarca): Captions should always be allowed!!
var node = cvox.DomUtil.findTableNodeInList(nodes, {allowCaptions: true});
if (node && !cvox.DomUtil.isLayoutTable(node)) {
return node;
} else {
return null;
}
};
/**
* Table Cell.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a table cell.
*/
cvox.DomPredicates.cellPredicate = function(nodes) {
for (var i = nodes.length - 1; i >= 0; --i) {
var node = nodes[i];
if (node.tagName == 'TD' || node.tagName == 'TH' ||
(node.getAttribute && node.getAttribute('role') == 'gridcell')) {
return node;
}
}
return null;
};
/**
* Visited link.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a visited link.
*/
cvox.DomPredicates.visitedLinkPredicate = function(nodes) {
for (var i = nodes.length - 1; i >= 0; --i) {
if (cvox.DomPredicates.linkPredicate([nodes[i]]) &&
cvox.ChromeVox.visitedUrls[nodes[i].href]) {
return nodes[i];
}
}
return null;
};
/**
* List.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a list.
*/
cvox.DomPredicates.listPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'list') ||
nodes[i].tagName == 'UL' || nodes[i].tagName == 'OL') {
return nodes[i];
}
}
return null;
};
/**
* List item.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a list item.
*/
cvox.DomPredicates.listItemPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute &&
nodes[i].getAttribute('role') == 'listitem') ||
nodes[i].tagName == 'LI') {
return nodes[i];
}
}
return null;
};
/**
* Blockquote.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a blockquote.
*/
cvox.DomPredicates.blockquotePredicate = function(nodes) {
return cvox.DomPredicates.containsTagName_(nodes, 'BLOCKQUOTE');
};
/**
* Form field.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is any type of form field.
*/
cvox.DomPredicates.formFieldPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (cvox.DomUtil.isControl(nodes[i])) {
return nodes[i];
}
}
return null;
};
/**
* ARIA landmark.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is an ARIA landmark.
*/
cvox.DomPredicates.landmarkPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (cvox.AriaUtil.isLandmark(nodes[i])) {
return nodes[i];
}
}
return null;
};
/**
* @param {Array} arr Array of nodes.
* @param {string} tagName The name of the tag.
* @return {?Node} Node if obj is in the array.
* @private
*/
cvox.DomPredicates.containsTagName_ = function(arr, tagName) {
var i = arr.length;
while (i--) {
if (arr[i].tagName == tagName) {
return arr[i];
}
}
return null;
};
/**
* MathML expression
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a math expression.
*/
cvox.DomPredicates.mathPredicate = function(nodes) {
return cvox.DomUtil.findMathNodeInList(nodes);
};
/**
* SECTION: A section is anything that indicates a new section. This includes
* headings and landmarks.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is considered a section marker.
*/
cvox.DomPredicates.sectionPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (cvox.DomUtil.isSemanticElt(nodes[i])) {
return nodes[i];
}
if (cvox.AriaUtil.isLandmark(nodes[i])) {
return nodes[i];
}
if (nodes[i].getAttribute && nodes[i].getAttribute('role') == 'heading') {
return nodes[i];
}
switch (nodes[i].tagName) {
case 'H1':
case 'H2':
case 'H3':
case 'H4':
case 'H5':
case 'H6':
return nodes[i];
}
}
return null;
};
/**
* CONTROL: A control is anything that the user can interact with. This includes
* form fields and links.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is considered a control.
*/
cvox.DomPredicates.controlPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (cvox.DomUtil.isControl(nodes[i])) {
return nodes[i];
}
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'link') ||
(nodes[i].tagName == 'A' && nodes[i].href)) {
return nodes[i];
}
}
return null;
};
/**
* Caption.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a caption.
*/
cvox.DomPredicates.captionPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].tagName == 'CAPTION') {
return nodes[i];
}
}
return null;
};
/**
* Article.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a article.
*/
cvox.DomPredicates.articlePredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'article') ||
nodes[i].tagName == 'ARTICLE') {
return nodes[i];
}
}
return null;
};
/**
* Media.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a media widget (video or audio).
*/
cvox.DomPredicates.mediaPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].tagName == 'AUDIO' || nodes[i].tagName == 'VIDEO') {
return nodes[i];
}
}
return null;
};
/**
* Ordered List.
* @param {Array<Node>} nodes An array of nodes to check.
* @return {?Node} Node in the array that is a ordered list.
*/
cvox.DomPredicates.orderedListPredicate = function(nodes) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].tagName == 'OL') {
return nodes[i];
}
}
return null;
};