summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js')
-rw-r--r--chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js1309
1 files changed, 0 insertions, 1309 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js
deleted file mode 100644
index 8927c1d8846..00000000000
--- a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js
+++ /dev/null
@@ -1,1309 +0,0 @@
-// 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.
-
-/**
- * TODO(stoarca): This class has become obsolete except for the shadow table.
- * Chop most of it away.
- * @fileoverview A DOM traversal interface for navigating data in tables.
- */
-
-goog.provide('cvox.TraverseTable');
-
-goog.require('cvox.DomPredicates');
-goog.require('cvox.DomUtil');
-goog.require('cvox.SelectionUtil');
-goog.require('cvox.TableUtil');
-goog.require('cvox.TraverseUtil');
-
-
-
-/**
- * An object that represents an active table cell inside the shadow table.
- * @constructor
- */
-function ShadowTableNode() {
- /**
- * The cells that are row headers of the corresponding active table cell
- * @type {!Array}
- */
- this.rowHeaderCells = [];
-
- /**
- * The cells that are column headers of the corresponding active table cell
- * @type {!Array}
- */
- this.colHeaderCells = [];
-}
-
-
-/**
- * Whether or not the active cell is spanned by a preceding cell.
- * @type {boolean}
- */
-ShadowTableNode.prototype.spanned;
-
-
-/**
- * Whether or not this cell is spanned by a rowSpan.
- * @type {?boolean}
- */
-ShadowTableNode.prototype.rowSpan;
-
-
-/**
- * Whether or not this cell is spanned by a colspan
- * @type {?boolean}
- */
-ShadowTableNode.prototype.colSpan;
-
-
-/**
- * The row index of the corresponding active table cell
- * @type {?number}
- */
-ShadowTableNode.prototype.i;
-
-
-/**
- * The column index of the corresponding active table cell
- * @type {?number}
- */
-ShadowTableNode.prototype.j;
-
-
-/**
- * The corresponding <TD> or <TH> node in the active table.
- * @type {?Node}
- */
-ShadowTableNode.prototype.activeCell;
-
-
-/**
- * Initializes the traversal with the provided table node.
- *
- * @constructor
- * @param {Node} tableNode The table to be traversed.
- */
-cvox.TraverseTable = function(tableNode) {
-
- /**
- * The active table <TABLE> node. In this context, "active" means that this is
- * the table the TraverseTable object is navigating.
- * @type {Node}
- * @private
- */
- this.activeTable_ = null;
-
- /**
- * A 2D array "shadow table" that contains pointers to nodes in the active
- * table. More specifically, each cell of the shadow table contains a special
- * object ShadowTableNode that has as one of its member variables the
- * corresponding cell in the active table.
- *
- * The shadow table will allow us efficient navigation of tables with
- * rowspans and colspans without needing to repeatedly scan the table. For
- * example, if someone requests a cell at (1,3), predecessor cells with
- * rowspans/colspans mean the cell you eventually return could actually be
- * one located at (0,2) that spans out to (1,3).
- *
- * This shadow table will contain a ShadowTableNode with the (0, 2) index at
- * the (1,3) position, eliminating the need to check for predecessor cells
- * with rowspan/colspan every time we traverse the table.
- *
- * @type {!Array<Array<ShadowTableNode>>}
- * @private
- */
- this.shadowTable_ = [];
-
- /**
- * An array of shadow table nodes that have been determined to contain header
- * cells or information about header cells. This array is collected at
- * initialization and then only recalculated if the table changes.
- * This array is used by findHeaderCells() to determine table row headers
- * and column headers.
- * @type {Array<ShadowTableNode>}
- * @private
- */
- this.candidateHeaders_ = [];
-
- /**
- * An array that associates cell IDs with their corresponding shadow nodes.
- * If there are two shadow nodes for the same cell (i.e. when a cell spans
- * other cells) then the first one will be associated with the ID. This means
- * that shadow nodes that have spanned set to true will not be included in
- * this array.
- * @type {Array<ShadowTableNode>}
- * @private
- */
- this.idToShadowNode_ = [];
-
- this.initialize(tableNode);
-};
-
-
-/**
- * The cell cursor, represented by an array that stores the row and
- * column location [i, j] of the active cell. These numbers are 0-based.
- * In this context, "active" means that this is the cell the user is
- * currently looking at.
- * @type {Array}
- */
-cvox.TraverseTable.prototype.currentCellCursor;
-
-
-/**
- * The number of columns in the active table. This is calculated at
- * initialization and then only recalculated if the table changes.
- *
- * Please Note: We have chosen to use the number of columns in the shadow
- * table as the canonical column count. This is important for tables that
- * have colspans - the number of columns in the active table will always be
- * less than the true number of columns.
- * @type {?number}
- */
-cvox.TraverseTable.prototype.colCount = null;
-
-
-/**
- * The number of rows in the active table. This is calculated at
- * initialization and then only recalculated if the table changes.
- * @type {?number}
- */
-cvox.TraverseTable.prototype.rowCount = null;
-
-
-/**
- * The row headers in the active table. This is calculated at
- * initialization and then only recalculated if the table changes.
- *
- * Please Note:
- * Row headers are defined here as <TH> or <TD> elements. <TD> elements when
- * serving as header cells must have either:
- * - The scope attribute defined
- * - Their IDs referenced in the header content attribute of another <TD> or
- * <TH> element.
- *
- * The HTML5 spec specifies that only header <TH> elements can be row headers
- * ( http://dev.w3.org/html5/spec/tabular-data.html#row-header ) but the
- * HTML4 spec says that <TD> elements can act as both
- * ( http://www.w3.org/TR/html401/struct/tables.html#h-11.2.6 ). In the
- * interest of providing meaningful header information for all tables, here
- * we take the position that <TD> elements can act as both.
- *
- * @type {Array}
- */
-cvox.TraverseTable.prototype.tableRowHeaders = null;
-
-
-/**
- * The column headers in the active table. This is calculated at
- * initialization and then only recalculated if the table changes.
- *
- * Please Note: see comment for tableRowHeaders.
- *
- * @type {Array}
- */
-cvox.TraverseTable.prototype.tableColHeaders = null;
-
-
-// TODO (stoarca): tighten up interface to {!Node}
-/**
- * Initializes the class member variables.
- * @param {Node} tableNode The table to be traversed.
- */
-cvox.TraverseTable.prototype.initialize = function(tableNode) {
- if (!tableNode) {
- return;
- }
- if (tableNode == this.activeTable_) {
- return;
- }
- this.activeTable_ = tableNode;
- this.currentCellCursor = null;
-
- this.tableRowHeaders = [];
- this.tableColHeaders = [];
-
- this.buildShadowTable_();
-
- this.colCount = this.shadowColCount_();
- this.rowCount = this.countRows_();
-
- this.findHeaderCells_();
-
- // Listen for changes to the active table. If the active table changes,
- // rebuild the shadow table.
- // TODO (stoarca): Is this safe? When this object goes away, doesn't the
- // eventListener stay on the node? Someone with better knowledge of js
- // please confirm. If so, this is a leak.
- this.activeTable_.addEventListener('DOMSubtreeModified',
- goog.bind(function() {
- this.buildShadowTable_();
- this.colCount = this.shadowColCount_();
- this.rowCount = this.countRows_();
-
- this.tableRowHeaders = [];
- this.tableColHeaders = [];
- this.findHeaderCells_();
-
- if (this.colCount == 0 && this.rowCount == 0) {
- return;
- }
-
- if (this.getCell() == null) {
- this.attachCursorToNearestCell_();
- }
- }, this), false);
-};
-
-
-/**
- * Finds the cell cursor containing the specified node within the table.
- * Returns null if there is no close cell.
- * @param {!Node} node The node for which to find the cursor.
- * @return {Array<number>} The table index for the node.
- */
-cvox.TraverseTable.prototype.findNearestCursor = function(node) {
- // TODO (stoarca): The current structure for representing the
- // shadow table is not optimal for this query, but it's not urgent
- // since this only gets executed at most once per user action.
-
- // In case node is in a table but above any individual cell, we go down as
- // deep as we can, being careful to avoid going into nested tables.
- var n = node;
-
- while (n.firstElementChild &&
- !(n.firstElementChild.tagName == 'TABLE' ||
- cvox.AriaUtil.isGrid(n.firstElementChild))) {
- n = n.firstElementChild;
- }
- while (!cvox.DomPredicates.cellPredicate(cvox.DomUtil.getAncestors(n))) {
- n = cvox.DomUtil.directedNextLeafNode(n);
- // TODO(stoarca): Ugly logic. Captions should be part of tables.
- // There have been a bunch of bugs as a result of
- // DomUtil.findTableNodeInList excluding captions from tables because
- // it makes them non-contiguous.
- if (!cvox.DomUtil.getContainingTable(n, {allowCaptions: true})) {
- return null;
- }
- }
- for (var i = 0; i < this.rowCount; ++i) {
- for (var j = 0; j < this.colCount; ++j) {
- if (this.shadowTable_[i][j]) {
- if (cvox.DomUtil.isDescendantOfNode(
- n, this.shadowTable_[i][j].activeCell)) {
- return [i, j];
- }
- }
- }
- }
- return null;
-};
-
-/**
- * Finds the valid cell nearest to the current cell cursor and moves the cell
- * cursor there. To be used when the table has changed and the current cell
- * cursor is now invalid (doesn't exist anymore).
- * @private
- */
-cvox.TraverseTable.prototype.attachCursorToNearestCell_ = function() {
- if (!this.currentCellCursor) {
- // We have no idea. Just go 'somewhere'. Other code paths in this
- // function go to the last cell, so let's do that!
- this.goToLastCell();
- return;
- }
-
- var currentCursor = this.currentCellCursor;
-
- // Does the current row still exist in the table?
- var currentRow = this.shadowTable_[currentCursor[0]];
- if (currentRow) {
- // Try last cell of current row
- this.currentCellCursor = [currentCursor[0], (currentRow.length - 1)];
- } else {
- // Current row does not exist anymore. Does current column still exist?
- // Try last cell of current column
- var numRows = this.shadowTable_.length;
- if (numRows == 0) {
- // Table has been deleted!
- this.currentCellCursor = null;
- return;
- }
- var aboveCell =
- this.shadowTable_[numRows - 1][currentCursor[1]];
- if (aboveCell) {
- this.currentCellCursor = [(numRows - 1), currentCursor[1]];
- } else {
- // Current column does not exist anymore either.
- // Move cursor to last cell in table.
- this.goToLastCell();
- }
- }
-};
-
-
-/**
- * Builds or rebuilds the shadow table by iterating through all of the cells
- * ( <TD> or <TH> or role='gridcell' nodes) of the active table.
- * @return {!Array} The shadow table.
- * @private
- */
-cvox.TraverseTable.prototype.buildShadowTable_ = function() {
- // Clear shadow table
- this.shadowTable_ = [];
-
- // Build shadow table structure. Initialize it as a 2D array.
- var allRows = cvox.TableUtil.getChildRows(this.activeTable_);
- var currentRowParent = null;
- var currentRowGroup = null;
-
- var colGroups = cvox.TableUtil.getColGroups(this.activeTable_);
- var colToColGroup = cvox.TableUtil.determineColGroups(colGroups);
-
- for (var ctr = 0; ctr < allRows.length; ctr++) {
- this.shadowTable_.push([]);
- }
-
- // Iterate through active table by row
- for (var i = 0; i < allRows.length; i++) {
- var childCells = cvox.TableUtil.getChildCells(allRows[i]);
-
- // Keep track of position in active table
- var activeTableCol = 0;
- // Keep track of position in shadow table
- var shadowTableCol = 0;
-
- while (activeTableCol < childCells.length) {
-
- // Check to make sure we haven't already filled this cell.
- if (this.shadowTable_[i][shadowTableCol] == null) {
-
- var activeTableCell = childCells[activeTableCol];
-
- // Default value for colspan and rowspan is 1
- var colsSpanned = 1;
- var rowsSpanned = 1;
-
- if (activeTableCell.hasAttribute('colspan')) {
-
- colsSpanned =
- parseInt(activeTableCell.getAttribute('colspan'), 10);
-
- if ((isNaN(colsSpanned)) || (colsSpanned <= 0)) {
- // The HTML5 spec defines colspan MUST be greater than 0:
- // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-colspan
- //
- // This is a change from the HTML4 spec:
- // http://www.w3.org/TR/html401/struct/tables.html#adef-colspan
- //
- // We will degrade gracefully by treating a colspan=0 as
- // equivalent to a colspan=1.
- // Tested in method testColSpan0 in rowColSpanTable_test.js
- colsSpanned = 1;
- }
- }
- if (activeTableCell.hasAttribute('rowspan')) {
- rowsSpanned =
- parseInt(activeTableCell.getAttribute('rowspan'), 10);
-
- if ((isNaN(rowsSpanned)) || (rowsSpanned <= 0)) {
- // The HTML5 spec defines that rowspan can be any non-negative
- // integer, including 0:
- // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-rowspan
- //
- // However, Chromium treats rowspan=0 as rowspan=1. This appears
- // to be a bug from WebKit:
- // https://bugs.webkit.org/show_bug.cgi?id=10300
- // Inherited from a bug (since fixed) in KDE:
- // http://bugs.kde.org/show_bug.cgi?id=41063
- //
- // We will follow Chromium and treat rowspan=0 as equivalent to
- // rowspan=1.
- //
- // Tested in method testRowSpan0 in rowColSpanTable_test.js
- //
- // Filed as a bug in Chromium: http://crbug.com/58223
- rowsSpanned = 1;
- }
- }
- for (var r = 0; r < rowsSpanned; r++) {
- for (var c = 0; c < colsSpanned; c++) {
- var shadowNode = new ShadowTableNode();
- if ((r == 0) && (c == 0)) {
- // This position is not spanned.
- shadowNode.spanned = false;
- shadowNode.rowSpan = false;
- shadowNode.colSpan = false;
- shadowNode.i = i;
- shadowNode.j = shadowTableCol;
- shadowNode.activeCell = activeTableCell;
- shadowNode.rowHeaderCells = [];
- shadowNode.colHeaderCells = [];
- shadowNode.isRowHeader = false;
- shadowNode.isColHeader = false;
- } else {
- // This position is spanned.
- shadowNode.spanned = true;
- shadowNode.rowSpan = (rowsSpanned > 1);
- shadowNode.colSpan = (colsSpanned > 1);
- shadowNode.i = i;
- shadowNode.j = shadowTableCol;
- shadowNode.activeCell = activeTableCell;
- shadowNode.rowHeaderCells = [];
- shadowNode.colHeaderCells = [];
- shadowNode.isRowHeader = false;
- shadowNode.isColHeader = false;
- }
- // Check this shadowNode to see if it is a candidate header cell
- if (cvox.TableUtil.checkIfHeader(shadowNode.activeCell)) {
- this.candidateHeaders_.push(shadowNode);
- } else if (shadowNode.activeCell.hasAttribute('headers')) {
- // This shadowNode has information about other header cells
- this.candidateHeaders_.push(shadowNode);
- }
-
- // Check and update row group status.
- if (currentRowParent == null) {
- // This is the first row
- currentRowParent = allRows[i].parentNode;
- currentRowGroup = 0;
- } else {
- if (allRows[i].parentNode != currentRowParent) {
- // We're in a different row group now
- currentRowParent = allRows[i].parentNode;
- currentRowGroup = currentRowGroup + 1;
- }
- }
- shadowNode.rowGroup = currentRowGroup;
-
- // Check and update col group status
- if (colToColGroup.length > 0) {
- shadowNode.colGroup = colToColGroup[shadowTableCol];
- } else {
- shadowNode.colGroup = 0;
- }
-
- if (! shadowNode.spanned) {
- if (activeTableCell.id != null) {
- this.idToShadowNode_[activeTableCell.id] = shadowNode;
- }
- }
-
- this.shadowTable_[i + r][shadowTableCol + c] = shadowNode;
- }
- }
- shadowTableCol += colsSpanned;
- activeTableCol++;
- } else {
- // This position has already been filled (by a previous cell that has
- // a colspan or a rowspan)
- shadowTableCol += 1;
- }
- }
- }
- return this.shadowTable_;
-};
-
-
-/**
- * Finds header cells from the list of candidate headers and classifies them
- * in two ways:
- * -- Identifies them for the entire table by adding them to
- * this.tableRowHeaders and this.tableColHeaders.
- * -- Identifies them for each shadow table node by adding them to the node's
- * rowHeaderCells or colHeaderCells arrays.
- *
- * @private
- */
-cvox.TraverseTable.prototype.findHeaderCells_ = function() {
- // Forming relationships between data cells and header cells:
- // http://dev.w3.org/html5/spec/tabular-data.html
- // #header-and-data-cell-semantics
-
- for (var i = 0; i < this.candidateHeaders_.length; i++) {
-
- var currentShadowNode = this.candidateHeaders_[i];
- var currentCell = currentShadowNode.activeCell;
-
- var assumedScope = null;
- var specifiedScope = null;
-
- if (currentShadowNode.spanned) {
- continue;
- }
-
- if ((currentCell.tagName == 'TH') &&
- !(currentCell.hasAttribute('scope'))) {
- // No scope specified - compute scope ourselves.
- // Go left/right - if there's a header node, then this is a column
- // header
- if (currentShadowNode.j > 0) {
- if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j - 1].
- activeCell.tagName == 'TH') {
- assumedScope = 'col';
- }
- } else if (currentShadowNode.j < this.shadowTable_[currentShadowNode.i].
- length - 1) {
- if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j + 1].
- activeCell.tagName == 'TH') {
- assumedScope = 'col';
- }
- } else {
- // This row has a width of 1 cell, just assume this is a colum header
- assumedScope = 'col';
- }
-
- if (assumedScope == null) {
- // Go up/down - if there's a header node, then this is a row header
- if (currentShadowNode.i > 0) {
- if (this.shadowTable_[currentShadowNode.i - 1][currentShadowNode.j].
- activeCell.tagName == 'TH') {
- assumedScope = 'row';
- }
- } else if (currentShadowNode.i < this.shadowTable_.length - 1) {
- if (this.shadowTable_[currentShadowNode.i + 1][currentShadowNode.j].
- activeCell.tagName == 'TH') {
- assumedScope = 'row';
- }
- } else {
- // This column has a height of 1 cell, just assume that this is
- // a row header
- assumedScope = 'row';
- }
- }
- } else if (currentCell.hasAttribute('scope')) {
- specifiedScope = currentCell.getAttribute('scope');
- } else if (currentCell.hasAttribute('role') &&
- (currentCell.getAttribute('role') == 'rowheader')) {
- specifiedScope = 'row';
- } else if (currentCell.hasAttribute('role') &&
- (currentCell.getAttribute('role') == 'columnheader')) {
- specifiedScope = 'col';
- }
-
- if ((specifiedScope == 'row') || (assumedScope == 'row')) {
- currentShadowNode.isRowHeader = true;
-
- // Go right until you hit the edge of the table or a data
- // cell after another header cell.
- // Add this cell to each shadowNode.rowHeaderCells attribute as you go.
- for (var rightCtr = currentShadowNode.j;
- rightCtr < this.shadowTable_[currentShadowNode.i].length;
- rightCtr++) {
-
- var rightShadowNode = this.shadowTable_[currentShadowNode.i][rightCtr];
- var rightCell = rightShadowNode.activeCell;
-
- if ((rightCell.tagName == 'TH') ||
- (rightCell.hasAttribute('scope'))) {
-
- if (rightCtr < this.shadowTable_[currentShadowNode.i].length - 1) {
- var checkDataCell =
- this.shadowTable_[currentShadowNode.i][rightCtr + 1];
- }
- }
- rightShadowNode.rowHeaderCells.push(currentCell);
- }
- this.tableRowHeaders.push(currentCell);
- } else if ((specifiedScope == 'col') || (assumedScope == 'col')) {
- currentShadowNode.isColHeader = true;
-
- // Go down until you hit the edge of the table or a data cell
- // after another header cell.
- // Add this cell to each shadowNode.colHeaders attribute as you go.
-
- for (var downCtr = currentShadowNode.i;
- downCtr < this.shadowTable_.length;
- downCtr++) {
-
- var downShadowNode = this.shadowTable_[downCtr][currentShadowNode.j];
- if (downShadowNode == null) {
- break;
- }
- var downCell = downShadowNode.activeCell;
-
- if ((downCell.tagName == 'TH') ||
- (downCell.hasAttribute('scope'))) {
-
- if (downCtr < this.shadowTable_.length - 1) {
- var checkDataCell =
- this.shadowTable_[downCtr + 1][currentShadowNode.j];
- }
- }
- downShadowNode.colHeaderCells.push(currentCell);
- }
- this.tableColHeaders.push(currentCell);
- } else if (specifiedScope == 'rowgroup') {
- currentShadowNode.isRowHeader = true;
-
- // This cell is a row header for the rest of the cells in this row group.
- var currentRowGroup = currentShadowNode.rowGroup;
-
- // Get the rest of the cells in this row first
- for (var cellsInRow = currentShadowNode.j + 1;
- cellsInRow < this.shadowTable_[currentShadowNode.i].length;
- cellsInRow++) {
- this.shadowTable_[currentShadowNode.i][cellsInRow].
- rowHeaderCells.push(currentCell);
- }
-
- // Now propagate to rest of row group
- for (var downCtr = currentShadowNode.i + 1;
- downCtr < this.shadowTable_.length;
- downCtr++) {
-
- if (this.shadowTable_[downCtr][0].rowGroup != currentRowGroup) {
- break;
- }
-
- for (var rightCtr = 0;
- rightCtr < this.shadowTable_[downCtr].length;
- rightCtr++) {
-
- this.shadowTable_[downCtr][rightCtr].
- rowHeaderCells.push(currentCell);
- }
- }
- this.tableRowHeaders.push(currentCell);
-
- } else if (specifiedScope == 'colgroup') {
- currentShadowNode.isColHeader = true;
-
- // This cell is a col header for the rest of the cells in this col group.
- var currentColGroup = currentShadowNode.colGroup;
-
- // Get the rest of the cells in this colgroup first
- for (var cellsInCol = currentShadowNode.j + 1;
- cellsInCol < this.shadowTable_[currentShadowNode.i].length;
- cellsInCol++) {
- if (this.shadowTable_[currentShadowNode.i][cellsInCol].colGroup ==
- currentColGroup) {
- this.shadowTable_[currentShadowNode.i][cellsInCol].
- colHeaderCells.push(currentCell);
- }
- }
-
- // Now propagate to rest of col group
- for (var downCtr = currentShadowNode.i + 1;
- downCtr < this.shadowTable_.length;
- downCtr++) {
-
- for (var rightCtr = 0;
- rightCtr < this.shadowTable_[downCtr].length;
- rightCtr++) {
-
- if (this.shadowTable_[downCtr][rightCtr].colGroup ==
- currentColGroup) {
- this.shadowTable_[downCtr][rightCtr].
- colHeaderCells.push(currentCell);
- }
- }
- }
- this.tableColHeaders.push(currentCell);
- }
- if (currentCell.hasAttribute('headers')) {
- this.findAttrbHeaders_(currentShadowNode);
- }
- if (currentCell.hasAttribute('aria-describedby')) {
- this.findAttrbDescribedBy_(currentShadowNode);
- }
- }
-};
-
-
-/**
- * Finds header cells from the 'headers' attribute of a given shadow node's
- * active cell and classifies them in two ways:
- * -- Identifies them for the entire table by adding them to
- * this.tableRowHeaders and this.tableColHeaders.
- * -- Identifies them for the shadow table node by adding them to the node's
- * rowHeaderCells or colHeaderCells arrays.
- * Please note that header cells found through the 'headers' attribute are
- * difficult to attribute to being either row or column headers because a
- * table cell can declare arbitrary cells as its headers. A guess is made here
- * based on which axis the header cell is closest to.
- *
- * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell
- * that has a 'headers' attribute.
- *
- * @private
- */
-cvox.TraverseTable.prototype.findAttrbHeaders_ = function(currentShadowNode) {
- var activeTableCell = currentShadowNode.activeCell;
-
- var idList = activeTableCell.getAttribute('headers').split(' ');
- for (var idToken = 0; idToken < idList.length; idToken++) {
- // Find cell(s) with this ID, add to header list
- var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_,
- idList[idToken]);
-
- for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) {
- if (idCellArray[idCtr].id == activeTableCell.id) {
- // Skip if the ID is the same as the current cell's ID
- break;
- }
- // Check if this list of candidate headers contains a
- // shadowNode with an active cell with this ID already
- var possibleHeaderNode =
- this.idToShadowNode_[idCellArray[idCtr].id];
- if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) {
- // This listed header cell will not be handled later.
- // Determine whether this is a row or col header for
- // the active table cell
-
- var iDiff = Math.abs(possibleHeaderNode.i - currentShadowNode.i);
- var jDiff = Math.abs(possibleHeaderNode.j - currentShadowNode.j);
- if ((iDiff == 0) || (iDiff < jDiff)) {
- cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells,
- possibleHeaderNode.activeCell);
- cvox.TableUtil.pushIfNotContained(this.tableRowHeaders,
- possibleHeaderNode.activeCell);
- } else {
- // This is a column header
- cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells,
- possibleHeaderNode.activeCell);
- cvox.TableUtil.pushIfNotContained(this.tableColHeaders,
- possibleHeaderNode.activeCell);
- }
- }
- }
- }
-};
-
-
-/**
- * Finds header cells from the 'aria-describedby' attribute of a given shadow
- * node's active cell and classifies them in two ways:
- * -- Identifies them for the entire table by adding them to
- * this.tableRowHeaders and this.tableColHeaders.
- * -- Identifies them for the shadow table node by adding them to the node's
- * rowHeaderCells or colHeaderCells arrays.
- *
- * Please note that header cells found through the 'aria-describedby' attribute
- * must have the role='rowheader' or role='columnheader' attributes in order to
- * be considered header cells.
- *
- * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell
- * that has an 'aria-describedby' attribute.
- *
- * @private
- */
-cvox.TraverseTable.prototype.findAttrbDescribedBy_ =
- function(currentShadowNode) {
- var activeTableCell = currentShadowNode.activeCell;
-
- var idList = activeTableCell.getAttribute('aria-describedby').split(' ');
- for (var idToken = 0; idToken < idList.length; idToken++) {
- // Find cell(s) with this ID, add to header list
- var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_,
- idList[idToken]);
-
- for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) {
- if (idCellArray[idCtr].id == activeTableCell.id) {
- // Skip if the ID is the same as the current cell's ID
- break;
- }
- // Check if this list of candidate headers contains a
- // shadowNode with an active cell with this ID already
- var possibleHeaderNode =
- this.idToShadowNode_[idCellArray[idCtr].id];
- if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) {
- // This listed header cell will not be handled later.
- // Determine whether this is a row or col header for
- // the active table cell
-
- if (possibleHeaderNode.activeCell.hasAttribute('role') &&
- (possibleHeaderNode.activeCell.getAttribute('role') ==
- 'rowheader')) {
- cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells,
- possibleHeaderNode.activeCell);
- cvox.TableUtil.pushIfNotContained(this.tableRowHeaders,
- possibleHeaderNode.activeCell);
- } else if (possibleHeaderNode.activeCell.hasAttribute('role') &&
- (possibleHeaderNode.activeCell.getAttribute('role') ==
- 'columnheader')) {
- cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells,
- possibleHeaderNode.activeCell);
- cvox.TableUtil.pushIfNotContained(this.tableColHeaders,
- possibleHeaderNode.activeCell);
- }
- }
- }
- }
-};
-
-
-/**
- * Gets the current cell or null if there is no current cell.
- * @return {?Node} The cell <TD> or <TH> or role='gridcell' node.
- */
-cvox.TraverseTable.prototype.getCell = function() {
- if (!this.currentCellCursor || !this.shadowTable_) {
- return null;
- }
-
- var shadowEntry =
- this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]];
-
- return shadowEntry && shadowEntry.activeCell;
-};
-
-
-/**
- * Gets the cell at the specified location.
- * @param {Array<number>} index The index <i, j> of the required cell.
- * @return {?Node} The cell <TD> or <TH> or role='gridcell' node at the
- * specified location. Null if that cell does not exist.
- */
-cvox.TraverseTable.prototype.getCellAt = function(index) {
- if (((index[0] < this.rowCount) && (index[0] >= 0)) &&
- ((index[1] < this.colCount) && (index[1] >= 0))) {
- var shadowEntry = this.shadowTable_[index[0]][index[1]];
- if (shadowEntry != null) {
- return shadowEntry.activeCell;
- }
- }
- return null;
-};
-
-
-/**
- * Gets the cells that are row headers of the current cell.
- * @return {!Array} The cells that are row headers of the current cell. Empty if
- * the current cell does not have row headers.
- */
-cvox.TraverseTable.prototype.getCellRowHeaders = function() {
- var shadowEntry =
- this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]];
-
- return shadowEntry.rowHeaderCells;
-};
-
-
-/**
- * Gets the cells that are col headers of the current cell.
- * @return {!Array} The cells that are col headers of the current cell. Empty if
- * the current cell does not have col headers.
- */
-cvox.TraverseTable.prototype.getCellColHeaders = function() {
- var shadowEntry =
- this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]];
-
- return shadowEntry.colHeaderCells;
-};
-
-
-/**
- * Whether or not the current cell is spanned by another cell.
- * @return {boolean} Whether or not the current cell is spanned by another cell.
- */
-cvox.TraverseTable.prototype.isSpanned = function() {
- var shadowEntry =
- this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]];
-
- return shadowEntry.spanned;
-};
-
-
-/**
- * Whether or not the current cell is a row header cell.
- * @return {boolean} Whether or not the current cell is a row header cell.
- */
-cvox.TraverseTable.prototype.isRowHeader = function() {
- var shadowEntry =
- this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]];
-
- return shadowEntry.isRowHeader;
-};
-
-
-/**
- * Whether or not the current cell is a col header cell.
- * @return {boolean} Whether or not the current cell is a col header cell.
- */
-cvox.TraverseTable.prototype.isColHeader = function() {
- var shadowEntry =
- this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]];
-
- return shadowEntry.isColHeader;
-};
-
-
-/**
- * Gets the active column, represented as an array of <TH> or <TD> nodes that
- * make up a column. In this context, "active" means that this is the column
- * that contains the cell the user is currently looking at.
- * @return {Array} An array of <TH> or <TD> or role='gridcell' nodes.
- */
-cvox.TraverseTable.prototype.getCol = function() {
- var colArray = [];
- for (var i = 0; i < this.shadowTable_.length; i++) {
-
- if (this.shadowTable_[i][this.currentCellCursor[1]]) {
- var shadowEntry = this.shadowTable_[i][this.currentCellCursor[1]];
-
- if (shadowEntry.colSpan && shadowEntry.rowSpan) {
- // Look at the last element in the column cell aray.
- var prev = colArray[colArray.length - 1];
- if (prev !=
- shadowEntry.activeCell) {
- // Watch out for positions spanned by a cell with rowspan and
- // colspan. We don't want the same cell showing up multiple times
- // in per-column cell lists.
- colArray.push(
- shadowEntry.activeCell);
- }
- } else if ((shadowEntry.colSpan) || (!shadowEntry.rowSpan)) {
- colArray.push(
- shadowEntry.activeCell);
- }
- }
- }
- return colArray;
-};
-
-
-/**
- * Gets the active row <TR> node. In this context, "active" means that this is
- * the row that contains the cell the user is currently looking at.
- * @return {Node} The active row node.
- */
-cvox.TraverseTable.prototype.getRow = function() {
- var childRows = cvox.TableUtil.getChildRows(this.activeTable_);
- return childRows[this.currentCellCursor[0]];
-};
-
-
-/**
- * Gets the table summary text.
- *
- * @return {?string} Either:
- * 1) The table summary text
- * 2) Null if the table does not contain a summary attribute.
- */
-cvox.TraverseTable.prototype.summaryText = function() {
- // see http://code.google.com/p/chromium/issues/detail?id=46567
- // for information why this is necessary
- if (!this.activeTable_.hasAttribute('summary')) {
- return null;
- }
- return this.activeTable_.getAttribute('summary');
-};
-
-
-/**
- * Gets the table caption text.
- *
- * @return {?string} Either:
- * 1) The table caption text
- * 2) Null if the table does not include a caption tag.
- */
-cvox.TraverseTable.prototype.captionText = function() {
- // If there's more than one outer <caption> element, choose the first one.
- var captionNodes = cvox.XpathUtil.evalXPath('caption\[1]',
- this.activeTable_);
- if (captionNodes.length > 0) {
- return captionNodes[0].innerHTML;
- } else {
- return null;
- }
-};
-
-
-/**
- * Calculates the number of columns in the shadow table.
- * @return {number} The number of columns in the shadow table.
- * @private
- */
-cvox.TraverseTable.prototype.shadowColCount_ = function() {
- // As the shadow table is a 2D array, the number of columns is the
- // max number of elements in the second-level arrays.
- var max = 0;
- for (var i = 0; i < this.shadowTable_.length; i++) {
- if (this.shadowTable_[i].length > max) {
- max = this.shadowTable_[i].length;
- }
- }
- return max;
-};
-
-
-/**
- * Calculates the number of rows in the table.
- * @return {number} The number of rows in the table.
- * @private
- */
-cvox.TraverseTable.prototype.countRows_ = function() {
- // Number of rows in a table is equal to the number of TR elements contained
- // by the (outer) TBODY elements.
- var rowCount = cvox.TableUtil.getChildRows(this.activeTable_);
- return rowCount.length;
-};
-
-
-/**
- * Calculates the number of columns in the table.
- * This uses the W3C recommended algorithm for calculating number of
- * columns, but it does not take rowspans or colspans into account. This means
- * that the number of columns calculated here might be lower than the actual
- * number of columns in the table if columns are indicated by colspans.
- * @return {number} The number of columns in the table.
- * @private
- */
-cvox.TraverseTable.prototype.getW3CColCount_ = function() {
- // See http://www.w3.org/TR/html401/struct/tables.html#h-11.2.4.3
-
- var colgroupNodes = cvox.XpathUtil.evalXPath('child::colgroup',
- this.activeTable_);
- var colNodes = cvox.XpathUtil.evalXPath('child::col', this.activeTable_);
-
- if ((colgroupNodes.length == 0) && (colNodes.length == 0)) {
- var maxcols = 0;
- var outerChildren = cvox.TableUtil.getChildRows(this.activeTable_);
- for (var i = 0; i < outerChildren.length; i++) {
- var childrenCount = cvox.TableUtil.getChildCells(outerChildren[i]);
- if (childrenCount.length > maxcols) {
- maxcols = childrenCount.length;
- }
- }
- return maxcols;
- } else {
- var sum = 0;
- for (var i = 0; i < colNodes.length; i++) {
- if (colNodes[i].hasAttribute('span')) {
- sum += colNodes[i].getAttribute('span');
- } else {
- sum += 1;
- }
- }
- for (i = 0; i < colgroupNodes.length; i++) {
- var colChildren = cvox.XpathUtil.evalXPath('child::col',
- colgroupNodes[i]);
- if (colChildren.length == 0) {
- if (colgroupNodes[i].hasAttribute('span')) {
- sum += colgroupNodes[i].getAttribute('span');
- } else {
- sum += 1;
- }
- }
- }
- }
- return sum;
-};
-
-
-/**
- * Moves to the next row in the table. Updates the cell cursor.
- *
- * @return {boolean} Either:
- * 1) True if the update has been made.
- * 2) False if the end of the table has been reached and the update has not
- * happened.
- */
-cvox.TraverseTable.prototype.nextRow = function() {
- if (!this.currentCellCursor) {
- // We have not started moving through the table yet
- return this.goToRow(0);
- } else {
- return this.goToRow(this.currentCellCursor[0] + 1);
- }
-
-};
-
-
-/**
- * Moves to the previous row in the table. Updates the cell cursor.
- *
- * @return {boolean} Either:
- * 1) True if the update has been made.
- * 2) False if the end of the table has been reached and the update has not
- * happened.
- */
-cvox.TraverseTable.prototype.prevRow = function() {
- if (!this.currentCellCursor) {
- // We have not started moving through the table yet
- return this.goToRow(this.rowCount - 1);
- } else {
- return this.goToRow(this.currentCellCursor[0] - 1);
- }
-};
-
-
-/**
- * Moves to the next column in the table. Updates the cell cursor.
- *
- * @return {boolean} Either:
- * 1) True if the update has been made.
- * 2) False if the end of the table has been reached and the update has not
- * happened.
- */
-cvox.TraverseTable.prototype.nextCol = function() {
- if (!this.currentCellCursor) {
- // We have not started moving through the table yet
- return this.goToCol(0);
- } else {
- return this.goToCol(this.currentCellCursor[1] + 1);
- }
-};
-
-
-/**
- * Moves to the previous column in the table. Updates the cell cursor.
- *
- * @return {boolean} Either:
- * 1) True if the update has been made.
- * 2) False if the end of the table has been reached and the update has not
- * happened.
- */
-cvox.TraverseTable.prototype.prevCol = function() {
- if (!this.currentCellCursor) {
- // We have not started moving through the table yet
- return this.goToCol(this.shadowColCount_() - 1);
- } else {
- return this.goToCol(this.currentCellCursor[1] - 1);
- }
-};
-
-
-/**
- * Moves to the row at the specified index in the table. Updates the cell
- * cursor.
- * @param {number} index The index of the required row.
- * @return {boolean} Either:
- * 1) True if the index is valid and the update has been made.
- * 2) False if the index is not valid (either less than 0 or greater than
- * the number of rows in the table).
- */
-cvox.TraverseTable.prototype.goToRow = function(index) {
- if (this.shadowTable_[index] != null) {
- if (this.currentCellCursor == null) {
- // We haven't started moving through the table yet
- this.currentCellCursor = [index, 0];
- } else {
- this.currentCellCursor = [index, this.currentCellCursor[1]];
- }
- return true;
- } else {
- return false;
- }
-};
-
-
-/**
- * Moves to the column at the specified index in the table. Updates the cell
- * cursor.
- * @param {number} index The index of the required column.
- * @return {boolean} Either:
- * 1) True if the index is valid and the update has been made.
- * 2) False if the index is not valid (either less than 0 or greater than
- * the number of rows in the table).
- */
-cvox.TraverseTable.prototype.goToCol = function(index) {
- if (index < 0 || index >= this.colCount) {
- return false;
- }
- if (this.currentCellCursor == null) {
- // We haven't started moving through the table yet
- this.currentCellCursor = [0, index];
- } else {
- this.currentCellCursor = [this.currentCellCursor[0], index];
- }
- return true;
-};
-
-
-/**
- * Moves to the cell at the specified index <i, j> in the table. Updates the
- * cell cursor.
- * @param {Array<number>} index The index <i, j> of the required cell.
- * @return {boolean} Either:
- * 1) True if the index is valid and the update has been made.
- * 2) False if the index is not valid (either less than 0, greater than
- * the number of rows or columns in the table, or there is no cell
- * at that location).
- */
-cvox.TraverseTable.prototype.goToCell = function(index) {
- if (((index[0] < this.rowCount) && (index[0] >= 0)) &&
- ((index[1] < this.colCount) && (index[1] >= 0))) {
- var cell = this.shadowTable_[index[0]][index[1]];
- if (cell != null) {
- this.currentCellCursor = index;
- return true;
- }
- }
- return false;
-};
-
-
-/**
- * Moves to the cell at the last index in the table. Updates the cell cursor.
- * @return {boolean} Either:
- * 1) True if the index is valid and the update has been made.
- * 2) False if the index is not valid (there is no cell at that location).
- */
-cvox.TraverseTable.prototype.goToLastCell = function() {
- var numRows = this.shadowTable_.length;
- if (numRows == 0) {
- return false;
- }
- var lastRow = this.shadowTable_[numRows - 1];
- var lastIndex = [(numRows - 1), (lastRow.length - 1)];
- var cell =
- this.shadowTable_[lastIndex[0]][lastIndex[1]];
- if (cell != null) {
- this.currentCellCursor = lastIndex;
- return true;
- }
- return false;
-};
-
-
-/**
- * Moves to the cell at the last index in the current row of the table. Update
- * the cell cursor.
- * @return {boolean} Either:
- * 1) True if the index is valid and the update has been made.
- * 2) False if the index is not valid (there is no cell at that location).
- */
-cvox.TraverseTable.prototype.goToRowLastCell = function() {
- var currentRow = this.currentCellCursor[0];
- var lastIndex = [currentRow, (this.shadowTable_[currentRow].length - 1)];
- var cell =
- this.shadowTable_[lastIndex[0]][lastIndex[1]];
- if (cell != null) {
- this.currentCellCursor = lastIndex;
- return true;
- }
- return false;
-};
-
-
-/**
- * Moves to the cell at the last index in the current column of the table.
- * Update the cell cursor.
- * @return {boolean} Either:
- * 1) True if the index is valid and the update has been made.
- * 2) False if the index is not valid (there is no cell at that location).
- */
-cvox.TraverseTable.prototype.goToColLastCell = function() {
- var currentCol = this.getCol();
- var lastIndex = [(currentCol.length - 1), this.currentCellCursor[1]];
- var cell =
- this.shadowTable_[lastIndex[0]][lastIndex[1]];
- if (cell != null) {
- this.currentCellCursor = lastIndex;
- return true;
- }
- return false;
-};
-
-
-/**
- * Resets the table cursors.
- *
- */
-cvox.TraverseTable.prototype.resetCursor = function() {
- this.currentCellCursor = null;
-};